Nutrition Research Assistant
Enables natural-language nutrition research for Indian foods, combining a curated knowledge base with hybrid RAG and specialized agents. Provides MCP tools for knowledge search, safe calculations, document retrieval, and optional live web search.
README
Nutrition Research Assistant
Production-grade multi-agent Indian nutrition research assistant: answers questions about Indian food and nutrition by combining a curated internal knowledge base, hybrid RAG, specialized agents, MCP tools, conversational memory, optional live web search, graceful failure handling, and end-to-end OpenTelemetry-compatible tracing.
graph TD
Client[CLI / Swagger / Demo script] --> API[FastAPI :8000]
API --> Sup[Agno Supervisor]
Sup --> KA[Knowledge Agent]
Sup --> CA[Calculator Agent]
Sup --> DA[Document Agent]
Sup --> WA[Health/Web Agent]
KA --> MCP[FastMCP Server :8001/mcp]
CA --> MCP
DA --> MCP
WA --> MCP
MCP --> Chroma[ChromaDB + BM25 + RRF + Reranker]
MCP --> Calc[Safe calculator]
MCP --> Web[Agno DDGSTools]
MCP --> Docs[documents.json]
Sup --> Mem[(SQLite memory)]
API --> OTel[OpenTelemetry + structlog]
Core principle (PRD §89): the LLM decides what should happen; MCP tools perform what must happen deterministically. Agents never touch Chroma directly — everything flows through MCP.
Tech Stack
| Layer | Choice |
|---|---|
| Python / packages | 3.12+ · uv (pyproject.toml + lock) |
| LLM | DeepSeek deepseek-v4-flash (OpenAI-compatible) |
| Agents | Agno (Supervisor + 4 specialized agents) |
| MCP | FastMCP server, streamable-http on :8001/mcp |
| Vector DB | ChromaDB (local, cosine) |
| Embeddings | BAAI/bge-small-en-v1.5 (local) |
| Reranker | BAAI/bge-reranker-base (local cross-encoder) |
| BM25 | rank_bm25 |
| Memory | Agno sessions + SQLite (data/app.db) |
| API | FastAPI + uvicorn |
| Observability | OpenTelemetry (console exporter; OTLP-swappable) + structlog |
| Web search | Agno built-in DDGSTools (optional) |
Repository Layout
corpus/ 8 curated nutrition documents (frontmatter + per-food tables)
scripts/ ingest.py · rebuild_index.py · test_retrieval.py · eval_questions.json
test_mcp_wiring.py · test_agents.py · test_memory.py · demo.py
src/
api/ FastAPI app, routes, schemas (chat / health / ready)
agents/ models.py (DeepSeek) · mcp_client.py · agents.py (specialists + supervisor)
mcp_server/ server.py + tools/{knowledge,calculator,documents,web}.py
rag/ embeddings · chroma · bm25 · fusion (RRF) · reranker · pipeline
memory/ SQLite storage
services/ circuit breaker · retry
observability tracing (OTel) · logging (structlog)
config/ settings.py core/ errors.py · models.py
cli.py REPL client
tests/ unit + API tests
Quick Start
# 1. Environment
uv venv --python 3.12
uv sync
# 2. Secrets
cp .env.example .env # set DEEPSEEK_API_KEY
# 3. Ingest the corpus (downloads bge-small-en-v1.5 on first run)
uv run python scripts/ingest.py
# 4. Start the MCP tool server (terminal 1)
uv run python -m src.mcp_server.server
# 5. Start the API (terminal 2)
uv run uvicorn src.api.app:app --reload --port 8000
Swagger UI: http://127.0.0.1:8000/docs (API port is configurable via API_PORT in .env — this workspace uses 8002 because 8000 is taken)
Try it:
uv run python -m src.cli # interactive REPL
uv run python scripts/demo.py # scripted 5-question demo
curl -X POST http://127.0.0.1:8000/api/v1/chat \
-H "Content-Type: application/json" \
-d '{"session_id":"demo","message":"How much protein is in 100g cooked chickpeas?"}'
API
| Endpoint | Purpose |
|---|---|
POST /api/v1/chat |
{session_id, message} → {status, session_id, response, sources[], trace_id} |
GET /health |
{status, mcp, chroma, llm} — liveness, no secrets |
GET /ready |
{status, checks} — alive vs ready distinction |
Degraded responses carry status: "degraded", a human-readable message, and the trace_id — never a 500 traceback.
MCP Tools (port 8001)
| Tool | Backend | Notes |
|---|---|---|
search_knowledge(query, top_k, filters) |
hybrid RAG (dense + BM25 + RRF + rerank) | returns chunks with document_id / score / method |
calculate(expression) |
AST-whitelisted safe evaluator | no eval, no code execution |
get_document(document_id) |
ingested documents.json |
never fabricates ids |
search_web(query) |
Agno DDGSTools |
optional; WEB_SEARCH_ENABLED |
search_health_information(query) |
DDGSTools + domain ranking | prioritizes WHO/ICMR/NIH/CDC |
Agents
| Agent | Tools | Handles |
|---|---|---|
| Supervisor | team of 4 | intent, routing, composition, memory |
| Knowledge | search_knowledge |
nutrition facts, comparisons, raw vs cooked |
| Calculator | calculate |
serving scaling, totals |
| Document | get_document |
document retrieval |
| Health/Web | web + health search | current info, general health (educational only) |
Failure Handling
- MCP down → controlled
degradedresponse with trace_id (no 500); circuit breaker fails fast, probes after cooldown (PRD §70/§85) - MCP timeout — 10s transport read timeout; agent run bounded by
AGENT_TIMEOUT_SECONDS - Chroma down → "Knowledge retrieval temporarily unavailable" (optionally falls back to web)
- LLM down / bad key →
LLM_UNAVAILABLE-style degraded message - Web disabled → "Live search is currently unavailable."
- Retry policy: 2 attempts with backoff, then degrade — never endless
Observability
Every request produces one trace: api.request → supervisor.run → mcp.<tool> → rag.dense_search / rag.bm25 / rag.rrf / rag.reranker, with trace_id echoed in the API response, logs, and spans. All logs are JSON (structlog) with timestamp, level, logger, trace_id, span_id. Set OTEL_EXPORTER_OTLP_ENDPOINT to export to Jaeger/Tempo/Collector (uv sync --extra otel).
Tests & Retrieval Eval
uv run pytest -q
uv run python scripts/test_retrieval.py --rerank # Recall@5/10, MRR, Hit@1/3 over 24 questions
The eval dataset (scripts/eval_questions.json, 24 questions across 7 categories) measures retrieval quality; the reranker lifts Hit@1/3 over raw fusion. Results land in data/eval/results.json.
Definition of Done (PRD §87)
- Core — FastAPI runs; DeepSeek via OpenAI-compatible client; Agno agents operational; FastMCP operational; Chroma populated; 8 documents; memory works
- MCP — all 5 tools work and agents use MCP as the real execution path
- RAG — dense + BM25 + RRF + cross-encoder rerank; metadata preserved; eval exists
- Agents — Supervisor + 4 specialists; multi-agent composition works
- Memory — session ids; follow-ups resolve; persisted in SQLite
- Observability — trace_id per request; nested spans; MCP/RAG/LLM/errors traced
- Failure handling — MCP unavailable/timeout, Chroma, LLM, web all controlled; no unhandled exception reaches the user
Troubleshooting
- Hugging Face symlink warning — Windows-only; set
HF_HUB_DISABLE_SYMLINKS_WARNING=1(cosmetic) - Retrieval returns nothing — Chroma empty: run
python scripts/ingest.py - "tool service unavailable" — MCP server not running (start it) or breaker cooling down (wait ~30s, or restart API)
- Health says llm not_configured —
DEEPSEEK_API_KEYmissing in.env
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.