# Nutrition MCP Personal calorie and nutrient tracking MCP server. Looks up food via OpenFood Facts, logs meals with portion-scaled nutrients, and tracks daily intake against gout and hypertension-aware goals. ## Tech Stack - [Rust](https://www.rust-lang.org/) — Systems language - [rmcp](https://github.com/modelcontextprotocol/rust-sdk) — Official Rust MCP SDK (streamable HTTP transport) - [axum](https://github.com/tokio-rs/axum) 0.8 — Web framework for HTTP transport + auth middleware - [sqlx](https://github.com/launchbadge/sqlx) 0.8 — Async SQLite - [reqwest](https://github.com/seanmonstar/reqwest) 0.12 — HTTP client for OpenFood Facts APIs - [SQLite](https://www.sqlite.org/) with FTS5 — Local database + full-text search ## Health Context This tracker is configured for two specific health conditions: - **Gout** — Tracks fructose (15g/day limit) and alcohol (0 limit). Both are known gout triggers. - **Hypertension** — Tracks salt (6g/day, NHS guideline), potassium (3500mg, helps lower BP), calcium (700mg), magnesium (300mg), and cholesterol (300mg). ## 14 Tracked Nutrients | Nutrient | OFF field | Default goal | Unit | Reason | |----------|-----------|-------------|------|--------| | Energy | `energy-kcal_100g` | 2500 | kcal | General | | Protein | `proteins_100g` | 150 | g | General | | Carbohydrates | `carbohydrates_100g` | 300 | g | General | | Fat | `fat_100g` | 70 | g | General | | Fiber | `fiber_100g` | 30 | g | General | | Sugars | `sugars_100g` | 90 | g | General | | Saturated fat | `saturated-fat_100g` | 20 | g | Hypertension | | Salt | `salt_100g` | 6 | g | Hypertension (key) | | Fructose | `fructose_100g` | 15 | g | Gout (key) | | Alcohol | `alcohol_100g` | 0 | % vol | Gout (key) | | Potassium | `potassium_100g` | 3500 | mg | Hypertension (key) | | Calcium | `calcium_100g` | 700 | mg | Hypertension | | Magnesium | `magnesium_100g` | 300 | mg | Hypertension | | Cholesterol | `cholesterol_100g` | 300 | mg | Hypertension | ## MCP Tools (13) ### Food Lookup - `search_food(query, page_size?)` — Full-text search via OpenFood Facts Search-a-licious - `get_food_by_barcode(barcode)` — Product lookup by barcode via OFF API v3 ### Logging - `log_food(product_code, grams?, servings?, date?, meal?)` — Log OFF product with portion scaling - `log_custom_food(name, calories, nutriments?, grams, date?, meal?)` — Log homemade/restaurant food - `delete_entry(id)` — Remove a logged entry ### Summary & History - `daily_summary(date?)` — All 14 nutrients vs goals, remaining, entry list - `history(days?)` — Last N days of totals (default 7) - `list_entries(date?)` — List entries with IDs for deletion ### Goals - `get_goals()` — Current daily nutrient targets - `set_goals(goals)` — Update goals (partial merge) ### Weight - `log_weight(weight_kg, date?)` — Log body weight in kg - `weight_history(days?)` — Weight trend (default 30 days) ### Future - `bulk_import(parquet_url?)` — Stub for offline Parquet import ## Build ```bash cargo build --release ``` Binary: `target/release/nutrition-mcp` ## Environment Variables | Variable | Default | Description | |----------|---------|-------------| | `NUTRITION_MCP_BIND` | `0.0.0.0:9432` | Bind address | | `NUTRITION_MCP_API_KEY` | (random UUID) | Bearer token for auth | | `NUTRITION_MCP_DB_PATH` | `~/.local/share/nutrition/nutrition.db` | SQLite database path | | `NUTRITION_MCP_OFF_USER_AGENT` | `NutritionMCP/0.1.0 (personal use)` | User-Agent for OFF API | ## Run ```bash NUTRITION_MCP_API_KEY=your-secret-key \ NUTRITION_MCP_BIND=0.0.0.0:9432 \ ./target/release/nutrition-mcp ``` ### Health Check ```bash curl http://localhost:9432/health # → OK ``` ### MCP Connect ```bash curl -X POST http://localhost:9432/mcp \ -H "Authorization: Bearer your-secret-key" \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"test","version":"0.1"}},"id":1}' ``` ## Deploy as systemd Service ```bash # Create service file cat > ~/.config/systemd/user/nutrition-mcp.service << 'EOF' [Unit] Description=Nutrition MCP Server After=network.target [Service] ExecStart=%h/workspace/nutrition-mcp/target/release/nutrition-mcp Environment=NUTRITION_MCP_BIND=0.0.0.0:9432 Environment=NUTRITION_MCP_API_KEY=your-secret-key Environment=NUTRITION_MCP_DB_PATH=%h/.local/share/nutrition/nutrition.db Environment=NUTRITION_MCP_OFF_USER_AGENT=NutritionMCP/0.1.0 (personal use; contact: atradeus@pm.me) Restart=on-failure RestartSec=5 [Install] WantedBy=default.target EOF # Enable linger (survive logout) loginctl enable-linger $USER # Start systemctl --user daemon-reload systemctl --user enable --now nutrition-mcp ``` ## Connect from Hermes ```bash hermes mcp add nutrition --url http://localhost:9432 --header "Authorization: Bearer your-secret-key" ``` ## Connect from Laptop (LAN) ```bash hermes mcp add nutrition --url http://chronos:9432 --header "Authorization: Bearer your-secret-key" ``` ## Data Sources - [OpenFood Facts Search-a-licious](https://search.openfoodfacts.org) — Full-text product search - [OpenFood Facts API v3](https://world.openfoodfacts.org/api/v3) — Barcode lookups - [OpenFood Facts Parquet](https://huggingface.co/datasets/openfoodfacts/product-database) — Future offline bulk import (7.82 GB, 4.7M products) All food data is cached locally in SQLite with FTS5 for instant repeat lookups. ## Architecture See [AGENTS.md](./AGENTS.md) for full project instructions, conventions, and module map. ## License MIT