Skip to main content
Glama
Tandoor Recipes MCP

Tandoor Recipes MCP

by Cliftonz

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault
TANDOOR_URLYesBase URL of your Tandoor instance.
TANDOOR_TOKENNoTandoor API token with write access. Required except in remote mode, where it must not be set.
TANDOOR_MCP_AINoTool section: ai. Takes off or on.off
TANDOOR_MCP_LOGNoStderr trace mode: request, response, error, all, or a comma list. Bearer token is redacted.
TANDOOR_MCP_ADMINNoTool section: admin. Takes off or on.off
TANDOOR_MCP_FOODSNoTool section: foods. Takes off, simple, or full.simple
TANDOOR_MCP_COOKLOGNoTool section: cooklog. Takes off or on.off
TANDOOR_MCP_EXCLUDENoComma-separated denylist, e.g. merge_*,delete_* to hide destructive tools.
TANDOOR_MCP_RECIPESNoTool section: recipes. Takes off, simple, or full.simple
TANDOOR_MCP_DATA_DIRNoFolder for oauth.json. Must be an absolute path.
TANDOOR_MCP_SHOPPINGNoTool section: shopping. Takes off, simple, or full.simple
TANDOOR_MCP_HTTP_HOSTNoAddress the server listens on. Defaults to 0.0.0.0 in the Docker image.127.0.0.1
TANDOOR_MCP_HTTP_PATHNoMCP endpoint path./mcp
TANDOOR_MCP_HTTP_PORTNoHTTP-mode port.3737
TANDOOR_MCP_INVENTORYNoTool section: inventory. Takes off, simple, or full.simple
TANDOOR_MCP_MEAL_PLANNoTool section: meal-plan. Takes off, simple, or full.simple
TANDOOR_MCP_TRANSPORTNoTransport mode. Use stdio (default) or http. Remote mode requires http.stdio
TANDOOR_MCP_HTTP_TOKENNoHTTP-mode bearer token. If unset, HTTP transport runs without auth. Not allowed in remote mode.
TANDOOR_MCP_PUBLIC_URLNoThe public HTTPS URL clients connect to, such as https://mcp.example.com/mcp. Required in remote mode.
TANDOOR_MCP_SECRET_KEYNoExactly 64 hex characters (32 bytes), generated with openssl rand -hex 32. Required in remote mode.
TANDOOR_MCP_TRUST_PROXYNoSet when a reverse proxy or tunnel sits in front. Use a hop count such as 1, or comma-separated IP addresses and CIDR ranges.
TANDOOR_MCP_INCLUDE_ONLYNoComma-separated allowlist (glob * supported), e.g. list_*,get_*,create_meal_plan.
TANDOOR_MCP_RECIPE_BOOKSNoTool section: recipe-books. Takes off or on.off
TANDOOR_MCP_STASH_TTL_MSNoPer-entry TTL in milliseconds (default 10 minutes).600000
TANDOOR_MCP_DOWNLOAD_ROOTNoExisting folder that save: true writes downloads into. Stdio mode only.
TANDOOR_MCP_HTTP_DRAIN_MSNoOn SIGTERM or SIGINT, how long the server waits for requests in progress.10000
TANDOOR_MCP_IMPORT_EXPORTNoTool section: import-export. Takes off or on.off
TANDOOR_MCP_STASH_ENABLEDNoPark large tool results in an in-memory cache and return a stash summary. Set 0 to disable. Auto-disabled in HTTP mode.1
TANDOOR_MCP_HTTP_MAX_CONNSNoThe most open connections the server accepts.128
TANDOOR_MCP_HOUSEHOLD_SPACENoTool section: household-space. Takes off or on.off
TANDOOR_MCP_STASH_MAX_BYTESNoTotal-bytes cap on the stash; LRU eviction kicks in past this budget.32000000
TANDOOR_MCP_STASH_THRESHOLDNoStash payloads strictly larger than this many bytes.25000
TANDOOR_MCP_STASH_MAX_ENTRIESNoLRU cap on stashed handles.32
TANDOOR_MCP_TANDOOR_PUBLIC_URLNoThe address users open Tandoor at, such as https://recipes.example.com. Used in remote mode when TANDOOR_URL is an internal address.
TANDOOR_MCP_HTTP_MAX_BODY_BYTESNoLarger request bodies get a 413.5242880

