
Tandoor Recipe Mcp
io.github.Cliftonz由 GitHub 帳號 Cliftonz 發布v3.4.0更新於 Nov 8, 2026
Recipes, meal plans, shopping lists and more for your local or self-hosted Tandoor instance.
概覽
將助理連接到自架的 Tandoor 實例,用來瀏覽食譜、安排餐點計畫、管理購物清單、庫存與食材。
- 可以做什麼
- 瀏覽、搜尋、讀取、建立、更新與匯入食譜,完整等級還包含步驟、配料、圖片與關鍵字
- 建立、讀取、更新與刪除餐點計畫項目與餐點類型,並支援自動排餐與 iCal 匯出
- 管理購物清單與項目、勾選已購買項目,並把食譜配料或單一食材加入清單
- 管理庫存位置與項目:新增、消耗、移動、更新與刪除
- 列出、建立、更新、合併與刪除食材與單位,並解析配料文字
- 可選擇開啟烹飪紀錄、食譜書、AI 匯入、匯入匯出、家庭空間與管理員等分組
- 適用情境
- 當你自己架設 Tandoor 伺服器,並希望助理在不切換應用的情況下回答食譜問題、調整餐點計畫、產生或勾選購物清單、維護儲藏庫存時使用。
- 執行需求
- 需要可連線的 Tandoor 實例,並在 TANDOOR_URL 填入其基礎位址;還需在 Tandoor 的 Settings、API Tokens 建立 API 權杖並填入 TANDOOR_TOKEN。本機執行 npm 套件需 Node 24 或更新版本,也可改用 Claude Desktop 擴充功能或 Docker 映像。遠端 HTTP 模式需要 HTTPS 公開位址與 64 位十六進位的 TANDOOR_MCP_SECRET_KEY。
安裝
在 SourceWeft 中
- 開啟 儀表板中的 Tandoor Recipe Mcp,將其新增到工作區。
- 為需要使用其工具的對話啟用該服務。
Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。
其他 MCP 客戶端
參照 儲存庫 中的啟動說明。
README
Tandoor Recipes MCP
[npm version] [CI] [OpenSSF Scorecard] [License: MIT]
A Model Context Protocol (MCP) server that gives LLM agents full read/write access to a Tandoor Recipes instance: recipes, meal plans, ingredients, shopping lists, cook logs, nutrition, AI-powered URL/image imports, invite/access tokens, storages, sync, spaces, exports, imports, and more.
Turn "plan the week and make me a grocery list" into one prompt.
New in 3.1.0
- File downloads (#10):
download_recipe_fileanddownload_filereturn a Tandoor file inline, or save it toTANDOOR_MCP_DOWNLOAD_ROOTwithsave: true. See Downloading files. get_external_recipe_file_linkworks again.get_recipe_file_metadatais removed; usedownload_recipe_fileinstead.
New in 3.0.0
- Per-section tool settings replace
TANDOOR_MCP_PROFILE. One env var per section: tiered sections takeoff,simple, orfull, and toggle sections takeofforon. See Tool sections and Migration from 2.x. - Pantry and freezer inventory tools in the
inventorysection, plus a combinedtandoor://pantry/on-handresource. - Households: list, get, create, update, and delete households, and
assign_householdto move user-spaces between them. - Named shopping lists, such as one per store. Foods and shopping entries take
shopping_lists. - Tandoor 2.6 API alignment. Requires Tandoor 2.6.0 or later.
- HTTP transport fix (#12): the HTTP transport now answers every request, not only the first.
- oxlint for linting.
307 tools in total; 46 are visible at startup with the default settings. The CHANGELOG has the full list of changes and breaking changes.
Why this server
- Full Tandoor 2.6 API coverage. 307 typed tools, spanning every domain the Tandoor OpenAPI exposes.
- Resources + prompts, not just tools. Read-only resources like
tandoor://meal-plan/this-weekandtandoor://pantry/on-hand, plus ready-made prompts (plan_week,grocery_list_for_plan,what_can_i_make_tonight). Most Tandoor MCP forks ship tools only. - Slim-by-default responses. A full recipe GET shrinks from ~20k characters to ~2k by stripping substitute trees, readonly timestamps, image URLs, etc. Pass
format: "full"anywhere for the raw Tandoor response. - Per-section tool settings. One env var per section (recipes, meal plan, shopping, and so on) sets it. Tiered sections take
off,simple, orfull. Toggle sections takeofforon. In stdio mode the LLM can turn a hidden section on mid-session withenable_tool_group. See Tool sections. - jq stash for large payloads. Responses larger than 5KB return a stash handle plus a schema summary; the LLM follows up with
jq_queryto project just the fields it needs. - 3-stage URL import with a real fallback. Tries Tandoor's native scraper first, then extracts schema.org JSON-LD from the page directly (works when Tandoor can't reach the URL or doesn't support the site), with an optional stub-on-failure escape hatch.
- AI import from images / PDFs. Multipart upload to Tandoor's
ai-import/endpoint, saves the parsed recipe. - Local or remote. Stdio (default, for Claude Desktop / Claude Code), stateless HTTP (for out-of-Claude callers), or remote mode: a self-hosted server with OAuth sign-in that Claude and ChatGPT connect to as a custom connector.
- Destructive-action safety layer. Every tree-shaped delete has a
preview_*sibling that projects the cascade before it runs; every token-mint tool leads with an ESCALATION SURFACE warning.
Install
Or npx without installing:
Node 24 or later is required (see engines.node in package.json).
Other ways to get it:
- Claude Desktop extension:
tandoor-recipes-mcp.mcpbfrom the latest GitHub release. It needs no Node install. See Claude Desktop, desktop extension. - Docker image:
ghcr.io/cliftonz/tandoor-recipes-mcp, for linux/amd64 and linux/arm64, tagged with each version andlatest. The image starts in remote mode by default. - MCP Registry: listed as
io.github.Cliftonz/tandoor-recipe-mcpin the MCP Registry, with the npm package, the.mcpbfile and the Docker image. - Claude Code plugin and Codex plugin: see Configure.
Configure
- In Tandoor: Settings, API Tokens, Create new token.
- Point your MCP client at the server.
Claude Code, one-shot plugin install
From a terminal, the same two steps are:
Claude Code prompts you for TANDOOR_URL and TANDOOR_TOKEN on install and stores the token securely. No mcpServers JSON editing needed.
Claude Desktop, desktop extension
Use the desktop extension for Claude Desktop Chat and Cowork. It bundles the server and runs on Claude Desktop's built-in Node, so you do not need to install Node.
- Download
tandoor-recipes-mcp.mcpbfrom the latest GitHub release. - Double-click the file. Or, in Claude Desktop, open Settings > Extensions > Advanced settings > Install Extension and select the file.
- Enter your Tandoor URL and API token. Claude Desktop stores the token securely.
- Optional: set the section settings you want.
Claude Desktop, plugin marketplace
Claude Desktop can also add the plugin:
- Open Customize > Plugins > Add > Add marketplace > Add from a repository.
- Enter
Cliftonz/tandoor-recipe-mcp. - Open Customize > Plugins > Discover, select the plugin, and select Add.
This path does not give you a working server in Chat or Cowork. Chat and Cowork do not prompt for plugin settings, so the plugin's Tandoor URL and API token settings stay empty. Chat ignores local MCP servers from plugins. Cowork loads them only when the session runs on your computer. Use the desktop extension or the manual config instead. Use the plugin from the Claude Code CLI.
Claude Desktop / Claude Code, manual config
Edit the Claude Desktop config file:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
For Claude Code, put the same mcpServers block in a project .mcp.json, or use claude mcp add.
Quit and reopen Claude Desktop to load the change. This config runs the server with npx, so Node 24 or later must be on your PATH.
Codex
The ChatGPT desktop app, the Codex CLI, and the Codex IDE extension share one config file, ~/.codex/config.toml. Every Codex path below runs the server with npx, so Node 24 or later must be on your PATH.
Codex, settings
- In the app, open Settings > MCP servers > Add server.
- Choose STDIO.
- Set the command to
npx. - Add the arguments
-yand@cliftonz/tandoor-recipes-mcp. - Add the environment variables
TANDOOR_URLandTANDOOR_TOKEN. Add anyTANDOOR_MCP_*section settings you want. - Select Save, then restart the app.
From a terminal, the same setup is:
codex mcp add writes this block to ~/.codex/config.toml. You can also add it to config.toml by hand:
Codex, plugin
- Open the Plugins page and select Add > Add a marketplace.
- Enter the source
Cliftonz/tandoor-recipe-mcp. - Open the Tandoor plugin's details and select the plus button.
- Restart the app.
From a terminal:
Codex does not prompt for settings on install, and you cannot override a plugin's environment. The plugin passes through only TANDOOR_URL and TANDOOR_TOKEN from the app's environment. An app you start from the Dock usually does not see exports in ~/.zshrc. On macOS, set the variables for your login session and then restart the app:
launchctl setenv does not survive a reboot. It also exposes the token to every app you start in that session.
The Codex plugin does not pass through section settings. To change sections, use saved sections or the settings path.
Recommended Claude Code permissions
The repo ships a .claude/settings.json that auto-allows read tools and prompts for everything else. Copy these into your own project's .claude/settings.json to reduce permission friction:
Transport modes
The server ships two transports. Default is stdio; opt in to HTTP with an env var. HTTP also has a remote mode for Claude and ChatGPT custom connectors.
HTTP mode
The server listens on http://127.0.0.1:${TANDOOR_MCP_HTTP_PORT:-3737}/mcp (POST only for JSON-RPC requests). Any other method on that path gets a 405 with Allow: POST and a JSON-RPC error body. Auth is checked first, so a request without a valid token gets a 401 instead. The server binds to 127.0.0.1 unless you set TANDOOR_MCP_HTTP_HOST. To reach it from another host, put a reverse proxy in front of it, or use remote mode.
If TANDOOR_MCP_HTTP_TOKEN is set, every request must include Authorization: Bearer <token>. Missing or wrong tokens get a 401 with WWW-Authenticate: Bearer.
HTTP-mode constraints: the jq stash and dynamic tool gating both assume a single long-lived session. In HTTP mode both are auto-disabled: enable_tool_group and disable_tool_group throw, and every tool response is inlined rather than stashed. The section settings fix the tool list at startup, so set every section you need before you start the server.
Remote mode
Remote mode runs the server on your own host, next to your Tandoor, with a built-in OAuth sign-in. You add it to Claude or ChatGPT as a custom connector, by its HTTPS URL. It works in Claude on the web, mobile, Desktop chat, Cowork and Claude Code, and in ChatGPT. Each person signs in by pasting their own Tandoor API token, so each person acts in Tandoor as themselves.
Why you host it yourself, and why it is not in a connector directory: every Tandoor is a separate, self-hosted server, so there is no single URL a directory listing could point to. A shared hosted copy would also have to hold everyone's Tandoor tokens. In remote mode the tokens stay on your server, encrypted with your key.
How a connection works:
- The app (Claude or ChatGPT) calls your public URL and gets a
401that points to the server's OAuth metadata. - The app identifies itself with its published client document, or registers itself automatically.
- The app opens the sign-in page in your browser. You paste a Tandoor API token and select Approve.
- The server checks the token against Tandoor, stores it encrypted, and gives the app its own tokens. The app never sees your Tandoor token.
Remote mode settings
Remote mode turns on when TANDOOR_MCP_PUBLIC_URL is set. These settings apply to remote mode, and the HTTP ones also apply to plain HTTP mode.
Header and request timeouts are fixed at 10 and 30 seconds.
Rules and behavior in remote mode:
TANDOOR_TOKENis refused. The server will not start while it is set. Each user brings their own token.TANDOOR_MCP_HTTP_TOKENis refused. It belongs to the shared-password HTTP mode and cannot be combined with remote mode.- Tool sections apply to every connected user. The section settings are read once at startup, and
enable_tool_groupis off in HTTP mode, so everyone sees the same tools. file_pathuploads are refused. The server's disk is not the user's disk. Usefile_urlinstead. Saving downloads withsave: trueis also stdio only.- A read-only Tandoor token works for reads only. Write tools return Tandoor's error. The connection stays.
- Host and Origin are checked. A request must carry the public URL's host in its
Hostheader, orlocalhost,127.0.0.1or[::1]. Anything else gets a403, logged ashost_rejected. A browserOriginheader on the MCP path must equal the public URL's origin, or the request gets a403, logged asorigin_rejected. - Responses are plain JSON. The server does not hold event streams open, so proxies need no streaming or buffering settings.
GETon the MCP path returns405.
Run with Docker
The image is ghcr.io/cliftonz/tandoor-recipes-mcp. It defaults to TANDOOR_MCP_TRANSPORT=http, TANDOOR_MCP_HTTP_HOST=0.0.0.0 and TANDOOR_MCP_DATA_DIR=/data, and runs as the node user.
-
Generate a secret key:
-
Copy the
tandoor-mcpservice and thetandoor-mcp-datavolume fromdocker-compose.example.ymlinto your Tandoor compose file, so both services share a network. -
Set the environment:
TANDOOR_URL: your Tandoor service on the internal network. The example useshttp://web_recipes:8080; replaceweb_recipeswith the name of your Tandoor service.TANDOOR_MCP_TANDOOR_PUBLIC_URL: the address users open Tandoor at, such ashttps://recipes.example.com.TANDOOR_MCP_PUBLIC_URL: your public HTTPS URL, ending in/mcp.TANDOOR_MCP_SECRET_KEY: the key from step 1.TANDOOR_MCP_TRUST_PROXY:"1"when one proxy or tunnel sits in front, as in the example.
-
Start it:
Notes on the example file:
- The
/datavolume holdsoauth.json. Keep the volume across upgrades, or everyone has to reconnect. - The port is published on
127.0.0.1only (127.0.0.1:3737:3737), so only a proxy or tunnel on the same host can reach it. Remove theports:entry if your proxy reaches the container over the Docker network. - Keep
TANDOOR_MCP_HTTP_HOST=0.0.0.0inside the container. Binding a specific address inside the container is not supported: the image's health check calls127.0.0.1, so the container reports unhealthy. Limit exposure with the port mapping instead. stop_grace_period: 15sis longer than the 10-second drain, so requests in progress can finish ondocker compose down.
The sign-in page shows your Tandoor address and links to its /settings/api page for creating a token. With an internal TANDOOR_URL such as http://web_recipes:8080, set TANDOOR_MCP_TANDOOR_PUBLIC_URL to the address users open Tandoor at, so the link opens from a browser.
Without Docker, run the npm package with the same settings:
Expose it over HTTPS
Claude and ChatGPT need a public HTTPS URL. Put a reverse proxy or a tunnel in front of the server. Whatever you use must:
- Forward the whole host, not only
/mcp. The OAuth routes live at the root. - Pass the original
Hostheader through. - Set
X-Forwarded-For, and you setTANDOOR_MCP_TRUST_PROXYto match.
Caddy
Caddy passes Host through unchanged and sets X-Forwarded-For. Set TANDOOR_MCP_TRUST_PROXY=1. If Caddy runs in the same compose network, use reverse_proxy tandoor-mcp:3737.
nginx
proxy_set_header Host $host; is required. Without it, nginx sends the upstream name as Host, and the server answers 403 unless that name is localhost or a loopback address. The log line host_rejected includes a hint about this. Responses are plain JSON, so no SSE or buffering settings are needed. Set TANDOOR_MCP_TRUST_PROXY=1.
Cloudflare Tunnel
A tunnel needs no open port. The example compose file has an optional cloudflared service.
- In the Cloudflare dashboard, go to Networking > Tunnels and select Create a tunnel. Copy the tunnel token.
- Uncomment the
cloudflaredservice and setTUNNEL_TOKENto the token. - In the tunnel's Routes tab, select Add route > Published application. Choose your subdomain and domain, and set Service URL to
http://tandoor-mcp:3737. - Leave HTTP Host Header empty, so the server sees your public hostname.
Set TANDOOR_MCP_TRUST_PROXY=1. You can remove the ports: entry from tandoor-mcp.
Tailscale Funnel
On the host where port 3737 is published on 127.0.0.1:
The public URL is https://<device-name>.<tailnet-name>.ts.net, so set TANDOOR_MCP_PUBLIC_URL to https://<device-name>.<tailnet-name>.ts.net/mcp. Funnel keeps the Host header and sets X-Forwarded-For. Set TANDOOR_MCP_TRUST_PROXY=1. tailscale funnel reset turns it off.
Access gateways
Warning: If you put an access gateway in front, such as Cloudflare Access, Authelia or another forward-auth proxy, it must let these routes through without its own login. Otherwise Claude and ChatGPT cannot connect.
POST /mcp(yourTANDOOR_MCP_HTTP_PATH)GET /.well-known/oauth-protected-resource/mcpGET /.well-known/oauth-authorization-serverPOST /registerPOST /tokenPOST /revokeGETandPOST /authorizePOST /consent
/authorizeand/consentopen in the user's own browser. The others are called by the app's servers. The simplest rule is to bypass the gateway for/mcp,/register,/token,/revoke,/authorize,/consentand/.well-known/*.
Connect a client
Use your public URL, for example https://mcp.example.com/mcp.
Claude
On a Free, Pro, Max or Ultra plan:
- Go to Customize > Connectors.
- Click Add custom connector.
- Enter the URL.
- If the dialog asks for the OAuth client, choose Use Claude's published identity or Register automatically. The server supports both. Do not enter a client ID or secret.
- Click Add. When you connect, the sign-in page opens in your browser.
On a Team or Enterprise plan, an Owner adds it under Organization settings > Connectors: select Add, then Custom, enter the URL, and click Add. Each member then finds it under Customize > Connectors and clicks Connect.
You can also share a link that opens the dialog with the name and URL filled in. Percent-encode the URL:
Owners can use the same parameters on https://claude.ai/admin-settings/connectors. The user still reviews and confirms before anything is added.
Claude Code
Then run /mcp in Claude Code and follow the sign-in steps in your browser.
ChatGPT
- Go to chatgpt.com/plugins.
- Select the plus button, then Add custom MCP server.
- Enter a name and description.
- Under Connection, enter the URL, including
/mcp. - Configure authentication. The server uses OAuth and supports both ways ChatGPT registers itself: a client ID metadata document and dynamic client registration. Review the risk warning and select I understand and want to continue.
- Select Create as a plugin.
Your ChatGPT account or workspace policies decide whether you can add custom MCP servers.
The sign-in page
The page is titled Connect to Tandoor. It names the app that wants access and the Tandoor URL, and shows where you return after you approve. Paste a Tandoor API token into Tandoor API token and select Approve, or Deny.
Create the token on your Tandoor's API settings page, /settings/api. The token decides what the app can do: a read-write token allows changes, a read-only token allows reads only.
If Tandoor rejects the token, the page says so and you can try again. The form expires after 10 minutes, and each IP address gets 10 attempts a minute.
Operate it
- Back up
oauth.jsonand the secret key separately.oauth.jsonin the data folder holds the registered apps, the connections, the encrypted Tandoor tokens and hashes of the tokens the server issued. Anyone with both the file and the key can read the stored Tandoor tokens. - Revoke everyone: stop the server, delete
oauth.json, and start it again. Every user has to reconnect. - Revoke one user's access: delete their API token in Tandoor. Their tool calls then fail in Tandoor.
- Rotate the secret key: the server refuses to start when the key cannot decrypt
oauth.json(cannot read OAuth store). Rotation means stopping the server, deletingoauth.json, setting the new key and starting again. Every user has to reconnect. - Connections end on their own after 30 days without use, or when Tandoor rejects the stored token with a
401. In the second case the server ends every connection that uses that token, and the tool call returns "Tandoor rejected your token. Reconnect this connector to sign in again." along with a sign-in challenge for the app. App registrations that never finish signing in are removed after 24 hours. - Run one server process per data folder. Each process keeps
oauth.jsonin memory and rewrites it on every change, so two processes overwrite each other. - Health check:
GET /healthzreturns{"status":"ok"}without auth. The Docker image checks it every 30 seconds. - Logs go to standard error (
docker compose logs tandoor-mcp). The startup line shows the public URL,auth=oauth, the Tandoor URL and version, and the sections. Request events look like[tandoor-mcp] http reqId=... level=warn event=host_rejected .... Other events includeorigin_rejected,forwarded_header_untrusted(a proxy sentX-Forwarded-ForbutTANDOOR_MCP_TRUST_PROXYis not set),tandoor_check_failed,cimd_fetch_failed,payload_too_largeandshutdown. PRIVACY.md lists what the logs contain. - Rate limits are per IP address: failed access tokens, sign-in form submissions, and the OAuth routes. Behind a proxy, set
TANDOOR_MCP_TRUST_PROXY, or every visitor shares one limit.
Firewall allowlisting
You can limit who reaches the server-to-server routes (/mcp, /.well-known/*, /register, /token, /revoke):
- Claude: Anthropic's outbound IP ranges, listed at platform.claude.com/docs/en/api/ip-addresses. On 2026-11-07 they were
160.79.104.0/21and153.61.208.0/20. - ChatGPT: OpenAI's ranges for connectors, published at openai.com/chatgpt-connectors.json. The list is long and changes; OpenAI recommends fetching it regularly and updating your allowlist automatically.
Do not limit /authorize and /consent this way. They come from each user's own browser. Claude Code and the MCP Inspector also run on the user's computer, so their requests come from the user's address.
The server itself makes outbound requests to your Tandoor, to the app's client document (such as one on claude.ai or chatgpt.com) when an app uses its published identity, and to web addresses users import from.
Test it
Check the health endpoint and the metadata:
Then connect with the MCP Inspector. It runs the sign-in flow in your browser:
Remote mode security
- The sign-in routes are reachable from the internet. Anyone who can reach the URL can start signing in, but only a valid Tandoor token completes it.
- The Tandoor token is the real credential. Whoever signs in with a token gets that token's full Tandoor access.
- The secret key protects the stored tokens. The server stores only hashes of the tokens it issues.
- Removing a connection in Claude or ChatGPT does not delete the Tandoor token. Delete it in Tandoor too.
- Tandoor content is untrusted input to the assistant.
SECURITY.md has the full threat model and how to report a problem.
Environment variables
The other HTTP settings and the remote-mode settings are in Remote mode settings.
INCLUDE_ONLY and EXCLUDE run before the section settings and give finer control ("give me only read tools" or "hide destructive ops from this agent").
Section values can also come from saved sections.
The Claude plugin and the desktop extension pass their settings as CLAUDE_PLUGIN_OPTION_<KEY>, such as CLAUDE_PLUGIN_OPTION_TANDOOR_URL or CLAUDE_PLUGIN_OPTION_RECIPES. A non-empty plugin setting wins over the variable it replaces. An empty one falls back to that variable.
Downloading files
download_recipe_file({recipe_id}) returns the file Tandoor stored for a recipe synced from a Storage backend (Dropbox, Nextcloud, or Local). It is in the recipes section's full tier. download_file({file_id}) returns a user file uploaded to Tandoor. It is in the admin section.
Files up to 5 MiB come back inline, after a text block with {filename, mime, size}. Images (PNG, JPEG, GIF, and WebP) come back as an MCP image block. Other files come back as an embedded resource block. A larger file returns an error that tells the model to retry with save: true.
To save a file to disk instead, set TANDOOR_MCP_DOWNLOAD_ROOT to an existing folder and call the tool with save: true. The tool streams the file into that folder and returns {path, size, mime}. Saving works over stdio only, because over HTTP the folder is on the server machine. The tool removes unsafe characters from the filename. It never overwrites a file: if the name is taken, it adds (2), (3), and so on.
Tandoor compatibility
Minimum: Tandoor server 2.6.0.
The 3.0.0 tools target the endpoints in the vendored Tandoor 2.6.15 OpenAPI spec (see tandoor api specification.yaml, info.version header). Older 2.x instances return 404 on the tools for newer endpoints, such as named shopping lists.
To check your instance version from an LLM session:
The server also probes /api/server-settings/current/ at boot and writes a WARNING to stderr if the connected instance is older than 2.6.0.
Migration from 2.x
3.0.0 removes TANDOOR_MCP_PROFILE and the old tool-group names. The per-section settings in Tool sections replace both.
TANDOOR_MCP_PROFILEis ignored. When it has a value, the server prints one stderr warning.TANDOOR_MCP_PROFILE=full: set every tiered section tofulland every toggle section toon.- Claude Code plugin users: the plugin used to default to
full(every tool). It now starts with the section defaults. To get more tools back, setrecipes,meal_plan,shopping,inventory,foods,cooklog,recipe_books,ai,import_export,household_space, andadminin the plugin settings. - Old group names are gone, with no aliases. Use the section names in
enable_tool_group. The always-on group is nowalways(wascore). The CHANGELOG lists every removed name.
Migration from 1.4.0
- Stdio users: nothing required. No breaking changes, no config edits.
- HTTP users: opt in with
TANDOOR_MCP_TRANSPORT=http. SetTANDOOR_MCP_HTTP_TOKENand expect the jq stash + dynamic gating to be off (both are stdio-session assumptions).
Tool sections
Each section has one env var. The server trims each value and ignores case. An unset, empty, or whitespace-only variable means the saved setting for this TANDOOR_URL if there is one, otherwise the default. If any section variable is invalid, the server stops at startup with one error that names every invalid variable and its valid values.
Tiered sections take off, simple, or full and default to simple. full includes everything in simple.
Toggle sections take off or on and default to off.
A delete preview (preview_*_delete_*) belongs to the same section and tier as its delete tool.
These tools are always on, whatever the section settings: list_tool_groups, enable_tool_group, disable_tool_group, get_version, jq_query, jq_stash_stats, get_guard_stats, and get_current_space. Every tandoor:// resource and every prompt is also always available. list_tool_groups reports them as the always group.
The server applies the settings in this order:
TANDOOR_MCP_INCLUDE_ONLYandTANDOOR_MCP_EXCLUDEfilter the tool list.EXCLUDEis a hard block: nothing can enable an excluded tool.- A section env var that is set and not empty or whitespace-only decides that section.
- For a section whose env var is unset, empty, or whitespace-only, the saved section for this
TANDOOR_URLdecides. - Every other section takes its default.
The result decides which of the remaining tools are visible at startup. An off section is hidden.
In stdio mode the LLM can change sections during a session:
enable_tool_group({group, tier?})widens a section:offtosimpleorfull, orsimpletofull. It never narrows a section. For a tiered section,tierissimpleorfulland defaults tofull. Passingtierfor an on/off section is an error.disable_tool_group({group})sets a section tooff. It cannot disablealways.enable_tool_group({group: "always"})succeeds and changes nothing.
If you see only some tools in Claude Desktop or Codex, ask the model to enable a section, for example "enable the cooklog section". If the new tools do not appear, restart the app. If the change was saved, it applies on the next start.
A section with no registered tools does not count as a section at runtime. That covers any section that INCLUDE_ONLY or EXCLUDE empties. enable_tool_group and disable_tool_group reject it as unknown, and list_tool_groups and the server instructions leave it out.
From an active session, call list_tool_groups for a live listing of sections and their tools.
Saved sections
When enable_tool_group or disable_tool_group changes a section, it saves the change, so the change survives a restart. disable_tool_group saves an explicit "off". The server saves sections per Tandoor instance, keyed by TANDOOR_URL. The key is the URL origin only: the scheme, host, and port, lowercased, with a default port dropped. The server ignores any path, query, or trailing slash, so https://host/tandoor and https://host share one entry. These are the same rules the server applies to TANDOOR_URL, including adding https:// to a URL without a scheme.
The server matches keys in the file by the same rules, so a key you write by hand, such as "https://Tandoor.example.com/tandoor", still matches. A save updates that entry under its key as written.
The file is $XDG_CONFIG_HOME/tandoor-mcp/sections.json. When XDG_CONFIG_HOME is unset or is not an absolute path, the file is ~/.config/tandoor-mcp/sections.json. The path is the same on every OS.
Inside each entry, the keys are the section names from the tables above: recipes, meal-plan, shopping, inventory, foods, cooklog, recipe-books, ai, import-export, household-space, and admin. Like the env vars, saved values ignore case and surrounding spaces, so "FULL" works.
- HTTP mode reads saved sections at startup but never changes them.
- A missing file is normal and gives no warning.
- If the file exists but cannot be read, is not valid JSON, has any invalid section value, or has two or more keys that match the same instance, the server ignores the whole file and starts from the env settings. It prints a warning to stderr that names the file.
- If you save a change while the file is invalid, the server replaces the file with a new one that holds only that change.
- If a save succeeds and the section's env var is set, the tool result notes that the env var will override the saved value on restart.
- If saving fails, the change still applies for the current session. The tool result says that the change was not saved.
- The server ignores saved entries for sections that are filtered out or no longer exist, and keeps them in the file.
- The server ignores a key it cannot parse as a URL, and keeps it in the file.
Example conversations
Plan the week + generate a grocery list:
You: "Plan dinners for the week biased toward vegetarian recipes I've rated 4+, using what's on-hand where possible. Then give me the shopping list grouped by aisle."
Claude:
- Reads
tandoor://pantry/on-handandtandoor://meal-types- Calls
list_recipes({keywords_or: [<vegetarian>], rating_gte: 4, sort_order: '-rating'})- Calls
create_meal_planfor each day of the week- Calls
add_recipe_to_shopping_listfor each plan- Reads
tandoor://shopping-list/activeand groups by supermarket category
Import a recipe from a URL and schedule it:
You: "Import https://example.com/lasagna and schedule it for Friday dinner, 4 servings."
Claude:
import_recipe_from_url({url: "..."}), JSON-LD extraction handles sites Tandoor doesn't knowcreate_meal_plan({recipe_id, meal_type_id: <Dinner>, from_date: "2026-04-24", servings: 4})
Preview a destructive delete before running it:
You: "Delete the 'Test Keyword' keyword."
Claude:
preview_keyword_delete_protecting({id: <k>})to see what recipes still reference it- If safe:
delete_keyword({id: <k>})- If not safe: reports the reference list back to you before proceeding
Destructive tools
Destructive tools (delete_*, merge_*, switch_active_space, create_access_token, create_invite_link) are irreversible or expose escalation surface. Their descriptions instruct the model to:
- For deletes on tree-shaped resources, call the matching
preview_*_delete_{cascading,nulling,protecting}first. - For merges, verify
numrecipecounts and base-unit compatibility first. - For token / invite creation, warn the user before generating the call. Every such description leads with an explicit ESCALATION SURFACE line.
Operators who want them gone entirely can set TANDOOR_MCP_EXCLUDE="delete_*,merge_*,switch_active_space,create_access_token,create_invite_link". The permission config in Recommended Claude Code permissions makes Claude Code ask before any destructive call.
Gotchas
FDC lookups rate-limit after 30 requests/hour
food_fdc_lookup delegates to Tandoor, which calls the USDA FoodData Central API. Out of the box Tandoor uses USDA's public DEMO_KEY, capped at 30 requests/hour per IP. Bulk-enriching a grocery list hits that ceiling fast and you'll see 429s on Tandoor's side.
Fix (takes 2 minutes):
-
Register a free personal key at api.data.gov/signup; no approval needed, instant.
-
Set it on the Tandoor container, not the MCP server:
Kubernetes / Helm: add
FDC_API_KEYto the Tandoor deployment's env or a secret. Restart the Tandoor pod. -
The limit jumps to 1,000 requests/hour; plenty for bulk enrichment.
If FDC returns 404 for a food, the fdc_id you passed doesn't exist in USDA's database; try food_ai_properties as a fallback (uses Tandoor's configured AI provider instead of USDA).
Resources
Prompts
Development
Tests use Vitest and mock fetch; no live Tandoor instance needed for the default run.
End-to-end tests
An opt-in E2E suite exercises the full write path against a real Tandoor instance. Run it only against a throwaway/isolated Tandoor space; the suite creates and deletes real rows.
Contributing
See CONTRIBUTING.md for how pull requests are reviewed, credited, and merged.
Support, security and privacy
- SUPPORT.md: where to ask questions and what to include in a bug report.
- SECURITY.md: supported versions, how to report a vulnerability, and the remote-mode threat model.
- PRIVACY.md: what the server stores, where data goes, and what it logs.
Security posture
This server treats the configured Tandoor instance as trusted enough to read+write on your behalf; you provide the bearer token. It does NOT treat the instance as fully trusted for content:
- Tool responses from
create_meal_plan/update_meal_planare slimmed. Only ids and structural fields flow back to the LLM. User-visible names (recipe.name,meal_type.name, keyword names) are omitted so a hostile or compromised Tandoor instance can't inject model-steering text into Claude's context via these paths. Callget_recipeorlist_meal_typesif you need the human-readable form. - Storage
token/passwordand AI-providerapi_keyare redacted in slim reads.format: "full"still surfaces them for operator inspection. Default (LLM-facing) responses never leak credentials into the model's context. - Keyword payloads are capped at 50 entries and projected to
{id, name}before being forwarded into writes; stops amplification and mass-assignment exposure. - Every mint-a-token / grant-access tool description leads with an ESCALATION SURFACE warning.
create_access_token,update_access_token,create_invite_link,update_invite_link, andauthenticateall warn in-context that the returned token bypasses this MCP's own bearer redaction. - Bearer tokens are redacted from every error message and log line.
- HTTP transport binds to
127.0.0.1by default.TANDOOR_MCP_HTTP_HOSTchanges the address. Each request gets a fresh MCP session, so no session state builds up across requests. - Remote mode uses OAuth with per-user Tandoor tokens. Stored Tandoor tokens are encrypted with
TANDOOR_MCP_SECRET_KEY, issued tokens are stored only as hashes, andHostandOriginheaders are checked. See Remote mode security and SECURITY.md.
If your Tandoor deployment is single-tenant and you own every recipe, the posture is conservative for your case. If you share a Tandoor instance with untrusted users, or run one exposed to the public internet with anonymous sharing, the hardening above matters.
License
MIT
來源:README.md,提交 67f43b9
工具
0版本歷史
1- v3.4.0最新Nov 8, 2026
評分
已滿足適用檢查項的 50%
該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。
- 近 30 天內可連通25 分
請保持端點能夠回應 MCP 握手;SourceWeft 每 30 天探測一次。
- 已宣告認證方式10 分
請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。
- 已列出工具15 分
請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。
- 描述必需15 分
- 圖示10 分
- 文件15 分
- 授權條款10 分