diff --git a/features/cooked-mcp/api-client.feature.yaml b/features/cooked-mcp/api-client.feature.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f06bba2516b15d838cd1732c9a2b32b7b01e75e0 --- /dev/null +++ b/features/cooked-mcp/api-client.feature.yaml @@ -0,0 +1,37 @@ +feature: + name: api-client + product: cooked-mcp + description: Call Cooked's customer API predictably while hiding HTTP details from MCP tools. + +components: + REQUESTS: + requirements: + 1: JSON Cooked requests send Content-Type application/json when they have a body. + 2: JSON Cooked requests send Accept application/json. + 3: Cooked request paths are built without allowing malformed path joins. + 4: Cooked query parameters are URL-encoded. + 5: User-scoped Cooked API paths use the authenticated username from the login response. + ERRORS: + requirements: + 1: Cooked API error responses are converted into concise user-facing MCP tool errors. + 2: Cooked API errors include the HTTP status when available. + 3: Cooked API errors include Cooked's error code when available. + 4: Cooked API errors include Cooked's error message when available. + 5: Cooked API error handling preserves enough detail for the agent to choose a follow-up action. + 6: Cooked API error handling does not expose session cookies or credentials. + RESPONSES: + requirements: + 1: Cooked API responses are decoded and validated for fields used by MCP tools before formatting. + 2: Unknown fields in Cooked API responses do not break otherwise valid tool calls. + 3: Unexpected Cooked response shapes are reported as recoverable tool errors when possible. + +constraints: + LIMITS: + requirements: + 1: Cooked response bodies read for error reporting are bounded. + 2: List-like MCP tool text responses are summarized instead of returning unbounded result sets. + 3: When a response is truncated or paginated, the tool response says how to fetch more results. + VERIFICATION: + requirements: + 1: API client behaviour is verified with fake Cooked HTTP servers. + 2: API client tests do not contact the real Cooked service. diff --git a/features/cooked-mcp/authentication.feature.yaml b/features/cooked-mcp/authentication.feature.yaml new file mode 100644 index 0000000000000000000000000000000000000000..7851f7d4f68da56a1867ae2a6601208e5769121d --- /dev/null +++ b/features/cooked-mcp/authentication.feature.yaml @@ -0,0 +1,62 @@ +feature: + name: authentication + product: cooked-mcp + description: Authenticate to Cooked with user credentials and maintain the Cooked session safely. + +components: + CREDENTIALS: + requirements: + 1: The server accepts the Cooked username from COOKED_LOGIN_USERNAME. + 2: The server accepts the Cooked password from COOKED_LOGIN_PASSWORD. + 3: The server accepts the Cooked username from TOML user.name. + 4: The server accepts the Cooked password from TOML user.password. + 5: The server accepts a command for producing the Cooked username from TOML user.name_cmd. + 6: The server accepts a command for producing the Cooked password from TOML user.password_cmd. + 7: A credential command returns the credential on stdout. + 7-1: Trailing newlines are trimmed from credential command stdout. + 7-2: Empty credential command output is rejected. + 8: The server rejects ambiguous TOML credential configuration for a single credential field. + 8-1: user.name and user.name_cmd are mutually exclusive. + 8-2: user.password and user.password_cmd are mutually exclusive. + 9: The server reports missing username or password configuration before serving. + 10: The server obtains Cooked session cookies by logging in with configured username and password credentials. + 11: COOKED_LOGIN_USERNAME takes precedence over TOML user.name and user.name_cmd. + 12: COOKED_LOGIN_PASSWORD takes precedence over TOML user.password and user.password_cmd. + 13: Browser-provided Cooked session cookies are not supported in this version. + 14: Social-login and passwordless Cooked accounts are not supported in this version. + 15: Credential command fields are TOML strings executed as shell commands. + 16: Credential commands must exit successfully within 5 seconds. + 17: Credential command stdout read by the server is capped at 64 KiB. + 18: Credential command stdout is trimmed of leading and trailing ASCII whitespace before use. + 19: Credential command stderr is not included in logs or user-facing errors. + 20: Non-zero credential command exits are reported as credential resolution failures without command output. + LOGIN: + requirements: + 1: The server logs in to Cooked with the configured username and password when an authenticated request needs a session. + 2: Login sends JSON credentials to Cooked's public login endpoint. + 3: Successful login stores response cookies in the session cookie jar. + 4: Failed login is reported as a user-facing authentication failure. + 5: Successful login records the authenticated username returned by Cooked. + 6: User-scoped Cooked API requests use the authenticated username returned by Cooked. + SESSION: + requirements: + 1: Authenticated Cooked API requests use an HTTP cookie jar for session cookies. + 2: Authenticated Cooked API requests do not manually construct Cookie headers. + 3: If Cooked rejects a session with 401, the server discards the stored session cookies. + 4: After a 401, the server retries login once. + 5: After a successful retry login, the server retries the original Cooked request once. + 6: If the retried request is rejected, the server reports the authentication failure without another retry. + 7: The Cooked session cookie jar is process-local and in-memory in this version. + 8: The server does not persist Cooked session cookies to disk. + 9: Restarting the server obtains a fresh session by logging in again with configured username and password credentials. + +constraints: + SECRETS: + requirements: + 1: The server never logs usernames, passwords, session cookies, credential command stdout, or credential command stderr. + 2: Credential command execution has a bounded runtime. + 3: Authentication error messages do not include credential values. + VERIFICATION: + requirements: + 1: Authentication behaviour is verified with fake Cooked HTTP servers. + 2: Authentication tests do not contact the real Cooked service. diff --git a/features/cooked-mcp/recipes.feature.yaml b/features/cooked-mcp/recipes.feature.yaml new file mode 100644 index 0000000000000000000000000000000000000000..1418163bc9fd7831c302c117c29d6d9e48219444 --- /dev/null +++ b/features/cooked-mcp/recipes.feature.yaml @@ -0,0 +1,81 @@ +feature: + name: recipes + product: cooked-mcp + description: Let an agent find, read, preview, save, update, import, and delete Cooked recipes through task-oriented tools. + +components: + READ: + requirements: + 1: The read tool lists saved recipes when its target is recipes and no search query is provided. + 2: The read tool searches saved recipes when its target is recipes and a search query is provided. + 3: Recipe list and search results include recipe IDs and titles. + 4: Recipe list and search results omit thumbnails. + 5: The read tool reads a single recipe when its target is recipe and a recipe ID is provided. + 6: A single recipe read includes recipe metadata needed for agent decisions. + 6-1: A single recipe read includes the recipe title when Cooked provides it. + 6-2: A single recipe read includes owner or edit-permission information when Cooked provides it. + 7: A single recipe read includes recipe content. + 8: A single recipe read includes portions. + 9: Recipe reads omit image URLs unless a future feature explicitly exposes them. + PREVIEW_TEXT: + requirements: + 1: The preview_recipe_text tool previews raw recipe text without saving a recipe. + 1-1: Previewing raw recipe text requires a recipe title. + 1-2: Previewing raw recipe text requires the raw recipe text. + 2: The preview_recipe_text tool returns the preview title. + 3: The preview_recipe_text tool returns preview markdown. + 4: The preview_recipe_text tool returns preview portions. + SAVE: + requirements: + 1: The save_recipe tool saves a recipe from raw text. + 1-1: Saving raw recipe text requires a recipe title. + 1-2: Saving raw recipe text requires raw recipe text. + 2: The save_recipe tool saves a recipe from prepared markdown and portions. + 2-1: Saving prepared markdown requires a recipe title. + 2-2: Saving prepared markdown requires recipe markdown. + 2-3: Saving prepared markdown requires portions. + 3: The save_recipe tool imports a recipe from a URL. + 3-1: Importing a recipe from a URL requires a URL. + 4: When URL import creates a saved recipe immediately, save_recipe returns the new recipe ID. + 5: When URL import creates an extraction draft, save_recipe returns the draft ID and draft content for review. + 6: URL import drafts are not automatically saved. + 7: The save_recipe tool saves a reviewed extraction draft. + 7-1: Saving a reviewed extraction draft requires a draft ID. + 7-2: Saving a reviewed extraction draft requires reviewed markdown. + 7-3: Saving a reviewed extraction draft requires portions. + 8: The save_recipe tool updates an existing saved recipe. + 8-1: Updating an existing saved recipe requires a recipe ID. + 8-2: Updating an existing saved recipe requires recipe markdown. + 8-3: Updating an existing saved recipe requires portions. + 9: Recipe save and update results include the affected recipe ID. + 10: Recipe save requests use explicit source values. + 10-1: The recipe save source values are raw_text, prepared, url, draft, and existing. + 11: Saving raw recipe text hides Cooked's preview-then-save sequence behind one tool call. + 12: URL import draft responses include draft portions when Cooked provides them. + 13: MCP recipe markdown maps to Cooked's description field. + 14: When URL import returns an extraction draft, save_recipe fetches the draft metadata and content before returning to the agent. + 15: URL import draft responses include draft_id, title when available, markdown, and portions when available. + 16: The server never saves an extraction draft unless save_recipe is called with source draft and an explicit draft ID. + DELETE: + requirements: + 1: The delete_recipe tool deletes a saved recipe by recipe ID. + 2: Recipe deletion reports which recipe ID was deleted. + +constraints: + PAGINATION: + requirements: + 1: Recipe list and search requests accept a page number. + 2: Recipe list requests accept a result limit that maps to Cooked's page-count parameter. + 3: Recipe search requests accept a result limit for client-side response truncation. + 4: Recipe list and search result limits default to 10. + 5: Recipe list and search result limits are capped at 30. + 6: Recipe list and search responses tell the agent when more pages may be available. + SAFETY: + requirements: + 1: Draft-saving requires an explicit draft ID. + 2: Existing recipe updates require an explicit recipe ID. + 3: Recipe deletion requires an explicit recipe ID. + VERIFICATION: + requirements: + 1: Recipe behaviour is verified with fake Cooked HTTP servers. + 2: Recipe tests do not contact the real Cooked service. diff --git a/features/cooked-mcp/server.feature.yaml b/features/cooked-mcp/server.feature.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f450c562cd985ff5b125f7656faec4041e979ff2 --- /dev/null +++ b/features/cooked-mcp/server.feature.yaml @@ -0,0 +1,63 @@ +feature: + name: server + product: cooked-mcp + description: Start Cooked as a single-user MCP server over stdio or local HTTP. + +components: + IDENTITY: + requirements: + 1: The MCP server identifies itself as Cooked. + 2: The command starts the MCP server without requiring subcommands. + TRANSPORT: + requirements: + 1: The server starts with stdio transport by default. + 2: The server accepts an explicit transport selection through a --transport flag. + 2-1: The --transport stdio value selects stdio transport. + 2-2: The --transport http value selects Streamable HTTP. + 3: HTTP transport listens on a configurable address. + 3-1: HTTP transport defaults to 127.0.0.1:8123. + 4: The server rejects unknown transport selections before serving. + 5: HTTP transport serves MCP requests at /mcp. + CONFIG: + requirements: + 1: The server loads configuration from environment variables. + 2: The server loads configuration from a TOML configuration file. + 3: Environment variables take precedence over TOML configuration values for the same setting. + 4: The Cooked base URL defaults to https://cooked.wiki. + 4-1: COOKED_BASE_URL configures the Cooked base URL. + 4-2: TOML cooked.base_url configures the Cooked base URL. + 5: The server validates configured HTTP URLs before serving. + 6: The server can be pointed at a specific TOML configuration file with --config. + 7: If no TOML configuration path is supplied, the server loads config.toml from $XDG_CONFIG_HOME/cooked-mcp. + 7-1: If XDG_CONFIG_HOME is unset, the server loads config.toml from ~/.config/cooked-mcp. + 8: HTTP transport address is configurable with --http-addr. + 8-1: COOKED_MCP_HTTP_ADDR configures the HTTP transport address. + 8-2: TOML mcp.http_addr configures the HTTP transport address. + +constraints: + SINGLE_USER: + requirements: + 1: The server supports exactly one configured Cooked account per process. + 2: MCP requests do not provide or select Cooked usernames. + 3: Multi-account routing, per-client users, and per-request Cooked credentials are out of scope. + SECURITY: + requirements: + 1: HTTP transport refuses non-loopback listeners unless MCP HTTP authentication is configured. + 2: HTTP authentication secrets are never logged. + 3: HTTP transport supports bearer-token authentication. + 4: HTTP bearer tokens can be configured through COOKED_MCP_HTTP_TOKEN. + 5: HTTP bearer tokens can be configured through TOML mcp.http_token or mcp.http_token_cmd. + 6: HTTP bearer authentication uses the Authorization header with the Bearer scheme. + 7: When an HTTP token is configured, every MCP HTTP request must provide the matching bearer token. + 8: TOML mcp.http_token and mcp.http_token_cmd are mutually exclusive. + 9: COOKED_MCP_HTTP_TOKEN takes precedence over TOML mcp.http_token and mcp.http_token_cmd. + 10: TOML mcp.http_token_cmd follows the same command execution, trimming, timeout, and secrecy rules as Cooked credential commands. + 11: Empty HTTP bearer tokens are rejected before serving. + COMPATIBILITY: + requirements: + 1: The server does not require MCP resource support from clients. + 2: Every operation described by the cooked-mcp feature specs is available through MCP tools. + 3: Agents do not need to call Cooked REST endpoints directly. + VERIFICATION: + requirements: + 1: Startup and configuration behaviour is verified without contacting the real Cooked service. diff --git a/features/cooked-mcp/shopping-list.feature.yaml b/features/cooked-mcp/shopping-list.feature.yaml new file mode 100644 index 0000000000000000000000000000000000000000..beb09fc076b9694b970ec5c2113ed4ccda387edd --- /dev/null +++ b/features/cooked-mcp/shopping-list.feature.yaml @@ -0,0 +1,69 @@ +feature: + name: shopping-list + product: cooked-mcp + description: Let an agent read and change the Cooked shopping list without needing to understand Cooked's REST update semantics. + +components: + READ: + requirements: + 1: The read tool reads the shopping list when its target is shopping_list. + 2: Shopping-list reads include aisle names. + 3: Shopping-list reads include aisle IDs. + 4: Shopping-list reads include product group IDs. + 5: Shopping-list reads include product names. + 6: Shopping-list reads include quantities when Cooked provides them. + 7: Shopping-list reads include selected state. + 8: Shopping-list reads include each product group's containing aisle ID. + 9: Shopping-list reads include recipe IDs associated with the list when Cooked provides them. + ADD: + requirements: + 1: The change_shopping_list tool adds plain-text ingredients to the shopping list. + 2: Adding ingredients accepts one or more newline-separated ingredients. + 3: Adding ingredients may associate the ingredients with a saved recipe ID or import draft ID. + 4: Adding ingredients reports how many ingredients Cooked added. + UPDATE_ITEM: + requirements: + 1: The change_shopping_list tool updates a shopping-list product group. + 2: Updating a product group accepts partial changes from the agent. + 3: The server reads the current shopping list before sending a product-group update when needed to preserve omitted fields. + 4: Updating a product group preserves omitted editable fields. + 5: Updating a product group reports the product group ID that changed. + 6: Updating a product group requires an explicit product group ID. + 7: Updating a missing product group reports a user-facing tool error. + 8: Partial product-group updates preserve the current name, quantity, aisle ID, and selected state unless replacements are provided. + 9: If update_item omits any editable field, the server fills omitted name, quantity, aisle ID, and selected values from the current shopping list before calling Cooked. + SELECTION: + requirements: + 1: The change_shopping_list tool replaces the complete selected product-group set. + 2: The change_shopping_list tool adds product groups to the selected set without requiring the agent to provide the complete selected set. + 3: The change_shopping_list tool removes product groups from the selected set without requiring the agent to provide the complete selected set. + 4: The server reads the current shopping list before add or remove selection changes. + 5: Selection changes report the resulting selected product-group IDs. + 6: Selection changes use explicit replace_selection, add_selection, and remove_selection actions. + 7: Selection changes require product group IDs. + 8: replace_selection sends exactly the provided product group IDs as the full selected set. + 9: add_selection reads the current list, unions the provided product group IDs with the current selected set, then sends the full selected set. + 10: remove_selection reads the current list, subtracts the provided product group IDs from the current selected set, then sends the full selected set. + REMOVE: + requirements: + 1: The change_shopping_list tool removes one or more product groups by ID. + 2: Removing product groups reports the removed product-group IDs. + CLEAR: + requirements: + 1: The change_shopping_list tool clears all shopping-list items. + 2: Clearing the shopping list reports that the list was cleared. + +constraints: + ACTIONS: + requirements: + 1: Shopping-list mutations use explicit action values. + 2: Unknown shopping-list actions are rejected before calling Cooked. + SAFETY: + requirements: + 1: Removing product groups requires at least one product group ID. + 2: Clearing the shopping list is treated as destructive. + 3: Clearing the shopping list requires the explicit clear action. + VERIFICATION: + requirements: + 1: Shopping-list behaviour is verified with fake Cooked HTTP servers. + 2: Shopping-list tests do not contact the real Cooked service. diff --git a/features/cooked-mcp/tools.feature.yaml b/features/cooked-mcp/tools.feature.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f71ccdcddd267d06e87571ec6f8526dda17aa0f7 --- /dev/null +++ b/features/cooked-mcp/tools.feature.yaml @@ -0,0 +1,86 @@ +feature: + name: tools + product: cooked-mcp + description: Expose Cooked through a small LLM-oriented MCP tool surface instead of a REST-shaped endpoint mirror. + +components: + SURFACE: + requirements: + 1: The server exposes a read tool named read. + 2: The server exposes a recipe text preview tool named preview_recipe_text. + 3: The server exposes a recipe save tool named save_recipe. + 4: The server exposes a recipe deletion tool named delete_recipe. + 5: The server exposes a shopping-list mutation tool named change_shopping_list. + 6: The server does not expose one MCP tool per Cooked API endpoint. + 7: The server does not expose MCP resources. + 8: The read tool supports recipe lists, single recipes, and the shopping list through explicit target values. + READ_TOOL: + requirements: + 1: The read tool target values are recipes, recipe, and shopping_list. + 2: The read tool accepts recipe_id when target is recipe. + 3: The read tool requires recipe_id when target is recipe. + 4: The read tool accepts query when target is recipes. + 5: The read tool accepts page with default 1 and minimum 1. + 6: The read tool accepts limit with default 10, minimum 1, and maximum 30. + PREVIEW_RECIPE_TEXT_TOOL: + requirements: + 1: The preview_recipe_text tool requires title. + 2: The preview_recipe_text tool requires text. + SAVE_RECIPE_TOOL: + requirements: + 1: The save_recipe source values are raw_text, prepared, url, draft, and existing. + 2: source raw_text requires title and text. + 3: source prepared requires title, markdown, and portions. + 4: source url requires url. + 5: source draft requires draft_id, markdown, and portions. + 6: source existing requires recipe_id, markdown, and portions. + DELETE_RECIPE_TOOL: + requirements: + 1: The delete_recipe tool requires recipe_id. + CHANGE_SHOPPING_LIST_TOOL: + requirements: + 1: The change_shopping_list action values are add, update_item, replace_selection, add_selection, remove_selection, remove, and clear. + 2: action add requires ingredients. + 3: action add accepts recipe_id. + 4: action update_item requires product_group_id. + 5: action update_item accepts name, quantity, aisle_id, and selected. + 6: action replace_selection requires product_group_ids. + 7: action add_selection requires product_group_ids. + 8: action remove_selection requires product_group_ids. + 9: action remove requires product_group_ids. + 10: action clear has no required parameters beyond action. + READ_WRITE_SPLIT: + requirements: + 1: Read-only operations are exposed separately from operations that change Cooked data. + 2: Recipe deletion is exposed separately from recipe saving and updating. + 3: Previewing recipe text is exposed separately from saving recipe text. + RESPONSES: + requirements: + 1: Tool responses include concise text written for an LLM agent. + 2: Tool responses include IDs required for likely follow-up calls. + 3: Tool responses omit thumbnail URLs. + 4: Tool responses avoid dumping raw Cooked API responses. + 5: Tool responses include structured output when supported by the MCP SDK. + 6: Tool errors are returned as user-facing MCP tool errors when the agent can recover. + 7: Tool text and structured outputs omit Cooked session cookies, credentials, credential command output, and HTTP bearer tokens. + ANNOTATIONS: + requirements: + 1: Read tools are annotated as read-only. + 2: Write tools are not annotated as read-only. + 3: Destructive tools are annotated as destructive. + 4: Tools that call Cooked are annotated as open-world. + 5: Every tool includes a human-readable title annotation. + 6: The shopping-list mutation tool is annotated as destructive because some actions remove data. + +constraints: + NAMING: + requirements: + 1: Tool names are concise when prefixed by the MCP client with the server name. + 2: Tool names do not repeat the server name Cooked. + SCHEMA: + requirements: + 1: Tool parameters use explicit enumerations for mode or action fields. + 2: Tool parameters validate bounded numeric inputs. + 3: Tool descriptions state important behavioural limits and follow-up tools. + 4: Tool schemas reject unsupported read targets, recipe save sources, and shopping-list actions before calling Cooked. + 5: The shopping-list mutation tool description distinguishes destructive remove and clear actions from non-destructive actions.