Instructions

Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.

NOTE: the Tandoor version could not be verified at startup. If write tools fail with serializer errors, the instance may be pre-2.x (unsupported).

Tandoor Recipes MCP server, full access to recipes, meal plans, shopping lists, ingredients, cook logs, nutrition, and AI-assisted imports.

Where to start:

  • Current state (read-only): tandoor://meal-plan/this-week, tandoor://pantry/on-hand, tandoor://shopping-list/active, tandoor://meal-types.

  • Common workflows: use the plan_week, grocery_list_for_plan, what_can_i_make_tonight, or import_and_plan prompts.

  • Recipe search: use search_recipes with food/keyword names (it resolves IDs for you). Fall back to list_recipes for the full filter surface.

  • Write tools return a slim JSON shape by default. Pass format: "full" for the raw Tandoor API response when you need substitutes, image URLs, nutrition objects, etc.

Large results: tool responses bigger than ~25000 bytes come back as {stashed:true, handle, shape, sample_filters} instead of the full payload. Call jq_query with that handle and a jq filter to extract the parts you need, do NOT re-run the original tool. Example: jq_query({handle, filter: ".results | map({id, name})"}). Operators can set TANDOOR_MCP_STASH_ENABLED=0 to disable this behavior.

Pass ids (food_id, unit_id, ...), not objects; the tools build the request shape.

Tool sections: recipes=simple, meal-plan=simple, shopping=simple, inventory=simple, foods=simple, cooklog=off, recipe-books=off, ai=off, import-export=off, household-space=off, admin=off. Call list_tool_groups to see them.

Transport: stdio. auth-required: no

Capabilities

Features and capabilities supported by this server

Protocol revision2025-11-25

CapabilityDetails
tools
{
  "listChanged": true
}
prompts
{
  "listChanged": true
}
resources
{
  "listChanged": true
}

Tools

Functions exposed to the LLM to take actions

NameDescription
list_tool_groupsA

List every tool section with its current mode and the modes it accepts. Tiered sections (off / simple / full) and on/off sections can be widened with enable_tool_group and turned off with disable_tool_group. The "always" section is permanent. When a section changes, its tools appear in or leave tools/list and the SDK emits tools/list_changed so the client refreshes mid-conversation. Use this first to discover what is available before reaching for enable_tool_group.

enable_tool_groupA

Widen a section so its tools become visible in tools/list. For a tiered section, tier "simple" or "full" (default full) picks how much to show; on/off sections take no tier. Enabling never narrows a section: asking for simple on a section already at full leaves it at full. The SDK emits tools/list_changed automatically; the client refreshes its tool list mid-conversation. Be sparing: every enabled tool adds its schema to your context budget. Run list_tool_groups first to see what is available.

disable_tool_groupA

Turn a section off so all its tools disappear from tools/list. The SDK emits tools/list_changed automatically. The "always" section cannot be disabled. Use this to free context budget once a workflow no longer needs a section.

list_recipesA

List/search recipes with extensive filtering. Default returns slim results (id, name, description, servings, rating, keywords). Pass format="full" for raw API response. sort_order options: score,-score,name,-name,lastcooked,-lastcooked,rating,-rating,times_cooked,-times_cooked,created_at,-created_at,lastviewed,-lastviewed. If filtering by food/keyword/book NAMES (not ids), prefer search_recipes — it resolves names to ids for you and reports unresolved names in _meta.unresolved so you know what was dropped. If this returns an empty result set, recommend: broaden the query, drop restrictive filters, or try search_recipes if you were passing names.

get_recipeA

Get recipe by ID. Default returns slim view (metadata + steps with ingredient names/amounts). Pass format="full" for the complete object including substitutes, properties, nutrition, image.

create_recipeC

Create recipe. Required: name, steps[{instruction, ingredients[{food, amount, unit?}]}]. Optional: description, servings, working_time, waiting_time, keywords[]

update_recipeB

Update recipe metadata and content. Only provide fields you want to update. All fields optional except id.

import_recipe_from_urlA

