spec: define Cooked MCP surface

Amolith created

Change summary

features/cooked-mcp/api-client.feature.yaml     | 37 ++++++++
features/cooked-mcp/authentication.feature.yaml | 62 +++++++++++++
features/cooked-mcp/recipes.feature.yaml        | 81 +++++++++++++++++
features/cooked-mcp/server.feature.yaml         | 63 +++++++++++++
features/cooked-mcp/shopping-list.feature.yaml  | 69 +++++++++++++++
features/cooked-mcp/tools.feature.yaml          | 86 +++++++++++++++++++
6 files changed, 398 insertions(+)

Detailed changes

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.

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.

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.

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.

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.

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.