内容
andoor Recipes 中转服务器
[](https://www.npmjs.com/package/@cliftonz/tandoor-rec)
一个 Model Context Protocol (MCP) 服务器,为提供对 [T Recipes](https://or.dev) 实例的完全读写访问权限 — 、餐饮计划、清单、烹、营养信息和驱动的 URL/图像导入。
> 将“计划一周并为我生成转化为一个提示。
使用这个服务器
-andoor API** — 100 多,涵盖食谱计划、食材、购物清单、烹、食谱书籍、食物、单位、关键词、超市类别、属性类型/营养信息、自定义筛选器、单位转换、自动化、用户文件、用户偏好、活动日志。
- **资源 + 提示,而不仅仅是工具** — 只读、可订阅的资源,如 `tandoor://meal-plan/this-week` 和 `or://pantry/on-hand`,以及预制的`plan_week`、`grocery_list_for_plan`、`what_can_i_make_tonight`)。大多数 Tandoor MCP 分叉仅提供工具。
- **默认情况下响应精简** — 通过去除替代树、只读时间戳、图像 URL 等,完整的食谱 GET 请求从 ~20k 字符缩小到 ~2k。 在任何地方传递 `format: "full"` 以获取原始 Tandoor 响应。
- **具有真正回退的三阶段 URL 导入** — 首先尝试 Tandoor 的原生爬虫,然后从页面中提取 schema.org JSON-LD(当 Tandoor 无法访问 URL 或不支持该站点时),并带有可选的故障时存根逃生阀。
- **从图像/PDF 中** — multipart 上传到 Tandoor 的 `ai-import/` 端点,保存解析后的食谱。
- **验证输入** — 每个工具都有一个 Zod 输入模式; 在到达处理程序之前,拒绝无效调用。
- **传输安全** — 在 429/5xx 时重试并带有抖动回退, 从错误消息中删除您的 bearer token, 仅一次读取响应正文(无“正文不可用”陷阱)。
- **`structuredContent` + 文本** — 支持结构化输出的客户端跳过 JSON 重新解析往返; 较旧的客户端仍然获得文本内容。
## 安装
> **兼容性:** 此服务器针对 **Tandoor 2.x** API。 Tandoor 1.x 实例不受支持 — 嵌套写入有效载荷(食谱、餐饮计划、购物条目)使用 2.x 序列化器形状。 服务器在启动时探测实例的版本,并在 stderr 和 MCP 说明中检测到预 2.x Tandoor 时发出警告。
```bash
npm install -g @cliftonz/tandoor-recipes-mcp
```
或在不安装的情况下使用 `npx`:
```bash
npx -y @cliftonz/tandoor-recipes-mcp
```
## 配置
1. 在 Tandoor 中:**设置 → API 令牌 → 创建新令牌**。
2. 将您的 MCP 客户端指向服务器。
### Claude Code — 一键式插件安装
```
/plugin marketplace add Cliftonz/tandoor-recipe-mcp
/plugin install tandoor-recipes-mcp@cliftonz-tandoor
```
Claude Code 在安装时提示输入 `TANDOOR_URL` 和 `TANDOOR_TOKEN`,并安全地存储令牌。 不需要编辑 `mcpServers` JSON。
### Claude Desktop / Claude Code — 手动配置
```json
{
"mcpServers": {
"tandoor": {
"command": "npx",
"args": ["-y", "@cliftonz/tandoor-recipes-mcp"],
"env": {
"TANDOOR_URL": "https://your-tandoor-instance.com",
"TANDOOR_TOKEN": "your-api-token",
"TCP_PROFILE": }
}
}
}
```
### Claude Code 权限库附带一个 `./settings.json`,自动允许读取工具并提示所有。 将这些复制到您自己的项目的 `.claude` 中,以减少权限摩擦:
```json "permissions": {
"allow": [
__tando_*",
"mcp__tandoor_*"
],
"ask": [
"mcp__tandoor__create_*",
"mcp__tandoor__update_*",
"mcp__tandoor__delete_*",
"mcp__tandoor__merge_*"
]
}
}
```
### 环境变量
| 变量 | 必需 | 默认值 | 目的 |
| --- | --- | --- | --- |
| `TANDOOR_URL` | ✅ | — | 您的 Tandoor 实例的基本 URL |
| `TANDOOR_TOKEN` | ✅ | — | 具有写访问权限的 API 令牌 |
| `TANDOOR_MCP_PROFILE` | — | `full` | `basic` 仅公开核心工具(~40);`full` 包括所有内容(~100) |
| `TANDOOR_MCP_INCLUDE_ONLY` | — | — | 以逗号分隔的允许列表(支持通配符 `*`)。例如 `list_*,get_*,create_meal_plan` |
| `TANDOOR_MCP_EXCLUDE` | — | — | 以逗号分隔的拒绝列表。例如 `merge_*,delete_*` 以隐藏破坏性工具 |
| `TANDOOR_MCP_LOG` | — | — | stderr 跟踪模式。`request`、`response`、`error`、`all` 或逗号列表。 bearer token 已删除。 |
| `TANDOOR_MCP_STASH_ENABLED` | — | `1` | 将大型工具结果存放在内存缓存中,并返回 `{stashed:true, handle, shape}` 摘要;LLM 调用 `jq_query` 提取子集。设置为 `0` 以禁用。 |
| `TANDOOR_MCP_STASH_THRESHOLD` | — | `25000` | 严格大于此字节数的有效载荷将被存放在内存中。 |
| `TANDOOR_MCP_STASH_MAX_ENTRIES` | — | `32` | 暂存句柄的 LRU 上限。超出时,最旧的条目将被逐出。 |
| `TANDOOR_MCP_STASH_MAX_BYTES` | — | `32000000` | 暂存区的总字节上限;超出此限制时,将触发 LRU 逐出。 |
| `TANDOOR_MCP_STASH_TTL_MS` | — | `600000` | 每个条目的毫秒 TTL(默认 10 分钟)。 |
`basic` 配置文件在上下文大小很重要时很有用 — 每个 MCP 客户端在启动时都会将每个工具模式加载到模型的上下文中。 `INCLUDE_ONLY` 和 `EXCLUDE` 在配置文件的基础上组合,以实现更精细的控制(例如,“给我只有只读工具”或“从此代理隐藏破坏性操作”)。
## 示例对话
**计划一周的餐饮并生成购物清单:**
> 你: “计划一周的晚餐,以素食食谱为主,评级 4+,并使用现有的食材。同时给我按过道分类的购物清单。”
>
> Claude:
> 1. 读取 `tandoor://pantry/on-hand` 和 `tandoor://meal-types`
> 2. 调用 `list_recipes({keywords_or: [<vegetarian>], rating_gte: 4, sort_order: '-rating'})`
> 3. 为每周每一天调用 `create_meal_plan`
> 4. 为每个计划调用 `add_recipe_to_shopping_list`
> 5. 读取 `tandoor://shopping-list/active` 并按超市类别分组从 URL 导入并安排:**
> 你: “导入 https://example.com/lasagna 并将其安排在周五晚餐,4 份。”
>
> Claude:
> 1. `import_recipe_from_url({url: "..."})` — JSON-LD 提取处理 Tandoor 不知道的站点
> 2. `create_meal_plan({recipe_id, meal_type_id: <Dinner>, from_date: "2026-04-24", servings: 4})`
**烹饪日志:**
> 你: “记录我昨晚做了泰国绿咖喱 — 之 4,很喜欢。”
>
: `create_cook_log({recipe: <id>, rating: 4, comment: "Kids loved it", servings: 4})`
## 工具目录
所有返回写入的工具默认使用精简的 JSON 形状。 传递 `format: "full"` 以获取原始 Tandoor 响应。
**破坏性工具**(`delete_*`、`merge_*`)是不可逆的。 它们的描述指示模型首先检查用法(例如 `list_recipes({keywords: [id]})` 在 `delete_keyword` 之前),并在存在非破坏性替代方案时优先使用它(`merge_*` 用于重复数据删除,`update_recipe` 带有 `private: true` 代替 `delete_recipe`)。 想要完全删除操作员可以设置 `TANDOOR_MCP_EXCLUDE="delete_*,merge_*"`; 上面的权限配置使 Claude Code 在任何破坏性调用之前询问。
### 食谱
`search_recipes` · `list_recipes` · `get_recipe` · `create_recipe` · `update_recipe` · `delete_recipe` · `import_recipe_from_url` · `upload_recipe_image`_recipes`_recipe_to_sh` · `recipe · `recipe`search_rec首选的高:它接受/书籍* (而非在内部解析 仅当使用 的筛选器界面时,才使用 `list`。
`list` 采用完整的or 筛选:`query`_order`、`rating[_gte|_lte]`、`timescooked[_gte|_lte]`、`cookedon_[gte|lte]`、`keywords[_or|_or_not|][]`、`、`books[...makenow`、`random`、`internal`、`filter`范围、分页。
餐饮计划(`bulk_create_meal_plans` 用于周规划)
`al_plans `get_meal · `create_meal_plan` · `update` · `al_plan` ·_meal_plan` · `list_meal · `bulk_createans`
### 步骤
ingredients` ·_ingredient` · `create_ingredient`update_ingredient` · `delete_ingredient`_ingredient`
` · `` · `create_step` · `update · `delete_step`
### 购物清单
`list_shopping_entries` · `get_shopping_entry` · `create_shopping_entry` · `update_shopping_entry` · `delete_shopping_entry` · `bulk_check_shopping_entries` · `list_shopping_list_recipes` · `get_shopping_list_recipe` · `create_shopping_list_recipe` · `update_shopping_list_recipe` · `delete_shopping_list `bulk_create_shopping_list_recipe_entries 食物 + 关键词 + 类别 *(完整配置文件)*
`list_foods `get · `create_food `update_food` · `delete_food` · `merge_food` · `move_food` · `food_shopping_update` · `food_fdc_lookup` · `food_ai_properties` · `food_batch_update`
`list_units` · `get_unit` · `create_unit` · `update_unit` · `delete_unit` · `merge_unit`
`list_unit_conversions` · `get_unit_conversion` · `create_unit_conversion` · `update_unit_conversion` · `delete_unit_conversion`
`list_keywords` · `get_keyword` · `create_keyword` · `update_keyword` · `delete_keyword` · `merge_keyword` · `move_keyword`
`list_supermarket `*_supermarket_category_relation`
###饪日志 + 食谱书籍 *(完整配置文件_cook_logs `get_cook_log` · `create` · `ok_log` ·ok_log`
`list_recipe_books`_recipe_book` · `create_recipe_book` · `update_recipe_book` · `delete_recipe_book` · `list_recipe_book_entries` · `get_recipe_book_entry` · `add_recipe_to_book` · `remove_recipe_from_book`
### 营养 + 属性类型 + 自定义筛选器 *(完整配置文件)*
`list_property_types` · `get_property_type` · `create_property_type` · `update_property_type` · `delete_property_type`
`list_properties` · `get_property` · `create_property` · `update_property` · `delete_property`
`list_custom_filters` · `get_custom_filter` · `create_custom_filter` · `update_custom_filter`delete_custom_filter`
### AI 导入 *(完整配置文件)*
`list_ai_providers` · `import_recipe_from_image` (文件路径、URL 或文本)
### 自动化、用户文件、偏好、日志 *(完整配置文件)*
`list_automations` · `*_automation` · `list_user_files` · `*_user_file` · `list_user_preferences` · `get_user_preference` · `update_user_preference` · `get_server_settings` · `get_share_link` · `list_view_logs` · `list_import_logs` · `list_ai_logs`
### 实用程序 *(两种配置文件)*
`get_version` · `jq_query` · `jq_stash_stats`
`get_version` 报告 MCP 服务器版本以及在启动时检测到的 Tandoor 版本和兼容性状态。 `jq_query` 从暂存的大型结果中提取子集(参见上面的 `TANDOOR_MCP_STASH_*`)。
## 注意事项
### FDC 查询在 30 次请求/小时后限速
`food_fdc_lookup` 委托给 Tandoor,后者调用 USDA FoodData Central API。开箱即用,Tandoor 使用 USDA 的公共 `DEMO_KEY`,上限为 **30 次请求/小时/ IP 地址**。批量丰富购物清单会迅速达到该上限,您将在 Tandoor 端看到 429 错误。
**修复(2 分钟即可完成):**
1. 在 [api.data.gov/signup](https://api.data.gov/signup/) 注册一个免费的个人密钥 — 无需审批,立即生效。
2. 在 Tando上设置**而不是** MCP
``` # docker-compose.yml - 在 tandoor 服务下
environment:
- FDC_API_KEY=<your-key>
```
Kubernetes / Helm:在 Tandoor 添加 `FDC_API_KEY` 或一个密钥。 重新启动 Tandoor Pod。
3. 限制跳转到 **1,000 次请求/小时** — 足以进行批量丰富。
如果 FDC 返回 404 表示食物,传递的 `fdc_id` 不存在于 USDA 的数据库中 — 尝试使用 `food_ai_properties` 作为回退(使用 Tandoor 配置的 AI 提供程序代替 USDA)。
## 资源
| URI | 目的 |
| --- | --- |
| `tandoor://meal-plan/this-week` | 星期一至星期日的餐饮计划条目,精简为 `{id, date, meal_type, recipe_name, servings, note}` |
| `tandoor://pantry/on-hand` | 标记为 `food_onhand=true` 的食物 |
| `tandoor://shopping-list/active` | 未勾选的、最近的购物条目 |
| `tandoor://meal-types` | 早餐/午餐/晚餐等,附带 ID |
## 提示
| 名称 | 目的 |
| --- | --- |
| `plan_week` | 计划 7 天的晚餐,可选地以食品储藏室为偏好,并通过关键词过滤 |
| `grocery_list_for_plan` | 将本周的餐饮计划转为按过道分组的购物清单 |
| `what_can_i_make_tonight` | 查找 ≤ N 分钟的可制作食谱,从现有的食材中 |
| `import_and_plan` | 导入 URL 食谱并一键安排 |
## 开发
```bash
git clone https://github.com/Cliftonz/tandoor-recipe-mcp.git
cd tandoor-recipe-mcp
npm install
npm run build
npm test
```
测试使用 Vitest 和模拟 `fetch` — 默认运行不需要真正的 Tandoor 实例。
### 端到端测试
一个可选的端到端测试套件针对真正的 Tandoor 实例进行完整的写入路径测试。 **仅在丢弃/隔离的 Tandoor 空间中运行它** — 套件创建并删除真正的行。
```bash
TANDOOR_URL=https://your-tandoor.example \
TANDOOR_TOKEN=... \
npm run test:e2e
```
| 变量 | 效果 |
| --- | --- |
| `TANDOOR_E2E_KEEP=1` | 跳过清理;为检查留下已创建的资源 |
| `TANDOOR_E2E_SKIP_IMPORT=1` | 跳过 URL 导入步骤(离线运行者) |
| `TANDOOR_E2E_IMPORT_URL=<url>` | 覆盖默认的食谱 URL |
| `TANDOOR_E2E_AI_PROVIDER=<id>` | 在此提供程序上运行 AI 导入步骤 |
## 安全姿态
该服务器将配置的 Tandoor 实例视为 **足够信任以代表您读写** — 您提供 bearer token。 它不将实例视为完全受信任的内容:
- **`create_meal_plan` / `update_meal_plan` 工具的响应已精简** — 只有 ID 和结构字段流回给 LLM。 用户可见的名称(`recipe.name`、`meal_type.name`,关键词名称)被省略,以便受损或泄露的 Tandoor 实例无法通过这些路径将模型导向文本注入到 Claude 的上下文中。 调用 `get 或 `list_meal_types` 如果您需要人类可读的表单。
- **关键词有效载荷限制为 50 个条目,并投影到 `{id, name}`** — 阻止放大和大规模赋值暴露。
- **从错误消息和日志行中删除 bearer token**。
如果您的 Tandoor 部署是单租户的,并且您拥有每个食谱,那么该姿态是保守的。 如果您与不受信任的用户共享 Tandoor 实例(或在公共互联网上运行带有匿名共享的 Tandoor 实例),那么上述强化很重要。
## 许可证
MIT