nutrition-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., "@nutrition-mcpWhat's the nutrition and glycemic load for 2 rotis and a katori of dal?"
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.
nutrition-mcp
An MCP server that gives any assistant or agent sourced nutrition, glycemic load and micronutrient data for Indian dishes and ingredients, for any amount in grams or household measures.

A lunch question in Claude Desktop, with the tool approvals sped up. The model looks up each food with search_food, then sends all three to compute_meal, and every number in the table comes from that call. One line below the table misreads the server: "the rice assumes about 15 g of ghee" is the whole pot. The tool says "15 g ghee, under 4 g per serving".
Problem
Assistants answer nutrition questions about Indian food from memory, with no source and no stated portion, and they often add up a meal wrongly. That matters most for people managing diabetes or a nutrient deficiency. The authoritative Indian tables can't be redistributed, and the largest open database has few Indian dishes. Details are in the case study.
Related MCP server: nutrition-mcp-server
Solution
Search for Indian dishes and ingredients by name, including spelling variants.
Nutrition for an amount in grams or household measures such as a katori or a roti, with the source and the assumed portion stated.
Meal totals, and rankings of foods by any nutrient.
Glycemic load where an openly licensed value exists, and missing, never guessed, where it doesn't.
Daily calorie and protein targets, labelled as estimates.
Every feature, with what it must do, is in docs/requirements.md.
Results
Two evaluations, kept apart (ADR 0006). The rows below are copied from evals/results/summary.md, and each row names its results file.
Grounding. A Gemini model answered 30 questions twice with the same prompt: from memory (Baseline) and with this server's tools over stdio (This project). Each golden answer is the server's own total for the foods in the question, and 4 of the questions name a dish that is not in the data. The error is the mean absolute percentage error (MAPE) over the 26 questions that both runs answered with numbers. Shares run from 0 to 1, and n is the number of questions behind each value, Baseline first, or the number of tool calls for tool errors and latency.
Measure | Baseline | This project | n | Model | Date | Commit | Dataset version | Results file |
Calorie error against the dataset totals, MAPE (%) | 16.0 | 0.0 | 26 / 26 | gemini-3.1-flash-lite | 2026-10-21 | 8262476 | 24143edbb56e | |
Protein error against the dataset totals, MAPE (%) | 13.2 | 0.0 | 26 / 26 | gemini-3.1-flash-lite | 2026-10-21 | 8262476 | 24143edbb56e | |
Carbohydrate error against the dataset totals, MAPE (%) | 20.2 | 0.0 | 26 / 26 | gemini-3.1-flash-lite | 2026-10-21 | 8262476 | 24143edbb56e | |
Answers within 10% on calories (share) | 0.38 | 1.0 | 26 / 26 | gemini-3.1-flash-lite | 2026-10-21 | 8262476 | 24143edbb56e | |
Missing dishes reported (share) | n/a | 0.5 | n/a / 4 | gemini-3.1-flash-lite | 2026-10-21 | 8262476 | 24143edbb56e | |
Failed answers (count) | 0 | 1 | 30 / 30 | gemini-3.1-flash-lite | 2026-10-21 | 8262476 | 24143edbb56e | |
API errors (count) | 0 | 0 | 30 / 30 | gemini-3.1-flash-lite | 2026-10-21 | 8262476 | 24143edbb56e | |
Tool errors (count) | n/a | 0 | n/a / 89 | gemini-3.1-flash-lite | 2026-10-21 | 8262476 | 24143edbb56e | |
p95 tool latency (ms) | n/a | 10.4 | n/a / 89 | gemini-3.1-flash-lite | 2026-10-21 | 8262476 | 24143edbb56e |
The 0.0 is by construction. The golden answers are the server's own totals, so it shows that with the tools the model picked the right food and unit and used the computed total on every one of the 26 questions; it does not show that the totals match what the dishes contain, which the data-accuracy rows below measure against independent records. The Baseline error is also distance from those totals, not from the dishes. The 0.5 for missing dishes and the one failed answer come from the 4 questions about dishes that are not in the data; see Limitations.
Data accuracy. The suite compares the values the server computes for each dish, per 100 g, with an independent record of the same dish: a CoFID 2021 homemade dish or a USDA survey food. The value is the MAPE against those records, and n is the number of dishes compared. A gated nutrient fails CI when it gets worse than evals/thresholds.toml allows.
Suite | Measure | n | Value | Date | Commit | Dataset version | Results file |
data accuracy | Energy (kcal) MAPE (%), gated | 18 | 18.7 | 2026-10-20 | 32aa03d | 24143edbb56e | |
data accuracy | Protein (g) MAPE (%), gated | 18 | 24.8 | 2026-10-20 | 32aa03d | 24143edbb56e | |
data accuracy | Available carbohydrate (g) MAPE (%), gated | 18 | 16.7 | 2026-10-20 | 32aa03d | 24143edbb56e | |
data accuracy | Iron (mg) MAPE (%), reported | 18 | 44.2 | 2026-10-20 | 32aa03d | 24143edbb56e | |
data accuracy | Calcium (mg) MAPE (%), reported | 18 | 46.1 | 2026-10-20 | 32aa03d | 24143edbb56e |
The references are UK and US records, so part of each error is a difference in recipe (water, fat, an enriched grain) or in energy basis rather than wrong data; ADR 0006 names the largest cases.
Architecture
flowchart LR
A[MCP client] -- stdio --> B[MCP server: tools and sources resource]
B --> C[Query functions]
C --> D[(In-memory registry)]
E[Loader and validation] --> D
F[Bundled and user datasets] --> EThe server reads dataset folders at startup, validates them, computes each dish's nutrition from its ingredients, and answers tool calls from memory. The longer description is in docs/architecture.md.
Tech stack
Layer | Choice | Why |
Language and tooling | Python 3.12, uv, ruff, mypy, pytest | One toolchain for build, lint, types and tests |
Protocol | MCP Python SDK 2.x | Typed tools over stdio for any MCP client |
Data models | Pydantic | Validation at every boundary |
Name matching | rapidfuzz | Fuzzy matching for spelling variants |
Container | Docker | A slim image that runs as a non-root user; CI builds it and calls its tools |
CI | GitHub Actions | Lint, tests, image build and secret scan on every push |
Quickstart
git clone https://github.com/Varun-Kambali/nutrition-mcp && cd nutrition-mcp
make setup
make testmake run starts the server on stdio, where it waits for an MCP client. Normally the client starts it for you: see Use it.
Use it
The server runs on your machine and your MCP client starts it. It needs uv. Run it straight from GitHub with uvx, or from a clone with uv.
Claude Desktop. Add the server to claude_desktop_config.json (on macOS in ~/Library/Application Support/Claude/, on Windows in %APPDATA%\Claude\), then restart the app.
From GitHub:
{
"mcpServers": {
"nutrition": {
"command": "uvx",
"args": ["--from", "git+https://github.com/Varun-Kambali/nutrition-mcp", "nutrition-mcp"]
}
}
}From a clone:
{
"mcpServers": {
"nutrition": {
"command": "uv",
"args": ["--directory", "/path/to/nutrition-mcp", "run", "nutrition-mcp"]
}
}
}If Claude Desktop cannot find uv or uvx, put its full path in command (which uvx prints it).
Claude Code.
# From GitHub
claude mcp add nutrition -- uvx --from git+https://github.com/Varun-Kambali/nutrition-mcp nutrition-mcp
# From a clone
claude mcp add nutrition -- uv --directory /path/to/nutrition-mcp run nutrition-mcpThen ask, for example, "protein and carbs in 2 rotis and a katori of rajma?". The tool descriptions tell the model to find the food ids with search_food and take the totals from compute_meal.
The five tools, all read-only, and the sources resource:
Tool or resource | What it gives |
| Food ids by name, spelling variants included, with kind, regions, diet and the units the food accepts (grams in one katori, piece, glass and so on). |
| Nutrients for an amount in grams or a household measure, with the grams used, the measure and oil assumptions, glycemic load when the GI is known, and the sources |
| Per-item and total nutrients and glycemic load for 1 to 30 items, each a food id, amount and unit |
| Foods ranked by one nutrient or by glycemic load, per 100 kcal, 100 g or serving |
| Estimated daily calorie and protein targets for an adult, from the Mifflin-St Jeor equation |
| Every loaded data source with its URL, license, version and attribution |
Nutrient values are rounded to 2 decimals, so 0.0 can be a small amount; a value the source does not report is null, never 0. The details given to compute_targets pass through the model your client uses before they reach the server. The server stores nothing and does not log tool inputs. Logs are JSON lines on stderr; NUTRITION_MCP_LOG_LEVEL sets the level: DEBUG, INFO (the default), WARNING, ERROR or CRITICAL.
Your own data
The bundled data is a starting point. A team with a licensed food table or lab-tested recipes can load its own folders next to it, with no change to the tools. List the folders in NUTRITION_MCP_DATA_DIRS, separated by : (; on Windows), in the env of the client config:
{
"mcpServers": {
"nutrition": {
"command": "uvx",
"args": ["--from", "git+https://github.com/Varun-Kambali/nutrition-mcp", "nutrition-mcp"],
"env": { "NUTRITION_MCP_DATA_DIRS": "/path/to/my-data" }
}
}
}A folder has the same three parts as the bundled data, each with a JSON Schema:
File | Holds | Schema |
| Each source: id, name, URL, license, version, retrieval date and attribution | |
| One row per ingredient: names, diet, source and record, and the 16 nutrients per 100 g, with an empty cell for a value the source does not report | |
| One recipe per file: raw grams of each ingredient, cooked weight, servings, regions, diet, household measures and the fat assumption |
A dish can use the bundled ingredients, so ingredients.csv may hold only its header. Leave water out of a recipe: cooked_weight_g accounts for it. For example, dishes/office_dal.yaml:
id: office_dal
name: Office canteen dal
aliases: [canteen dal]
regions: [south]
diet: veg
servings: 4
cooked_weight_g: 800
ingredients:
- ingredient_id: toor_dal
grams: 150
- ingredient_id: ghee
grams: 20
measures:
katori: 180
assumptions: 5 g ghee per serving in the tadka.
source_id: my_kitchenThe server computes the dish's nutrition from its ingredients, as it does for the bundled dishes. A bad row, an unknown ingredient or an id that is already loaded stops startup with an error on stderr that names the file. The fields are described in docs/architecture.md.
Evaluation
Two evaluations, kept apart because one checks the data and the other checks a model using it (ADR 0006):
Data accuracy compares computed dish values with independent records of the same dishes (
evals/datasets/references.csv, built fromdata_build/reference_map.csvbyscripts/build_references.py). It needs no model and no API key. Run it withmake eval-data; CI runs it on every pull request with--checkagainstevals/thresholds.toml. The latest result is under Results.Grounding asks a Gemini model each question in
evals/datasets/questions.jsonltwice with the same task prompt: once from memory, and once with this server's tools over stdio and the instructions the server sends when a client connects (ADR 0007). Each question lists the food ids, amounts and units behind it, and its golden answer comes from the server's own query functions; a few ask about dishes that are not in the data, where the right answer names the dish as missing. The model ends its reply with a JSON answer, so scoring needs no judge model. A run reports the error against the dataset totals (the mean absolute percentage error on kcal, protein and available carbohydrate, over the questions both runs answered with numbers), the share of answers within 10%, the missing dishes reported, failed answers, API and tool errors and p95 tool latency, and writes them to a results file named by date, model and commit. It calls the model, so it runs only on demand: putGEMINI_API_KEYin.env(see.env.example) and runmake eval.EVAL_MODELoverrides the default model set inevals/grounding.py. Requests start at least 4 seconds apart by default. That is a conservative default, not a published limit: Google shows free-tier rate limits per project in AI Studio.make eval ARGS="--min-interval 2"changes the gap, andmake eval ARGS="--limit 2 --out /tmp/smoke"asks only the first two questions to check the setup; a run with--limitnever replaces the headline. The first full run is under Results.
Testing
make test and make lint. CI runs both, checks data accuracy against evals/thresholds.toml, and builds the Docker image, then starts it the way an MCP client would and calls the tools (tests/integration/test_container.py, which skips unless NUTRITION_MCP_IMAGE names a built image).
Deployment
The server runs on the user's machine: the MCP client starts it and talks to it over stdio, so there is nothing to host and no demo endpoint. A remote server with authentication, rate limits and a spend cap is outside v1 (requirements).
To run it in a container, build the image with docker build -t nutrition-mcp . in a clone, then give the client docker as the command and ["run", "-i", "--rm", "nutrition-mcp"] as the arguments.
Project structure
src/nutrition_mcp/ server, loader, queries and search
src/nutrition_mcp/data/ bundled dataset, with its sources and licenses
schemas/ JSON Schemas for a dataset folder
evals/ evaluation runners, datasets and results
data_build/, scripts/ the reviewed maps and scripts that build the bundled data
tests/ unit and integration tests, and the container test
docs/ case study, requirements, architecture, data notes, decisionsDesign decisions
0001: write the dish recipes for this project and take ingredient values only from openly licensed tables, because the Indian food tables cannot be redistributed.
0002: USDA FoodData Central first, CoFID 2021 where USDA lacks the food or has it only in the wrong form.
0003: a value the source does not report stays null, and a dish total that lacks one names the ingredients behind the gap.
0004: the server answers questions about food and stores nothing about the person; condition-aware logic belongs to the planner.
0005: calorie and protein targets from Mifflin-St Jeor, each constant cited, with the limits of the estimate.
0006: two evaluations, because grounding scores a model against the bundled data and cannot catch wrong data; data accuracy compares computed dishes with independent records and gates CI.
0007: an empty search result says the food is not in the data and is to be reported as missing, and the grounding run passes the server's instructions to the model as a client would.
Limitations and next steps
The grounding result is one run of one model, on the free tier. Its model runs at the provider's default temperature, as Google advises for Gemini 3 models, so its answers, and the numbers built on them, vary from run to run. Its with-tools run passed the model the server's tools but not the instructions the server sends when a client connects, which tell the model to get food ids from search_food, use get_nutrition or compute_meal, and take every number from a tool result. For clients that pass those instructions on to the model, the measured gain is a lower bound. The harness now passes them (ADR 0007), so the next run measures the server as such a client uses it.
Two behaviours with dishes that are not in the data, as observed in the recorded run. For pulao the model searched 5 times and found no pulao, then answered with the numbers for jeera rice. That is a substitution, the behaviour the requirements list as a failure. The data has no pulao, so the choice of another food was the model's, and this run did not pass the server's instructions, so it does not show whether they change it. For masala dosa the model made 7 searches for the dosa and its filling and reached the 8-turn cap without an answer, which is the one failed answer in the table. Only chicken curry and kheer were reported as missing. Since that run, an empty search result carries a note that says the food is not in the data and is to be reported as missing instead of answered with another food (ADR 0007). A new run is needed to show whether that changes either behaviour.
Search reads a plural by trying the query again without the final "s" when it finds nothing as typed, so "rotis", "plain dosas" and "batata vadas" find their dish. Irregular plurals and plurals in other languages are not handled. The data-accuracy evaluation covers only the dishes that have an independent record (n under Results); ADR 0006 lists the others and why they have none. Known limits of the design are in the case study.
What I learned
A tool description is read once; a tool result is read when the model decides. The
search_fooddescription already said to report a missing dish, and in the first grounding run the model still answered pulao with jeera rice after four empty searches. The fix was to put the instruction in the empty result itself (ADR 0007).An evaluation scored against your own data cannot tell you the data is right. With the tools, calorie error against the server's totals was 0.0, by construction. Checking the computed dishes against independent CoFID and USDA records gave 18.7% mean error on calories over 18 dishes, which is what the data-accuracy gate in CI now tracks (ADR 0006, results file).
Fuzzy search needs word rules, not just a score. A plain similarity score matched "tea" inside "steamed" and returned chana masala for "chicken curry" through its alias "chickpea curry". Requiring every query word to match a word of the name fixed both, and plurals came later as a retry in the singular (search.py).
Two open food tables can mean different things by the same nutrient. USDA reports carbohydrate by difference, fibre included; CoFID reports available carbohydrate and gives fibre no energy. Every source value is mapped to one definition before a dish is computed (data notes).
Missing has to stay missing. Urad dal has no fibre value in its source, so idli's fibre is reported as a lower bound with the ingredient named, not as a smaller number that looks complete (ADR 0003).
License and data
The code is MIT. The bundled ingredient table comes from USDA FoodData Central (public domain) and CoFID 2021 (Open Government Licence v3.0); glycemic index values come from a compendium of GI values for non-Western foods (CC BY 4.0). The license and attribution for each data file are in src/nutrition_mcp/data/bundled/LICENSE.md, and the nutrient mappings are in docs/data.md.
Available Tools
5 toolscompute_mealARead-onlyIdempotent
Per-item and total nutrients and glycemic load for a meal of 1 to 30 items, each a food id, amount and unit as in get_nutrition. Use this for any total instead of adding numbers yourself. If an item fails, the error names it and no totals are returned. Totals listed in total_incomplete are lower bounds, and glycemic_load_missing names the items the meal's glycemic load leaves out. A dish's folate, vitamin C and vitamin B12 are totals of its raw ingredients, with no cooking losses applied; each item names them in raw_ingredient_totals, and a meal total that includes a dish carries the same caveat. Values are rounded to 2 decimals, so 0.0 can be a small amount; a value the source does not report is null, never 0.
| Name | Required | Description | Default |
|---|---|---|---|
| items | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| items | Yes | |
| total | Yes | Nutrient amounts, the sum over the items. |
| glycemic_load | Yes | |
| total_incomplete | Yes | |
| glycemic_load_missing | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only cover the read-only/idempotent/closed-world profile, and the description goes well beyond them: atomic failure behavior ('If an item fails, the error names it and no totals are returned'), lower-bound semantics via `total_incomplete`, partial coverage of glycemic load, the no-cooking-loss caveat for folate/vitamin C/B12, 2-decimal rounding, and null-vs-0 convention. These are exactly the traits an agent needs to interpret results correctly.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads purpose and scope in the first clause, then layers caveats in decreasing order of importance: failure atomicity, lower bounds, cooking-loss caveat, rounding/null convention. Dense but every sentence carries an operational fact; slightly long, though nothing is filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, yet the description still pre-explains the fields an agent is most likely to misread (`total_incomplete` as lower bounds, `glycemic_load_missing`, `raw_ingredient_totals`). Combined with the error and rounding semantics, an agent can call this and correctly interpret edge-case results with no further information.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Top-level schema description coverage is reported as 0%, so the description has to compensate, and it does partially: it fixes the item count range and routes the agent to get_nutrition for argument format. However, the amount/unit/food_id semantics it relies on are only implicit ('each a food id, amount and unit'), and the nested $defs do the real work, so this is adequate rather than additive.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (compute) and resource (meal nutrients and glycemic load) with explicit scope: 1 to 30 items, each a food id + amount + unit. The mention of 'as in get_nutrition' and the totals framing lets an agent distinguish it from the single-item sibling get_nutrition and from search_food/find_foods without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a clear directive: 'Use this for any total instead of adding numbers yourself,' which tells the agent when to reach for this over manual arithmetic or repeated get_nutrition calls. It stops short of explicitly excluding the single-item sibling or naming a failure-mode alternative, so it is clear context rather than full when/when-not guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
compute_targetsARead-onlyIdempotent
Estimated daily calorie and protein targets for an adult, from the Mifflin-St Jeor equation with an activity multiplier and a goal adjustment. Use it when asked how much someone should eat in a day. The result shows the formula and the inputs, applies a calorie floor, and is an estimate, not medical advice. Nothing is stored.
| Name | Required | Description | Default |
|---|---|---|---|
| age | Yes | ||
| sex | Yes | ||
| goal | Yes | ||
| activity | Yes | ||
| height_cm | Yes | ||
| weight_kg | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| kcal | Yes | |
| notes | Yes | |
| inputs | Yes | |
| formula | Yes | |
| bmr_kcal | Yes | |
| protein_g | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover safety (readOnly, idempotent, closed-world), and the description adds real value beyond them: the output includes the formula and inputs, a calorie floor is applied, results are estimates not medical advice, and nothing is stored. It doesn't mention validation failures (e.g., age outside 18-100) or rounding behavior, keeping it below a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four tight sentences: what it computes, when to use it, what the result contains, and a disclaimer. The purpose is front-loaded and no sentence is filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so the description needn't detail the return shape, yet it still flags the formula echo, calorie floor, and disclaimer — useful for an agent deciding whether to trust the numbers. The only gap is silence on input validation boundaries defined in the schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description carries the burden; it explains that 'activity' is a multiplier and 'goal' is a goal adjustment, adding meaning beyond the enum lists. The remaining four parameters (sex, age, height_cm, weight_kg) are left to titles and min/max bounds, so compensation is partial.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: 'Estimated daily calorie and protein targets for an adult,' plus the exact method (Mifflin-St Jeor with activity multiplier and goal adjustment). This cleanly separates it from the sibling retrieval tools (search_food, get_nutrition, find_foods) and the adjacent compute_meal.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
'Use it when asked how much someone should eat in a day' gives a clear triggering context. It stops short of naming alternatives (e.g., use compute_meal for a per-meal breakdown), so differentiation is implied rather than explicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
find_foodsARead-onlyIdempotent
Rank foods by one nutrient or by glycemic load, per 100 kcal, per 100 g or per serving (dishes only), for questions such as high-protein vegetarian dishes or iron-rich foods. Highest first, except lowest first for kcal, sugars, saturated fat, sodium and glycemic load; order says which. Foods with no value for the ranked nutrient are left out and counted in skipped_missing, and so is a dish with an incomplete value for the ranked nutrient. Per 100 g or per 100 kcal, seasonings, fats and salt can top a ranking though nobody eats 100 g of them; kind="dish" or per="serving" ranks foods eaten as a portion. Values are rounded to 2 decimals, so 0.0 can be a small amount.
| Name | Required | Description | Default |
|---|---|---|---|
| per | No | 100kcal | |
| diet | No | Keep foods suitable for this diet: veg includes vegan. | |
| kind | No | Keep only dishes or only ingredients. | |
| limit | No | ||
| region | No | Keep dishes from this region. Ingredients have none, so leave it out with kind=ingredient. | |
| rank_by | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| per | Yes | |
| order | Yes | |
| rank_by | Yes | |
| results | Yes | |
| skipped_missing | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the read-only/idempotent annotations, the description discloses meaningful behavior: default sort direction, nutrient-specific exceptions, omission of missing values into skipped_missing, and rounding to 2 decimals. The reference to an `order` field that is not present in the schema is a notable transparency flaw, but the overall behavioral disclosure is strong.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but front-loaded with the core ranking behavior and then adds operational caveats. It is not bloated, though the reference to a nonexistent `order` parameter and the long run-on caveat sentence reduce structural crispness.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a six-parameter ranking tool with an output schema, the description covers the core ranking semantics, missing-value handling, units, rounding, and a practical caveat about misleading top results. It is nearly complete, but the mention of `order` without a corresponding schema field and the absence of guidance on limit/region leave small gaps.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The description adds useful meaning for rank_by, per, kind, and diet-related filtering, including caveats about per-100 g and per-100 kcal rankings. However, it leaves limit and region unaddressed and misleadingly refers to an `order` parameter that does not exist in the input schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: ranking foods by one nutrient or glycemic load, with units and example questions. It is clearly distinguishable from generic search/get tools, but it does not explicitly name the sibling tools or contrast itself with them.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It provides clear usage context through example questions such as high-protein vegetarian dishes and iron-rich foods, plus practical guidance to use kind="dish" or per="serving" when ranking portions. It does not offer explicit when-not conditions or direct alternatives among the sibling tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_nutritionARead-onlyIdempotent
Nutrients for an amount of one food, with the grams used, the measure and oil assumptions, glycemic load when the GI is known, and the sources. unit is g (always valid) or a household measure such as katori, piece or tbsp; search_food lists the units each food accepts, and an unknown unit returns them. A nutrient listed in incomplete is a lower bound. A dish's folate, vitamin C and vitamin B12 are totals of its raw ingredients, with no cooking losses applied; raw_ingredient_totals names them. For more than one food, use compute_meal. Values are rounded to 2 decimals, so 0.0 can be a small amount; a value the source does not report is null, never 0.
| Name | Required | Description | Default |
|---|---|---|---|
| unit | Yes | g, or a unit that search_food lists for the food, such as katori or piece. | |
| amount | Yes | How many of `unit`; above 0. | |
| food_id | Yes | A food id from search_food, such as toor_dal. |
Output Schema
| Name | Required | Description |
|---|---|---|
| gi | Yes | |
| food | Yes | |
| unit | Yes | |
| grams | Yes | |
| amount | Yes | |
| gi_ref | Yes | |
| sources | Yes | |
| nutrients | Yes | Nutrient amounts in `grams` of this food. |
| incomplete | Yes | |
| glycemic_load | Yes | |
| oil_assumption | Yes | |
| measure_assumption | Yes | |
| raw_ingredient_totals | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent, so the description earns credit by adding substantive behavior: `incomplete` means a lower bound, dish micronutrients are raw-ingredient totals without cooking losses, values are rounded to 2 decimals, and unreported values are null rather than 0. This is unusually rich, though it doesn't discuss any limits like response size or rate.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The core purpose and the compute_meal boundary are front-loaded, and almost every sentence carries real information. It is a dense run-on with several semicolon-chained caveats, which costs some readability but not correctness.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, yet the description still explains the interpretive semantics an agent needs to read the results correctly (null vs 0, rounding, lower bounds, raw-ingredient totals). Combined with unit handling and the sibling routing, nothing essential to a correct call is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3; the description still adds meaning beyond the schema by clarifying that g is always valid while household measures are food-specific and that an unknown unit returns the accepted list. It doesn't explain amount edge cases beyond what the schema's exclusiveMinimum already conveys.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource: nutrients for an amount of one food, including the measures used and sources. It explicitly distinguishes itself from compute_meal for multi-food queries, so an agent can pick between the two siblings without opening schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
States the single-food use case, names the alternative for multiple foods, and directs the agent to search_food for both food ids and the valid unit list. It even specifies that an unknown unit returns the accepted units, which is an explicit recovery path.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_foodARead-onlyIdempotent
Find foods by name and get their ids. Call this first: the other food tools take ids from it. Spelling variants match (sambar, sambhar), but every word of the query has to match a word of one name, so a dish that is not in the data returns no results, a note saying so and up to 5 close names as suggestions. Check that a result's name or one of its aliases is the food asked for; when no result is, report the dish as missing instead of using a suggestion or another result in its place. Each result has its kind (dish or ingredient), regions, diet and units: the units get_nutrition and compute_meal accept for that food, with the grams in one of each. total counts every match before limit; when it is larger than the results shown, raise limit or narrow with region, diet or kind.
| Name | Required | Description | Default |
|---|---|---|---|
| diet | No | Keep foods suitable for this diet: veg includes vegan. | |
| kind | No | Keep only dishes or only ingredients. | |
| limit | No | ||
| query | Yes | A food name. | |
| region | No | Keep dishes from this region. Ingredients have none, so leave it out with kind=ingredient. |
Output Schema
| Name | Required | Description |
|---|---|---|
| note | No | Set only when there are no results: says the food is not in the data and is to be reported as missing, not answered with a suggestion or another food, or, when a filter was given, to search again without it first. |
| query | Yes | |
| total | Yes | |
| results | Yes | |
| suggestions | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the readOnly/idempotent annotations: explains spelling-variant matching (sambar/sambhar), the all-words-must-match token rule, that zero hits return a `note` plus up to 5 close-name suggestions, and that `total` counts pre-limit matches. It also names the result fields and, critically, that each food carries `units` with gram weights accepted by get_nutrition and compute_meal.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the identity and the 'call this first' instruction, then adds detail in priority order. The prose is dense and the second sentence is long and comma-chained, but nearly every clause carries operational information an agent needs.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers matching semantics, the empty-result contract, result field meanings, cross-tool unit compatibility, and pagination behavior — all the decision-relevant context for a search tool. An output schema exists, yet the description's explanation of `units`/grams adds genuinely useful cross-tool linkage rather than duplicating it.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 80% and the individual params are already documented, but the description adds real semantics the schema lacks: the relationship between `total` and `limit` and the corrective actions (raise limit, narrow by region/diet/kind). It does not restate enum meanings, which is appropriate since the schema covers them.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource+payload: 'Find foods by name and get their ids.' It immediately positions itself relative to siblings by noting the other food tools take ids from it, so an agent knows this is the entry point rather than a nutrition or meal tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit sequencing guidance ('Call this first') plus concrete fallback instructions: when total exceeds shown results, raise limit or narrow by region/diet/kind, and when no result matches, report the dish missing rather than substituting a suggestion. The one gap is that the sibling `find_foods` is never mentioned, so an agent cannot tell how this differs from that similarly-named tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
5 tool updates
v0.1.0- First observed
compute_meal - First observed
compute_targets - First observed
find_foods - First observed
get_nutrition - First observed
search_food
TDQS
Scored across 5 tools
The tools have largely distinct purposes: search_food resolves names to IDs, get_nutrition handles one food, compute_meal aggregates multiple items, find_foods ranks foods, and compute_targets estimates daily needs. The only minor overlap is get_nutrition versus compute_meal when a meal has a single item, but the descriptions clarify the intended boundary.
All names use consistent snake_case with a verb_noun structure, which is readable and predictable. The verbs vary between search/find and get/compute, but the resource nouns make each tool's role clear.
Five tools is well-scoped for a nutrition data server. Each tool covers a distinct operation—lookup, nutrient retrieval, meal aggregation, ranking, and daily targets—without obvious redundancy.
The surface covers the core workflows well: finding foods, getting nutrition, computing meals, ranking foods, and estimating targets. Minor gaps exist, such as no explicit ingredient-list tool or comprehensive vitamin/mineral target calculations, but agents can work around these.
Maintenance
Related MCP Connectors
Food list analyzer and cited food answers for agents. Also recipes, meal plans and grocery lists.
Recipe search, ingredient matching, nutrition filters and a private cookbook for any AI agent.
Food photo recognition, nutrition search, meal logging and glucose prediction for health apps
Food and nutrition data: search, macros, and comparisons
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceProvides intelligent access to the USDA nutrition database through AI assistants, enabling users to search foods, compare nutritional content, find foods high in specific nutrients, and query authoritative nutrition data across 7,146+ food items through natural language.1-
- AlicenseAqualityDmaintenanceEnables natural language access to USDA's FoodData Central database with 1M+ foods, supporting search, nutrition facts, food comparison, and daily value calculations.814 PyPIMIT
- FlicenseNot gradedqualityDmaintenanceEnables natural-language nutrition research for Indian foods, combining a curated knowledge base with hybrid RAG and specialized agents. Provides MCP tools for knowledge search, safe calculations, document retrieval, and optional live web search.-
- FlicenseNot gradedqualityBmaintenanceEnables agents to log and edit meals, query a food journal, and retrieve calorie/macro/quality KPIs with server-side nutritional grading, plus manage staples and targets.1-