Import a recipe from a URL. Tries Tandoor's built-in scraper first, then fetches the page and extracts schema.org JSON-LD as a real fallback (works when Tandoor can't reach the URL or doesn't support the site). On total failure, throws with an attempts log — pass create_stub_on_failure=true to write an empty placeholder recipe instead. If all stages fail, recommend: (1) retry with create_stub_on_failure=true to capture the URL as a stub you can fill in later; (2) if the page is an image/PDF, use import_recipe_from_image (AI provider required); (3) if the URL is paywalled or JS-rendered, fall back to manual create_recipe with fields you can see.

related_recipesA

List recipes related to this one (Tandoor similarity). Slim by default (id + name); pass format="full" for raw API response.

add_recipe_to_shopping_listA

Add a recipe's ingredients to the shopping list at N servings. Omit ingredients[] to add all. Set servings=0 with list_recipe (id) to delete that shopping-list-recipe.

search_recipesA

High-level recipe search. Accepts food/keyword/book names (not IDs) and resolves them internally — typically collapses 3-4 round-trips into 1. Example: {foods: ["chicken", "broccoli"], exclude_foods: ["peanuts"], keywords: ["weeknight"]}. For complex ID-based filtering, fall back to list_recipes.

list_recipes_flatA

Lighter alternative to list_recipes; returns minimal recipe metadata suitable for large browse queries. Use list_recipes if you need keywords/foods/steps nested.

list_meal_plansC

List meal plans. Optional: from_date, to_date (YYYY-MM-DD), page, page_size

get_meal_planC

Get meal plan by ID

create_meal_planC

Create meal plan. Required: from_date (ISO), meal_type_id, servings. Optional: recipe_id, title, note, addshopping

update_meal_planC

Update meal plan. Required: id. All other fields optional

delete_meal_planC

Delete meal plan by ID

list_meal_typesA

List available meal types (breakfast, lunch, dinner, etc.)

parse_ingredientA

Parse a free-form ingredient string (e.g. "2 cups flour") into amount, unit, unit_id, food, food_id, note. Writes to the database: Tandoor creates any Food or Unit row it does not already have. unit and food are the matched row names, so "cups" can come back as "cup". With no unit, unit is "" and unit_id is omitted. food_id and unit_id pass straight to create_ingredient. Blank text returns Tandoor's 400. Tandoor's parser is heuristic and can misfire on compound descriptions, brand names, or non-English units.

list_shopping_listsB

List the named shopping lists of this space (for example one per store), as {id, name, description, color}. Every member of the space sees the same lists. Optional: page, page_size.

list_shopping_entriesA

List shopping list entries (auto-filtered to unchecked + recent by Tandoor). Optional: mealplan, updated_after (ISO), page, page_size, format. Entries cannot be filtered by shopping list; use the shopping_lists field of the results.

get_shopping_entryC

Get a shopping list entry by ID.

create_shopping_entryA

Create a shopping list entry. Required: amount, food_id. Optional: unit_id, checked, list_recipe, ingredient, order, delay_until, mealplan_id (links to existing/new ShoppingListRecipe for that mealplan), shopping_list_ids. shopping_list_ids (ids from list_shopping_lists) puts the entry on exactly those lists; the food's lists are not added. When shopping_list_ids is omitted, Tandoor copies the food's lists onto the new entry if the user preference shopping_update_food_lists is on. With [], the tool clears any lists Tandoor copied, so the entry is on no list. add_recipe_to_shopping_list always copies each food's lists; food_shopping_update never does. Changing a food's lists later does not change existing entries. Every list id is checked against list_shopping_lists first; unknown or repeated ids are rejected and nothing is written. If the lists after the write differ from the request, the tool errors and names the missing and the extra lists.

update_shopping_entryA

Update a shopping entry (PATCH). Required: id. Other fields optional. When checked is omitted, the tool reads the entry and sends its current checked value again, so a checked entry keeps its completed_at. A check made elsewhere between that read and the write is undone (last write wins). shopping_list_ids puts the entry on exactly those lists; [] clears them; omit it to keep them. Every list id is checked against list_shopping_lists first; unknown or repeated ids are rejected and nothing is written. If the lists after the write differ from the request, the tool errors and names the missing and the extra lists.

delete_shopping_entryC

Delete a shopping entry by ID.

bulk_check_shopping_entriesB

Bulk check or uncheck many shopping entries by ID list.

