plan-to-eat-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@plan-to-eat-mcp Save the Bon AppΓ©tit miso pasta from this URL and tag it weeknight."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
π½οΈ plan-to-eat-mcp
Stop typing recipes. Start telling them. Hand your favorite LLM the keys to your Plan to Eat recipe book, planner, and shopping list β over the Model Context Protocol.
A drop-in MCP server, a CLI, and a standalone Node client that give Claude, ChatGPT, any MCP-aware assistant, or your own shell full read/write access to your Plan to Eat account.
You: "Save the Bon AppΓ©tit miso pasta from this URL and tag it weeknight."
LLM: β created recipe #48,872,094 β 7 ingredients, 25 min, tagged weeknight.
You: "Plan dinner around it Tuesday and add the missing pantry items to my
shopping list."
LLM: β event created 2026-05-05 / dinner / 4 servings.
β shopping list now reflects miso, mirin, and dashi.β¦or, when you'd rather not talk to anything:
$ plan-to-eat get-planner-week 2026-05-04
start_date 2026-05-04
end_date 2026-05-10
ID DATE SECTION KIND RECIPE_ID RECIPE_TITLE DESCRIPTION SERVINGS
ββββββββ ββββββββββ βββββββββ ββββββββββ βββββββββ ββββββββββββββββββ βββββββββββββββββββββ ββββββββ
67698872 2026-05-05 breakfast recipe 17314561 Breakfast Sandwich 8
67483177 2026-05-05 dinner ingredient Asparagus (in season) 0
67710784 2026-05-06 dinner note Cucumber salad 0
3 rowsThat's it. No recipe re-typing. No copy-pasting URLs into a phone app. Just talk.
β€οΈ Why Plan to Eat?
If you're not already using it, Plan to Eat is genuinely the best meal-planning app I've ever used:
A clip-anywhere recipe importer that actually works on real-world food blogs.
Drag-and-drop weekly planner that automatically rolls ingredients into a shopping list, with smart units and pantry deduplication.
Yours forever β your recipe book is portable, exportable, and not held hostage by an algorithm.
Family-friendly β share recipes and menus with friends in-app.
14-day free trial, no credit card needed.
π Sign up with my referral link β you get the trial, and I get a tiny thank-you. Win/win.
Related MCP server: Mealie MCP Server
β¨ What this gives you
π€ MCP server out of the box β point Claude Desktop, Claude Code, OpenClaw, or any MCP-compatible host at it and start talking to your recipe book.
β¨οΈ A CLI over the same tools β every MCP tool is also a
plan-to-eatsubcommand, with tables for humans and--jsonfor scripts. One registry feeds both surfaces, so they can't drift.π Auto-auth & auto-recovery β set your credentials once, the server handles login, caches the cookie session to disk, and silently re-auths whenever Plan to Eat invalidates it.
π§° 36 tools, all the verbs that matter β full recipe CRUD, full meal-planner CRUD (add / move / duplicate / delete recipes, notes, and ingredient entries), reorder events in a slot, leftovers as a first-class workflow, freezer tracking, browse courses & cuisines & tags, count what's in your queue.
ποΈ Real planner control β view a week's plan with recipe titles pre-joined, schedule recipes on dates, attach prep notes, reschedule with one tool call, change servings, duplicate, search for duplicates, reorder same-slot events.
π A shopping list you can actually work with β read it with the store and aisle each item is filed under, add items (Plan to Eat guesses the aisle and reuses the store you last picked), retitle or re-quantify a line, move items between stores, remove them, put them back.
βοΈ Freezer tracking β after cooking, mark N portions as frozen with
freeze_recipe_portions; check what's stashed withlist_frozen_recipes; consume entries when you eat them (soft-delete, history preserved).π³ Real CRUD β including ingredient lists with proper units, directions, prep/cook times, nutrition, ratings, and tags.
π¦ Tiny runtime β no Playwright, no headless browser, no native modules. Just
fetch, the MCP SDK, and Zod. Boots in under a second.π¦Ί 100% TypeScript β fully typed
Recipe,Ingredient,PlannerEventshapes plus a generic_json<T>so your tools never have to guess what comes back.π A library too β
core/client.tsis a clean, plain-Node API client you can drop into any script.π§ Bundled Claude Code skill β
.claude/skills/plan-to-eat/SKILL.mdteaches any agent the common workflows and gotchas (the supper-vs-dinner alias, thedescription-vs-titlemismatch, etc.) so it doesn't have to rediscover them.
Reverse-engineered from the live web app. There's no public Plan to Eat API, but the desktop site uses these same endpoints internally. Use at your own risk β they could change anything at any time.
π Quick start
npx -y plan-to-eat-mcp # run the MCP server
npm i -g plan-to-eat-mcp # β¦or install both bins on PATHRequires Node 18+ (for built-in fetch) and a
Plan to Eat account.
The package ships two bins: plan-to-eat-mcp (the MCP server) and
plan-to-eat (the CLI). To run the CLI through npx without installing, note
that you have to select it explicitly, since the default bin is the server:
npx -y -p plan-to-eat-mcp plan-to-eat --helpgit clone https://github.com/alex-zwingli/plan-to-eat-mcp.git
cd plan-to-eat-mcp
npm install
npm run buildThe build emits CommonJS to dist/.
Configuration
Var | Required | Default | Description |
| yes | β | Plan to Eat login email |
| yes | β | Plan to Eat password |
| no |
| Where the cookie session is cached. Set to |
The CLI also reads a .env in the working directory (shell variables win). The
MCP server does not β MCP hosts pass env explicitly, as shown below.
π€ Use it with an agent
Any host that can launch a local stdio MCP server works. The server needs one command, two env vars, and nothing else β no ports, no OAuth, no daemon.
Claude Code, the easy way: install the plugin
The repo ships as a Claude Code plugin β MCP server and both skills in one step:
export PLAN_TO_EAT_USERNAME=you@example.com
export PLAN_TO_EAT_PASSWORD=hunter2
git clone https://github.com/alex-zwingli/plan-to-eat-mcp.git
cd plan-to-eat-mcp && npm install && npm run build
claude plugin marketplace add "$(pwd)"
claude plugin install plan-to-eat@plan-to-eatThat gives you:
Component | What it is |
MCP server | all 36 tools |
Skill | how to use the MCP tools well β workflows and gotchas |
Skill | the same, for agents driving the CLI instead |
Confirm with claude plugin details plan-to-eat@plan-to-eat and claude mcp list.
The server reads PLAN_TO_EAT_USERNAME / PLAN_TO_EAT_PASSWORD from the
environment Claude Code was launched with, so export them in your shell profile
rather than committing them anywhere.
Claude Code, manually
claude mcp add plan-to-eat \
--env PLAN_TO_EAT_USERNAME=you@example.com \
--env PLAN_TO_EAT_PASSWORD=hunter2 \
-- npx -y plan-to-eat-mcpAdd --scope user to make it available in every project instead of just this
one. Check it connected with claude mcp list, or /mcp inside a session.
Claude Desktop
Edit claude_desktop_config.json β Settings β Developer β Edit Config, or:
macOS:
~/Library/Application Support/Claude/claude_desktop_config.jsonWindows:
%APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"plan-to-eat": {
"command": "npx",
"args": ["-y", "plan-to-eat-mcp"],
"env": {
"PLAN_TO_EAT_USERNAME": "you@example.com",
"PLAN_TO_EAT_PASSWORD": "hunter2"
}
}
}
}Restart Claude Desktop, and you're cooking.
Any other MCP host
Cursor, Windsurf, Zed, Cline, Continue, OpenClaw, VS Code's MCP support, and custom SDK clients all take the same three things. Point them at:
Field | Value |
Transport | stdio |
Command |
|
Args |
|
Env |
|
Most of them use the same mcpServers JSON block as Claude Desktop above β
often in .cursor/mcp.json, .vscode/mcp.json, or the host's settings UI.
If you installed globally (npm i -g plan-to-eat-mcp), use the
plan-to-eat-mcp bin as the command and drop the args entirely. If you built
from a clone, the command is node with
["/absolute/path/to/plan-to-eat-mcp/dist/mcp/server.js"].
Absolute paths matter for the clone route. MCP hosts don't launch servers from your project directory, so a relative path will fail to resolve. The
npxcommand above sidesteps this entirely.
Verify it works
PLAN_TO_EAT_USERNAME=you@example.com PLAN_TO_EAT_PASSWORD=hunter2 npx -y plan-to-eat-mcpYou should see [plan-to-eat] mcp server ready on stdio (36 tools) on stderr.
That's the server waiting for a client β Ctrl-C out. If instead you get a
credentials error, fix that before wiring up a host, where the failure is
harder to see.
Teach your agent the workflows
Two skills ship in .claude/skills/, covering the common flows and the sharp
edges (the supper-vs-dinner alias, the description-vs-title mismatch on
note entries, checking for duplicates before scheduling):
plan-to-eatβ for agents calling the MCP tools.plan-to-eat-cliβ for agents that have a shell but no MCP server. Same 30 capabilities, driven through subcommands, with--jsonfor parsing.
The plugin install above registers both. Agents on other hosts can read them as plain context β point them at the files, or paste one into your system prompt.
Or install just the skills, into any of 18+ agents, with the skills.sh CLI:
npx skills add alex-zwingli/plan-to-eat-mcpThat copies both SKILL.md files into .agents/skills/ and symlinks them for
Claude Code, Cursor, Codex, Copilot, Gemini CLI and the rest. Add
--skill plan-to-eat-cli to take only one. Note that this installs the skills
and not the server β each skill's setup section walks the agent through building
the MCP server or CLI if it isn't already there.
For OpenClaw agents, both skills are on ClawHub:
clawhub install plan-to-eat # MCP tools
clawhub install plan-to-eat-cli # shell / CLITheir frontmatter declares the credentials and binaries each one needs under
metadata.openclaw, so ClawHub can check your environment at install time.
β¨οΈ Use it from the terminal
Every MCP tool is also a subcommand. Underscores become dashes; both spellings work.
npm i -g plan-to-eat-mcp
plan-to-eat --helpOr without installing β note the -p, since the package's default bin is the
MCP server, not the CLI:
npx -y -p plan-to-eat-mcp plan-to-eat --help$ plan-to-eat list-frozen-recipes
ID RECIPE_ID COUNT SERVINGS FROZEN_ON
ββββββ βββββββββ βββββ ββββββββ ββββββββββ
227854 44676380 6 1.0 2025-05-04
227908 17314561 3 1.0 2025-05-05
2 rows
$ plan-to-eat add-planner-note "Defrost chicken" 2026-05-05 dinner
id 67710999
date 2026-05-05
section dinner
$ plan-to-eat get-counts --json | jq .frozen
10Positional or flagged β
get-recipe 123andget-recipe --id 123are the same.<command> --helplists which arguments are positional.--jsonprints the raw payload instead of a table, for piping intojq.Arrays repeat the flag (
--event_ids 11 --event_ids 22) or take JSON (--event_ids '[11,22]'). Object arguments take JSON:--ingredients '{"title":"bread","amount":"2"}'.Validation is the same Zod schema the MCP server uses, so a bad enum or a malformed date fails the same way in both surfaces.
π§° The 36 tools
Each is an MCP tool and a CLI subcommand β add_planner_recipe the tool is
plan-to-eat add-planner-recipe in the shell. Full reference with input
schemas and return shapes: docs/TOOLS.md.
Recipes
Tool | What it does |
| Your whole recipe book, optionally filtered by title words ( |
| One recipe with directions, ingredients, tags, prep_notes, comments. |
| Create. Only |
| Patch any subset of fields. |
| Delete by id. |
Planner β read
Tool | What it does |
| All planner entries, no date filter. |
| Events in a date range, with |
Planner β write
Tool | What it does |
| Schedule a recipe on a date + section. |
| Attach a freeform ingredient ("2 lbs ground beef") to a meal slot. |
| Attach a freeform note ("Defrost chicken") to a meal slot. |
| Schedule a leftover from a previously planned recipe event (duplicate with |
| Reschedule any planner event to a new date/section. |
| Reorder events within a section (pass ids in desired order). |
| Edit the text of a note or ingredient entry. |
| Change servings on a recipe event. |
| Duplicate any event. Optional |
| Delete by id. |
| Find planner events for a recipe in a date range. Useful for duplicate checks. |
Freezer
Tool | What it does |
| What's currently in the freezer. |
| Mark N portions of a cooked recipe as frozen, tied to the planner event they came from. |
| Mark a frozen entry as consumed (soft-delete: API zeroes count, row persists). |
Shopping list
A line is addressed by its item_ids array, not a scalar id β Plan to Eat
merges duplicate ingredients into one line that keeps every underlying row id.
Tool | What it does |
| The list, each line with the store ( |
| Add items. Only |
| Retitle / re-quantify a line, or move any number of lines to a different store or aisle. |
| Take lines off the list (soft delete). |
| Put removed lines back. |
Lookup tables & extras
Tool | What it does |
| Lookup tables. |
| Your stores and grocery aisles β the ids |
| Saved menus. |
| Friends list. |
|
|
| Set planner display preferences (timezone, start day, nutrition columns). Rarely needed. |
π Use the client as a library
import { PlanToEat, type Recipe } from 'plan-to-eat-mcp';
const pte = new PlanToEat();
await pte.login(process.env.PLAN_TO_EAT_USERNAME!, process.env.PLAN_TO_EAT_PASSWORD!);
// Or restore: pte.importSession(JSON.parse(fs.readFileSync('session.json', 'utf8')));
const recipes = await pte.listRecipes(); // every active recipe
const [dumplings] = await pte.listRecipes({ search: 'pork chive' });
const detail: Recipe = await pte.getRecipe(dumplings.id); // ingredients + directions
const created = await pte.createRecipe({
title: 'Grilled cheese',
servings: 1,
directions: 'Butter bread. Cheese. Pan. Flip.',
ingredients: [
{ title: 'bread', amount: '2', unit: 'slice', position: 1 },
{ title: 'cheese', amount: '1', unit: 'slice', position: 2 },
],
tag_titles: 'easy,lunch',
});
await pte.updateRecipe(created.id, { rating: 5 });
await pte.deleteRecipe(created.id);The client stashes credentials internally on first login so it can transparently re-authenticate if the cached cookies expire mid-session.
The tool registry is exported too, if you want to build your own adapter over the same 36 capabilities:
import { toolsByName, createSession } from 'plan-to-eat-mcp';
const { pte, ensure } = createSession();
await ensure();
const getRecipe = toolsByName.get('get_recipe')!;
console.log(getRecipe.description); // same text the MCP host sees
const recipe = await getRecipe.run(pte, { id: 123 });π§ͺ API reference (the parts that work)
All paths under https://app.plantoeat.com.
JSON endpoints (/api/v1/*)
Method | Path | Notes |
GET |
| The whole recipe book as one array of light listings (title, course/cuisine/main-ingredient titles, tags, flags; no ingredients). What the web app's recipe grid uses. Includes inactive/removed/hidden rows β filter on |
GET |
| Don't use for listing. Returns only the oldest 500 recipes by id, with ingredients embedded. Ignores every paging/sorting param tried: |
GET |
| Single recipe with |
POST |
| Create. Body: |
PUT |
| Update. Same body shape. |
DELETE |
| Delete. Returns the deleted recipe. |
GET |
| Lookup tables. |
GET |
| Planner entries (calendar). Returns the entire calendar β filter by date client-side. |
GET |
| Saved menus. |
GET |
| Sync metadata only ( |
GET |
| The shopping list itself, one entry per merged line. |
GET |
| Grocery stores. |
GET |
| Grocery aisles. |
GET |
| Friends. |
GET |
| Freezer: |
DELETE |
| Soft-delete (sets |
GET |
|
|
Planner write endpoints (/planner/*)
A different style: form-encoded bodies, text/javascript (empty) responses. The server mutates state and the UI re-fetches separately. Required headers: X-CSRF-Token, X-Requested-With: XMLHttpRequest, Accept: text/javascript. Because the response body is empty, the client recovers any new event ID by diffing /api/v1/events before and after.
Method | Path | Body |
POST |
|
|
POST |
|
|
POST |
|
|
POST |
|
|
POST |
|
|
POST |
|
|
POST |
|
|
POST |
|
|
POST |
| Nested Rails keys: |
POST |
|
|
GET |
| Returns rendered HTML, not JSON β not used by the client. We filter |
section values are breakfast, lunch, dinner, snacks. Note that the server may normalize dinner β supper based on the user's per-account preference; reads will reflect the canonical name.
Authentication
Cookie-based. The client supports two flows:
Login.
POST /loginwithlogin[email]andlogin[password]plus theauthenticity_tokenfrom the meta tag onGET /login. Server returns a 302 and sets cookies.Reuse cookies. The "remember me" pair
ptermid2(user id) andptermxt2(long-lived token) is sufficient on its own β stash those and skip re-login until they're invalidated.
Write requests need an X-CSRF-Token header. The client grabs it from the
<meta name="csrf-token"> tag on the /recipes page on demand.
HTTP Basic auth on /api/v1/... is not supported (returns 401).
Recipe payload shape
The single-recipe response has 65 fields. Writable on POST/PUT include:
title, description, source, url, servings, yield, scaling,
prep_time, cook_time, total_time (minutes),
course_id, cuisine_id, main_ingredient_id, tag_titles (comma list),
directions (free text), private, draft, rating,
nutrition strings (calories, sodium, etc.), and ingredients.
Ingredients use Rails nested-attributes β the wire field is
recipe_ingredients_attributes, not ingredients. The client and the
create_recipe / update_recipe tools accept the friendlier name
ingredients and translate. Each entry:
{ "title": "water", "amount": "1", "unit": "cup", "note": "", "position": 1 }To delete an existing ingredient on update, include its id plus
"_destroy": true. The server back-fills amount_float, metric_amount,
metric_unit, and similar_titles.
π Layout
One package, three layers. The logic lives in core/; the MCP server and the
CLI are thin adapters over the same registry.
src/
core/
client.ts API client β no MCP, no CLI, no I/O beyond fetch
tools.ts the tool registry: name, description, Zod shape, handler
session.ts credentials + cookie-cache bootstrap, shared by both adapters
version.ts version read from package.json at runtime
mcp/server.ts registry -> MCP tools (stdio transport)
cli/
main.ts registry -> subcommands, help, dispatch
args.ts argv -> Zod-validated arguments
render.ts results -> tables (or raw JSON under --json)
scripts/ smoke tests and the docs coverage check
tests/ offline unit tests (fetch is stubbed)
index.ts public entrypoint for library consumers
server.ts back-compat shim, see belowAdding a tool means adding one entry to src/core/tools.ts. It appears in
the MCP server and the CLI at once, with the same name, schema, and
description β they can't drift apart.
Other files worth knowing:
docs/TOOLS.mdβ per-tool reference with input/output shapes and gotchas. Hand-written;npm run check:docsfails if it and the registry disagree about which tools exist..claude/skills/plan-to-eat/SKILL.mdβ how to use the MCP tools well..claude/skills/plan-to-eat-cli/SKILL.mdβ the same, for the CLI..claude-plugin/β Claude Code plugin and marketplace manifests..mcp.jsonβ the plugin's MCP server declaration.dist/β emitted bynpm run build.
Upgrading from β€0.4? The server moved from
dist/server.jstodist/mcp/server.js. The old path still works β it's a shim that loads the new one β so existing host configs and deployments keep running. New configs should use the new path or theplan-to-eat-mcpbin.
Playwright was used during reverse engineering and is kept as a devDependency
for any future API-discovery work; the runtime depends only on fetch, the
MCP SDK, and Zod.
Scripts
Command | What it does |
| Compile |
| Same, in watch mode. |
| Run the compiled MCP server ( |
| Run the CLI without linking it globally. |
| Smoke-test the client end-to-end against your account (recipes). |
| Smoke-test the planner write endpoints (creates + cleans up test events on a date 6 months out). |
| Smoke-test the MCP server end-to-end (spawns it and calls tools). |
| Check |
| Offline unit tests with |
| Remove |
The verify* and test scripts hit your real account (test:unit doesn't). They create and delete
their own test data, but they are not a dry run β see
CONTRIBUTING.md.
π€ Contributing
Bug reports, new endpoints, and upstream-breakage fixes are all welcome β see CONTRIBUTING.md. The one thing to know up front: the test scripts run against a real Plan to Eat account and create real data (then clean it up). Use your own.
π Support Plan to Eat
This whole project exists because Plan to Eat is great. If you find this useful, the best thing you can do is give Plan to Eat a try with my referral link. Free 14-day trial, no card needed.
License
MIT β see LICENSE. Do whatever you want with it; just don't blame me if Plan to Eat ships a breaking change.
Available Tools
36 toolsadd_leftover_mealA
Schedule a leftover meal derived from a previously planned recipe event. Duplicates the source event with plan_leftover=true and optionally moves the duplicate to a different date/section. Returns the new event.
Inputs: source_event_id (required), date, section.
add_planner_ingredientA
Add a freeform "ingredient" entry to a meal slot (e.g. "2 lbs ground beef" attached to Wednesday dinner).
Inputs: date (required), title (required), section (required).
add_planner_noteB
Add a freeform "note" entry to a meal slot (e.g. "Defrost chicken" on Tuesday breakfast).
Inputs: date (required), title (required), section (required).
add_planner_recipeB
Add a recipe to a meal slot on the planner. Returns the newly created event (id, date, section, servings, ...).
Inputs: date (required), section (required), recipe_id (required), frozen_id.
add_shopping_list_itemsA
Add items to the shopping list. Only title is required per item. Leave category_id out and Plan to Eat guesses the aisle; leave store_id out and the item goes to the store it was last bought at, falling back to the default store if it's new. Returns the lines that were created.
Inputs: items (required).
create_recipeB
Create a new recipe. Only title is required. Ingredients are an array of {title, amount, unit, note, position}.
Inputs: fat, url, draft, fiber, +23 more.
delete_frozen_recipeA
Mark a frozen entry as consumed (the portion was eaten or thrown out). Soft-delete: API sets count to 0; the entry persists as history.
Inputs: id (required).
delete_planner_eventB
Delete a planner event by id.
Inputs: id (required).
delete_recipeB
Delete a recipe by id.
Inputs: id (required).
duplicate_planner_eventA
Duplicate a planner event. Set plan_leftover=true to mark the copy as a leftover.
Inputs: id (required), plan_leftover.
find_planned_datesA
Find planner events for a given recipe, optionally constrained to a date range. Useful for "is this recipe already planned this week?" checks before scheduling.
Inputs: recipe_id (required), end_date, start_date.
freeze_recipe_portionsA
Mark N portions of a previously cooked recipe as frozen. event_id ties the frozen entry back to the planner event the portions came from.
Inputs: count (required), event_id (required), servings (required), recipe_id (required).
get_countsA
Get recipe-book counts: { friends, queued, frozen }.
Inputs: no named parameters.
get_planner_weekA
Fetch all planner events between start_date and end_date (inclusive, YYYY-MM-DD). end_date defaults to start_date + 6 days. Recipe events are enriched with recipe_title.
Inputs: start_date (required), end_date.
get_recipeA
Get a single recipe by id, including directions, ingredients, tags, prep_notes, comments.
Inputs: id (required).
get_shopping_listA
Read the shopping list. Each line gives what to buy plus the store it's assigned to (store_title / store_id, where a null store_id means the account's default store) and the aisle it's filed under (grocery_category_title / grocery_category_id). item_ids is the handle for updating or removing that line β Plan to Eat merges duplicate ingredients into one line, so it's an array. recipe_ids shows which planned recipes pulled the item in.
Inputs: no named parameters.
list_coursesA
List all courses (Appetizers, Main Course, etc.).
Inputs: no named parameters.
list_cuisinesA
List all cuisines.
Inputs: no named parameters.
list_friendsA
List friends.
Inputs: no named parameters.
list_frozen_recipesA
List what's in the freezer. Each entry: { id, recipe_id, count, servings, frozen_on }. By default only returns active entries (count > 0); pass include_consumed=true to also see history (the API soft-deletes by zeroing the count rather than removing the row).
Inputs: include_consumed.
list_grocery_categoriesA
List the user's grocery aisles (Produce, Dairy, ...). Use the ids as category_id on shopping list items.
Inputs: no named parameters.
list_main_ingredientsA
List all main-ingredient categories.
Inputs: no named parameters.
list_planner_eventsA
List all planner (calendar) events. Each entry: { date, recipe_id, section, servings, ... }.
Inputs: no named parameters.
list_recipesA
List the user's whole recipe book (own + saved + queued + friend recipes), optionally filtered by title words, course, or ownership. Narrow with a filter when you can: the unfiltered book can run to well over a thousand rows. Each row has id, title, owned, and whichever of course_title, cuisine_title, main_ingredient_title, tag_titles, total_time (minutes) and rating are set β empty ones are left out. Rows carry no ingredients, directions, description, servings or nutrition β call get_recipe on an id for those. Inactive, removed and hidden recipes are left out.
Inputs: full, course, search, owned_only.
list_storesA
List the user's grocery stores. Use the ids as store_id when adding or re-filing shopping list items.
Inputs: no named parameters.
list_tagsB
List all tags.
Inputs: no named parameters.
move_planner_eventA
Move an existing planner event to a different date and/or section. Works for recipe, note, and ingredient events.
Inputs: date (required), section (required), event_id (required).
remove_shopping_list_itemsA
Remove lines from the shopping list. This is a soft delete, the same one the app does, and restore_shopping_list_items undoes it β but nothing can list removed lines, so keep the item_ids around if the removal might need undoing.
Inputs: item_ids (required).
reorder_planner_eventsB
Reorder events within a section. Pass event_ids in the desired order. All events should belong to the same date+section for the reorder to be meaningful.
Inputs: event_ids (required).
restore_shopping_list_itemsA
Put previously removed shopping list lines back on the list. Takes the item_ids the line had before it was removed.
Inputs: item_ids (required).
set_planner_servingsC
Set the servings count on a recipe planner event.
Inputs: event_id (required), servings (required).
update_planner_entry_textA
Update the text of a note or ingredient planner entry. Note: uses the description field on the wire even though entries are created with title.
Inputs: id (required), description (required).
update_planner_optionsA
Update planner display/behaviour preferences (time_zone, planner_start_day, calendar_settings.show_calories, etc). Use Rails-style nested keys like user[time_zone] or calendar_settings[show_calories]. Rarely needed.
Inputs: options (required).
update_recipeB
Update an existing recipe. Pass only the fields you want to change. To remove an ingredient include {id, _destroy: true} in ingredients.
Inputs: id (required), fat, url, draft, +24 more.
update_shopping_list_itemsA
Change shopping list lines. Pass item_ids from get_shopping_list β naming any id of a line affects that whole line. Moving items to a different store or aisle (store_id / category_id on their own) works across as many lines as you like; editing the text (title, amount, unit, note) applies to one line at a time, and collapses a merged line into a single row keeping its combined quantity. Returns the lines as they now stand, so re-read item_ids from the result rather than reusing the ones you sent.
Inputs: item_ids (required), note, unit, title, +3 more.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelogβ¦
TDQS
Scored across 36 tools
Most tools have clearly distinct resource/action targets across recipes, planner events, shopping list, and freezer. A few planner tools overlap meaningfully: add_leftover_meal largely duplicates duplicate_planner_event with plan_leftover=true, and list_planner_events/get_planner_week/find_planned_dates serve similar lookup needs. Still, descriptions generally help an agent choose correctly.
Almost every tool follows a predictable snake_case verb_noun convention: get_recipe, create_recipe, list_recipes, add_planner_recipe, move_planner_event, update_shopping_list_items. Minor names like get_counts and find_planned_dates are still readable and consistent enough. There is no mixed casing or chaotic verb style.
36 tools is heavy for the apparent domain and exceeds the typical well-scoped range. Many planner operations are highly granular (add_note, add_ingredient, set_servings, reorder, update_entry_text, update_options), which could likely be consolidated. The large surface increases selection burden even though the domain is broad.
Core lifecycles are covered: recipes have CRUD, planner events have create/list/move/reorder/duplicate/delete, shopping list has read/add/update/remove/restore, and freezer entries have list/freeze/consume. Minor gaps exist, such as menus only being listable with no create/update/delete, and stores/grocery categories only being listable. These are workable omissions rather than severe dead ends.
Maintenance
Related MCP Connectors
Recipe search, ingredient matching, nutrition filters and a private cookbook for any AI agent.
AI meal plans that fill your Kroger/Instacart cart - pantry-aware lists, all from chat.
Search, save, organize, cook, and share recipes with any AI assistant.
Persistent food journal for AI assistants. Log meals, save recipes, plan meals and track nutrition.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to interact with Mealie for recipe management, meal planning, and shopping list operations. Supports searching and managing recipes, creating meal plans, and generating shopping lists from recipes or meal plans.7MIT
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to interact with Mealie recipe databases, allowing users to manage and query their recipes through natural language conversations.17 npmMIT
- AlicenseAqualityDmaintenanceEnables AI assistants to interact with AnyList for managing shopping lists, recipes, and meal planning. Users can retrieve recipe details, add ingredients to lists, and schedule meals on their AnyList calendar.10ISC
- FlicenseNot gradedqualityBmaintenanceEnables AI assistants to search recipes, compose nutritionally balanced meals, optimize weekly meal plans based on macro targets for family members, and generate consolidated grocery lists from a personal recipe database.-