OFF data is sparse for fresh produce — spinach, oats, most frozen fruit
have no potassium. Added a curated fallback table (114 foods, USDA-sourced)
that injects estimated K/Ca/Mg at log time when OFF has no data.
How it works:
- data/fallback_nutrients.json: 114 foods with mg/100g values
- FallbackTable: case-insensitive exact + substring name matching
- log_food: injects _est keys (potassium_100g_est etc.) when OFF missing
- sum_nutriments: tries OFF value first (grams×1000), then _est (already mg)
Results for today's smoothie + lunch:
- Potassium: 691mg → 2,530mg (47% of 5400mg goal)
- Calcium: 14mg → 304mg (30% of 1000mg goal)
- Magnesium: 61mg → 330mg (73% of 450mg goal)
38 tests passing (7 new: fallback lookup, est fallback, OFF vs est priority)
OFF stores potassium_100g, calcium_100g, magnesium_100g in grams.
Goals are in mg. sum_nutriments now multiplies these by 1000.
Before: potassium showed 0.7mg (was 0.0007g displayed as mg)
After: potassium shows 691.2mg (banana: 0.32g/100g × 216g × 1000)
Added MG_NUTRIENTS const to db.rs. Updated test assertions.
31 tests passing.
- POST /ingest endpoint for Apple Health vitals (src/ingest.rs)
- GET /scriptable/health-sync.js for phone download
- recipes/daily-smoothie.json — Atrdeus's daily smoothie with barcodes
- Scriptable script reads HealthKit and POSTs to /ingest
Closes nutrition-mcp-aav
Old script used deprecated Health.read() with Health.Types.* and .quantity
— every read silently failed in try/catch, so no samples were ever collected.
Rewrote to use Health.queryQuantitySamples() with string type identifiers
and .quantityValue(unit) per current Scripting app docs. Added proper units
(kg, mmHg, count/min, count, percent) for each metric type.
Also adds /scriptable/health-sync.js endpoint to serve the script directly.
- Add vitals table (type, timestamp_utc, value JSON, source) with UNIQUE
(type,timestamp_utc) for idempotent upserts.
- POST /ingest: Bearer-protected HTTP endpoint accepting batched health
samples (weight, blood_pressure, heart_rate, spo2, steps, sleep, ...).
Weight samples are mirrored into weight_log so existing weight tools see them.
- recent_vitals MCP tool: reads back ingested vitals, grouped by type with
min/avg/max/units over a configurable window.
- 6 new tests (31 integration + 2 unit total, all passing).
- Verify live: /ingest POST -> recent_vitals round-trip over MCP protocol.
Parameter-less tools in rmcp generated inputSchema: {} (empty dict
with no type field), which the Hermes MCP client rejects — one bad
tool fails the entire tools/list response.
Fix: added NoParams struct (empty, derives JsonSchema) and changed
get_goals to accept Parameters<NoParams>. schemars generates
{"type": "object", ...} for empty structs, which is valid.
- Search URL: search.openfoodfacts.org/search (not /api/v1/search)
- Search-a-licious returns no nutriments in hits — fetch each via v3 API
- Fall back to nutriments_estimated when no_nutrition_data is set
or nutriments object has <3 keys (common for fresh produce)
- Handle brands as array or string from search API
- systemd user service deployed on Pi5 (port 9432, API key auth)
- Registered with Hermes via mcp_servers config
- Hermes skill at ~/.hermes/skills/productivity/nutrition-tracker/
- Git remote: git.darkmark.co.uk/amerlo/nutrition-mcp
Closes nutrition-mcp-foe, nutrition-mcp-aea