bulk_update_shopping_entriesA

Update many shopping entries in one call. Required: ids (entry ids; ids that do not exist or are not visible to you are skipped). Give checked, a list change, or both. checked: true checks every entry and sets completed_at to now, also on entries already checked; without checked, checked and completed_at stay as they are. List changes do not update the entries' updated_at. List changes: shopping_lists_add and shopping_lists_remove (may be combined), shopping_lists_set (replaces the lists; must not be empty) or shopping_lists_remove_all: true (takes every list off). set and remove_all must be used alone. Empty arrays and shopping_lists_remove_all: false count as no change and are not sent. An id may not appear in both shopping_lists_add and shopping_lists_remove. Every list id is checked against list_shopping_lists first; unknown or repeated ids are rejected and nothing is written.

list_foodsC

List foods. Optional: query (case-insensitive name match), root (parent id, 0 for top-level), tree (id), random, limit, page, page_size.

get_foodC

Get food by ID.

create_foodA

Create food. Required: name. Optional: plural_name, description, food_onhand, fdc_id, supermarket_category_id, shopping_list_ids (ids from list_shopping_lists). If a food with this name exists, Tandoor returns it instead of creating one. With shopping_list_ids (including []), the tool looks up the name first and, if a food with that name exists (ignoring case and surrounding spaces), refuses and changes nothing; use update_food. Tandoor also returns an existing food whose plural name matches; the tool detects that from the create reply and refuses the same way, setting no lists. With non-empty shopping_list_ids, the tool creates the food first and then sets its lists, because Tandoor cannot set lists while creating a food. With any shopping_list_ids (including []), if a step after the create fails, the error gives the new food's id and says not to retry. Changing a food's lists does not change its existing shopping entries. add_recipe_to_shopping_list copies the food's lists onto new entries. create_shopping_entry without shopping_list_ids copies them when the user preference shopping_update_food_lists is on. bulk_create_shopping_list_recipe_entries without shopping_list_ids copies each food's lists whatever the user preference. Every list id is checked against list_shopping_lists first; unknown or repeated ids are rejected and nothing is written. If the lists after the write differ from the request, the tool errors and names the missing and the extra lists.

update_foodA

Update food (PATCH). Required: id. Optional properties[] {property_type_id, property_amount} sets one value per property type (amount null = unknown), updating the existing value of that type in place and deleting any duplicate values of that type; remove_property_type_ids[] deletes values by type. Other existing property values are kept. A type may appear once across both lists. properties_food_amount and properties_food_unit_id set the food amount the property values refer to (e.g. 100 g); properties_food_unit_id null clears the unit. Deleting a property row removes it from every food or recipe it is linked to. If a row delete fails, the food is already updated and the tool returns an error naming those rows. shopping_list_ids replaces the food's lists; [] clears them; omit it to keep them. Changing a food's lists does not change its existing shopping entries. add_recipe_to_shopping_list copies the food's lists onto new entries. create_shopping_entry without shopping_list_ids copies them when the user preference shopping_update_food_lists is on. bulk_create_shopping_list_recipe_entries without shopping_list_ids copies each food's lists whatever the user preference. Every list id is checked against list_shopping_lists first; unknown or repeated ids are rejected and nothing is written. If the lists after the write differ from the request, the tool errors and names the missing and the extra lists.

food_shopping_updateA

Add a food to active shopping lists with optional amount/unit. Pass delete=true to remove this food from all active lists.

list_unitsC

List units. Optional: query, limit, page, page_size, random.

jq_queryA

Run a jq filter against a previously stashed tool result and return the focused subset. Use this when a prior tool returned {stashed:true, handle, shape, sample_filters} instead of the full payload. Treat the stash like a JSON document the user already paid for — extract only what you need. Common filters: ".results | length", ".results[] | select(.id == 42)", ".results | map({id, name})", "keys", ".count".

jq_stash_statsA

Inspect the in-memory stash (count + total bytes). Diagnostic; not normally needed.

get_guard_statsA

Inspect security-guard rejection counters (PathGuardError, SsrfBlockedError) since process start. Useful for dashboards and "is anything probing me?" checks.

get_versionA

