Skip to main content
Glama
Cronometer MCP

Cronometer MCP

by ChrisJVanek

Cronometer MCP

One MCP server for Cronometer that works in Claude (Claude Code, Claude Desktop, claude.ai web and mobile) and ChatGPT. It merges what every public Cronometer MCP could do (see Credits) into 65 tools, all tested against a live account.

Cronometer has no public API. This server uses the two private ones the official apps use:

  • The mobile API (mobile.cronometer.com, plain JSON) for almost everything.

  • The web app's GWT-RPC for what only the website does: repeating foods, macro templates and the weekly schedule, copying between any two days, deleting notes, cancelling a recurring fast, and the CSV exports.

Both log in with your Cronometer email and password. Either API can change without notice when Cronometer ships an update.

Tools

Area

Tools

Diary

get_food_log, add_food_entry, edit_food_entry, remove_food_entry (any entry type), copy_day (any day to any day), mark_day_complete, find_entries_by_food, list_diary_groups

Nutrition

get_daily_nutrition, get_nutrition_scores, get_nutrition_summary (day ranges with averages), nutrient_report (every target, lowest first), analyze_day (per meal and top foods), check_food_fit ("can I eat this?")

Foods

search_foods, lookup_barcode, get_food_details, get_recent_foods, list_custom_foods, add_custom_food, update_custom_food, retire_custom_food, list_nutrients

Recipes

list_recipes, create_recipe, update_recipe, import_recipe (free text, e.g. "2 tbsp ketchup")

Repeating foods

get_repeated_items, add_repeat_item, delete_repeat_item

Targets

get_targets, set_nutrient_target, get_macro_targets, get_daily_macro_targets, set_macro_targets, list_macro_templates, create_macro_template, delete_macro_template, set_weekly_macro_schedule

Biometrics and sleep

list_biometrics, get_biometrics, add_biometric, edit_biometric, remove_biometric, log_sleep (with stages and score)

Exercise

search_activities, add_exercise, edit_exercise, remove_exercise

Notes

add_note, edit_note, remove_note

Fasting

get_fasting_history, get_fasting_stats, add_fast, edit_fast, delete_fast, cancel_active_fast

Account and export

connection_status, get_profile, get_streak, export_data (servings, daily summary, exercises, biometrics, notes; JSON or CSV)

ChatGPT search

search, fetch: the shape ChatGPT's connectors and deep research look for

Paging

get_result_page, for results too large for one reply

Tools are annotated read-only or destructive, so Claude and ChatGPT ask before anything that deletes.

Related MCP server: cronometer-api-mcp

Three ways to use it

  1. Connect to a hosted server: nothing to install. Works in Claude (web, desktop, mobile) and ChatGPT.

  2. Run it on your own computer: for Claude Code and Claude Desktop.

  3. Host your own server: for yourself, or open to anyone.

1. Connect to a hosted server

A public server runs at https://cronometer.chrisvanek.com/mcp, or use the address of any server set up as in part 3.

  • Claude (claude.ai, Claude Desktop, mobile): Settings → Connectors → Add custom connector. Paste the URL, then Connect.

  • ChatGPT: Plugins → Add → Add custom MCP server. Paste the URL and choose Authentication OAuth. Custom connectors depend on your ChatGPT plan.

A Connect Cronometer window opens. Sign in with your own Cronometer email and password, and the tools appear in your chats, working on your own diary.

Before you sign in, know that the server keeps your Cronometer password, encrypted, so it can stay signed in for you. Its operator could read it, so only use a server run by someone you trust. Accounts with two-factor login on can't connect.

2. Run it on your own computer

Needs uv and Python 3.12+.

git clone https://github.com/ChrisJVanek/cronometer-mcp.git && cd cronometer-mcp
uv sync
cp .env.example .env    # then fill in CRONOMETER_USERNAME and CRONOMETER_PASSWORD

.env is gitignored. If your account uses two-factor login, also set CRONOMETER_TOTP_SECRET and run uv sync --extra totp.

Then add this to the mcpServers section of ~/.claude.json (Claude Code) or ~/Library/Application Support/Claude/claude_desktop_config.json (Claude Desktop):

"cronometer": {
  "command": "uv",
  "args": ["run", "--frozen", "--directory", "/path/to/cronometer-mcp", "cronometer-mcp"]
}

If you also added a hosted connector to your Claude account, Claude Desktop already gets the tools from it; skip the local entry there so the tools are not listed twice.

3. Host your own server

Hosted, the server runs over HTTPS and is its own OAuth server, so Claude and ChatGPT can connect as in part 1. It runs one of two ways:

Just you

Open to anyone

Set

CRONOMETER_USERNAME, CRONOMETER_PASSWORD, MCP_AUTH_PASSWORD

MCP_SECRET_KEY

The connect page asks for

the server password (MCP_AUTH_PASSWORD)

the person's own Cronometer email and password

Tools work on

your account

each person's own account

On Railway (or any Docker host)

