fatsecret-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., "@fatsecret-mcplog this photo as lunch"
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.
fatsecret-mcp
Log food to your FatSecret diary by talking to Claude or ChatGPT on your phone. You see a draft first, and nothing is saved until you say yes.
https://github.com/user-attachments/assets/44dfd991-5434-47ad-8b84-bcad24415a1f
A photo of dinner, the draft Claude proposes, a "yes", the day so far, and the new entry in the FatSecret app.
fatsecret-mcp is a self-hosted MCP server. It connects Claude, or ChatGPT when the Operator turns it on, to your own FatSecret food diary, the one you see in the FatSecret app. Claude can read a day or a few weeks of it against your daily calorie and macro targets. It can find foods in the FatSecret catalog and draft entries from a description or a photo. Each draft shows the amounts, calories, macros, a suggested meal and how your day looks after adding it. When a packaged product isn't in the catalog, a photo of its nutrition label is enough. A wrong entry can be fixed or removed the same way, through a draft.
One server serves a household: a few people, each signed in with their own FatSecret account. There's no public instance, so you run your own.
This project is unofficial. It is not affiliated with or endorsed by FatSecret, Anthropic or OpenAI, and it is not medical or dietary advice.
Powered by fatsecret Platform API
What you can ask
The server gives Claude five tools. You never call them yourself; you just talk.
search_foods
Finds foods in the catalog with servings that can be logged.
You: How many calories are in a cup of cooked jasmine rice?
Claude: FatSecret has "Jasmine Rice (cooked)": 1 cup (158 g) is 205 kcal, 4.3 g protein, 0.4 g fat, 44.5 g carbohydrate.
draft_entries
Proposes entries without writing anything.
You: (photo of a plate) Log this as lunch.
Claude: Here's the draft for lunch today:
Beef bibimbap, 480 g (1 bowl): 640 kcal, 32 g protein, 21 g fat, 80 g carbohydrate
I assumed one restaurant-sized bowl. After adding it you'll be at 1,420 of 2,200 kcal and 78 of 140 g protein. Save it?
A portion of known weight, such as a bowl or a roll, is saved in grams when the food can be measured in grams, so the FatSecret app shows the same amount as the draft.
confirm_draft
Writes the draft you approved.
You: Yes.
Claude: Saved. Beef bibimbap is in your FatSecret diary under lunch.
get_diary_day
Reads one day with its totals and what's left of your targets.
You: How much is left today?
Claude: You've had 1,420 kcal. 780 kcal are left, along with 62 g protein, 18 g fat and 95 g carbohydrate.
get_period_summary
Reads daily totals for up to 31 days.
You: How did my week go compared with the week before?
Claude: This week you averaged 2,050 kcal and 128 g protein a day; last week 2,240 kcal and 117 g protein. You went over your calorie target on two days this week, and on four the week before.
The numbers above are illustrative.
Related MCP server: indian-food-nutrition-mcp
How a meal gets logged
sequenceDiagram
actor U as You (Claude app)
participant C as Claude
participant S as fatsecret-mcp
participant F as FatSecret API
U->>C: photo + "log this as lunch"
C->>S: search_foods
S->>F: foods.search, food.get
C->>S: draft_entries
S-->>C: draft (amounts, kcal, macros, meal, the day after adding)
C-->>U: shows the draft
U->>C: "yes"
C->>S: confirm_draft(draft_id)
S->>F: food_entry.create, one per item
S-->>C: saved entriesThe draft is signed by the server and expires after thirty minutes. It can only be confirmed by the person it was made for. In Claude's connector settings, keep confirm_draft on "Needs approval" so that every write asks you first.
Products the catalog doesn't have
A free FatSecret API key reaches only the US catalog, in English. Most local and branded products aren't in it. For those, Claude asks for a photo of the nutrition label, or you type its values. The server logs the product as a stand-in: the closest catalog food, in the amount whose calories match the label, under the product's own name.
flowchart TD
S[Claude searches the catalog] --> Q{Found?}
Q -- yes --> E[Normal food entry]
Q -- no --> P["You send a photo of the label<br/>or type its values"]
P --> V[Claude reads the calories and macros]
V --> M["The server picks the closest catalog food<br/>and sets the amount so calories match"]
M --> N[Entry saved under the product's own name]Calories match the label exactly, but macros are only close, and sometimes noticeably off. This comes from translating a product outside the US catalog into a US one. A diary entry can't carry its own calories or macros; it only points at a catalog food and takes that food's values. A free key can't create foods, so the server picks the US food whose macros are nearest to the label. Your product's exact macros usually exist nowhere in the US catalog, so there's nothing closer to pick. The draft shows the label's values, the logged ones and the difference before you confirm, and opening the entry's details in the FatSecret app shows the catalog food underneath.
The server rejects a label whose calories don't roughly agree with its macros, which catches a misread photo or kJ taken for kcal. It doesn't remember stand-ins: next time, Claude reuses the label from the conversation or asks again. Repeating a stand-in from your diary keeps its name and its approximate macros.
If you log a product often, adding it once in the FatSecret app gives you exact values there. After that, Claude can repeat it from your diary.
Repeat an earlier meal
You: The same as yesterday's breakfast.
Claude: Yesterday's breakfast was:
Rolled oats, 80 g: 300 kcal
2% milk, 250 ml: 125 kcal
Greek yoghurt, 150 g: 90 kcal
Was it all the same today, or should I leave something out or change an amount?
You: Without the yoghurt, and half the milk.
Claude: Here's the draft, repeated from yesterday: rolled oats, 80 g, and 2% milk, 125 ml, 362 kcal in all. Should it go under breakfast today? Then I'll save it.
Claude reads the earlier day and drafts the entries you keep, without searching the catalog. The server copies each entry's food, serving, name and nutrition from your diary and scales them when you change an amount, always shown in grams or ml. It also works for products the catalog can't return, such as ones you added in the FatSecret app, and for stand-ins, which keep the product's name and don't need the label again. An amount that wasn't logged in grams or ml, such as "2 slices", is looked up once to show it in grams; when no weight is known, the entry is repeated as it was.
Fix or undo an entry
You: (right after saving) Undo that.
Claude: Here's the draft: remove beef bibimbap, 480 g, 640 kcal, from today's lunch. You'd be back at 780 of 2,200 kcal. A removed entry can't be restored, only logged again. Remove it?
You: Actually, keep it, it was just 300 g.
Claude: Here's the new draft: beef bibimbap, 480 g → 300 g, 640 → 400 kcal, still under lunch. Save the change?
A draft can also change entries already in your diary, including ones you logged in the FatSecret app: a new amount in grams or ml, another meal on the same day, or a new name. It can remove them too. Each change shows the entry before and after, and how each day looks afterwards. Nothing changes until you say yes. For "undo that", Claude removes the entries the last save wrote. For anything else, it reads the day first and asks when it's unclear which entry you mean.
FatSecret can't move an entry to another food or another day. So "it was chicken thigh, not breast" or "that was yesterday" becomes one draft that adds the right entry and removes the wrong one. The new entry is saved first, so if something fails halfway you see a duplicate rather than a gap. Changes reach back up to a year.
The FatSecret app in another language may open a US catalog entry without the serving it was saved in: an entry of "1 roll" can open as "1 gram". Saving it there may change the entry. Change such entries through Claude, or check the amount before saving in the app.
What it doesn't do
It can't look up barcodes, regional catalogs such as a Polish one, or custom foods. A free key reaches only the US catalog, so Claude searches it in English. Entries are still saved under the name you used, in your language, with a short note that the food was matched in the US catalog, such as "Kajzerka (katalog USA)"; the draft shows the catalog's name beside it.
It doesn't track weight, exercise or recipes, only food entries.
It doesn't serve strangers. There's no public server, and each server has a fixed list of people.
Run your own
A household server has one Operator, the person who deploys it and adds everyone else. Each person in the household signs in to FatSecret once through the Operator, then adds the server to their own Claude account as a custom connector, or to ChatGPT when the Operator turns it on.
flowchart LR
A["Anna's phone (Claude)"] --> CL[Claude connector]
J["Jan's phone (Claude)"] --> CL
CL -->|"each signs in with their own ID and password"| S[fatsecret-mcp on your server]
O[Operator] -->|"add-user, check"| S
S -->|"one FatSecret token per person"| F[FatSecret API]
F --> D[("each person's own diary, the same as in the FatSecret app")]You need:
your own FatSecret Platform API key. Register at platform.fatsecret.com; the free Basic plan is enough. Using it means accepting FatSecret's API terms. This project ships no keys;
a container host with HTTPS for the server, such as Railway;
a Claude or ChatGPT account for each person, with custom connectors available on their plan.
The short path:
Create a
.envwith your FatSecret key and a signing key, then runuvx --env-file .env fatsecret-mcp add-useronce per person (setup).Deploy the image
ghcr.io/jaronskijakub/fatsecret-mcpwith those values (deploy).In Claude, add
https://<your-server>/mcpas a custom connector and sign in (connector).
To try it on your own computer first, run it over stdio with Claude Code; setup covers that too. Request quotas, pacing and caching are described in limits, and the trust model in SECURITY.md.
Use it with ChatGPT
ChatGPT signs in the same way as Claude, with the same user id and connector password, and uses the same five tools. It's off by default. The Operator turns it on by setting FATSECRET_MCP_CLIENTS=claude,chatgpt and redeploying.
Each person then adds https://<your-server>/mcp in ChatGPT as a custom connector. ChatGPT offers custom connectors in developer mode, on some plans only; OpenAI's developer mode article lists them. Someone who uses both apps signs in to each one separately, and signing them out ends both (connector). The examples on this page come from Claude.
ChatGPT sometimes stops a confirmed change or removal with "This tool call was blocked by OpenAI's safety checks" before it reaches the server, while adding entries works. Nothing changes in your diary then. Try again in a new chat, or make the change with Claude or in the FatSecret app.
Documentation
Setup: configuration, adding people, their settings, and running it locally with Claude Code.
Deploy: Railway, other container hosts, Docker, and building the image yourself.
Connector: adding the server to Claude or ChatGPT, sign-in, and signing someone out.
Limits: the daily quota, pacing, search, drafts and input sizes.
Changelog: what changed in each version.
Report vulnerabilities through SECURITY.md, which also describes the trust model and what is stored where.
Roadmap
Custom foods for users with a FatSecret Premier key, so a branded product can be logged with exact values instead of a stand-in.
Open Food Facts lookups for products the US catalog lacks, by name or barcode.
Search your own foods by name. Anything in your diary can already be repeated from the day you logged it, but products you often log aren't searched yet; reading them from your whole diary took too many API requests, so this needs another approach.
Remember products logged from a label, so a stand-in doesn't need the photo again when it isn't repeated from your diary. This needs persistent storage.
Native confirmation dialogs in Claude for saving a draft, instead of a "yes" in the chat.
Development
uv sync
uv run ruff check && uv run ruff format --check
uv run pyright
uv run pytestThe tests never call FatSecret. They replay recorded responses from tests/fixtures/fatsecret/. An opt-in smoke test reads your real diary: FATSECRET_LIVE_SMOKE=1 uv run --env-file .env pytest tests/test_live_smoke.py. Container tests are described in deploy.
To release a version, set it with uv version X.Y.Z and in both places in server.json, and give it a dated entry in CHANGELOG.md; tests/test_package.py checks that they agree. Push that commit to main and wait for CI, which publishes its image. Then push the tag vX.Y.Z. The release workflow checks that CI passed on main for that commit, builds and checks the package, adds the version tag to the commit's image, publishes the package to PyPI and creates the GitHub release from the changelog entry. In a public repository it also publishes server.json to the MCP registry.
License
Available Tools
5 toolsconfirm_draftADestructive
Write a Draft to the User's Food Diary: its new Food Entries first, then its changes.
New entries go under the Draft's Meal and Diary Day. A removed entry can't be restored. Call this only after the User explicitly approved this Draft in their latest message. After a correction, draft again and confirm the new Draft instead. Confirm each Draft once: a repeat usually returns the earlier result, but after a server restart it would write the Draft again.
| Name | Required | Description | Default |
|---|---|---|---|
| draft_id | Yes | The draft_id returned by draft_entries. |
Output Schema
| Name | Required | Description |
|---|---|---|
| meal | Yes | The Meal of the new entries; null when the Draft only has changes. |
| status | Yes | partially_written: writing stopped at an item without a food_entry_id or a change not written. already_confirmed: an earlier call wrote this Draft; nothing new was written. |
| changes | Yes | Changes, written after the new entries. |
| entries | Yes | New Food Entries, written first. |
| failure | Yes | Why writing stopped part-way through. |
| diary_day | Yes | The Diary Day of the new entries; null when the Draft only has changes. |
| attribution | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes beyond the annotations by naming the concrete irreversible consequence ('A removed entry can't be restored') and by qualifying the non-idempotency: a repeat call usually returns the earlier result, but after a server restart it would write the Draft again. That restart caveat is genuinely useful operational context. Minor gap: it does not restate what the response looks like, though an output schema exists.
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-loaded with the core action and write ordering, then the preconditions and idempotency caveat in short, purposeful sentences. No filler; each sentence carries a distinct constraint.
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?
A destructive, non-idempotent mutation is fully covered: what it writes, where new entries land, irreversibility, the approval precondition, and the repeat-call edge case. An output schema exists, so return-value explanation is unnecessary.
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?
Only one parameter, and schema coverage is 100% — the schema already documents draft_id as the value returned by draft_entries. The description adds no format or validation guidance beyond that, so this sits at the baseline.
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: writing a Draft into the Food Diary, plus the internal ordering (new entries first, then changes). Combined with 'New entries go under the Draft's Meal and Diary Day,' an agent can tell this is the commit step, distinct from draft_entries which produces the Draft.
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 gating condition: 'Call this only after the User explicitly approved this Draft in their latest message.' It also names the correct alternative path after a correction ('draft again and confirm the new Draft instead') and the one-shot rule ('Confirm each Draft once'), leaving nothing to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
draft_entriesARead-only
Propose new Food Entries, and changes to logged ones, without writing to the Food Diary.
Show the Draft to the User.
A Food item uses Food and Serving ids from search_foods. number_of_units is the FatSecret unit count, not necessarily grams: scale it from the Serving's number_of_units. Name it with food_entry_name the way the User said it, in their language, followed by a short note in the same language that it was matched in the US catalog, e.g. "Kajzerka (katalog USA)" or "Kaiser roll (US catalog)". The Draft shows the catalog's name beside it. A Serving of known weight, such as "1 roll (57 g)", is logged as that weight in the Food's Serving in grams or ml when it has one, so the Draft may show that Serving instead.
A Nutrition Label item logs a branded product missing from the catalog as a Stand-in: the closest catalog Food for search_phrase, in the amount whose kcal match the label, under the product's name. Ask for a photo of the Nutrition Label unless the User already gave its values. For a product logged before as a Stand-in, reuse its label from this conversation or your memory, and ask again otherwise. Estimate the label, with estimated=true, only when the User has no packaging. Show the label values, the logged values and the difference. If the User dislikes the Stand-in, draft again with a different search_phrase.
A Diary Entry item logs a Food Entry from the Food Diary again, under its own name, with its Food, Serving and nutrition, so no catalog search is needed. For "the same as yesterday's breakfast", read that Diary Day with get_diary_day, list its Food Entries with their amounts to the User and ask whether everything was the same, or what to leave out or eat more or less of. Then draft the chosen entries by diary_day and food_entry_id, with metric_amount for a changed amount in grams or ml.
A change item changes or removes a Food Entry already in the Food Diary, by diary_day and food_entry_id: change="remove", or change="edit" with a new metric_amount in grams or ml, a meal on the same Diary Day or a food_entry_name. For "undo that" after a confirm_draft, remove the food_entry_ids it returned; undoing a change is a change back. For any other change, read the Diary Day with get_diary_day first, name the entries to change, and ask when it's unclear which one is meant. The Food or Diary Day of an entry can't change: for "chicken thigh, not breast", remove the entry and add a Food item; for "that was yesterday", remove it and add a Diary Entry item with the same food_entry_id, in one Draft whose diary_day is yesterday. Show each change before and after, and the removal_note when there is one.
A Draft holds at most 20 items: split a larger meal into several Drafts.
Include assumptions about estimated amounts. When the Draft has new items, always ask the User whether the suggested Meal is correct. If previous_day_suggested is true, ask whether to use Snacks on the previous Diary Day instead. meal and diary_day are for new items only. A correction needs a new Draft with the explicit Meal or Diary Day. Nothing is written by this tool; a Draft expires after thirty minutes.
| Name | Required | Description | Default |
|---|---|---|---|
| meal | No | ||
| items | Yes | At most 20 items. Split a larger meal into several Drafts. | |
| diary_day | No | The Diary Day, as YYYY-MM-DD. Omit for today in the User's timezone. |
Output Schema
| Name | Required | Description |
|---|---|---|
| meal | Yes | The Meal of the new items; null when the Draft only has changes. |
| items | Yes | New Food Entries. |
| totals | Yes | The new items added up. |
| changes | Yes | Changes to Food Entries, each before and after. |
| draft_id | Yes | |
| day_after | Yes | The new items' Diary Day after the whole Draft; null when the Draft only has changes. |
| diary_day | Yes | The Diary Day of the new items; null when the Draft only has changes. |
| expires_at | Yes | |
| attribution | No | |
| removal_note | Yes | Set when the Draft removes a Food Entry; tell the User. |
| meal_suggested | Yes | |
| other_days_after | Yes | Each other Diary Day the changes are on, after the whole Draft. |
| previous_day_suggested | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnlyHint annotation, the description discloses key behavior: no Food Diary writes occur, drafts expire after thirty minutes, each Draft holds at most 20 items, meal confirmation is required for new items, and assumptions must be included. It also explains nuanced handling for Stand-ins, label reuse, estimation, immutable Food/Diary Day fields, and undo behavior.
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 front-loaded, grouped by item type, and dense with necessary workflow rules for a complex drafting tool. It is long and repeats the 20-item limit already stated in the schema, but most sentences carry operational value rather than 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?
Given the nested oneOf item schema, 67% schema description coverage, and an output schema, the description supplies the missing procedural and behavioral context an agent needs: source of ids, label handling, stand-in matching, change/undo rules, meal confirmation, assumptions, and draft expiry. Nothing critical appears omitted.
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 substantial meaning beyond the 67%-covered schema: number_of_units is a FatSecret unit count rather than grams, food_entry_name must follow the User's language plus a catalog-matched note, diary_day and food_entry_id come from get_diary_day or confirm_draft, metric_amount is grams or ml, and change remove/edit have specific consequences. It also clarifies that meal and diary_day apply only to new items and that estimated=true is used only when packaging is unavailable.
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 opening sentence gives a specific verb and resource: 'Propose new Food Entries, and changes to logged ones, without writing to the Food Diary.' It distinguishes the tool from siblings by stating that ids come from search_foods, diary reads use get_diary_day, and confirm_draft is the later commit step.
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?
The description explicitly routes each item type: Food items use search_foods ids, Diary Entry items use get_diary_day results, and change items require reading the Diary Day first unless undoing a confirm_draft. It also states when to ask the User for clarification, a Nutrition Label photo, or a corrected Draft.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_diary_dayARead-only
Read the User's Food Diary for one Diary Day.
Returns the Food Entries grouped by Meal (name, amount, kcal and Macros), the day's totals, the User's Daily Targets and what remains of them. Use it for questions like "what have I eaten today?" or "how much is left?". Past days reuse entries already read during this server session, for up to twelve hours. Today is always fresh. Set refresh=true when the User requests fresh data or says they changed this past day in the FatSecret app.
| Name | Required | Description | Default |
|---|---|---|---|
| refresh | No | ||
| diary_day | No | The Diary Day, as YYYY-MM-DD. Omit for today in the User's timezone. |
Output Schema
| Name | Required | Description |
|---|---|---|
| meals | Yes | Meals that have Food Entries, in the order of the day. |
| totals | Yes | |
| diary_day | Yes | |
| remaining | Yes | Daily Targets minus totals; negative once a target is exceeded. |
| daily_targets | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=true; the description adds the non-obvious caching contract that past days may be up to twelve hours stale while today is always fresh. That directly changes whether an agent trusts the result, and the refresh=true escape hatch plus its trigger condition is spelled out, not left implicit.
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-loaded with the action, then return contents, then usage examples, then the caching caveat, in a natural decision order. Every sentence carries weight; the only mild redundancy is restating return fields that the output schema already enumerates.
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?
With an output schema present and readOnlyHint set, the description covers what remains: scope of one day, cache freshness, and the refresh trigger. An agent has everything needed to call it correctly and to judge result staleness.
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 only 50% and the undocumented parameter is refresh, which the description fully compensates for by defining its default behavior and the exact user signals that should flip it. diary_day is already covered in the schema as YYYY-MM-DD with today as default, so the description adds little there.
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 ("Read the User's Food Diary for one Diary Day") and enumerates what comes back: entries grouped by Meal, day totals, Daily Targets and remaining amounts. It is unambiguous on its own, but never references the sibling get_period_summary, so the agent must infer that multi-day aggregates belong there rather than here.
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 concrete triggering questions ("what have I eaten today?", "how much is left?") and an explicit refresh condition ("when the User requests fresh data or says they changed this past day in the FatSecret app"). It does not name alternatives or exclusions, so it is clear context rather than full when/when-not routing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_period_summaryARead-only
Read daily kcal and Macro totals alongside the User's configured Daily Targets.
Includes both endpoints, at most 31 Diary Days, and marks days without Food Entries. To compare two periods, call once for each period. Returns numbers without evaluation. Past days reuse Food Entries already read during this server session, for up to twelve hours. Missing days use FatSecret's month totals; today is always fresh. Set refresh=true when the User requests fresh data or says they changed these days in the FatSecret app.
| Name | Required | Description | Default |
|---|---|---|---|
| to_day | Yes | Last Diary Day in the period, inclusive, as YYYY-MM-DD. | |
| refresh | No | ||
| from_day | Yes | First Diary Day in the period, inclusive, as YYYY-MM-DD. |
Output Schema
| Name | Required | Description |
|---|---|---|
| days | Yes | Every Diary Day in the period, in chronological order. |
| to_day | Yes | |
| from_day | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond readOnlyHint=true: it discloses a 12-hour session cache for past days, that today is always fresh, that missing days fall back to FatSecret month totals, that days without entries are marked, and that results are returned without evaluation. These are exactly the caching/staleness behaviors an agent needs to reason about correctness.
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?
Purpose and scope are front-loaded, followed by caching and freshness notes that each carry real information. A few clauses ('To compare two periods, call once for each period') are slightly redundant, but nothing is padding.
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 return values need no explanation; the description still covers the important behavioral edges (stale data, missing days, marked empty days). For a 3-parameter, read-only aggregation tool this is sufficient to call correctly.
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 67%, but the one undocumented parameter (refresh) is fully explained in the description, including the trigger conditions for setting it. The description also adds constraints the schema lacks, such as the 31-day maximum span and inclusive endpoints.
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: read daily kcal and macro totals alongside the user's daily targets, with explicit scope (both endpoints inclusive, max 31 diary days). The distinction from get_diary_day (per-day detail) is implied by 'totals' but no sibling is named, keeping it just short of a 5.
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 concrete when-to-call guidance for the refresh parameter ('when the User requests fresh data or says they changed these days in the FatSecret app') and explains how to compare two periods. It never states when not to use it (e.g. when a single day's entries are needed, use get_diary_day), so no alternatives are covered.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_foodsARead-only
Find catalog Foods with loggable Servings, kcal and Macros.
Search one food at a time. Search a composed dish, such as a plate in a photo, as a whole first (e.g. "beef bibimbap"), and its components separately only when the dish has no good match. Pass the User's original description and, for non-English input, an English rendering: the catalog is searched with the English rendering, or the original when omitted. Nutrition and metric amounts describe the whole Serving; number_of_units is its FatSecret unit count. A not_found result is normal. When a branded product isn't among the matches, ask the User for a photo of its Nutrition Label and draft it as a Stand-in with draft_entries. This tool does not write to the Food Diary.
| Name | Required | Description | Default |
|---|---|---|---|
| description | Yes | The description of one food as the User said it. | |
| english_rendering | No | English rendering for the catalog. Pass this for non-English input. |
Output Schema
| Name | Required | Description |
|---|---|---|
| status | Yes | |
| matches | Yes | |
| attribution | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Adds real behavior beyond the readOnlyHint annotation: results are searched via the English rendering, a not_found result is expected rather than an error, and nutrition/metric amounts describe the whole Serving while number_of_units is the FatSecret unit count. The closing 'does not write to the Food Diary' largely restates readOnlyHint=true, and no result-count or ranking behavior is described.
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?
Purpose is front-loaded in the first line and every subsequent sentence carries a distinct rule (one-at-a-time, dish decomposition, language handling, not_found, branded fallback). It is dense but not padded; the only near-redundant sentence is the Food Diary disclaimer.
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 return values needn't be explained, and the description still covers the semantics an agent needs to call it correctly: input wording, language handling, the single-food constraint, and the escalation path for branded products. Nothing material is missing for a two-parameter read-only search.
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 the baseline is 3, but the description goes further by specifying the priority rule: the catalog is searched with the English rendering, or the original when it is omitted. It also clarifies that 'description' should be the user's own wording, which the schema only hints at.
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 ('Find catalog Foods') plus scope detail ('loggable Servings, kcal and Macros'), which tells the agent exactly what kind of object comes back. It also implicitly separates itself from draft_entries, the sibling it points to for branded products.
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 explicit operating rules: one food at a time, search composed dishes whole before components, pass the user's original wording plus an English rendering, and treat not_found as normal. It names the alternative path (ask for a Nutrition Label photo, draft with draft_entries) and the condition that triggers it.
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.3.0- First observed
confirm_draft - First observed
draft_entries - First observed
get_diary_day - First observed
get_period_summary - First observed
search_foods
TDQS
Scored across 5 tools
The tools mostly have distinct roles: search catalog foods, read one diary day, read period totals, draft changes, and confirm a draft. The main ambiguity is that draft_entries is an umbrella for adding, editing, removing, and re-logging entries, so its name undersells its scope, though it does not directly overlap with another tool.
All tool names use snake_case with a predictable verb_noun structure: get_diary_day, get_period_summary, search_foods, draft_entries, confirm_draft. The draft_entries/confirm_draft pair is slightly asymmetric in noun choice, but the overall convention is consistent and readable.
Five tools is a compact, well-scoped set for a food diary logging server, with clear separation between reading, searching, drafting, and confirming. Although draft_entries is dense, the overall count is appropriate and each tool earns its place.
The core diary lifecycle is covered: catalog search, daily diary reads, period summaries, drafting new entries and changes, and confirming writes. Minor gaps remain, such as no explicit target management and no multi-day entry-level read beyond period totals.
Maintenance
Related MCP Connectors
Calorie, workout and body-weight log inside ChatGPT and Claude. Log meals by text, photo or voice.
Calorie tracker app for ChatGPT and Claude: read meals, macros, weight and log meals from chat.
All the ease of Claude. All the rigor of a real macro tracker.
Log meals and read calories and macros from any AI assistant. Pairs with the whatevr iPhone app.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceA filesystem-based MCP server that turns any MCP-capable AI agent into a conversational calorie and protein tracker with natural-language estimates, confidence-aware logging, daily/weekly progress, food-history search, and export, working offline with local fallback data.65 npmMIT
- AlicenseNot gradedqualityDmaintenanceAn MCP server that lets you log meals through Claude (and soon ChatGPT) in plain language, using India's official food composition data (IFCT 2017) plus USDA, enabling accurate calorie and macro tracking for Indian dishes with household units and photo logging.34 npm2AGPL 3.0
- AlicenseNot gradedqualityDmaintenanceA Model Context Protocol (MCP) server that connects AI assistants to your MyFitnessPal data, enabling reading food diaries, nutrition goals, weight measurements, and more via MFP's web scraping.MIT
- FlicenseNot gradedqualityCmaintenanceRemote MCP server for natural-language calorie/macro and weight tracking, designed to connect to Claude.ai as a custom connector.-