Agent-friendly coffee recipe and feedback dial-in system. MCP server for managing coffee recipes, beans, grinders, drippers, and user feedback.
BrewDial MCP has 13 well-named tools with mostly complete schemas and descriptions. Naming follows verb_noun convention (create_recipe, update_recipe, list_beans, etc.). Descriptions are detailed and domain-specific, averaging ~250 chars, above the 194-char baseline. However, several critical gaps reduce the score: (1) Many parameter descriptions lack explicit constraints (e.g., 'limit' params state 1 - 25 or 1 - 50 but not in all cases); (2) Complex nested objects like 'grind' and 'dripperPortability' have minimal inline documentation despite being central to the domain; (3) No explicit error handling guidance in descriptions (e.g., what happens if a recipe code is invalid?); (4) Output schemas are not documented, responses are inferred from descriptions rather than formally specified. The domain expertise is evident (ROB-611, ROB-612 references, grind specs, dripper portability), but LLM-facing clarity could improve.
Soft-delete or re-status a recipe (ROB-605). Default sets status='archived' so it disappears from the mini-app list/deep links without deleting data. status can also be active/test/superseded/reference.
Record user feedback on a recipe (ratings, actual brew params, quick tags, desired direction, next hints, comment). Feedback is linked to the recipe code and optional bean. Use this to capture dial-in sessions, taste notes, and adjustment suggestions.
Persist a newly generated coffee recipe to BrewDial. It appears in the App-in-Toss mini-app immediately, grouped under its bean. Returns the COF-NNNN code. To map onto an EXISTING bean (avoid duplicate beans), first call brew.find_bean and reuse the matched bean's exact name+roaster in beanSnapshot (or pass beanId). If a near-identical recipe exists it is STILL created and the response includes possibleDuplicateOf as a soft warning (link variants with supersede_recipe). Steps may include atSec/endSec/waterG/pourRateGPerSec; legacy {atSec,waterG,note} steps remain valid. GRIND (ROB-611): prefer a STRUCTURED params.grind object over free text — { target: { brewMethodPosition e.g. "v60 medium-fine", targetDrawdownSec }, perGrinder: [{ grinder, clicks, source: "measured" }], legacyText }. target MUST carry brewMethodPosition OR targetDrawdownSec (microns is advisory only). Put the operator's MEASURED grinder+clicks in perGrinder; first call brew.list_grinders and use the EXACT registry name so the app can convert clicks to other grinders at read time. Keep the original wording in legacyText. A plain string grind is still accepted for legacy/quick entry. DRIPPER (ROB-612): for portability across drippers, set top-level dripperPortability = { origin: { dripper, sizeModel? }, anchors: { ratio, tempC, targetDrawdownSec } } — the app derives per-dripper size match + grind/pour DIRECTION + the 40g bed-overflow warning at read time. Call brew.list_drippers and use the EXACT registry name in origin.dripper. params.doseG drives the bed check, so set it (especially for 40g+ large doses).
Complex nested objects (grind, dripperPortability) lack inline parameter descriptions. LLMs cannot infer the structure of GrindSpec or dripperPortability.anchors without explicit field-level docs.
Output schemas are not documented. Descriptions mention return values (e.g., 'Returns the COF-NNNN code', 'Returns id/name/roaster/origin/process/roastLevel + recipeCount') but formal response schemas are absent. LLMs cannot plan downstream calls without knowing exact field names and types.
No error handling guidance. Descriptions do not explain what happens on invalid input (e.g., invalid recipe code format, non-existent bean ID, duplicate recipe detection). LLMs have no recovery path.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 69 | 2026-07-28+ | v2 |
Search existing beans by name/roaster substring. Call this BEFORE create_recipe to map a new recipe onto an existing bean instead of creating a duplicate. Returns id/name/roaster/origin/process/roastLevel + recipeCount (most recipes first).
Fetch recent recipes and feedback across all beans. Use this to understand the current dial-in activity and trends before creating new recipes.
Fetch a recipe and its feedback context (recent feedback, similar recipes, bean info). Use this to understand a recipe's history and user feedback before updating or superseding it.
List all beans in the registry, sorted by recipe count (most-used first). Use this to explore available beans or to seed a UI dropdown.
List all drippers in the registry. Use this to populate dripper dropdowns or to find the EXACT registry name for dripperPortability.origin.dripper.
List all grinders in the registry. Use this to populate grinder dropdowns or to find the EXACT registry name for params.grind.perGrinder[].grinder.
Mark an old recipe as superseded by a newer one (ROB-609 lineage). Sets old.status=superseded + old.supersededBy=newCode and new.supersedes=oldCode. The old one drops out of the active list while staying reachable via its code.
Update bean attributes (origin, process, roastLevel, notes, flavor categories, acidity, body, agtron range, etc.). Provide the beanId (from find_bean or list_beans) and any fields to change. Attributes are validated and merged; missing fields are left unchanged.
Update global user preferences (likes, dislikes, default brew parameters). Use this to capture user taste preferences and default settings.
Edit an existing recipe in place (ROB-605). Provide the COF-NNNN code and any fields to change; version is bumped automatically. Use this to fix a recipe rather than creating a near-duplicate. params.grind may be upgraded from a legacy string to a structured GrindSpec (see create_recipe / ROB-611).
Parameter constraints inconsistently documented. 'limit' params in find_bean (1 - 25) and list_beans (1 - 50) state ranges, but update_bean_attributes and update_preferences lack field-level constraints entirely. Validation rules should be explicit in descriptions.
Idempotency not declared. Tools like create_recipe, create_feedback, and update_recipe do not state whether repeated calls with identical input produce the same result or side effects. Agents need this to decide retry safety.