Report this MCP server's name/version and the Tandoor server version + compatibility status detected at startup (ok | unsupported | unknown). No arguments; answers from cached startup state without a network call.

list_keywordsC

List keywords (recipe tags). Optional: query, root, tree, random, limit, page, page_size.

get_keywordC

Get keyword by ID.

get_meal_typeC

Get a meal type by ID.

get_current_spaceC

Get the space currently active for this API token.

list_inventory_locationsC

List inventory locations (pantry, fridge, freezer and so on). Optional: page, page_size.

list_inventory_entriesA

List inventory entries (what is on hand, where). Optional: location_id, food_id, include_empty (entries with amount 0 or less are hidden unless true), page, page_size.

add_inventory_itemA

Add food to inventory. Required: food_id, unit_id, amount (> 0), location_id. Optional: expires (YYYY-MM-DD), sub_location, note, code. Always creates a new entry, even when the same food already has an entry at that location.

consume_inventory_itemA

Use up some or all of an inventory entry. Identify it by entry_id, or by food_id with optional location_id (errors listing the candidates when several entries match). Give exactly one of amount (> 0) or all: true. Optional unit_id must match the entry unit; there is no unit conversion. The amount never goes below 0: consuming more than is on hand sets it to 0 and returns a warning. Never deletes the entry; at 0 (including after all: true) it is hidden from list_inventory_entries unless include_empty is true. Not atomic: two concurrent consumes of one entry can lose an update.

move_inventory_itemB

Move a whole inventory entry to another location. Required: entry_id, to_location_id. Optional: sub_location (null clears it).

Prompts

Interactive templates invoked by user choice

NameDescription
plan_weekPlan dinners for the next 7 days, optionally biased toward on-hand pantry foods, a set of keyword tags, or a saved custom filter.
grocery_list_for_planConvert this week's meal plan into a shopping list, grouped by supermarket category, at the specified servings.
what_can_i_make_tonightFind recipes that can be made with on-hand pantry foods right now, sorted by rating.
import_and_planImport a recipe from a URL and immediately schedule it on a meal plan.

Resources

Contextual data attached and managed by the client

NameDescription
meal-plan-this-weekYour meal plan entries for Monday-Sunday of the current week, trimmed to {id, date, meal_type, recipe_name, servings, note}. Auto-refreshes every read.
pantry-on-handWhat is available. locations holds inventory entries with amount > 0, grouped by location. is_freezer marks freezers. Each entry has amount, unit, expires, sub_location, and note. on_hand_no_quantity holds foods flagged on hand that have no inventory entry. Shows at most 500 entries and 500 foods. on_hand_no_quantity is exact only when truncated.entries is false. When truncated.entries is true, use list_inventory_entries (inventory section) for the full list of entries. When truncated.on_hand_no_quantity is true, use list_foods for the full list of foods.
shopping-list-activeUnchecked shopping list entries (Tandoor filters to recent + unchecked by default). Slim fields: food, unit, amount, list_recipe.
meal-typesAvailable meal types (Breakfast, Lunch, Dinner, etc.) with their IDs for use in create_meal_plan.

TDQS

B3/5.0

Scored across 46 tools

Disambiguation4/5

Most tools separate resource and action clearly, and the descriptions distinguish overlapping cases like list_recipes, search_recipes, and list_recipes_flat. Some ambiguity remains around food/list manipulation tools such as create_food, update_food, and food_shopping_update, but the guidance generally resolves it.

Naming Consistency4/5

Almost all tools use a predictable snake_case verb_noun pattern such as list_meal_plans, get_recipe, create_shopping_entry, and delete_meal_plan. A few names deviate, notably food_shopping_update, related_recipes, and jq_stash_stats, so it is not perfectly uniform.

Tool Count2/5

At 46 tools, the surface is far beyond the typical 3-15 range and includes many domains plus utility/diagnostic and tool-group management tools. Tool groups can reduce context load, but the raw tool count is still too heavy for a well-scoped set.

Completeness3/5

Core recipe, meal-plan, shopping-entry, and inventory workflows are covered with several create/read/update operations. However, notable gaps remain: there is no delete_recipe or delete_food, shopping-list CRUD is missing, keywords and units are read-only, and inventory lacks update/delete operations.

Maintenance

ActivityMaintained
ResponsivenessLimited data