Skip to main content
Glama
josepenam

cookidoo-mcp

by josepenam
README.md
# cookidoo-mcp

This is what happens when you love your wife, love AI, but she loves her Thermomix even more. 😘
An [MCP](https://modelcontextprotocol.io) server that lets an AI assistant **browse, compare, plan, and shop** from [Cookidoo](https://cookidoo.international) β€” and **save arbitrarily tweaked custom recipes** back to your account ("load this recipe, apply my manual tweaks, save it").

It ships with a companion **usage skill** that teaches the assistant *how* to use the tools well (tweak methodology, Thermomix step formatting, safety rails). The skill is discoverable at runtime through a `get_skill` tool, so even MCP clients without filesystem-skill support can pull the methodology on demand.

> **Unofficial project.** Not affiliated with, endorsed by, or connected to Vorwerk, Thermomix, or Cookidoo. It talks to an unofficial/undocumented API that may change or break at any time. Use at your own risk and only within Cookidoo's Terms of Service (personal use β€” no scraping at scale). You need an active Cookidoo subscription.

---

## What it can do

| Area | Tools |
|---|---|
| **Discovery** | `search_recipes`, `get_recipe_details` |
| **Custom recipes** | `add_custom_recipe_from` (copy at a serving size), **`create_custom_recipe` (create from scratch β€” the "save my tweaks" path)**, `get_custom_recipe`, `remove_custom_recipe`, `update_custom_recipe` |
| **Tweak workflow** | `draft_tweak_from_recipe` β†’ edit in conversation β†’ `create_custom_recipe` |
| **Meal planning** | `get_calendar_week`, `add_recipes_to_calendar`, `remove_recipe_from_calendar`, `add_custom_recipes_to_calendar`, `remove_custom_recipe_from_calendar` |
| **Shopping list** | recipe / ingredient / additional-item management, check-off, `clear_shopping_list` |
| **Collections** | managed + custom collection CRUD |
| **Account** | `get_user_info`, `get_active_subscription` |
| **Suggestions** | `suggest_recipes_from_ingredients` |
| **Skill** | `get_skill` β€” serves the usage methodology at runtime |

The single biggest capability over prior art is **`create_custom_recipe`**: it builds a fully custom recipe (name, ingredients, steps, servings, times, hints) via the undocumented `created-recipes` endpoint. This is what makes "load a recipe, apply my manual tweaks, save the result" possible.

### Known limitations (v1)

- **No guided-cooking automation.** Uploaded custom recipes are readable on the Thermomix but do **not** drive the machine (per-step temperature/speed/time annotations are not reliably reverse-engineered). Write steps machine-adjacent (`3 min / 100Β°C / speed 2`) so they're easy to follow on-device.
- **No cooking steps from the library.** `get_recipe_details` returns ingredients, timing, nutrition, etc., but the Cookidoo API does not expose guided-cooking steps for library recipes, so a draft made from a library recipe starts with an empty step list for you to fill in.
- Custom recipes are always **PRIVATE**.
- No in-place edit is guaranteed in v1: the safe flow is create-new (+ optionally delete the old one). `update_custom_recipe` is provided as a best-effort PATCH but shares the reverse-engineered schema risk.
- No recipe images; no multi-account support.

---

## Requirements

- Python **β‰₯ 3.13**
- [`uv`](https://docs.astral.sh/uv/)
- An active Cookidoo subscription

## Configuration

All configuration is via environment variables (or a local `.env`; **environment wins** over `.env`). Copy the example:

```bash
cp .env.example .env
# then edit COOKIDOO_EMAIL / COOKIDOO_PASSWORD
```

| Variable | Required | Default | Meaning |
|---|---|---|---|
| `COOKIDOO_EMAIL` | βœ… | β€” | Cookidoo account email |
| `COOKIDOO_PASSWORD` | βœ… | β€” | Cookidoo account password |
| `COOKIDOO_COUNTRY_CODE` | | `ch` | Cookidoo TLD (`cookidoo.<cc>`) |
| `COOKIDOO_LANGUAGE` | | `de-CH` | Content locale (`fr-FR`, `en-GB`, `it-IT`, `es-ES`, …) |
| `COOKIDOO_URL` | | derived | Overrides the derived `https://cookidoo.<cc>/foundation/<lang>` |

> **Credentials are stored in plaintext locally** (in `.env` or your client config). They are sent only to the configured Cookidoo host. `.env` is gitignored. Never commit real credentials.

## Run it

```bash
# install deps into a project venv
uv sync

# dev loop with the MCP Inspector
uv run mcp dev src/cookidoo_mcp/server.py

# run the server directly (stdio transport)
uv run cookidoo-mcp
```

## Client integration

This is a **local stdio** server: it runs as a process on your machine. Which of
the following applies depends on the client. In all cases the server reads
credentials from your `.env`, so no secrets go into client configs.

| Client / surface | Works with a local server? | How |
|---|---|---|
| Claude Code (CLI, and the Code/Cowork sessions in Claude Desktop) | βœ… | `claude mcp add` |
| Claude Desktop chat β€” classic builds | βœ… | `claude_desktop_config.json` |
| Claude Desktop chat β€” newer "Extensions" builds | βœ… | install the `.mcpb` extension |
| Cursor / other stdio MCP clients | βœ… | `mcpServers` config entry |
| ChatGPT, and Claude on mobile | ❌ (local) | needs a **remote** server β€” see [Remote / mobile / ChatGPT](#remote--mobile--chatgpt) |

### Claude Code (CLI or the Code/Cowork sessions in Claude Desktop)

Register it once at user scope (available in every session):

```bash
claude mcp add --scope user cookidoo -- \
  "$(command -v uv)" run --directory /absolute/path/to/cookidoo-mcp cookidoo-mcp
```

Verify with `claude mcp list` (should show `cookidoo … βœ” Connected`). MCP tools
load when a session starts, so open a **new** session to see them.

### Claude Desktop chat β€” classic builds (config file)

Edit `claude_desktop_config.json`
(macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`) while
Claude Desktop is **fully quit**, then relaunch:

```json
{
  "mcpServers": {
    "cookidoo": {
      "command": "/absolute/path/to/uv",
      "args": ["run", "--directory", "/absolute/path/to/cookidoo-mcp", "cookidoo-mcp"]
    }
  }
}
```

Use the **absolute** path to `uv` (find it with `command -v uv`) β€” Claude Desktop
launches with a minimal `PATH` and often can't find a bare `uv`.

> Note: newer Claude Desktop builds (the "Cowork" / Extensions UI) **rewrite this
> file and drop custom `mcpServers`**. If your added server disappears after a
> restart, you're on a newer build β€” use the extension below instead.

### Claude Desktop chat β€” newer "Extensions" builds (`.mcpb`)

Newer builds load servers as **Extensions**. Build and install the bundle:

```bash
sh extension/build.sh            # writes ~/Downloads/cookidoo.mcpb
```

The launcher (`extension/launch.sh`) finds `uv` on `PATH` and expects the repo at
`~/Documents/GitHub/cookidoo-mcp`; if yours lives elsewhere, either edit that file
or export `COOKIDOO_MCP_DIR=/path/to/cookidoo-mcp` before launching Claude, then
rebuild.

Install it:

1. Double-click `~/Downloads/cookidoo.mcpb` in Finder β†’ **Install** in Claude, **or**
   Claude β†’ **Settings β†’ Extensions β†’ Advanced β†’ Install Extension…** and pick the file.
2. If blocked as "unverified", enable local/unverified extensions in
   Settings β†’ Extensions (Advanced), then install.
3. `cookidoo` now appears in the **Connectors** menu (like the built-in
   extensions) β€” toggle it **on**. First use logs in via your `.env`.

### Cursor / other stdio clients

Use the same `mcpServers` JSON as the classic-build example above.

### Remote / mobile / ChatGPT

**ChatGPT and the Claude mobile apps cannot run a local process on your machine**,
and ChatGPT does not support `.mcpb` extensions at all β€” both only talk to a
**remote** MCP server reachable over HTTPS. Making that work means:

1. Run the server with HTTP transport instead of stdio.
2. Add an authentication layer (a bearer token / OAuth) β€” a hosted server exposes
   your Cookidoo account, so it must not be open to the internet.
3. Expose it over HTTPS (a `cloudflared`/`ngrok` tunnel for testing, or a small
   always-on host).
4. Add it as a **custom connector** by URL: claude.ai β†’ Settings β†’ Connectors
   (then it syncs to the Claude mobile app), or ChatGPT β†’ Settings β†’ Connectors /
   developer mode (paid plans).

This is not wired up in v1 β€” it's the natural next step if you want mobile/ChatGPT.

---

Any client can call `get_skill("index")` first to discover the recommended workflows; clients with filesystem-skill support can instead load `skills/cookidoo-usage/SKILL.md` directly. Same content, two delivery paths.

---

## Example: "load a recipe, apply my tweaks, save it"

A representative transcript of the canonical flow (see `get_skill("recipe-tweaking")`):

> **User:** Load recipe r59322, make it for 6 people, swap the butter for olive oil, and save it.
>
> **Assistant:** *(calls `draft_tweak_from_recipe("r59322", target_servings=6)`)* Here's the scaled draft (4 β†’ 6 servings). I applied your tweaks:
> - `200 g butter` β†’ **`75 g olive oil`** *(swap + reduced, fat swaps aren't 1:1)*
> - `300 g flour` β†’ `450 g flour` *(scaled)*
> - "salt to taste" β€” left unchanged (unscaled)
>
> Note: the library doesn't expose the original steps, so I've drafted them from your instructions. **Review and confirm before I save.**
>
> **User:** Looks good, save it.
>
> **Assistant:** *(calls `create_custom_recipe(...)` after confirmation)* Saved βœ… β†’ `https://cookidoo.ch/created-recipes/de-CH/<id>`
> Reminder: it's readable on your Thermomix but does not include guided-cooking automation.

The rule the skill enforces: **never call `create_custom_recipe` without showing the final draft and getting an explicit yes.**

---

## Development

```bash
uv sync --extra dev
uv run ruff check .
uv run ruff format --check .
uv run pytest -q            # pure-python unit tests, no network
```

Manual end-to-end verification against your own account: see [`tests/smoke.md`](tests/smoke.md).

### Security & privacy

- TLS verification is **always on**. The one workaround is clearing `VERIFY_X509_STRICT` (Vorwerk's certificate is missing the key-usage extension that Python 3.13+ enforces strictly); hostname and chain verification remain enabled. PRs that disable TLS verification are rejected on principle.
- Credentials only via env/`.env`; never logged (only the login email is logged, once, at INFO).
- No telemetry. The server talks only to the configured Cookidoo host.
- Destructive tools (`remove_*`, `clear_shopping_list`) declare their irreversibility in their docstrings so MCP clients surface it in approval prompts. The skill mandates explicit confirmation before `create_custom_recipe`, every `remove_*`, and `clear_shopping_list`.

## Acknowledgements

Builds on the prior art of [`otisthescribe/cookidoo-mcp`](https://github.com/otisthescribe/cookidoo-mcp) (structure, correct TLS handling, read/plan/shop tools), [`alexandrepa/mcp-cookidoo`](https://github.com/alexandrepa/mcp-cookidoo) (the `created-recipes` two-step create/PATCH, reimplemented here **without** disabling TLS), and the [`miaucl/cookidoo-api`](https://github.com/miaucl/cookidoo-api) library (auth, session, endpoint constants, typed models).

## License

[MIT](LICENSE)