The Dockerfile here runs the server in HTTP mode.

  • Build: point a Railway service (or any Docker host) at this repo. The Dockerfile needs no build arguments.

  • Variables: the ones in the table above, plus MCP_PUBLIC_URL (your public HTTPS address, e.g. https://cronometer.example.com). Optional: MCP_API_KEY, CRONOMETER_ACCOUNT_TZ, and MCP_ALLOWED_HOSTS for any other domain it should answer on (Railway's own *.up.railway.app name is accepted automatically).

  • Volume: mount one at /data. It holds the Cronometer sessions, the OAuth clients and tokens, and in open mode the encrypted accounts, so redeploys don't disconnect anyone.

  • Health check: /health.

The "approve on this computer" shortcut on the connect page only works when the server runs on your own machine.

When moving from one host to another, set MCP_OAUTH_IMPORT to the old oauth.json, base64 encoded, for one deploy. Connected apps then keep working without signing in again. Remove the variable once the log says Imported OAuth state.

Open it to anyone with a Cronometer account

Set MCP_SECRET_KEY (16+ characters, e.g. from openssl rand -base64 32). Nothing about the people using it goes in the server's settings: each person adds https://<your server>/mcp in Claude or ChatGPT and signs in on the connect page with their own Cronometer email and password. The server checks them by signing in to Cronometer, and from then on every tool their app calls reads and writes their own diary.

  • CRONOMETER_USERNAME and CRONOMETER_PASSWORD become optional. If set, that account signs in like anyone else, and apps connected to it before keep working.

  • MCP_ALLOWED_EMAILS (optional) limits who may connect, e.g. mum@example.com,dad@example.com. Unset or * means anyone.

  • MCP_AUTH_PASSWORD is not needed. MCP_API_KEY and the local "approve on this computer" shortcut still mean the owner account.

Good to know:

  • Keep MCP_SECRET_KEY and never change it: it encrypts the Cronometer passwords the server keeps (in accounts.json on the volume) so it can sign people in again when Cronometer's session expires. A new key means everyone connects again. You, as operator, could read those passwords, so people are trusting you.

  • Five wrong passwords for one email lock that email out for 15 minutes; other people are unaffected.

  • Accounts with two-factor login on can't connect: the server has no way to get their code. They can turn two-factor off in Cronometer.

On your own Mac, through a tunnel

  1. Set up .env as in part 2, and add MCP_AUTH_PASSWORD (8+ characters) or MCP_SECRET_KEY.

  2. Point a public hostname at http://localhost:8430. With a Cloudflare named tunnel, add this to ~/.cloudflared/config.yml before the catch-all rule, then run cloudflared tunnel route dns <tunnel> cronometer.example.com:

    - hostname: cronometer.example.com
      service: http://localhost:8430
  3. Run the server at login (macOS launchd, restarts if it stops):

    scripts/install-macos-service.sh https://cronometer.example.com

    Or run it in the foreground: uv run cronometer-mcp --transport http --public-url https://cronometer.example.com

  4. Connect as in part 1 with https://cronometer.example.com/mcp. On the Mac running the server you can also click Approve there without a password. That approval only works on localhost, so nothing coming in through the tunnel can use it.

Tokens last 24 hours and refresh for 90 days. They are kept in ~/.local/share/cronometer-mcp/oauth.json, so a restart does not disconnect anything. Clients that can send headers can use Authorization: Bearer $MCP_API_KEY instead of OAuth.

Logs: ~/Library/Logs/cronometer-mcp.log. Health check: GET /health.

Development

uv run pytest          # 87 tests, offline
uv run ruff check src

tests/test_oauth.py runs the whole connector flow (discovery, registration, consent, code exchange, refresh, tools/list) without touching Cronometer.

Notes from live testing (Nov 2026)

  • get_fasting_with_date_range returns an empty list for every range. History now reads /api/v2/get_fasting and filters by date. A fast's end must be sent as finish; end is silently dropped.

  • The v3 diary-entries DELETE rejects notes. Notes go through the web app's removeNote instead.

  • The web app's repeat items label the mobile food id as food_source_id and the measure id as food_id. The tools translate both ways.

  • GWT back-references (the same weekday shared by two repeat items) are resolved instead of being dropped.

Credits

Built on rollecode/cronometer-mcp (MIT, itself from rwestergren/cronometer-api-mcp) for the mobile API. The web-app client in src/cronometer_mcp/web.py is from cphoskins/cronometer-mcp (MIT). Recipe import comes from rwestergren, sleep logging from firaskudsy/cronometer-api-mcp, barcode and activity search from ekelly95/cronometer-mcp, and the "can I eat it" and day-analysis ideas from LucasHenriqueDiniz/gocronometer-mcp. Also reviewed: atticoos, Pop101, DiegoDeLaCruz220, DoubleAV, maxperron and DanielBrahano.

Not affiliated with Cronometer. See DISCLAIMER.md.

Available Tools

65 tools
add_biometricA
Idempotent

Record a biometric measurement, such as weight or body fat.

Use list_biometrics to find metric IDs and their valid unit IDs. Correct a wrong value with edit_biometric, or drop the reading entirely with remove_biometric when the measurement never happened.

Args: metric_id: Metric to record, from list_biometrics. unit_id: Unit the amount is in, from that metric's units. amount: The measured value. date: Date as YYYY-MM-DD (defaults to today).

Inputs: amount (required), unit_id (required), metric_id (required), date. Returns: result

add_custom_foodA

Create a custom food with any nutrients you have, from 1 to all 94.

Amounts are for one whole serving of serving_grams, not per 100 g, each in that nutrient's own unit. Call list_nutrients for the accepted names and their units. Only pass the nutrients you actually know: a nutrient you leave out stays blank in Cronometer, while passing 0 states the food contains none of it, and the app treats those differently. An unrecognised name is an error, so nothing is silently dropped from a food that then looks complete.

Two label conveniences: energy_kj is converted to calories, salt_g to sodium. Pass either one or its underlying nutrient, not both.

After creation, use the returned food_id with add_food_entry to log it.

Args: name: Food name. nutrients: Nutrient name to amount per serving, e.g. {"energy": 250, "protein": 12.5, "vitamin_c": 30, "b12_cobalamin": 1.2}. serving_name: Name for the serving size (default "1 serving"). serving_grams: Weight of one serving in grams (default 100). label_type: "AMERICAN_2016" or "EUROPEAN". notes: Free-text note stored on the food.

Inputs: name (required), nutrients (required), notes, label_type, +2 more. Returns: result

add_exerciseA
Idempotent

Log an exercise entry.

A wrong entry can be corrected with edit_exercise or deleted outright with remove_exercise.

Args: name: What the exercise was called. minutes: Duration in minutes. calories_burned: Calories burned, as a positive number. date: Date as YYYY-MM-DD (defaults to today). activity_id: Optional Cronometer activity, from search_activities. Leave 0 for a free-form entry named name.

Inputs: name (required), minutes (required), calories_burned (required), date, +1 more. Returns: result

add_fastA
Idempotent

Record a fast.

Args: start: When the fast started, as YYYY-MM-DD HH:MM. end: When it ended, same format. Omit for an ongoing fast. goal_hours: Target length in hours.

Inputs: start (required), end, goal_hours. Returns: result

add_food_entryA

Add a food entry to the Cronometer diary.

Use search_foods to find food_id and measure_id, then get_food_details to confirm serving sizes and gram weights.

Args: food_id: Numeric food ID from search_foods results. measure_id: Measure/unit ID from get_food_details. grams: Weight of the serving in grams. Real grams for recipes too: the conversion to Cronometer's batch units happens here. date: Date to log as YYYY-MM-DD (defaults to today). translation_id: Translation ID from search results (usually 0). diary_group: Meal slot -- one of "auto", "breakfast", "lunch", "dinner", "snacks" (case-insensitive, default "auto"). time: Time of day as HH:MM or HH:MM:SS. Defaults to now. Pass the real eating time when logging after the fact; an "auto" diary_group then follows that hour instead of the current one.

Inputs: grams (required), food_id (required), measure_id (required), date, +3 more. Returns: result

add_noteA
Idempotent

Add a note to a day in the Cronometer diary.

Cronometer cannot delete notes, only rewrite them, so a note added here can be changed but only removed in the Cronometer app.

Args: text: The note text. date: Date as YYYY-MM-DD (defaults to today).

Inputs: text (required), date. Returns: result

add_repeat_itemA
Idempotent

Make a food log itself automatically on chosen days of the week.

Give food_id (from search_foods) and optionally measure_id; without a measure the food's default serving is used. Or give only food_name and the best search match is used: its name is echoed back, so check it.

Args: quantity: How many of the measure, e.g. 1 for one medium banana. food_id: From search_foods. measure_id: A measure of that food, from get_food_details. food_name: Search text, when food_id is not known. days: "all", "weekdays", "weekends", or e.g. "Mon,Wed,Fri". diary_group: Meal to log into, e.g. "Breakfast" (defaults by time of day).

Inputs: quantity (required), days, food_id, food_name, +2 more. Returns: result

analyze_dayB
Read-onlyIdempotent

A day at a glance: calories and macros per meal, and the foods that contributed the most energy and protein.

Args: date: YYYY-MM-DD (defaults to today). top: How many top foods to list.

Inputs: top, date. Returns: result

cancel_active_fastA
DestructiveIdempotent

Cancel a fast that is still running. A recurring fast keeps its schedule; a one-off fast is removed.

To end it with the time fasted kept instead, use edit_fast with an end.

Args: fast_id: The running fast's id, from get_fasting_history.

Inputs: fast_id (required). Returns: result

check_food_fitA
Read-onlyIdempotent

Would eating this food still fit today's targets? Shows the day's calories and macros now, what the food adds, and what would be left.

Args: food_id: From search_foods. grams: Amount you plan to eat, in grams. date: YYYY-MM-DD (defaults to today).

Inputs: grams (required), food_id (required), date. Returns: result

connection_statusA
Read-onlyIdempotent

Check the Cronometer login works, and which account it is.

Inputs: no named parameters. Returns: result

copy_dayA

Copy all diary entries from one day onto another.

Additive -- does not remove existing entries on the destination date, so running it twice duplicates them.

Args: date: Destination date as YYYY-MM-DD (defaults to today). source_date: Day to copy from, as YYYY-MM-DD. Defaults to the day before the destination.

Inputs: date, source_date. Returns: result

create_macro_templateA
Idempotent

Save a new macro target template, and optionally schedule it.

Args: template_name: Name for the template; must not already exist. calories: Energy target in kcal. protein_grams: Protein in grams. carbs_grams: Net carbs in grams. fat_grams: Fat in grams. days: Also make it the weekly default on these days: "all", "weekdays", "weekends" or e.g. "Mon,Wed,Fri".

Inputs: calories (required), fat_grams (required), carbs_grams (required), protein_grams (required), +2 more. Returns: result

create_recipeA
Idempotent

Create a recipe from ingredients, or update one by passing recipe_id.

Cronometer sums the nutrients from the ingredients itself, so unlike add_custom_food you give foods and amounts, not nutrient values. Use search_foods to find each ingredient's food_id and measure_id first.

Cronometer locks a recipe's serving type when it is created and it cannot be changed afterwards, so choose deliberately:

  • "weight" (default) measures the recipe in grams. Portions log as plain grams in every client, including the mobile app's own entry screen. The ingredient list is recorded in the notes rather than as editable ingredients, because Cronometer will not compute a correct weight from ingredients that use non-weight measures.

  • "servings" keeps an editable ingredient list in Cronometer, but the mobile app then offers only a 1 g serving and hides the amount field. Logging through this server is still correct in grams.

cooked_grams is the weight of the finished dish. Give it whenever the food was baked or simmered: nutrients are stored per 100 g, and a dish that lost water is denser than its raw ingredients, so without it a portion weighed off the plate logs too little.

Updating with recipe_id replaces the whole ingredient list, so pass every ingredient, not only new ones. Recipes are removed with retire_custom_food, the same as custom foods.

Args: name: Recipe name. ingredients: List of {"food_id": int, "grams": float, "measure_id": int (optional)}. servings: How many portions the batch makes. notes: Free-text note stored on the recipe. recipe_id: Existing recipe to update in place. serving_type: "weight" or "servings". Cannot be changed later. cooked_grams: Weight of the finished dish, if it lost water in cooking.

Inputs: name (required), ingredients (required), notes, servings, +3 more. Returns: result

delete_fastA
DestructiveIdempotent

Remove a recorded fast.

Args: fast_id: The fast ID, from get_fasting_history.

Inputs: fast_id (required). Returns: result

delete_macro_templateB
DestructiveIdempotent

Delete a saved macro target template.

Args: template_id: From list_macro_templates.

Inputs: template_id (required). Returns: result

delete_repeat_itemA
DestructiveIdempotent

Stop a food from repeating. Entries it already logged are kept.

Args: repeat_item_id: From get_repeated_items.

Inputs: repeat_item_id (required). Returns: result

edit_biometricB
Idempotent

Change the value of a recorded biometric.

Args: biometric_id: The biometric ID, from get_food_log. amount: The corrected value, in the unit the entry already uses. date: Date the entry is on as YYYY-MM-DD (defaults to today).

Inputs: amount (required), biometric_id (required), date. Returns: result

edit_exerciseA
Idempotent

Change the duration or calorie burn of a logged exercise.

Args: exercise_id: The exercise ID, from get_food_log. minutes: New duration in minutes. calories_burned: New burn, as a positive number. date: Date the entry is on as YYYY-MM-DD (defaults to today).

Inputs: exercise_id (required), date, minutes, calories_burned. Returns: result

edit_fastA
Idempotent

Change a recorded fast, including ending one that is still open.

Args: fast_id: The fast ID, from get_fasting_history. start: New start as YYYY-MM-DD HH:MM. end: New end as YYYY-MM-DD HH:MM. goal_hours: New target length in hours.

Inputs: fast_id (required), end, start, goal_hours. Returns: result

edit_food_entryA
Idempotent

Change the amount or time of an already logged food entry.

Use get_food_log to find entry IDs.

Args: entry_id: The serving ID of the entry to change. grams: New amount in grams. time: New time as HH:MM or HH:MM:SS. date: Date the entry is on as YYYY-MM-DD (defaults to today).

Inputs: entry_id (required), date, time, grams. Returns: result

edit_noteA
Idempotent

Replace the text of an existing diary note.

Args: note_id: The note ID, from get_food_log. text: The replacement text. date: Date the note is on as YYYY-MM-DD (defaults to today).

Inputs: text (required), note_id (required), date. Returns: result

export_dataA
Read-onlyIdempotent

Cronometer's own data export, for long date ranges.

data_type is one of:

  • servings: every food logged, with its full nutrients

  • daily_summary: one row of nutrient totals per day

  • exercises, biometrics, notes

The rows are the columns of Cronometer's CSV export. Large results are paged; follow the get_result_page hint.

Args: data_type: servings, daily_summary, exercises, biometrics or notes. start_date: First day, YYYY-MM-DD. end_date: Last day, YYYY-MM-DD (defaults to today). format: "json" for parsed rows or "csv" for the raw text.

Inputs: data_type (required), start_date (required), format, end_date. Returns: result

fetchA
Read-onlyIdempotent

Read one search result in full: a food's nutrients, or a diary day.

Args: id: An id from search, "food:" or "diary:".

Inputs: id (required). Returns: result

find_entries_by_foodA
Read-onlyIdempotent

Every diary entry that references a food, with dates and amounts.

Use before replacing or retiring a food, so its entries can be moved rather than left pointing at something retired.

Cronometer cannot search entries by food, so this reads the diary one day at a time and the range costs a request per day. It defaults to the last 30 days; widen it deliberately.

Args: food_id: The food to look for. start_date: First day as YYYY-MM-DD (defaults to 30 days back). end_date: Last day as YYYY-MM-DD (defaults to today).

Inputs: food_id (required), end_date, start_date. Returns: result

get_biometricsA
Read-onlyIdempotent

Get a biometric time series such as weight or body fat from Cronometer.

Returns the recorded values over the date range as a list of {day, value} points.

Use list_biometrics to find metric_id and unit_id (e.g. Weight is metric_id 1, with unit_id 1 for kg or 2 for lbs).

Args: metric_id: Numeric metric ID from list_biometrics. unit_id: Numeric unit ID from the metric's units in list_biometrics. start_date: Start date as YYYY-MM-DD (defaults to 30 days ago). end_date: End date as YYYY-MM-DD (defaults to today).

Inputs: unit_id (required), metric_id (required), end_date, start_date. Returns: result

get_daily_macro_targetsB
Read-onlyIdempotent

Calorie, protein, carb and fat targets that apply on one day.

Args: date: YYYY-MM-DD (defaults to today).

Inputs: date. Returns: result

get_daily_nutritionA
Read-onlyIdempotent

Get daily nutrition totals for every nutrient eaten that day.

The response has:

  • summary: flat macro totals (energy, protein, carbs, net_carbs, fat, fiber, alcohol). A value is null if nothing that day contained it.

  • nutrients: each with id, name, amount, unit, category, confidence and tracked.

  • tracked_count and untracked_count.

tracked says whether that nutrient has a target set in Cronometer. Untracked nutrients are still eaten and still counted here; they simply have nothing to measure against, and Cronometer's own summary leaves them out. So an absent nutrient means none was eaten, rather than none being tracked.

Report an untracked amount as a plain figure. It has no target, so never describe it as over, under or on track, and offer set_nutrient_target if a target would be useful.

Args: date: Date as YYYY-MM-DD (defaults to today). include_untracked: Leave true for everything eaten. False restricts the response to nutrients with targets, matching the app's own summary.

Inputs: date, include_untracked. Returns: result

get_fasting_historyA
Read-onlyIdempotent

Get fasting history from Cronometer.

Returns fasts within the date range including status, timestamps, and duration.

Args: start_date: Start date as YYYY-MM-DD (defaults to 30 days ago). end_date: End date as YYYY-MM-DD (defaults to today).

Inputs: end_date, start_date. Returns: result

get_fasting_statsA
Read-onlyIdempotent

Get aggregate fasting statistics.

Returns total fasting hours, longest fast, average fast duration, and completed fast count.

Inputs: no named parameters. Returns: result

get_food_detailsA
Read-onlyIdempotent

Get detailed food information including nutrition and serving sizes.

Use this after search_foods to get the full nutrient profile and available measure_ids needed for add_food_entry.

nutrients is for one serving of serving_grams, keyed and scaled exactly as add_custom_food and update_custom_food take them, so a value read here can be written straight back unchanged. Cronometer's own stored rows, per 100 g and by numeric id, are alongside as nutrients_per_100g.

Args: food_id: Food ID from search_foods results.

Inputs: food_id (required). Returns: result

get_food_logA
Read-onlyIdempotent

Get all diary entries for a given date.

Returns every food entry logged for the day. Each "Serving" entry is enriched (best-effort) with the food's name, source, the serving measure (unit name and grams per unit), the number of servings, and that food's own nutrient profile scaled to the amount eaten. Non-food entries (exercise, biometrics) carry their own name.

Note: the per-entry "nutrients" are each food's individual contribution, which is distinct from the day-level nutrition_summary aggregate below.

Also returns a top-level energy_summary field with pre-computed values most relevant to the user:

  • total_target_kcal: daily calorie target dynamically adjusted for expenditure and weight goal (equivalent to Cronometer's "Total Target" in the Energy Summary screen)

  • consumed_kcal: total calories consumed

  • remaining_kcal: calories remaining to stay on target (total_target_kcal - consumed_kcal). Always report this when summarizing the user's day. Prefer this over manually deriving values from the burn breakdown fields.

Also returns a nutrition_summary field with consumed totals for every nutrient eaten that day:

  • macros: flat macro totals (energy, protein, carbs, net_carbs, fat, fiber, alcohol)

  • nutrients: every nutrient with amount, unit and tracked, which says whether it has a target set in Cronometer. Untracked ones were still eaten; they just have nothing to measure against, so report them as plain figures and never as over or under target.

Args: date: Date as YYYY-MM-DD (defaults to today). include_untracked: Leave true for everything eaten. False restricts the summary to nutrients with targets, matching the app's own summary.

Inputs: date, include_untracked. Returns: result

get_macro_targetsA
Read-onlyIdempotent

Get current macro targets including weekly schedule and templates.

Returns the weekly macro schedule (which template applies to each day) and all saved macro target templates with their values.

Inputs: no named parameters. Returns: result

get_nutrition_scoresA
Read-onlyIdempotent

Get nutrition scores with per-nutrient consumed amounts and category grades.

Returns category scores (All Targets, Vitamins, Minerals, Electrolytes, Antioxidants, Immune Support, Metabolism, Bone Health) with the actual consumed amount and confidence level for each nutrient.

This is Cronometer's own scoring, so it covers only nutrients with a target; scoring one without a target would have nothing to score against. Use it to see how close each nutrient is to its target, and get_daily_nutrition when you need everything that was actually eaten.

Args: date: Date as YYYY-MM-DD (defaults to today).

Inputs: date. Returns: result

get_nutrition_summaryA
Read-onlyIdempotent

Daily macro totals over a range of days, with averages.

Each day carries energy, protein, carbs, net carbs, fat, fiber and alcohol. Days with nothing logged are listed but left out of the averages. Pass nutrient names (e.g. ["vitamin_d", "iron"]) to add those as well. At most 62 days per call.

Args: start_date: First day, YYYY-MM-DD. end_date: Last day, YYYY-MM-DD (defaults to today). nutrients: Extra nutrient names to include, as in list_nutrients.

Inputs: start_date (required), end_date, nutrients. Returns: result

get_profileA
Read-onlyIdempotent

The account profile: birthdate, gender, timezone and language.

Inputs: no named parameters. Returns: result

get_recent_foodsA
Read-onlyIdempotent

Recently logged foods with how often each was logged.

The fastest route for "log my usual": find the food here, then add_food_entry. Recipes are flagged so a whole batch is not logged as one portion by mistake.

Inputs: no named parameters. Returns: result

get_repeated_itemsB
Read-onlyIdempotent

Foods set to log themselves automatically on chosen days of the week.

Inputs: no named parameters. Returns: result

get_result_pageA
Read-onlyIdempotent

Read more of a result that was too large to return in one piece.

Any tool whose answer is too large returns its first page with a paging block. Pass that block's result_id here. Nothing is fetched again: the stored result is paged, filtered and narrowed.

Args: result_id: From the paging block of the earlier answer. offset: First item to return, usually the previous next_offset. limit: At most this many items. Fewer come back if they would not fit. fields: Only these keys of each item. Dots reach nested keys, such as "statistics.sizeOnDisk". The paging.fields list shows what exists. match: Keep only items whose field contains this text, ignoring case, for example {"title": "alien", "year": "1979"}. path: Which list to page when the result holds several, as named in paging.lists. Defaults to the largest.

Inputs: result_id (required), path, limit, match, +2 more. Returns: result

get_streakA
Read-onlyIdempotent

Diary logging streaks: current run of fully logged days, and the record.

Args: date: Day to count back from as YYYY-MM-DD (defaults to today).

Inputs: date. Returns: result

get_targetsB
Read-onlyIdempotent

Nutrient targets for the account, as shown beside the diary totals.

Inputs: no named parameters. Returns: result

import_recipeA
Idempotent

Create a recipe from a free-text ingredient list.

Cronometer's own parser matches each line ("2 tbsp ketchup", "1 cup rice") to a database food and works out the grams, the same as Import Recipe in the app. Matching is fuzzy: show the matched ingredients to the user, and run with save=false first when unsure. create_recipe is the exact alternative when food ids and grams are already known.

Args: ingredients: One ingredient per line. name: Recipe name. Defaults to the one Cronometer suggests. servings: How many portions the batch makes. save: False to only parse and preview the matches.

Inputs: ingredients (required), name, save, servings. Returns: result

list_biometricsA
Read-onlyIdempotent

List the biometric metrics tracked in Cronometer.

Returns every metric type the account can record (Weight, Body Fat, Heart Rate, Blood Glucose, Waist Size, Sleep, blood panels, body measurements, etc.). Use the metric_id and a unit_id from the results with get_biometrics.

Inputs: no named parameters. Returns: result

list_custom_foodsA
Read-onlyIdempotent

Every custom food and recipe you own, with no database results mixed in.

Leave query empty for the whole library. This is the way to audit it: to find, say, every food whose name lost its Finnish characters, list them all and read the names, then fix each with update_custom_food.

Retired foods are hidden by Cronometer's own library listing, so include_retired cannot bring them back; it only affects rows already returned.

Args: query: Narrow the list by name. Empty lists everything. include_retired: Keep retired foods in the result when they appear.

Inputs: query, include_retired. Returns: result

list_diary_groupsA
Read-onlyIdempotent

The diary groups (meals) this account uses, e.g. Breakfast, Lunch.

Inputs: no named parameters. Returns: result

list_macro_templatesB
Read-onlyIdempotent

Saved macro target templates, with their ids and values.

Inputs: no named parameters. Returns: result

list_nutrientsA
Read-onlyIdempotent

Every nutrient add_custom_food accepts, with its unit and category.

Read from the account's own catalog, so it stays right as Cronometer adds nutrients. Use the returned keys for the add_custom_food nutrients dict.

Inputs: no named parameters. Returns: result

list_recipesA
Read-onlyIdempotent

Every recipe you own, with its serving type.

serving_type says which kind each one is: "weight" logs in grams everywhere, "servings" keeps an editable ingredient list but shows a 1 g serving in the mobile app. It is fixed at creation and cannot be changed.

Args: include_retired: Keep retired recipes in the result when they appear.

Inputs: include_retired. Returns: result

log_sleepA
Idempotent

Record a night's sleep, optionally with its stages and a sleep score.

Each value is stored as its own biometric (Sleep, and the Deep, Light, REM and Score metrics when the account has them), so they show in Cronometer's trends. Fix or remove one with edit_biometric or remove_biometric.

Args: hours: Total sleep in hours, e.g. 7.5. date: The diary day it belongs to, as YYYY-MM-DD (defaults to today). deep_hours: Deep sleep in hours. light_hours: Light sleep in hours. rem_hours: REM sleep in hours. score: Sleep score from 0 to 100.

Inputs: hours (required), date, score, rem_hours, +2 more. Returns: result

lookup_barcodeA
Read-onlyIdempotent

Find a packaged food by its UPC/EAN barcode.

Returns matching foods with the same fields as search_foods, so a hit can go straight to get_food_details or add_food_entry.

Args: barcode: The digits printed under the barcode.

Inputs: barcode (required). Returns: result

mark_day_completeA
Idempotent

Mark a diary day as complete or incomplete.

Args: date: Date to mark as YYYY-MM-DD. complete: True to mark complete, False for incomplete.

Inputs: date (required), complete. Returns: result

nutrient_reportA
Read-onlyIdempotent

Every tracked nutrient for a day against its target: amount, target, upper limit and percent of target, lowest first.

Good for spotting gaps (what to eat more of) and anything over a limit.

Args: date: YYYY-MM-DD (defaults to today).

Inputs: date. Returns: result

remove_biometricA
DestructiveIdempotent

Delete recorded biometrics, such as a reading from a misbehaving scale.

Deleting is right when the measurement never happened or is impossible; edit_biometric is for a value that is merely wrong, since editing keeps the day's entry and its history.

Args: biometric_ids: List of biometric IDs to remove, from get_food_log. date: Date the entries belong to as YYYY-MM-DD (defaults to today).

Inputs: biometric_ids (required), date. Returns: result

remove_exerciseA
DestructiveIdempotent

Delete logged exercise entries.

Use this for an entry that should not be there at all, such as a duplicate from a tracker sync. edit_exercise is for one whose duration or burn is merely wrong.

Args: exercise_ids: List of exercise IDs to remove, from get_food_log. date: Date the entries belong to as YYYY-MM-DD (defaults to today).

Inputs: exercise_ids (required), date. Returns: result

remove_food_entryA
DestructiveIdempotent

Remove diary entries of any kind: foods, exercise, biometrics, notes.

Use get_food_log to find entry IDs (servingId, exerciseId, biometricId or noteId). Deleting cannot be undone.

Args: entry_ids: List of entry IDs to remove. date: Date the entries belong to as YYYY-MM-DD (defaults to today).

Inputs: entry_ids (required), date. Returns: result

remove_noteA
DestructiveIdempotent

Delete a note from the diary.

Args: note_id: The noteId, from get_food_log. date: The note's day, YYYY-MM-DD (defaults to today); used to confirm it went.

Inputs: note_id (required), date. Returns: result

retire_custom_foodA
DestructiveIdempotent

Retire a custom food so it stops being offered for new entries.

This is how Cronometer removes a food; there is no delete. Diary entries that already use it keep working. Pass retired=False to bring it back.

Args: food_id: The custom food's ID. retired: True to retire, False to restore.

Inputs: food_id (required), retired. Returns: result

search_activitiesA
Read-onlyIdempotent

Search Cronometer's exercise activities by name, for add_exercise.

Every word of the query must appear in the name, e.g. "running 6 mph" or "cycling". Returns up to 50 activity ids.

Args: query: Words to look for in the activity name.

Inputs: query (required). Returns: result

search_foodsA
Read-onlyIdempotent

Search Cronometer's food database by name.

Returns matching foods with their IDs and source information. Use the food_id and measure_id from results with add_food_entry, or pass food_id to get_food_details for full nutrition info.

Args: query: Food name or keyword (e.g. "eggs", "chicken breast").

Inputs: query (required). Returns: result

set_macro_targetsA
Idempotent

Change the calorie and macro targets for one day.

Only the values given change; the rest are read first and kept. For a lasting change across the week, use create_macro_template and set_weekly_macro_schedule instead.

Args: date: YYYY-MM-DD (defaults to today). calories: Energy target in kcal. protein_grams: Protein target in grams. carbs_grams: Net carbs target in grams. fat_grams: Fat target in grams. template_name: Label for the day's targets (defaults to "Custom Targets").

Inputs: date, calories, fat_grams, carbs_grams, +2 more. Returns: result

set_nutrient_targetA
Idempotent

Set a nutrient's daily target, its upper limit, or whether it is tracked.

Giving a minimum or maximum makes it a custom target, replacing the default Cronometer works out from the profile. Setting visible turns tracking of that nutrient on or off, which is how a micronutrient starts showing up in the diary at all.

Only what you pass changes: the rest of the nutrient's settings are read first and kept, so turning on visibility never disturbs an existing target.

Going back to Cronometer's own default is done in the app, under Settings then Targets. Report the current value from get_targets before overwriting one, so it can be put back by hand if wanted.

Args: nutrient: Nutrient name, e.g. "protein", "iodine", "choline", "biotin". Call list_nutrients for the accepted names. minimum: Daily target, in that nutrient's own unit. maximum: Upper limit, in that nutrient's own unit. visible: True to track the nutrient, False to hide it.

Inputs: nutrient (required), maximum, minimum, visible. Returns: result

set_weekly_macro_scheduleA
Idempotent

Make a saved template the default targets on chosen days of the week.

This changes the recurring schedule for every future week.

Args: template_name: A template from list_macro_templates, or "default" to put those days back on the profile's default targets. days: "all", "weekdays", "weekends" or e.g. "Mon,Wed,Fri".

Inputs: template_name (required), days. Returns: result

update_custom_foodA
Idempotent

Edit one of your custom foods in place, keeping its diary entries.

Only what you pass changes. Entries already logged stay attached to this same food and their nutrition follows the edit, so a typo or a wrong nutrient can be fixed without re-logging anything.

Nutrients are merged into the existing profile, so correcting one value leaves the rest alone. Call list_nutrients for the accepted names.

Amounts are for one serving, the same basis get_food_details reports and returns as serving_grams. That is the food's default measure, which is not always 100 g: a juice measured in millilitres takes per-millilitre values. Read the food first and the two always agree.

Args: food_id: The custom food to edit. name: New name. notes: New note text. nutrients: Nutrient name to amount per serving, merged in. measures: [{"measure_id": int, "name": str, "grams": float}] to fix a wrongly weighted measure. name and grams are each optional. Leave measure_id out to ADD a serving size instead, giving name and grams: that is how a food gets a per-piece measure such as "1 karkki" or "1 viipale" alongside plain grams, so it can be logged by the count as well as by weight.

Inputs: food_id (required), name, notes, measures, +1 more. Returns: result

update_recipeA
Idempotent

Edit one of your recipes in place, keeping its diary entries.

Only what you pass changes, and the serving type never does: Cronometer fixes it at creation. Passing ingredients replaces the whole list, so send every ingredient rather than only the new ones.

Args: food_id: The recipe to edit. name: New name. notes: New note text. ingredients: Full replacement list of {"food_id": int, "grams": float, "measure_id": int (optional)}. cooked_grams: New finished weight. Weight-based recipes only, and it needs the ingredients too, since the nutrition is recomputed.

Inputs: food_id (required), name, notes, ingredients, +1 more. Returns: result

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.5/5.0

    Scored across 65 tools

    Disambiguation2/5

    Several tools overlap significantly: remove_food_entry can delete foods, exercise, biometrics, or notes even though dedicated remove_note, remove_exercise, and remove_biometric tools exist; generic search/fetch duplicate search_foods/get_food_details; and create_recipe can update recipes while update_recipe also edits them. Nutrition reporting is split across get_food_log, get_daily_nutrition, analyze_day, get_nutrition_summary, nutrient_report, and get_nutrition_scores, whose boundaries are described but still easy to confuse.

    Naming Consistency4/5

    All names use snake_case and mostly follow verb_noun patterns. Minor inconsistencies exist in verb choice, such as remove versus delete, add versus create, and generic search/fetch names that are less descriptive than the rest.

    Tool Count1/5

    With 65 tools, the surface is far beyond a well-scoped set, even for a rich domain like Cronometer. The large count is compounded by overlapping removal, nutrition-reporting, and recipe-editing tools.

    Completeness4/5

    Coverage is broad: diary entries, custom foods, recipes, biometrics, exercise, fasting, nutrient and macro targets, repeated items, notes, exports, and account status are all represented. There are minor gaps and inconsistencies, such as contradictory note-deletion guidance and no explicit water or supplement logging surface.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      A
      quality
      A
      maintenance
      An MCP server that provides access to Cronometer nutrition data, enabling users to pull food logs, macro and micronutrient summaries, and biometric data into Claude or Cursor. It supports daily nutrition tracking and raw CSV exports by interfacing with the Cronometer web protocol.
      27
      606 PyPI
      28
      MIT
    • A
      license
      A
      quality
      B
      maintenance
      Enables nutrition tracking with Cronometer, including food logging, food search, diary management, and nutrition data retrieval via natural language.
      13
      MIT
    • A
      license
      A
      quality
      B
      maintenance
      Unofficial, self-hosted MCP server that connects Claude or ChatGPT to your own FatSecret food diary. It drafts entries from a description, a plate photo or a nutrition label, saves them only after you confirm, and changes or removes entries the same way. It also reads a day or several weeks against your calorie and macro targets.
      2
      5
      64 PyPI
      6
      MIT