fix: use modern /food/add endpoint for diary writes#5
Open
sverrejoh wants to merge 3 commits into
Open
Conversation
The legacy /food/diary/{username}/add endpoint that
add_food_to_diary() and mfp_add_food_to_diary tool target was
removed by MyFitnessPal. All write attempts silently fail:
GET https://www.myfitnesspal.com/food/diary/{user}/add → 404
POST https://www.myfitnesspal.com/food/diary/{user}/add → 404
(Reproducible today with a fresh authenticated session, fully
populated Rails session cookies, and the exact post_data the
function constructs.)
The "Add Food to Diary" form on the live site now POSTs to a
different endpoint with a substantially different field
contract — discovered by capturing a real browser submission
via Playwright:
POST /food/add
authenticity_token=<from search-results page>
food_entry[food_id]=<data-original-id, NOT mfp_id>
food_entry[date]=YYYY-MM-DD
food_entry[quantity]=<servings>
food_entry[weight_id]=<first data-weight-ids value>
food_entry[meal_id]=0|1|2|3
Required headers:
X-CSRF-Token: <meta name="csrf-token" content>
Referer: https://www.myfitnesspal.com/food/search
(NO X-Requested-With — that triggers a 302 to /account/login)
The new flow rewrites add_food_to_diary to:
1. Warm the session by visiting /food/diary
2. Fetch /food/add_to_diary?meal=X (for initial authenticity_token)
3. POST /food/search with the food's name (resolved via
get_food_item_details) to surface the result row, which
exposes data-original-id and data-weight-ids
4. POST /food/add with the modern field structure and the
X-CSRF-Token header
Also raises AddFoodToDiaryInput.quantity upper bound from 100
to 10000 — the previous cap blocked common gram-based entries
like "200 g cottage cheese" or "109 g ham".
Verified end-to-end: adding 5 foods via mfp_add_food_to_diary
produces 5 entries in the diary on the subsequent
mfp_get_diary read, with correct quantities, units, and
nutrition totals. The original code on the same session,
inputs, and food IDs writes 0 entries (silent 404 from the
dead endpoint).
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
Adds delete support for diary entries — the missing inverse of
mfp_add_food_to_diary.
Endpoint:
DELETE /food/remove/{food_entry_id}
Headers:
X-CSRF-Token: <meta name="csrf-token" content>
Referer: https://www.myfitnesspal.com/food/diary
X-Requested-With: XMLHttpRequest
Success = 200/204/302.
The food_entry_id is the internal entry identifier (different from
mfp_id of the food itself). It can be scraped from the rendered
diary page on each <a data-food-entry-id="..."
class="js-show-edit-food">.
The tool takes one of two inputs:
1. entry_id — precise deletion of a known entry.
2. name_contains — fuzzy substring match against entry names,
optionally filtered by meal, capped at max_matches (default 1)
for safety.
Two internal helpers added:
list_diary_entries(client, date) — scrape entry IDs/names/meals
remove_food_entry(client, eid) — perform the DELETE
Verified end-to-end:
add → get_diary (visible) → remove → get_diary (gone)
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
Author
|
Added a second commit: Same investigation pattern as the write fix — used Playwright to find that each diary entry row exposes its The new MCP tool supports two modes:
Round-trip verified through the MCP tools: Happy to split this into a separate PR if you'd prefer the fix and the feature reviewed independently — just say the word. |
AdamWalt
requested changes
Jun 7, 2026
Addresses two review comments on AdamWalt#5: 1. **No silent fallback to first search result.** Previously, when the food's data-external-id wasn't found in the first page of /food/search results, add_food_to_diary would substitute the first result. That risked silently writing the wrong food to a user's diary — a much worse failure than failing the call. The fallback is removed. add_food_to_diary now raises with an actionable message that distinguishes "id not on this page of results" from "no results at all", and tells the caller to use mfp_search_food to disambiguate. get_food_item_details is wrapped in try/except so a bogus id degrades gracefully into the search-step error, rather than throwing an empty lxml/HTTP exception earlier in the pipeline. 2. **Remove `unit` from AddFoodToDiaryInput.** The rewritten add_food_to_diary always uses the food's first weight_id and treats quantity as servings-of-default-unit. The public schema still exposed `unit`, which created the surprising contract "you can pass '1 cup' but it'll be ignored". `unit` is dropped from the model entirely and the schema now sets `extra="forbid"`, so a caller passing `unit="1 cup"` gets a clear ValidationError at the boundary. The docs for `quantity` now point the caller to mfp_get_food_details to inspect what the default unit actually is for any given food. Verified: - Schema rejects unit='1 cup' with extra_forbidden ValidationError - Happy-path add still works (Bama agurk x0.5) - Bogus mfp_id 999999999999999 raises "No search results returned for food 999999999999999 ... Try mfp_search_food directly" 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
AdamWalt
approved these changes
Jun 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fix: use modern
/food/addendpoint for diary writesSummary
add_food_to_diary()(and therefore themfp_add_food_to_diaryMCP tool)currently writes nothing. The legacy endpoint it targets —
POST /food/diary/{username}/add—was removed from MyFitnessPal and now returns 404. Every write attempt looks
successful (no exception) but the diary never receives the entry.
This PR rewrites the function to use MFP's current
POST /food/addendpoint,discovered by capturing a real "Add Food to Diary" browser submission via Playwright.
The bug, reproduced
Tested on June 4, 2026 with a freshly authenticated session
(
__Secure-next-auth.session-tokencookie, fully populated Rails session cookiesincluding
_mfp_session,remember_me,known_user):The MCP tool returns generic "Failed to add food to diary" but the diary on the
subsequent read has zero matching entries.
The new endpoint
Captured by Playwright submitting the visible Add Food form:
Two key differences from the legacy contract:
food_entry[food_id]isdata-original-id, notmfp_id. They are different IDs.The MFP search results expose both:
data-external-id="23647715544933"← matchesmfp_idfrommfp_search_fooddata-original-id="1501419233"← the value/food/addexpectsfood_entry[meal_id], notmealis the field name for meal selection.How the rewritten function works
/food/diary(populates Rails session cookies).GET /food/add_to_diary?meal=<index>to get an initialauthenticity_token.client.get_food_item_details(mfp_id),then
POST /food/searchto surface a result row withdata-original-idanddata-weight-ids.POST /food/addwith the modern field structure, the metaX-CSRF-Tokenheader, and
Referer: /food/search.A 302 to
/food/diary/*confirms success. A 302 to/account/loginindicatesthe session is missing/expired, and a non-redirect response is treated as
failure.
Additional fix
AddFoodToDiaryInput.quantitypreviously hadle=100, which blocked anygram-quantity entry larger than 100 g — e.g. "200 g cottage cheese",
"109 g ham", "150 g rice" — all common cases. Raised to
le=10000.Verification
Adding 5 foods (109 g ham, 57 g bread, 5 g butter, 43 g cucumber,
200 g cottage cheese, 1 medium orange) via the patched
mfp_add_food_to_diary:{"success": true, ...}mfp_get_diaryshows all 5 entries with correct quantities,units, and nutrition totals
The original code on the same session and food IDs persists 0 entries.
🤖 This patch was developed and verified by Claude by reverse-engineering the live site
behavior with Playwright. The original test transcript (failure proofs + new
endpoint discovery + post-fix verification) is available on request.