mise-en-place
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., "@mise-en-place what are the macros for 4 oz raw chicken breast?"
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.
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, 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.
Related MCP server: nutrition-mcp-server
How to use it
Requirements: Python 3.11+, a free USDA FoodData Central API key (instant signup).
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:
claude mcp add mise-en-place -s local -e USDA_API_KEY=<your key> -- \
uv run --directory /path/to/mise-en-place mise-en-placeRestart 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.tomldefaults) 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_overrideandsave_recipemean 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 |
|
Module layout, data flow, testing strategy |
|
Exact tool signatures and data schemas |
|
What's built and what's left |
|
Rules for anyone (human or agent) working on this codebase |
|
Available Tools
8 toolsget_nutritionA
Get verified macros (kcal, protein, carbs, fat) for one ingredient at one
quantity. fdc_id must come from a prior search_foods call -- never guess one.
amount accepts grams/kg, oz/lb, or a count phrase like "1 medium onion" (the
server resolves counts through a cited chain of real sources; it will raise
rather than guess if it cannot). label is the user's own word for this
ingredient, written verbatim -- do not paraphrase or clean it up, it is used to
judge how confident this match is. query should be a search string that
actually returns fdc_id among its results -- the server re-runs it and checks.
This catches an ID invented from memory; it does not confirm query is the
search you actually used, only that fdc_id is findable through it right now.
Open Food Facts fallback: set source to whatever
search_foods returned for the candidate you chose ("usda", the default, or
"off"). For "off", fdc_id holds the Open Food Facts barcode search_foods
already gave you, query is not consulted (no re-verification exists yet for
this source), and count-phrase quantities only resolve if the product itself
states a serving size -- otherwise give an explicit mass (grams/oz). The
response always carries a caveat naming this a crowdsourced, not USDA, source.
The response always includes a summary, a report_path, and totals (the
plate sum, computed server-side, even for one item and even with no target --
never add it up yourself). Read the summary in full and relay every flagged
item to the user, not just the confirmed ones -- a caveat you drop silently
(a low-confidence match, an assumed mass) defeats the only safety mechanism
this tool has. A macro genuinely absent from a food (no protein data at all,
for example) comes back as null with a caveat naming it, never as zero --
zero means the food truly has none.
The macros this tool returns are the only ones to state as fact for a food it matched -- never invent or estimate your own number in their place. The one narrow exception: if search_foods finds nothing at all for an item, you may estimate that single item's nutrition instead of leaving it out, but only if you say plainly it is your own guess, not verified data (C-28).
Pass target if the user has a kcal/protein/carb/fat goal for this lookup; use
the returned totals.outcomes, never compute whether a target was met yourself.
Inputs: req (required), target.
get_nutrition_batchA
Same as get_nutrition, for multiple ingredients in one call -- use this instead
of calling get_nutrition in a loop, it is far faster to iterate on a recipe
with. One bad item does not fail the batch: a failed lookup comes back as a
marked failure alongside the successful ones, in the same order as reqs.
Read the whole summary before presenting results to the user: it names every
item that needed judgment, not just the ones that failed outright. Burying
three flagged items under eleven confirmed ones and only mentioning the
confirmed ones is the single most damaging way to misuse this tool. A macro
absent from one ingredient (no protein data at all, for example) comes back
null with a caveat naming it, not zero, and makes that macro's plate total
totals.macros come back null too -- reported as incomplete, not silently
summed as though the absent ingredient contributed nothing.
totals (the plate sum) is always computed server-side, whether or not you
pass target -- never add up the plate's macros yourself, even for a bare
"what does this plate total" question. Pass target once for the whole plate
to also get totals.outcomes, the per-macro met/not-met judgment; without
one, every macro's outcome reads "no target" and only the sum is judged.
Inputs: reqs (required), target.
get_recipeA
Fetch a saved recipe. Omit version for the latest. Macros are computed
fresh from the stored plate on every call, never cached -- if this ever
disagrees with something said earlier in the conversation, the plate or the
underlying food data changed since then; trust this call.
Inputs: name (required), version.
get_recipe_historyA
Fetch every saved version of a recipe, in order, to see how it evolved and
why -- each version's notes explains what changed. Use this before assuming
a recipe's current numbers are the only numbers it ever had.
Inputs: name (required). Returns: result
list_recipesA
List saved recipes, optionally filtered by protein range, calorie range, or whether a given fdc_id appears as an ingredient. Filters run against each recipe's current plate, computed fresh on read.
Inputs: filter. Returns: result
save_overrideA
Permanently correct what a phrase means or what a count weighs, for this user. Use this the moment a lookup's summary flags something as wrong or low-confidence and the user tells you the right answer -- that correction is the entire point of reading the summary in the first place.
Pass fdc_id to fix which food a phrase resolves to, grams to fix what a
count weighs, or both. At least one is required. You cannot supply your own
description of the food: the server fetches the real USDA description and
writes that, so a bad correction is caught immediately rather than saved
blind.
When correcting grams, phrase must be the exact count phrase you would
pass as amount to get_nutrition (e.g. "1 medium onion"), not just the
ingredient's name (e.g. "onion") -- masses are looked up by that phrase,
and a mass saved under the wrong key is never found again.
Every future lookup of this phrase short-circuits to this correction at high
confidence. note should say why, briefly -- it is the only record of that
later.
Inputs: phrase (required), note, grams, fdc_id.
save_recipeA
Save a new version of a recipe. plate is the user's own plate only, in
absolute grams per ingredient -- never a household total. Each ingredient
needs an fdc_id from a real lookup (never invent one) and its exact amount in
grams. shares records everyone else at the table as a fraction of the user's
plate (e.g. {"maria": 0.75}) -- leave it empty if cooking just for the user,
that is the common case, not a special one.
Saves are append-only: this always creates a new version, it never overwrites
one. notes is required and should say what changed and why (e.g. "corrected
cooked weight after weighing it") -- it is the only record of that later, and
the most useful field in the whole store.
Inputs: name (required), notes (required), plate (required), shares (required).
search_foodsA
Search USDA FoodData Central for candidate foods matching a plain-language query (e.g. "chicken breast", "greek yogurt"). Returns ranked candidates: generic (Foundation, SR Legacy) before branded, always. Raises only if nothing at all matches -- never returns an empty list.
This is the ONLY way to find a valid fdc_id. Never invent, recall, or reuse an
fdc_id from memory or an earlier conversation: call this tool first, read the
returned descriptions, and choose the one that actually matches what the user
meant. Pass this exact query string as query to get_nutrition or
get_nutrition_batch so the server can verify your chosen fdc_id was really one
of these results. Each candidate carries a source field ("usda" or "off") --
pass it through as NutritionRequest.source unchanged; fdc_id itself always
goes in the same field either way.
A returned candidate can still fail to fetch (USDA's own search index can be ahead of its detail records, especially for newer Foundation entries) -- if get_nutrition/get_nutrition_batch reports that id as failed, do not give up: try the next candidate from this same list. A failed fetch on its own is not evidence the food itself doesn't exist.
Open Food Facts fallback: set expect_branded=True when
the ingredient names or clearly implies a specific commercial product (a
brand name, "Mission Carb Balance," "Nature's Own keto bun," or a stated
attribute like "low-carb"/"keto" you suspect USDA does not carry under that
name) -- the same judgment X-7 already asks you to make about when to search
using a stated attribute, extended one step further. When set, this also
searches Open Food Facts (a free, crowdsourced, barcode-indexed database)
and reserves a slice of limit for any candidate that passes a real
plausibility gate (no impossible or self-contradictory nutrition values) --
still fundamentally less trustworthy than a USDA record, anyone can edit an
entry, and get_nutrition always attaches a caveat naming this plainly.
Leave it False (the default) for a plainly generic ingredient (eggs, potato,
olive oil, chicken breast) -- Open Food Facts is a packaged-goods database
and has nothing to offer a commodity food, so trying it there is pure
latency with no upside. USDA alone is still searched every time regardless
of this flag, and Open Food Facts is still tried as a last resort if USDA
returns literally nothing even when this flag is False, so a real zero-
result case is never silently unhandled just because you forgot to set it.
Inputs: query (required), limit, expect_branded. Returns: result
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 8 tools
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.
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.
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.
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
Related MCP Connectors
Proof-backed nutrition for foods, chain menus, barcodes, supplements. Agents add what's missing.
Natural-language calorie lookup. Send '2 large eggs', get USDA-sourced nutrition data.
Food and nutrition data: search, macros, and comparisons
USDA FoodData Central nutrient lookup, FDA recall watch, EU FMCG labelling via remote MCP
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables AI agents to search the USDA's FoodData Central database and retrieve detailed nutritional information and ingredient lists. It supports comprehensive food data access through keyword searches and structured queries for specific food items.3MIT
- AlicenseAqualityDmaintenanceEnables natural language access to USDA's FoodData Central database with 1M+ foods, supporting search, nutrition facts, food comparison, and daily value calculations.8MIT
- AlicenseNot gradedqualityCmaintenanceProvides tools to search and retrieve USDA Food Data Central information, including food items, nutrients, and food groups, enabling AI agents to query food data through natural language.295 npmMIT
- FlicenseNot gradedqualityDmaintenanceProvides AI assistants with real-time access to nutrition data from USDA FoodData Central and FatSecret, enabling accurate answers with citations for nutrition queries.-