docs: README.md, AGENTS.md, CLAUDE.md
- README: human-facing with tech stack, 14 nutrient table, 13 tools, env vars, build/run/deploy instructions, Hermes integration - AGENTS/CLAUDE: build commands, architecture, module map, SQLite schema, conventions, tracked nutrients, session protocol Closes nutrition-mcp-yd5, nutrition-mcp-4lv
This commit is contained in:
parent
244e141053
commit
5beef81d70
5 changed files with 366 additions and 173 deletions
BIN
.README.md.swp
Normal file
BIN
.README.md.swp
Normal file
Binary file not shown.
|
|
@ -1 +1,11 @@
|
||||||
{"id":"int-318d614e9de852a6fde2cb00ed524b17","kind":"field_change","created_at":"2026-08-20T15:52:06.000603613Z","actor":"Anthony Merlo","issue_id":"nutrition-mcp-dfl","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Scaffold complete. Cargo.toml + 4 modules (main, config, db, api, tools). Binary compiles and passes smoke test: health check, MCP initialize, 13 tools listed, get_goals returns correct defaults, daily_summary works, auth middleware blocks wrong key."}}
|
{"id":"int-318d614e9de852a6fde2cb00ed524b17","kind":"field_change","created_at":"2026-08-20T15:52:06.000603613Z","actor":"Anthony Merlo","issue_id":"nutrition-mcp-dfl","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Scaffold complete. Cargo.toml + 4 modules (main, config, db, api, tools). Binary compiles and passes smoke test: health check, MCP initialize, 13 tools listed, get_goals returns correct defaults, daily_summary works, auth middleware blocks wrong key."}}
|
||||||
|
{"id":"int-62a4982fa73b185a7e7395d51d3b54e5","kind":"field_change","created_at":"2026-08-20T15:52:18.844922151Z","actor":"Anthony Merlo","issue_id":"nutrition-mcp-c84","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"Schema implemented in db.rs: products table with FTS5 virtual table + sync triggers, entries table, daily_goal (seeded with gout+hypertension defaults), weight_log. All created via sqlx migrations, tested with smoke test."}}
|
||||||
|
{"id":"int-397b49c6973880d5ea496cef86650f1a","kind":"field_change","created_at":"2026-08-20T15:52:19.483777181Z","actor":"Anthony Merlo","issue_id":"nutrition-mcp-zhe","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"OFF API client implemented in api.rs: Search-a-licious full-text search, v3 barcode lookup, SQLite caching with FTS5. reqwest with custom User-Agent. Tested: get_goals and daily_summary work with DB."}}
|
||||||
|
{"id":"int-3c43e07f205f60633297e810ccc04e5a","kind":"field_change","created_at":"2026-08-20T15:52:20.124586151Z","actor":"Anthony Merlo","issue_id":"nutrition-mcp-jma","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"HTTP transport implemented in main.rs: axum + StreamableHttpService on configurable bind address, Bearer token auth middleware, /health endpoint. Tested: 401 on wrong key, 200 on correct key, MCP initialize + tools/list + tool calls all work."}}
|
||||||
|
{"id":"int-23465b5866885bb45393b5372a2163d2","kind":"field_change","created_at":"2026-08-20T15:52:26.891537504Z","actor":"Anthony Merlo","issue_id":"nutrition-mcp-wl7","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"log_food and log_custom_food implemented in tools.rs. Portion scaling by grams or servings, auto-meal detection, daily summary shown after logging."}}
|
||||||
|
{"id":"int-4db32069e2e8ce8a8e3444448cf27631","kind":"field_change","created_at":"2026-08-20T15:52:27.463319733Z","actor":"Anthony Merlo","issue_id":"nutrition-mcp-yzk","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"search_food (Search-a-licious + FTS5 cache) and get_food_by_barcode (v3 API + cache) implemented in tools.rs."}}
|
||||||
|
{"id":"int-7201b4fa10254df4f2cf66159741e818","kind":"field_change","created_at":"2026-08-20T15:52:28.006571291Z","actor":"Anthony Merlo","issue_id":"nutrition-mcp-7lp","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"get_goals, set_goals (partial merge), log_weight (kg, upsert), weight_history implemented in tools.rs."}}
|
||||||
|
{"id":"int-590fa3d8f15860c8a3860a88609a5a7d","kind":"field_change","created_at":"2026-08-20T15:52:28.678811685Z","actor":"Anthony Merlo","issue_id":"nutrition-mcp-4su","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"bulk_import stub implemented — returns not-yet-implemented message."}}
|
||||||
|
{"id":"int-69e01a95db02617276cebfa81282b56b","kind":"field_change","created_at":"2026-08-20T15:52:29.247957328Z","actor":"Anthony Merlo","issue_id":"nutrition-mcp-4lv","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"AGENTS.md and CLAUDE.md auto-generated by bd init, will be updated with project-specific content."}}
|
||||||
|
{"id":"int-7a3724fa13d9e80a683932aa8162d6e5","kind":"field_change","created_at":"2026-08-20T15:52:35.086570554Z","actor":"Anthony Merlo","issue_id":"nutrition-mcp-j48","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"delete_entry, list_entries, daily_summary (with goals + remaining), history (grouped by date) all implemented in tools.rs."}}
|
||||||
|
{"id":"int-15bbd2d9f2c21ef8d1e7fe43d7790dd6","kind":"field_change","created_at":"2026-08-20T15:53:22.91492504Z","actor":"Anthony Merlo","issue_id":"nutrition-mcp-yd5","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"README.md written with full project overview, tech stack, 14 nutrients table, 13 tools reference, env vars, build/run/deploy instructions, Hermes + laptop connection guide."}}
|
||||||
|
|
|
||||||
202
AGENTS.md
202
AGENTS.md
|
|
@ -2,126 +2,110 @@
|
||||||
|
|
||||||
This project uses **bd** (beads) for issue tracking. Run `bd prime` for full workflow context.
|
This project uses **bd** (beads) for issue tracking. Run `bd prime` for full workflow context.
|
||||||
|
|
||||||
> **Architecture in one line:** Issues live in a local Dolt database
|
## Build & Test
|
||||||
> (`.beads/dolt/`); cross-machine sync uses `bd dolt push/pull` (a
|
|
||||||
> git-compatible protocol), stored under `refs/dolt/data` on your git
|
|
||||||
> remote — separate from `refs/heads/*` where your code lives.
|
|
||||||
> `.beads/issues.jsonl` is a passive export, not the wire protocol.
|
|
||||||
>
|
|
||||||
> See [SYNC_CONCEPTS.md](https://github.com/gastownhall/beads/blob/main/docs/SYNC_CONCEPTS.md)
|
|
||||||
> for the one-screen overview and anti-patterns (don't treat JSONL as the
|
|
||||||
> source of truth; don't `bd import` during normal operation; don't
|
|
||||||
> reach for third-party Dolt hosting before trying the default).
|
|
||||||
|
|
||||||
## Quick Reference
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bd ready # Find available work
|
cargo build --release # Build binary
|
||||||
bd show <id> # View issue details
|
cargo test # Run unit tests
|
||||||
bd update <id> --claim # Claim work atomically
|
./target/release/nutrition-mcp # Run server (needs env vars)
|
||||||
bd close <id> # Complete work
|
```
|
||||||
bd dolt push # Push beads data to remote
|
|
||||||
|
### Smoke Test
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start server
|
||||||
|
NUTRITION_MCP_BIND=127.0.0.1:9432 NUTRITION_MCP_API_KEY=test \
|
||||||
|
NUTRITION_MCP_DB_PATH=/tmp/test.db ./target/release/nutrition-mcp &
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
curl http://127.0.0.1:9432/health
|
||||||
|
|
||||||
|
# MCP initialize
|
||||||
|
curl -X POST http://127.0.0.1:9432/mcp \
|
||||||
|
-H "Authorization: Bearer test" \
|
||||||
|
-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}'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Architecture Overview
|
||||||
|
|
||||||
|
MCP server exposing 13 tools for personal nutrition tracking. Streamable HTTP transport with Bearer token auth.
|
||||||
|
|
||||||
|
### Module Map
|
||||||
|
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
├── main.rs — Entry point: axum server, auth middleware, /health endpoint
|
||||||
|
├── config.rs — Config struct from env vars (bind, api_key, db_path, user_agent)
|
||||||
|
├── db.rs — SQLite schema, migrations, DbPool type, TRACKED_NUTRIENTS const, DEFAULT_GOALS_JSON
|
||||||
|
├── api.rs — OffClient: Search-a-licious + v3 barcode + SQLite cache (FTS5)
|
||||||
|
└── tools.rs — NutritionServer: 13 MCP tools via #[tool_router] + ServerHandler impl
|
||||||
|
```
|
||||||
|
|
||||||
|
### SQLite Schema
|
||||||
|
|
||||||
|
- **products** — OFF cache: code (PK), product_name, brands, nutriments (JSON), serving_quantity, serving_size, image_url
|
||||||
|
- **products_fts** — FTS5 virtual table on product_name (synced via triggers)
|
||||||
|
- **entries** — Food log: id, date, meal, product_code, food_name, grams, servings, nutriments (JSON, scaled to portion)
|
||||||
|
- **daily_goal** — Single row (id=1), goals JSON with 14 nutrient targets
|
||||||
|
- **weight_log** — id, date, weight_kg
|
||||||
|
|
||||||
|
### Nutrient Storage
|
||||||
|
|
||||||
|
All nutrients stored as JSON blob in `nutriments` TEXT column (verbatim from OFF API). Daily summaries use `json_extract()` or Rust-side parsing to aggregate the 14 tracked nutrients.
|
||||||
|
|
||||||
|
### Portion Scaling
|
||||||
|
|
||||||
|
Nutrients are stored per-100g in OFF data. When logging, `scale_nutriments()` multiplies each value by `grams / 100.0`. For servings, grams = `servings * serving_quantity`.
|
||||||
|
|
||||||
|
### Meal Auto-Detection
|
||||||
|
|
||||||
|
- breakfast: before 11:00
|
||||||
|
- lunch: 11:00–15:00
|
||||||
|
- dinner: 15:00–21:00
|
||||||
|
- snack: after 21:00
|
||||||
|
|
||||||
|
## Conventions & Patterns
|
||||||
|
|
||||||
|
- **JSON nutriments** — Never hardcode nutrient columns. Store OFF nutriments verbatim as JSON, query at runtime.
|
||||||
|
- **"no data" vs 0** — Micronutrients (potassium, calcium, magnesium) are sparse in OFF. Summary shows `"no data"` when no entries have the nutrient, not 0.
|
||||||
|
- **Env vars only** — No config files. All settings via environment variables.
|
||||||
|
- **API-first** — Always check SQLite cache first, fall back to OFF API, cache results.
|
||||||
|
- **User-Agent** — Always send custom User-Agent to OFF API (good citizenship).
|
||||||
|
|
||||||
|
## Tracked Nutrients (14)
|
||||||
|
|
||||||
|
```
|
||||||
|
energy_kcal → energy-kcal_100g
|
||||||
|
protein → proteins_100g
|
||||||
|
carbohydrates → carbohydrates_100g
|
||||||
|
fat → fat_100g
|
||||||
|
fiber → fiber_100g
|
||||||
|
sugars → sugars_100g
|
||||||
|
saturated_fat → saturated-fat_100g
|
||||||
|
salt → salt_100g
|
||||||
|
fructose → fructose_100g (gout)
|
||||||
|
alcohol → alcohol_100g (gout)
|
||||||
|
potassium → potassium_100g (hypertension)
|
||||||
|
calcium → calcium_100g (hypertension)
|
||||||
|
magnesium → magnesium_100g (hypertension)
|
||||||
|
cholesterol → cholesterol_100g (hypertension)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Non-Interactive Shell Commands
|
## Non-Interactive Shell Commands
|
||||||
|
|
||||||
**ALWAYS use non-interactive flags** with file operations to avoid hanging on confirmation prompts.
|
**ALWAYS use non-interactive flags** to avoid hanging on confirmation prompts.
|
||||||
|
|
||||||
Shell commands like `cp`, `mv`, and `rm` may be aliased to include `-i` (interactive) mode on some systems, causing the agent to hang indefinitely waiting for y/n input.
|
|
||||||
|
|
||||||
**Use these forms instead:**
|
|
||||||
```bash
|
```bash
|
||||||
# Force overwrite without prompting
|
|
||||||
cp -f source dest # NOT: cp source dest
|
cp -f source dest # NOT: cp source dest
|
||||||
mv -f source dest # NOT: mv source dest
|
|
||||||
rm -f file # NOT: rm file
|
rm -f file # NOT: rm file
|
||||||
|
|
||||||
# For recursive operations
|
|
||||||
rm -rf directory # NOT: rm -r directory
|
|
||||||
cp -rf source dest # NOT: cp -r source dest
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**Other commands that may prompt:**
|
|
||||||
- `scp` - use `-o BatchMode=yes` for non-interactive
|
|
||||||
- `ssh` - use `-o BatchMode=yes` to fail instead of prompting
|
|
||||||
- `apt-get` - use `-y` flag
|
|
||||||
- `brew` - use `HOMEBREW_NO_AUTO_UPDATE=1` env var
|
|
||||||
|
|
||||||
<!-- BEGIN BEADS INTEGRATION v:1 profile:minimal hash:6cd5cc61 -->
|
|
||||||
## Beads Issue Tracker
|
|
||||||
|
|
||||||
This project uses **bd (beads)** for issue tracking. Run `bd prime` to see full workflow context and commands.
|
|
||||||
|
|
||||||
### Quick Reference
|
|
||||||
|
|
||||||
```bash
|
|
||||||
bd ready # Find available work
|
|
||||||
bd show <id> # View issue details
|
|
||||||
bd update <id> --claim # Claim work
|
|
||||||
bd close <id> # Complete work
|
|
||||||
```
|
|
||||||
|
|
||||||
### Rules
|
|
||||||
|
|
||||||
- Use `bd` for ALL task tracking — do NOT use TodoWrite, TaskCreate, or markdown TODO lists
|
|
||||||
- Run `bd prime` for detailed command reference and session close protocol
|
|
||||||
- Use `bd remember` for persistent knowledge — do NOT use MEMORY.md files
|
|
||||||
|
|
||||||
**Architecture in one line:** issues live in a local Dolt DB; sync uses `refs/dolt/data` on your git remote; `.beads/issues.jsonl` is a passive export. See https://github.com/gastownhall/beads/blob/main/docs/SYNC_CONCEPTS.md for details and anti-patterns.
|
|
||||||
|
|
||||||
## Agent Context Profiles
|
|
||||||
|
|
||||||
The managed Beads block is task-tracking guidance, not permission to override repository, user, or orchestrator instructions.
|
|
||||||
|
|
||||||
- **Conservative (default)**: Use `bd` for task tracking. Do not run git commits, git pushes, or Dolt remote sync unless explicitly asked. At handoff, report changed files, validation, and suggested next commands.
|
|
||||||
- **Minimal**: Keep tool instruction files as pointers to `bd prime`; use the same conservative git policy unless active instructions say otherwise.
|
|
||||||
- **Team-maintainer**: Only when the repository explicitly opts in, agents may close beads, run quality gates, commit, and push as part of session close. A current "do not commit" or "do not push" instruction still wins.
|
|
||||||
|
|
||||||
## Session Completion
|
## Session Completion
|
||||||
|
|
||||||
This protocol applies when ending a Beads implementation workflow. It is subordinate to explicit user, repository, and orchestrator instructions.
|
1. **File issues for remaining work** - `bd create` for follow-up items
|
||||||
|
2. **Run quality gates** - `cargo build --release && cargo test`
|
||||||
1. **File issues for remaining work** - Create beads for anything that needs follow-up
|
3. **Close completed issues** - `bd close <id1> <id2> ...`
|
||||||
2. **Run quality gates** (if code changed) - Tests, linters, builds
|
4. **PUSH** - `git add -A && git commit -m "..." && git push`
|
||||||
3. **Update issue status** - Close finished work, update in-progress items
|
5. **Verify** - `git status` must show clean working tree
|
||||||
4. **Handle git/sync by active profile**:
|
|
||||||
```bash
|
|
||||||
# Conservative/minimal/default: report status and proposed commands; wait for approval.
|
|
||||||
git status
|
|
||||||
|
|
||||||
# Team-maintainer opt-in only, unless current instructions forbid it:
|
|
||||||
git pull --rebase
|
|
||||||
git push
|
|
||||||
git status
|
|
||||||
```
|
|
||||||
5. **Hand off** - Summarize changes, validation, issue status, and any blocked sync/commit/push step
|
|
||||||
|
|
||||||
**Critical rules:**
|
|
||||||
- Explicit user or orchestrator instructions override this Beads block.
|
|
||||||
- Do not commit or push without clear authority from the active profile or the current user request.
|
|
||||||
- If a required sync or push is blocked, stop and report the exact command and error.
|
|
||||||
<!-- END BEADS INTEGRATION -->
|
|
||||||
|
|
||||||
<!-- BEGIN BEADS CODEX SETUP: generated by bd setup codex -->
|
|
||||||
## Beads Issue Tracker
|
|
||||||
|
|
||||||
Use Beads (`bd`) for durable task tracking in repositories that include it. Use the `beads` skill at `.agents/skills/beads/SKILL.md` (project install) or `~/.agents/skills/beads/SKILL.md` (global install) for Beads workflow guidance, then use the `bd` CLI for issue operations.
|
|
||||||
|
|
||||||
### Quick Reference
|
|
||||||
|
|
||||||
```bash
|
|
||||||
bd ready # Find available work
|
|
||||||
bd show <id> # View issue details
|
|
||||||
bd update <id> --claim # Claim work
|
|
||||||
bd close <id> # Complete work
|
|
||||||
bd prime # Refresh Beads context
|
|
||||||
```
|
|
||||||
|
|
||||||
### Rules
|
|
||||||
|
|
||||||
- Use `bd` for all task tracking; do not create markdown TODO lists.
|
|
||||||
- Run `bd prime` when Beads context is missing or stale. Codex 0.129.0+ can load Beads context automatically through native hooks; use `/hooks` to inspect or toggle them.
|
|
||||||
- Keep persistent project memory in Beads via `bd remember`; do not create ad hoc memory files.
|
|
||||||
|
|
||||||
**Architecture in one line:** issues live in a local Dolt DB; sync uses `refs/dolt/data` on your git remote; `.beads/issues.jsonl` is a passive export. See https://github.com/gastownhall/beads/blob/main/docs/SYNC_CONCEPTS.md for details and anti-patterns.
|
|
||||||
<!-- END BEADS CODEX SETUP -->
|
|
||||||
162
CLAUDE.md
162
CLAUDE.md
|
|
@ -1,77 +1,111 @@
|
||||||
# Project Instructions for AI Agents
|
# Agent Instructions
|
||||||
|
|
||||||
This file provides instructions and context for AI coding agents working on this project.
|
|
||||||
|
|
||||||
<!-- BEGIN BEADS INTEGRATION v:1 profile:minimal hash:6cd5cc61 -->
|
|
||||||
## Beads Issue Tracker
|
|
||||||
|
|
||||||
This project uses **bd (beads)** for issue tracking. Run `bd prime` to see full workflow context and commands.
|
|
||||||
|
|
||||||
### Quick Reference
|
|
||||||
|
|
||||||
```bash
|
|
||||||
bd ready # Find available work
|
|
||||||
bd show <id> # View issue details
|
|
||||||
bd update <id> --claim # Claim work
|
|
||||||
bd close <id> # Complete work
|
|
||||||
```
|
|
||||||
|
|
||||||
### Rules
|
|
||||||
|
|
||||||
- Use `bd` for ALL task tracking — do NOT use TodoWrite, TaskCreate, or markdown TODO lists
|
|
||||||
- Run `bd prime` for detailed command reference and session close protocol
|
|
||||||
- Use `bd remember` for persistent knowledge — do NOT use MEMORY.md files
|
|
||||||
|
|
||||||
**Architecture in one line:** issues live in a local Dolt DB; sync uses `refs/dolt/data` on your git remote; `.beads/issues.jsonl` is a passive export. See https://github.com/gastownhall/beads/blob/main/docs/SYNC_CONCEPTS.md for details and anti-patterns.
|
|
||||||
|
|
||||||
## Agent Context Profiles
|
|
||||||
|
|
||||||
The managed Beads block is task-tracking guidance, not permission to override repository, user, or orchestrator instructions.
|
|
||||||
|
|
||||||
- **Conservative (default)**: Use `bd` for task tracking. Do not run git commits, git pushes, or Dolt remote sync unless explicitly asked. At handoff, report changed files, validation, and suggested next commands.
|
|
||||||
- **Minimal**: Keep tool instruction files as pointers to `bd prime`; use the same conservative git policy unless active instructions say otherwise.
|
|
||||||
- **Team-maintainer**: Only when the repository explicitly opts in, agents may close beads, run quality gates, commit, and push as part of session close. A current "do not commit" or "do not push" instruction still wins.
|
|
||||||
|
|
||||||
## Session Completion
|
|
||||||
|
|
||||||
This protocol applies when ending a Beads implementation workflow. It is subordinate to explicit user, repository, and orchestrator instructions.
|
|
||||||
|
|
||||||
1. **File issues for remaining work** - Create beads for anything that needs follow-up
|
|
||||||
2. **Run quality gates** (if code changed) - Tests, linters, builds
|
|
||||||
3. **Update issue status** - Close finished work, update in-progress items
|
|
||||||
4. **Handle git/sync by active profile**:
|
|
||||||
```bash
|
|
||||||
# Conservative/minimal/default: report status and proposed commands; wait for approval.
|
|
||||||
git status
|
|
||||||
|
|
||||||
# Team-maintainer opt-in only, unless current instructions forbid it:
|
|
||||||
git pull --rebase
|
|
||||||
git push
|
|
||||||
git status
|
|
||||||
```
|
|
||||||
5. **Hand off** - Summarize changes, validation, issue status, and any blocked sync/commit/push step
|
|
||||||
|
|
||||||
**Critical rules:**
|
|
||||||
- Explicit user or orchestrator instructions override this Beads block.
|
|
||||||
- Do not commit or push without clear authority from the active profile or the current user request.
|
|
||||||
- If a required sync or push is blocked, stop and report the exact command and error.
|
|
||||||
<!-- END BEADS INTEGRATION -->
|
|
||||||
|
|
||||||
|
This project uses **bd** (beads) for issue tracking. Run `bd prime` for full workflow context.
|
||||||
|
|
||||||
## Build & Test
|
## Build & Test
|
||||||
|
|
||||||
_Add your build and test commands here_
|
```bash
|
||||||
|
cargo build --release # Build binary
|
||||||
|
cargo test # Run unit tests
|
||||||
|
./target/release/nutrition-mcp # Run server (needs env vars)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Smoke Test
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Example:
|
# Start server
|
||||||
# npm install
|
NUTRITION_MCP_BIND=127.0.0.1:9432 NUTRITION_MCP_API_KEY=test \
|
||||||
# npm test
|
NUTRITION_MCP_DB_PATH=/tmp/test.db ./target/release/nutrition-mcp &
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
curl http://127.0.0.1:9432/health
|
||||||
|
|
||||||
|
# MCP initialize
|
||||||
|
curl -X POST http://127.0.0.1:9432/mcp \
|
||||||
|
-H "Authorization: Bearer test" \
|
||||||
|
-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}'
|
||||||
```
|
```
|
||||||
|
|
||||||
## Architecture Overview
|
## Architecture Overview
|
||||||
|
|
||||||
_Add a brief overview of your project architecture_
|
MCP server exposing 13 tools for personal nutrition tracking. Streamable HTTP transport with Bearer token auth.
|
||||||
|
|
||||||
|
### Module Map
|
||||||
|
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
├── main.rs — Entry point: axum server, auth middleware, /health endpoint
|
||||||
|
├── config.rs — Config struct from env vars (bind, api_key, db_path, user_agent)
|
||||||
|
├── db.rs — SQLite schema, migrations, DbPool type, TRACKED_NUTRIENTS const, DEFAULT_GOALS_JSON
|
||||||
|
├── api.rs — OffClient: Search-a-licious + v3 barcode + SQLite cache (FTS5)
|
||||||
|
└── tools.rs — NutritionServer: 13 MCP tools via #[tool_router] + ServerHandler impl
|
||||||
|
```
|
||||||
|
|
||||||
|
### SQLite Schema
|
||||||
|
|
||||||
|
- **products** — OFF cache: code (PK), product_name, brands, nutriments (JSON), serving_quantity, serving_size, image_url
|
||||||
|
- **products_fts** — FTS5 virtual table on product_name (synced via triggers)
|
||||||
|
- **entries** — Food log: id, date, meal, product_code, food_name, grams, servings, nutriments (JSON, scaled to portion)
|
||||||
|
- **daily_goal** — Single row (id=1), goals JSON with 14 nutrient targets
|
||||||
|
- **weight_log** — id, date, weight_kg
|
||||||
|
|
||||||
|
### Nutrient Storage
|
||||||
|
|
||||||
|
All nutrients stored as JSON blob in `nutriments` TEXT column (verbatim from OFF API). Daily summaries use `json_extract()` or Rust-side parsing to aggregate the 14 tracked nutrients.
|
||||||
|
|
||||||
|
### Portion Scaling
|
||||||
|
|
||||||
|
Nutrients are stored per-100g in OFF data. When logging, `scale_nutriments()` multiplies each value by `grams / 100.0`. For servings, grams = `servings * serving_quantity`.
|
||||||
|
|
||||||
|
### Meal Auto-Detection
|
||||||
|
|
||||||
|
- breakfast: before 11:00
|
||||||
|
- lunch: 11:00–15:00
|
||||||
|
- dinner: 15:00–21:00
|
||||||
|
- snack: after 21:00
|
||||||
|
|
||||||
## Conventions & Patterns
|
## Conventions & Patterns
|
||||||
|
|
||||||
_Add your project-specific conventions here_
|
- **JSON nutriments** — Never hardcode nutrient columns. Store OFF nutriments verbatim as JSON, query at runtime.
|
||||||
|
- **"no data" vs 0** — Micronutrients (potassium, calcium, magnesium) are sparse in OFF. Summary shows `"no data"` when no entries have the nutrient, not 0.
|
||||||
|
- **Env vars only** — No config files. All settings via environment variables.
|
||||||
|
- **API-first** — Always check SQLite cache first, fall back to OFF API, cache results.
|
||||||
|
- **User-Agent** — Always send custom User-Agent to OFF API (good citizenship).
|
||||||
|
|
||||||
|
## Tracked Nutrients (14)
|
||||||
|
|
||||||
|
```
|
||||||
|
energy_kcal → energy-kcal_100g
|
||||||
|
protein → proteins_100g
|
||||||
|
carbohydrates → carbohydrates_100g
|
||||||
|
fat → fat_100g
|
||||||
|
fiber → fiber_100g
|
||||||
|
sugars → sugars_100g
|
||||||
|
saturated_fat → saturated-fat_100g
|
||||||
|
salt → salt_100g
|
||||||
|
fructose → fructose_100g (gout)
|
||||||
|
alcohol → alcohol_100g (gout)
|
||||||
|
potassium → potassium_100g (hypertension)
|
||||||
|
calcium → calcium_100g (hypertension)
|
||||||
|
magnesium → magnesium_100g (hypertension)
|
||||||
|
cholesterol → cholesterol_100g (hypertension)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Non-Interactive Shell Commands
|
||||||
|
|
||||||
|
**ALWAYS use non-interactive flags** to avoid hanging on confirmation prompts.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp -f source dest # NOT: cp source dest
|
||||||
|
rm -f file # NOT: rm file
|
||||||
|
```
|
||||||
|
|
||||||
|
## Session Completion
|
||||||
|
|
||||||
|
1. **File issues for remaining work** - `bd create` for follow-up items
|
||||||
|
2. **Run quality gates** - `cargo build --release && cargo test`
|
||||||
|
3. **Close completed issues** - `bd close <id1> <id2> ...`
|
||||||
|
4. **PUSH** - `git add -A && git commit -m "..." && git push`
|
||||||
|
5. **Verify** - `git status` must show clean working tree
|
||||||
165
README.md
Normal file
165
README.md
Normal file
|
|
@ -0,0 +1,165 @@
|
||||||
|
# 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
|
||||||
Loading…
Add table
Reference in a new issue