Skip to main content
Glama
illinigirl

Meal Planner MCP

by illinigirl
README.md
# Meal Planner MCP

A self-contained [MCP](https://modelcontextprotocol.io) server that plans a
week of meals from a local recipe library — optimizing for **shared
ingredients**, reusing **leftovers**, and **avoiding recent repeats** — then
generates a consolidated shopping list and a Markdown plan you can stick on the
fridge.

No cloud, no API keys, no database. Clone it and it runs.

```bash
python3 -m venv .venv && source .venv/bin/activate    # isolate deps
pip install -e ".[test]" && pytest -q                 # 76 tests
python -m mealplanner.cli plan --days 7               # try the planner
python -m mealplanner.cli shopping                    # and the shopping list
```

## Example

It ships with a seed library, so it plans the moment you clone it — no data
entry. The planner optimizes for **ingredient overlap** across the week, then
**consolidates** everything into one deduplicated shopping list:

```text
$ python -m mealplanner.cli plan --days 5

Plan for 5 days (household 4):

  2026-06-05   Veggie Fried Rice  (serves 4)
  2026-06-06   Chicken Stir-Fry  (serves 4)
  2026-06-07   Sheet-Pan Chicken & Peppers  (serves 4)
  2026-06-08   Poached Eggs in Tomato Sauce  (serves 4)
  2026-06-09   Chickpea & Spinach Stew  (serves 4)

$ python -m mealplanner.cli shopping

Shopping list:

  [ ] 5  bell pepper
  [ ] 2 can  canned tomato
  [ ] 2 pound  chicken breast
  [ ] 11 clove  garlic
  [ ] 6 tablespoon  soy sauce
  …
```

The MCP tools run the same logic — `plan_week` then `generate_shopping_list` —
so in Claude Desktop you just ask for it in plain language.

## In Claude Desktop

Wired in as a local MCP server, Claude discovers the tools and drives them from a
plain-language ask — here it picks up `plan_week`, calls it with `days: 5`, then
explains the result and offers next steps:

![Claude discovering the meal-planner MCP tools and calling plan_week with days=5](docs/claude-desktop-tools.jpeg)

![The resulting 5-day plan, with Claude explaining the ingredient-overlap optimization and offering to swap a day or build the shopping list](docs/claude-desktop-plan.jpeg)

## Why an MCP (and not just asking Claude)?

A tool only earns its place if it does something the model *can't*. This one
clears that bar on four counts — which is the whole reason it exists:

| Ask | Plain Claude | This server |
|---|---|---|
| "Suggest a sci-fi… er, a pasta dish" | ✅ fine on its own | (a tool adds nothing) |
| "Plan around **my** recipes" | ❌ doesn't know them | ✅ grounded in your local library |
| "Don't repeat what we ate last week" | ❌ no memory across chats | ✅ persistent history file |
| "Give me a plan + list I can keep" | ❌ can't write files | ✅ Markdown export |
| "Merge 1 + 2 + ½ onion across 4 recipes, scaled to 6" | ❌ hand-waves the math | ✅ exact, deterministic |

The model does the *creative* part (which week feels good); the server supplies
the private data, the memory, the persisted artifact, and the exact arithmetic.

## What it does

- **`plan_week`** — greedy optimizer: picks recipes that share the most
  ingredients with what's already chosen, skips anything cooked recently, and
  fills extra nights from serving-surplus leftovers (a batch of chili that
  serves 8 covers two dinners for a family of four). The overlap objective
  *rewards similar recipes*, so it clusters same-protein nights by design;
  `diversity_weight` (off by default) dials in variety vs. waste.
- **`swap_meal` / `remove_meal`** — iterate per day: *"put tacos on Tuesday,"
  "skip Thursday."* The plan, shopping list, and export all update with you.
  ("Make Friday quicker" needs no new tool — Claude calls `suggest_recipes`
  then `swap_meal`.)
- **`generate_shopping_list`** — merges and scales ingredients across the plan's
  cook days, deduped, with no silent unit conversion.
- **`export_plan`** — writes the week + shopping list **and returns it inline**
  (so a remote caller who can't read the server's disk still gets it).
  `format="markdown"` (table + checklist, renders in Claude and note apps) or
  `"text"` (plain text for pasting into Notes / Reminders). With no path it writes
  to a known location under the data dir (not the process cwd, which is
  unpredictable when Claude Desktop launches the server).
- **`set_course`** — recategorize a recipe (mark a stray import as a `Sauce` so
  it stops landing in dinner slots). Curation; the planner relies on this
  normalized field, never on title guessing.
- **`suggest_recipes` / `list_recipes` / `get_recipe`** — query the library.
- **`record_cooked`** — log what you actually made; this is the memory that
  powers avoid-repeats.
- **`add_recipe`** — save one recipe from free text. The everyday way to build
  your library — no file or format needed.
- **`add_recipes`** — bulk-add a whole batch in one call: the fast way to build a
  starter library with no Plan to Eat export. Generate a batch from your tastes,
  review it, and save them all at once (Claude generates; the tool just persists).
- **`import_recipes`** — optional bulk shortcut for an existing Plan to Eat
  export: by `csv_path` (a file on the server — local use) or `csv_content`
  (pasted CSV text — works for a remote caller with no server-disk access).
  Offline, no scraping.

## Seeding your library

Four ways, none requiring any particular app or format:

1. **Just start** — 16 recipes ship in `data/recipes.seed.json`, so it plans a
   week the moment you clone it.
2. **Add as you go (the normal path)** — paste or describe a recipe in chat;
   Claude structures it and calls `add_recipe`. *"Save my chili: 2 lb ground
   beef, an onion, 2 cans tomatoes, kidney beans, chili powder — serves 8."* No
   CSV, no schema.
3. **Generate a starter set (no export needed)** — don't want to add them one by
   one? Ask Claude to generate a batch from your tastes — *"25 quick weeknight
   dinners I'd like, mostly vegetarian"* — review it, and it saves them all in one
   `add_recipes` call. The planner optimizes over recipes you'll actually cook, so
   keep the batch to food you'd really make rather than generic filler.
4. **Bulk migrate (optional)** — already have a Plan to Eat export? `import_recipes`
   loads it in one offline pass. It's a convenience, not a requirement — and
   adding other formats (Paprika, Mealie, plain JSON) is a documented seam.

## Architecture

Pure core + thin adapters. All the logic is I/O-free and unit-tested without a
runtime; the MCP server and the CLI are two adapters over the same functions,
and one module does all the file I/O.

```
src/mealplanner/
  models.py       Recipe · Ingredient · HistoryEntry · PlanDay
  ingredients.py  parse free text → {qty,unit,item} · canonicalize · aggregate   (pure)
  core.py         library search · overlap scoring · avoid-repeats               (pure)
  planner.py      greedy week optimizer (overlap + leftovers + avoid-repeats)    (pure)
  exports.py      shopping-list build · Markdown render                          (pure)
  store.py        JSON persistence · Plan to Eat CSV import          (the only I/O)
  server.py       MCP adapter (FastMCP)
  cli.py          CLI adapter
data/recipes.seed.json   bundled starter library (clones-and-runs)
```

The bundled seed ships in the repo; your mutable state (history, plans,
imported recipes) lives in a gitignored `state.json` under
`~/.meal-planner/` — so your real recipes never land in a commit.

## Use it from Claude

The server runs over **two transports** from one codebase — stdio for a local
Claude Desktop subprocess, or streamable-HTTP so it can be added as a remote
*custom connector* by URL.

**Local (stdio) — Claude Desktop.** Clone, make a virtualenv, and install — the
install puts a `meal-planner` console script inside `.venv/bin`:

```bash
git clone https://github.com/illinigirl/meal-planner-mcp
cd meal-planner-mcp
python3 -m venv .venv && source .venv/bin/activate
pip install -e .
```

Then point your `claude_desktop_config.json` at that script (absolute path), and
restart Claude Desktop — the **meal-planner** tools will appear:

```json
{
  "mcpServers": {
    "meal-planner": {
      "command": "/absolute/path/to/meal-planner-mcp/.venv/bin/meal-planner"
    }
  }
}
```

**As a custom connector (HTTP).** Run it as an HTTP server and point a connector
at the URL:

```bash
meal-planner --http --port 8765          # or MEAL_PLANNER_HTTP=1
# then add http://localhost:8765/mcp as a custom connector
```

(For a *remote* connector — claude.ai / mobile — host it behind a public HTTPS
URL with auth, the same way a production MCP deployment would.)

Then just talk: *"Plan us 7 dinners this week, nothing we had recently, keep
weeknights under 30 minutes — then give me the shopping list."* — and iterate:
*"swap Tuesday for something vegetarian," "skip Thursday."*

## For reviewers — drive it with Claude Code

It's built to be worked in by an agent. Good first tasks, easiest first:

1. **Run the tests** — `pip install -e ".[test]" && python -m pytest -q` (75; the pure-core subset runs on stdlib alone, the tool-layer tests use the MCP SDK).
2. **Improve the ingredient parser** to handle `1 (14 oz) can tomatoes` — see
   `ingredients.parse_ingredient` and add a test.
3. **Add leftover mode B** (cook-once-eat-twice): give recipes `produces`/`uses`
   tags so roast chicken → chicken soup chains. The seed already has both
   recipes waiting.
4. **Add an explicit unit-conversion table** (3 tsp → 1 tbsp) — but only convert
   when asked, never silently.

`CLAUDE.md` is the orientation file: architecture, conventions, design
rationale, and every deliberate simplification (each one a place to extend).

## License

[MIT](LICENSE).

TDQS

A3.7/5.0

Scored across 14 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: adding recipes (single, bulk, CSV import), listing/getting recipes, planning (plan_week, swap_meal, remove_meal), generating shopping lists, exporting, and logging cooked meals. No two tools overlap in function.

Naming Consistency5/5

All tool names use a consistent snake_case verb_noun pattern (e.g., add_recipe, export_plan, record_cooked). The verbs are meaningful and predictably describe the action. No naming convention violations.

Tool Count5/5

14 tools cover the full meal planning workflow without being excessive. Each tool serves a specific need, from recipe management to weekly planning and shopping list generation.

Completeness3/5

Core planning operations are present, but there are gaps: no tool to update or delete user-added recipes (only add and read). This is a notable missing feature for a recipe library, though the planning cycle itself is complete.

Maintenance

ActivityInactive
ResponsivenessNo issues