npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@cliftonz/tandoor-recipes-mcp

v1.3.0

Published

Model Context Protocol server for Tandoor — recipes, meal plans, shopping lists, ingredients, cook logs, nutrition, AI import, and more.

Downloads

787

Readme

Tandoor Recipes MCP

npm version

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, and AI-powered URL/image imports.

Turn "plan the week and make me a grocery list" into one prompt.

Why this server

  • Full Tandoor API coverage — 100+ typed tools spanning recipes, meal plans, ingredients, steps, shopping lists, cook logs, recipe books, foods, units, keywords, supermarket categories, property types / nutrition, custom filters, unit conversions, automations, user files, user prefs, activity logs.
  • Resources + Prompts, not just tools — read-only subscribable resources like tandoor://meal-plan/this-week and tandoor://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.
  • 3-stage URL import with a real fallback — tries Tandoor's native scraper first, then extracts schema.org JSON-LD from the page ourselves (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.
  • Validated inputs — every tool has a Zod input schema; invalid calls are rejected at the MCP boundary before reaching the handler.
  • Transport-safe — retries with jittered backoff on 429 / 5xx, redacts your bearer token from error messages, reads response bodies exactly once (no "Body is unusable" footguns).
  • structuredContent + text — clients that support structured output skip a JSON re-parse round-trip; older clients still get text content.

Install

npm install -g @cliftonz/tandoor-recipes-mcp

Or npx without installing:

npx -y @cliftonz/tandoor-recipes-mcp

Configure

  1. In Tandoor: Settings → API Tokens → Create new token.
  2. Point your MCP client at the server.

Claude Code — one-shot plugin install

/plugin marketplace add Cliftonz/tandoor-recipe-mcp
/plugin install tandoor-recipes-mcp@cliftonz-tandoor

Claude Code prompts you for TANDOOR_URL and TANDOOR_TOKEN on install and stores the token securely. No mcpServers JSON editing needed.

Claude Desktop / Claude Code — manual config

{
  "mcpServers": {
    "tandoor": {
      "command": "npx",
      "args": ["-y", "@cliftonz/tandoor-recipes-mcp"],
      "env": {
        "TANDOOR_URL": "https://your-tandoor-instance.com",
        "TANDOOR_TOKEN": "your-api-token",
        "TANDOOR_MCP_PROFILE": "full"
      }
    }
  }
}

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:

{
  "permissions": {
    "allow": [
      "mcp__tandoor__list_*",
      "mcp__tandoor__get_*"
    ],
    "ask": [
      "mcp__tandoor__create_*",
      "mcp__tandoor__update_*",
      "mcp__tandoor__delete_*",
      "mcp__tandoor__merge_*"
    ]
  }
}

Environment variables

| Var | Required | Default | Purpose | |---|---|---|---| | TANDOOR_URL | ✅ | — | Base URL of your Tandoor instance | | TANDOOR_TOKEN | ✅ | — | API token with write access | | TANDOOR_MCP_PROFILE | — | full | basic exposes core tools only (~40); full includes everything (~100) | | TANDOOR_MCP_INCLUDE_ONLY | — | — | Comma-separated allowlist (glob * supported). E.g. list_*,get_*,create_meal_plan | | TANDOOR_MCP_EXCLUDE | — | — | Comma-separated denylist. E.g. merge_*,delete_* to hide destructive tools | | TANDOOR_MCP_LOG | — | — | Stderr trace mode. request, response, error, all, or comma list. Bearer token redacted. |

The basic profile is useful when context size matters — every MCP client loads every tool schema into the model's context on startup. INCLUDE_ONLY and EXCLUDE compose on top of the profile for finer control (e.g. "give me only read tools" or "hide destructive ops from this agent").

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:

  1. Reads tandoor://pantry/on-hand and tandoor://meal-types
  2. Calls list_recipes({keywords_or: [<vegetarian>], rating_gte: 4, sort_order: '-rating'})
  3. Calls create_meal_plan for each day of the week
  4. Calls add_recipe_to_shopping_list for each plan
  5. Reads tandoor://shopping-list/active and 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:

  1. import_recipe_from_url({url: "..."}) — JSON-LD extraction handles sites Tandoor doesn't know
  2. create_meal_plan({recipe_id, meal_type_id: <Dinner>, from_date: "2026-04-24", servings: 4})

