The Origin Story
Once upon a time, in the middle of a global pandemic, I caught COVID.
What followed was a week of quarantine that changed how I think about food. Despite eating nutritious meals - and consuming nearly 1000 calories above my maintenance level - I lost 8 kilograms in seven days. The body, fighting an inflammatory storm, had a different agenda than my plate did. That idea of “eating right for what your body is going through” during an illness is what stayed with me.
That experience surfaced a question I kept coming back to: most digital nutrition tools don't know your conditions, your medications, or your recovery state. They generate plans for an idealised healthy person - not for you, on your recovery, on your medications.
NutriPlan was born from that question.
The vision: an agentic AI system that ingests a patient's full clinical context - their conditions, their active medications with full pharmacokinetic awareness, their allergies, their vitals and recent observations, their procedures and treatment history, and their personal dietary preferences and cultural patterns - and produces a single day's meal plan that is clinically safe, evidence-grounded, and tailored to who they actually are. From there, the day plan can extend naturally into a week, into a recovery arc, into a sustainable healthy lifestyle.
This is a first version. There's a lot of room to grow. But the foundation is here.
Inside the System
NutriPlan is a multi-agent clinical AI system built on the FHIR healthcare interoperability standard, with a layered architecture designed for clinical rigour, traceability, and safety.
5 MCP (Model Context Protocol) Servers
Each MCP server exposes a focused set of tools that a specialist agent calls to do its work. The separation of concerns is deliberate: each server owns a single domain of clinical knowledge or data, and can be independently scaled, updated, or audited. The Python MCP template from the Prompt Opinion community serving as the foundation and entry point for server development.
FHIR Parser MCP - Connects to any FHIR compliant server and extracts structured patient data: demographics, vitals, active conditions, active medications, allergies, recent observations (labs), procedures, and clinical history. Uses standardised FHIR resource queries with bearer-token auth via the
x-fhir-server-urlandx-fhir-access-tokenheaders.Clinical Knowledge Base MCP - Houses an evidence-based clinical nutrition library: condition-specific dietary guidelines (sourced from AHA, ADA, ACC, AAN), the Mifflin-St Jeor equation for caloric calculation, macronutrient ratio profiles (weight loss, maintenance, diabetes, ketogenic), micronutrient priorities by condition, hard exclusion rules, and recommended dietary protocols (Mediterranean, DASH, MIND, low-FODMAP).
User Context MCP - Captures and structures dietary preferences with sensible defaults. Builds a Dietary Preference Profile from cuisines, dislikes, diet patterns (omnivore, vegetarian, vegan, pescatarian), cooking skill, equipment, budget, and schedule. Includes a curated cultural food patterns library covering staples, common proteins, and cooking traditions across cuisines.
Nutrition Database MCP - Direct integration with the USDA FoodData Central API. Tools include
SearchFood,GetRecipeNutrients(for full-meal nutrient aggregation),SuggestFoodSubstitute(nutrient-equivalent swaps), and supporting tools for food allergens, glycemic index, and serving sizes. Every nutrient value the system uses traces back to a real FDC entry.Drug-Nutrient Interaction MCP - A curated database of clinically-significant drug-food interactions with severity classifications (contraindicated, significant, moderate, advisory) and curated substitution recommendations. Catches grapefruit-CYP3A4 interactions, MAOI-tyramine reactions, warfarin-vitamin K conflicts, and dozens more. Severity drives automated decision-making downstream.
5 Specialist Agents
Each agent owns a stage of the clinical reasoning pipeline. Each accesses only the MCP servers relevant to its responsibility.
FHIR Parser Agent - Reads from the FHIR Parser MCP. Builds a Structured Patient Profile (SPP) - a clean, deduplicated JSON object representing everything clinically known about the patient. The SPP is the foundation every downstream agent works from.
Clinical Analysis Agent - Reads from the Clinical Knowledge Base MCP, takes the SPP as input. Identifies the dominant clinical driver (e.g., weight management vs. blood-sugar control), calculates the daily caloric target via Mifflin-St Jeor, derives macronutrient ratio ranges, builds a list of hard exclusions (a critical safety construct combining food allergies, condition-driven exclusions, and medication-driven exclusions), and selects a recommended dietary protocol. Outputs the Clinical Nutrition Brief.
Dietary Preference Agent - Reads from the User Context MCP. Extracts user preferences from natural-language input, applies defaults silently for unstated fields, and looks up cultural food patterns when a recommended protocol is set. Outputs the Dietary Preference Profile.
Meal Planning Agent - Reads from the Nutrition Database MCP. Takes the Brief and the Profile and generates a day's meal plan - typically four meals (breakfast, snack, lunch, dinner) - grounded in real USDA nutrient data. Every ingredient resolves to an FDC ID. Every meal's macronutrients aggregate from real USDA values. Respects the constraint hierarchy strictly: hard exclusions are absolute, micronutrient limits are caps, caloric target is met within ±10%, macronutrient ratios fall within range, then preferences and culture flavour the choices.
Drug-Nutrient Safety Agent - Reads from the Drug-Nutrient Interaction MCP. Takes the meal plan and the patient's active medications, runs a bulk interaction check, and for any contraindicated or significant interaction it applies an inline substitution - replacing the offending ingredient with a curated safe alternative while preserving meal structure. Moderate and advisory interactions surface as warnings. Outputs the Safety Findings.
The Original A2A Architecture: NutriPlan - Clinical Nutrition AI Agent System
Stitching the five specialists together is an orchestrator agent: NutriPlan - Clinical Nutrition AI Agent System.
The orchestrator implements an A2A (Agent-to-Agent) communication pattern: each specialist agent registers an A2A skill describing what it can do, and the orchestrator consults them in sequence - passing each agent's structured output as input to the next. The flow is linear and deterministic: SPP → Brief → Profile → Meal Plan → Safety Findings → Final Plan. Each handoff is auditable. Each output is structured JSON.
Figure 1: The Original A2A Architecture: NutriPlan - Clinical Nutrition AI Agent System Architecture
The 403 Wall
During development, a critical infrastructure issue emerged on the Prompt Opinion platform: the FHIR Context - which carries the bearer token authorising patient data access - was being stripped when the orchestrator made BYO-to-BYO (Build-Your-Own to Build-Your-Own) A2A calls between agents. The downstream agents received the call but lacked the token. Every FHIR-dependent tool returned 403 Forbidden. The pipeline broke at the very first stage.
The bug was confirmed with the platform team (discord thread). A fix was on the roadmap. It would not land before the hackathon deadline.
The Collapsed Architecture: NutriPlan - Holistic Nutrition AI Agent
Rather than ship a broken demo, the architecture was deliberately collapsed into a single agent that internally executes the same six-stage pipeline: NutriPlan - Holistic Nutrition AI Agent.
The same five MCP servers. The same six logical stages. The same clinical reasoning. But all stage-to-stage handoffs happen within the agent's own working memory rather than across A2A boundaries - which sidesteps the FHIR Context stripping bug entirely. The agent narrates each stage as it executes (Stage 1: Pulling patient data from FHIR... Stage 2: Building clinical nutrition brief... etc.), calls the MCP tools directly, and emits a single structured JSON output containing the SPP, Brief, Profile, Meal Plan, Safety Findings, and a clean markdown rendering of the approved plan.
The collapse was a pragmatic adaptation that preserved every clinical capability while routing around an infrastructure constraint outside our control. The full A2A architecture remains the production target - the collapsed agent is a faithful demonstration of what the orchestrated system will do once the platform fix lands.
Figure 2: The Collapsed Architecture: NutriPlan - Holistic Nutrition AI Agent
Under the Hood
MCP Servers
Every MCP server is a Python service exposing a structured tool API over the Model Context Protocol. Each server has a defined input/output contract per tool, with parameters typed via Pydantic and described for LLM consumption. The starting point for every server was the Python MCP template provided by the Prompt Opinion community - a clean FastMCP-based scaffold that handled the streamable-HTTP transport, request context handling, and tool registration boilerplate. From that foundation, each server was built out with its own domain logic, tool surface, and data integrations.
FHIR Parser MCP - Async
httpx-based client wrapping the FHIR REST API. Reads thex-fhir-server-urlandx-fhir-access-tokenheaders from the MCP request context, decodes the JWT to extract the patient claim, and issues authenticated FHIR queries. Handles 404s gracefully (returningNonerather than raising), and applies defensive parsing throughout -clinicalStatusmay be missing on Synthea-generated test data, so the tool defaults to "active" when null.Clinical Knowledge Base MCP - Backed by curated JSON knowledge bases (conditions, protocols, guidelines, RDAs). Manually compiled from AHA, ADA, ACC, AAN, and DRI publications. Includes Epilepsy and ADHD entries added during this project, with explicit alcohol-avoidance flags, vitamin D and calcium targets, and dietary pattern recommendations.
User Context MCP - Lightweight server building structured preference profiles from input fields with defaults. Cultural food patterns are stored as a curated lookup keyed by protocol name (Mediterranean, DASH, MIND, etc.).
Nutrition Database MCP - Connects to USDA's FoodData Central API. Early development used the public
DEMO_KEYfor prototyping, but the deployed version uses a registered USDA API key from thex-usda-api-keyrequest header for higher rate limits and reliability. Defensive engineering throughout: bulk nutrient fetches handle retired FDC IDs gracefully (USDA periodically deprecates entries, returning 404), with the tool returning partial results plus a warning rather than failing the whole request. Timeouts are explicit (connect=10, read=30, write=10standard,read=45for bulk fetches), and a retry helper handles transientReadTimeout,ConnectTimeout, and 5xx responses.Drug-Nutrient Interaction MCP - Local curated JSON database. Each interaction entry includes the drug, the food/ingredient, severity classification, mechanism description (e.g., "CYP3A4 inhibition"), evidence source, and a curated safe substitute. Built up to 33 entries during this project, including new entries for diazepam-grapefruit, diazepam-alcohol, and fexofenadine-fruit-juices that the test patient's medications required.
All five servers are deployed on a Linux virtual machine, each running on its own dedicated port, accessible to the Prompt Opinion platform via streamable-HTTP MCP transport.
The Orchestrator (Original A2A Design)
Each specialist agent is registered with an A2A skill descriptor: a name, a description of the agent's capability, and the input/output JSON schema. The orchestrator's role is sequencing: it determines which specialist to consult next based on what data has accumulated, calls the specialist via the platform's A2A consult API, parses the structured response, and threads the result into the next consult call.
The general flow:
- Orchestrator receives the user's request (e.g., "Build a meal plan").
- FHIR Context (URL + bearer token) is attached to the orchestrator's call.
- Orchestrator consults the FHIR Parser Agent, passing the FHIR Context. Receives the SPP.
- Orchestrator consults the Clinical Analysis Agent with the SPP. Receives the Brief.
- Orchestrator consults the Dietary Preference Agent with the user's preference message and the Brief. Receives the Profile.
- Orchestrator consults the Meal Planning Agent with the Brief and the Profile. Receives the draft Meal Plan.
- Orchestrator consults the Drug-Nutrient Safety Agent with the Meal Plan and the SPP's medications. Receives the Safety Findings (with inline substitutions if needed).
- Orchestrator emits the final plan with stage-by-stage narration.
NutriPlan - Holistic Nutrition AI Agent (Collapsed)
The collapsed agent runs the full pipeline as six internal stages, gated by strict sequencing rules. Each stage is allowed to call only the MCP tools relevant to that stage - the system prompt enforces a tool-to-stage binding that prevents cross-contamination (e.g., the agent cannot invoke nutrition-database tools during the FHIR-extraction stage).
End-to-end data flow:
Stage 1 - FHIR Patient Profile. Calls the FHIR Parser MCP tools to pull demographics, vitals, allergies, conditions, medications, procedures, and observations. Builds the SPP in working memory.
Stage 2 - Clinical Nutrition Brief. Calls Clinical KB MCP tools per active condition (deduplicating where conditions overlap), calculates the caloric target, derives macronutrient ratios, merges micronutrient priorities, and constructs the hard exclusion list from food allergies + condition-driven rules + medication-driven rules.
Stage 3 - Dietary Preference Profile. Calls User Context MCP. Applies silent defaults for unstated fields. Looks up cultural food patterns.
Stage 4 - Draft Meal Plan. Calls Nutrition Database MCP. For each meal, picks a recipe concept respecting the protocol and cuisine, resolves ingredients to FDC IDs (caching aggressively to avoid repeated lookups), and calls
GetRecipeNutrientsonce per meal for full nutrient aggregation. Verifies hard exclusions via substring matching ("wheat" excludes couscous, bulgur, semolina, durum, farro, seitan; "alcohol" excludes wine, beer, spirits). Substitutes viaSuggestFoodSubstituteif needed.Stage 5 - Drug-Nutrient Safety Check. Calls Drug-Nutrient MCP with the deduplicated ingredient list and the active medication list. For contraindicated/significant interactions: applies inline substitution via
GetSubstitutionForInteraction, falling back toSuggestFoodSubstitutefrom the Nutrition DB if no curated substitute exists. For moderate/advisory: surfaces as warnings.Stage 6 - Finalize and Format. No tool calls. Pure assembly from working memory. Builds the final structured JSON containing all stage outputs and a markdown-formatted meal plan suitable for direct rendering.
Walls We Hit
The 403 Wall - FHIR Context Stripping in BYO-to-BYO A2A Calls
The most consequential challenge was an infrastructure-level bug in the platform's A2A communication layer. When the orchestrator (configured with a Build-Your-Own model) consulted a specialist agent (also configured with a Build-Your-Own model), the FHIR Context - specifically the x-fhir-access-token and x-fhir-server-url headers - was being stripped from the inter-agent request. The downstream agent's MCP tool calls would then hit the FHIR server without authentication, and every request returned 403 Forbidden.
The bug was reproducible. It was already reported on the platform's Discord, where the engineering team had confirmed the issue and acknowledged that A2A scope propagation was on their roadmap. The fix would not ship before the hackathon deadline.
Output Guardrails Misfiring as Input Guardrails
A second platform-level issue arose during specialist-agent testing: an output guardrail configured to validate the JSON schema of an agent's response was being incorrectly applied to that agent's input during A2A consult calls. The result was that perfectly valid input (a Clinical Nutrition Brief, a Dietary Preference Profile) failed the output schema check - because input and output schemas are different - and the agent call would error before any work was done.
The workaround was to relax guardrails on agents intended to be A2A consult targets, and instead enforce schema correctness via the system prompt itself plus the response_format constraint.
Gemini Free-Tier Load Storms
Even when the architecture worked, Gemini 3.x Flash free-tier reliability was tied to global usage patterns. During afternoon and evening hours - overlapping US working hours - 503 errors became the dominant response. Stage 1 and Stage 2 would complete in low-load testing windows; the same prompt during peak hours would die mid-pipeline.
Smaller Issues Worth Naming
- Synthea-generated test data sometimes returned
nullforclinicalStatuson conditions, which broke the FHIR Parser's active-condition filter. Patched with a(clinicalStatus or "active").lower()defensive default. - USDA FoodData Central retired FDC IDs during testing - bulk nutrient fetches returned 404 for some entries. The fix: partial-result aggregation with explicit warnings rather than full failure.
- System prompt size limit of 16,384 characters required compressing the original 18k-character mega-agent prompt by 35% without losing any of the eight identified gap-protections (stage sequencing, error handling, working memory, tool budgets, etc.).
Wins Worth Talking About
I came into this project with one thing in my technical toolkit: Python. I had no prior experience with Large Language Models, no experience designing agentic systems, no experience with MCP servers, no experience with A2A communication patterns, and no idea what FHIR was when I started.
What I had instead was curiosity, a real-world problem I cared about, and a willingness to keep going when things broke.
By the end of this project, I had:
- Designed a six-component clinical AI architecture
- Built and deployed five Model Context Protocol servers, each with its own tool surface and clinical domain
- Integrated with a real-world healthcare data standard (FHIR) and a real-world nutrition database (USDA FoodData Central)
- Designed a multi-agent orchestration pattern using A2A communication
- Diagnosed an infrastructure bug, and successfully pivoted my architecture to ship despite an unresolved upstream issue
- Curated a clinically-grounded drug-food interaction database covering interactions that real patients on real medications need to know about
- Validated the system end-to-end on a real Synthea patient profile, catching a real grapefruit-diazepam and fexofenadine-fruit-juices interaction that a generic meal-planning app would have missed
More importantly, this project has given me a demonstrated foundation for building agentic AI systems that solve real problems. I'm no longer asking "could I build this?" I'm now asking "what should I build next?"
Why this matters beyond the code
NutriPlan, in its full vision, is the kind of tool that makes a measurable difference. Every year, millions of patients are admitted to emergency rooms because of preventable drug-food interactions. Every year, recovering patients get discharged with a stack of medications and a generic dietary handout that does not consider drug-nutrient interaction.
NutriPlan is built to close that gap - to give every patient, dietitian, and clinician an automated safety net that thinks about food the way a careful pharmacist would. A small contribution. But a real one.
Skills Earned Along the Way
A condensed list of what I picked up:
- Model Context Protocol (MCP) - designing tool servers, structuring tool contracts, header-based authentication, typed Pydantic parameters with LLM-readable descriptions
- Agentic AI architecture - decomposing complex tasks into specialist agents, designing handoff contracts, balancing autonomy against deterministic execution
- A2A (Agent-to-Agent) patterns - skill registration, consult calls, FHIR Context propagation, and the failure modes that emerge at agent boundaries
- System prompt engineering at scale - 14,000+ character prompts encoding strict stage sequencing, tool-to-stage binding, error handling protocols, and working memory contracts without contradiction
- Structured output schemas - JSON Schema design, model-specific validation trade-offs (Anthropic strict vs. Gemini permissive)
- FHIR - resource models, bundle parsing, defensive field access, and FHIR bearer-token authentication
- USDA FoodData Central API - food search, FDC ID resolution, recipe nutrient aggregation, public-API operational realities
- Production async Python -
httpxclients with explicit timeouts, retry policies, and graceful degradation - Pivoting under constraint - knowing when to fight an infrastructure issue and when to architect around it
The single most valuable meta-skill: debugging at the seams between systems - where my Python code meets a platform's A2A layer meets a model provider's tool-calling API meets a clinical data standard. Every bug lived at one of those seams. Every fix required understanding both sides.
The Roadmap Ahead
Where NutriPlan goes next, in brief:
- Real database layer - migrate the Clinical KB, Drug-Nutrient interactions, and cultural patterns from curated JSON to Postgrese + a vector store, unlocking continuous expansion and provenance tracking
- Validate the full orchestrator - once the A2A FHIR Context propagation fix lands, run the original five-agent + orchestrator architecture end-to-end
- Interactive preference capture - when a critical preference is missing or ambiguous, the orchestrator pauses and prompts the user mid-pipeline, threading the answer into the Dietary Preference Agent
- Week-long and recovery-arc planning - extend single-day plans into multi-day, multi-week trajectories with variety constraints and meal-prep optimisation
- Lab-driven dynamic adjustment - integrate FHIR Observations into the Brief, so high LDL tightens saturated fat targets and low ferritin prioritises iron-rich foods
The work continues. This is a first version of NutriPlan - and I'm just getting started.
Log in or sign up for Devpost to join the conversation.