Skip to main content
Glama
edgargalvan

mise-en-place

by edgargalvan
README.md
# mise-en-place

An MCP server that returns trustworthy macronutrient data for a named
ingredient at a named quantity, sourced from USDA FoodData Central.

It has one job: when an LLM is planning a meal or building a recipe, give it
real calories/protein/carbs/fat for a real food at a real quantity, never a
plausible-sounding guess. Composition, recipe design, and arithmetic stay the
calling model's problem. Correct inputs are this server's only problem.

## What it is

`mise-en-place` is not a nutrition database of its own. It's a lookup layer
in front of [USDA FoodData Central](https://fdc.nal.usda.gov/), built so an
LLM can search for a food, resolve a quantity ("1 medium onion", "2
tablespoons", "6 oz") to grams, and get back real, sourced macros instead of
inventing one. It runs over MCP (stdio), so any MCP-capable client (Claude
Code, Claude Desktop, or anything else that speaks the protocol) can connect
to it directly.

Eight tools: `search_foods`, `get_nutrition`, `get_nutrition_batch`,
`save_override`, `save_recipe`, `get_recipe`, `get_recipe_history`,
`list_recipes`. Full signatures and schemas are in `INTERFACES.md`.

## How to use it

**Requirements:** Python 3.11+, a free [USDA FoodData Central API
key](https://fdc.nal.usda.gov/api-key-signup) (instant signup).

```bash
git clone <this repo>
cd mise-en-place
uv sync   # or: pip install -e .
export USDA_API_KEY=<your key>
```

Register it with an MCP client, e.g. Claude Code:

```bash
claude mcp add mise-en-place -s local -e USDA_API_KEY=<your key> -- \
  uv run --directory /path/to/mise-en-place mise-en-place
```

Restart the client so it picks up the new connection, then just ask for what
you're cooking. The server's own `instructions` (visible in `server.py`'s
`_SERVER_INSTRUCTIONS`) travel with the connection and tell the model how to
use the tools correctly, so nothing needs to be pasted into the chat by hand.
If you're running recurring meal-planning sessions with your own macro
targets, see `MEAL_PLANNING_PROMPT.md`. Targets are per-person and
per-meal, so the server never stores them; that one file is where they live.

Personal corrections and saved recipes live in `~/.mise-en-place/`
(`MISE_HOME`), never committed. `masses.toml` at the repo root is the one
shipped, checked-in default table of common household-measure weights (a
medium onion, a large egg): human-written, cited, and safe to disagree with
by editing it.

## Why use it

A lot of "get me the calories" tools answer a question they don't actually
know the answer to. This one doesn't:

- **Never invents a nutrient value.** If USDA doesn't have the number, the
  tool raises or reports it as explicitly absent, never a zero and never an
  estimate standing in for a fact.
- **Never falls back to a round number.** A quantity that can't be resolved
  to a mass (through USDA's own portion data, your own saved corrections, or
  the shipped `masses.toml` defaults) raises instead of defaulting to 100g
  or guessing a density.
- **Every uncertain value says so, out loud.** A low-confidence match, a
  candidate the tool wasn't sure about, an assumed mass: all of it rides
  back to the calling model as a structured caveat, not a number that looks
  exactly as confident as a verified one.
- **Corrections make the tool better, permanently.** `save_override` and
  `save_recipe` mean a mistake gets fixed once, not re-guessed on every
  future lookup of the same ingredient.
- **Measurably more accurate than a model guessing on its own.** A 10-recipe
  benchmark against hand-verified label data put this tool's mean error at
  4.99%, against 8.26% for the same model with no tool access. Methodology
  and the current number are below.

### How we test that this is actually true

The rule "never invent a value" is easy to claim and easy to quietly violate.
Two things back it up.

**Live, re-verified test data.** Every USDA ID this project cites, in
`masses.toml` and in the test suite's golden values, is re-fetched from the
real API on every run and checked against what it's claimed to be
(`tests/test_golden_values.py`, `tests/test_masses_toml.py`). A citation
that's gone stale fails loudly instead of sitting there looking authoritative.

**Accuracy validated against real product data, not another recipe site's
opinion.** An early accuracy pass compared this tool's output against a
popular health-recipe site's published nutrition figures and found the tool
diverging by a lot on some recipes. A closer look showed the divergence ran
the other way: hand-checking the worst cases against actual product
nutrition labels showed this tool's numbers were the ones close to reality,
and the third-party site's were the outlier. Comparing against any recipe
site turned out to be the wrong test, since its own methodology is opaque
and there's no way to know what product it priced, what serving-yield
convention it used, or what nutrition database sits behind it.

The real accuracy benchmark this points toward (`tests/golden_recipes/`) is
built from two sources deliberately kept apart: **USDA data for genuine
commodity ingredients** (a potato, granulated sugar: nothing with a brand to
speak of), and **real product nutrition labels for anything a specific
commercial product plausibly means** (a brand of tortilla, a packaged
tofu). Using USDA data to validate a USDA-backed tool's generic matches
would just be checking whether it agrees with itself; label data is the
only independent check that actually means something for branded-shaped
ingredients. Several of the recipes deliberately carry the *same*
ingredient phrased two ways: a generic name ("tortilla chips") and a
specific one ("Mission tortilla chips"), so the benchmark checks both the
tool's default generic match and whether it correctly finds and prefers a
named product when the recipe asks for one by name.

**Latest result** (10 recipes, tool-equipped agent vs. bare-LLM-recall
agent, both scored against the same hand-verified label data, 2026-08-21):
the tool-equipped agent's mean absolute error across calories/protein/
carbs/fat was **4.99%**, against **8.26%** for the same agent with no
tool access. The tool wins outright on 7 of 10 recipes, decisively on
recipes with a genuinely hard branded ingredient or a meat cut whose fat
content is easy to misremember (chicken thigh, skirt steak). The three
recipes where bare recall did as well or better aren't evidence the tool's
own matching is weak. They're cases where the recipe itself left a
quantity uncountable (a garnish with no stated weight), forcing both
conditions to independently guess and adding noise unrelated to what the
benchmark is trying to measure. See `note_on_TC10` in
`tests/golden_recipes/eval_prompts.json` for the one case worth fixing
before trusting its score. This number will drift as the fixture and the
tool both keep changing; treat it as the most recent reading, not a
permanent claim.

## Where to read more

| If you want to know | Read |
|---|---|
| What the system must do, the confidence model, the macro report | `REQUIREMENTS.md` |
| Module layout, data flow, testing strategy | `ARCHITECTURE.md` |
| Exact tool signatures and data schemas | `INTERFACES.md` |
| What's built and what's left | `TASKS.md` |
| Rules for anyone (human or agent) working on this codebase | `CLAUDE.md` |

TDQS

A4.4/5.0

Scored across 8 tools

Disambiguation5/5

Each tool has a clearly distinct role: search_foods discovers candidate ingredients, get_nutrition and get_nutrition_batch retrieve verified nutrition data for one or many ingredients, save_override permanently corrects lookup data, and save_recipe/get_recipe/get_recipe_history/list_recipes manage the lifecycle of saved recipes. The boundary between fetching a food and fetching nutrition, or between correcting a lookup and saving a full recipe, is strongly reinforced by detailed descriptions.

Naming Consistency4/5

Most tools follow verb_noun (search_foods, get_nutrition, save_recipe, list_recipes). Minor deviations: save_override is verb_verb rather than verb_noun, and get_nutrition_batch is a noun modifier rather than verb_noun. Mixed conventions, plus the label get_nutrition_batch when save/retrieve/recipes, however, are predictable and readable, so the set remains usable.

Tool Count5/5

8 tools are correctly calibrated for a nutritional dataset and recipe store: 2 ingredient retrieval tools, batch mode, correction, recipe storage, and versioning. Each of the 8 tools is clearly justified and applicable, and complete omission is saved in the moderation of a useful compact-size dataset.

Completeness4/5

The core workflow is well covered: find ingredient, get nutritional info, batch retrieval, correct with override, save and read recipes, history and listing. The missing note is delete_recipe; save_recipe remains an append-only operation (each save creates a new version) with no way to remove old recipes. Which is a real limitation—a user can add recipes but cannot delete them. The current scope may be intentional, but this omission prevents entire recipe lifecycle management.

Maintenance

ActivityMaintained
ResponsivenessNo issues