Cook log:

You: "Log that I made the Thai green curry last night — 4 out of 5, kids loved it."

Claude: create_cook_log({recipe: <id>, rating: 4, comment: "Kids loved it", servings: 4})

Tool catalog

All write-returning tools default to a slim JSON shape. Pass format: "full" for the raw Tandoor response.

Recipes

search_recipes · list_recipes · get_recipe · create_recipe · update_recipe · import_recipe_from_url · upload_recipe_image · related_recipes · add_recipe_to_shopping_list · recipe_ai_properties · recipe_batch_update

search_recipes is the preferred high-level entry point: it accepts food/keyword/book names (not IDs) and resolves them internally. Use list_recipes only for the raw ID-based filter surface.

list_recipes takes the full Tandoor filter surface: query, sort_order, rating[_gte|_lte], timescooked[_gte|_lte], cookedon_[gte|lte], keywords[_or|_and|_or_not|_and_not][], foods[...], books[...], makenow, new, random, internal, filter, date ranges, pagination.

Meal plans (bulk_create_meal_plans for week-planning)

list_meal_plans · get_meal_plan · create_meal_plan · update_meal_plan · delete_meal_plan · auto_meal_plan · list_meal_types · bulk_create_meal_plans

Ingredients + steps

list_ingredients · get_ingredient · create_ingredient · update_ingredient · delete_ingredient · parse_ingredient list_steps · get_step · create_step · update_step · delete_step

Shopping lists

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_recipe · bulk_create_shopping_list_recipe_entries

Foods + units + keywords + categories (full profile)

list_foods · get_food · 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_categories · *_supermarket_category_relation

Cook log + recipe books (full profile)

list_cook_logs · get_cook_log · create_cook_log · update_cook_log · delete_cook_log list_recipe_books · get_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

Nutrition + property types + custom filters (full profile)

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 imports (full profile)

list_ai_providers · import_recipe_from_image (file path, URL, or text)

Automations, user files, prefs, logs (full profile)

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

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):

  1. Register a free personal key at api.data.gov/signup — no approval needed, instant.

  2. Set it on the Tandoor container, not the MCP server:

    # docker-compose.yml — under the tandoor service
    environment:
      - FDC_API_KEY=<your-key>

    Kubernetes / Helm: add FDC_API_KEY to the Tandoor deployment's env or a secret. Restart the Tandoor pod.

  3. 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

| URI | Purpose | |---|---| | tandoor://meal-plan/this-week | Monday–Sunday meal plan entries, slimmed to {id, date, meal_type, recipe_name, servings, note} | | tandoor://pantry/on-hand | Foods flagged food_onhand=true | | tandoor://shopping-list/active | Unchecked, recent shopping entries | | tandoor://meal-types | Breakfast/Lunch/Dinner/etc. with IDs |

Prompts

| Name | Purpose | |---|---| | plan_week | Plan 7 days of dinners, optionally pantry-biased and keyword-filtered | | grocery_list_for_plan | Turn this week's meal plan into a shopping list grouped by aisle | | what_can_i_make_tonight | Find makeable recipes ≤ N minutes from on-hand foods | | import_and_plan | Import a URL recipe and schedule it in one shot |

Development

git clone https://github.com/Cliftonz/tandoor-recipe-mcp.git
cd tandoor-recipe-mcp
npm install
npm run build
npm test

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.

TANDOOR_URL=https://your-tandoor.example \
TANDOOR_TOKEN=... \
npm run test:e2e

| Var | Effect | |---|---| | TANDOOR_E2E_KEEP=1 | Skip cleanup; leave created resources for inspection | | TANDOOR_E2E_SKIP_IMPORT=1 | Skip the URL-import step (offline runners) | | TANDOOR_E2E_IMPORT_URL=<url> | Override the default recipe URL | | TANDOOR_E2E_AI_PROVIDER=<id> | Run the AI-import step against this provider |

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_plan are 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. Call get_recipe or list_meal_types if you need the human-readable form.
  • Keyword payloads are capped at 50 entries and projected to {id, name} before being forwarded into writes — stops amplification and mass-assignment exposure.
  • Bearer tokens are redacted from every error message and log line.

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 a Tandoor instance exposed to the public internet with anonymous sharing), the hardening above matters.

License

MIT