
NutriPulse is an MCP server that gives a host LLM a patient's clinical profile, live wearable telemetry, and a nutrition-resolved food catalog at the same time, then scores every candidate meal against safety, taste, budget, and craving before anything reaches the user.
What this enables
- Blocks meals that conflict with allergies or active medications before any ranking happens.
- Adjusts protein, sugar, and fluid targets in real time from sleep, stress, and hydration telemetry.
- Resolves clinical fit, taste, budget, and craving through a four-objective Pareto scorer instead of a single weighted formula.
- Explains every recommendation with a calculation trace showing which rules fired and why a runner-up lost.
- Runs the same server against ChatGPT or any MCP-compatible client with no logic duplicated per client.
The gap between two systems that don't talk
Most food delivery apps optimize for what a person will click, not what their body can safely process. A wearable, meanwhile, logs sleep, stress, and recovery into a dashboard nobody acts on. Neither system sees the other's data, and the ordering flow that decides what actually gets eaten has no access to a person's HbA1c, their warfarin prescription, or last night's four hours of sleep.
That gap is not a minor personalization miss. A patient on warfarin who orders a vitamin-K-heavy dish is interfering with their own medication, and no delivery app checks for that interaction. A diabetic with an iron deficiency can pick a low-glycemic meal that is also low in the nutrient their last blood panel flagged as low. Generic calorie counters treat a diabetic patient and an athlete identically, and a real dietitian, however accurate, cannot know a patient is dehydrated and stressed right now.
Why this needed a constrained interface, not a chatbot
NutriPulse's answer is to put a deterministic rules engine between the user and the food catalog, and expose that engine to the LLM only through typed MCP tools. The host model narrates results, but it never performs the clinical reasoning itself. That boundary matters here specifically because the failure mode of a model inventing or softening a drug interaction is not a stylistic bug, it is a safety incident. Keeping the scoring, the BMR math, and the allergen cross-referencing in ordinary server-side TypeScript means the same computation runs the same way every time, and the model's job narrows to explaining a result it did not compute.
How the recommendation pipeline works
A single tool, resolve_recommendation, is the primary entry point and internally runs six stages:
- Assemble context from MCP resources:
profile://{userId},labs://{userId}/latest,telemetry://{userId}/today,intake://{userId}/today, andbudget://{userId}. - Compute a per-meal nutritional envelope from Mifflin-St Jeor BMR, telemetry-adjusted TDEE, and today's logged intake.
- Assemble up to 60 candidate dishes from the catalog and screen every one against 20-plus clinical rules covering five conditions, nine allergen categories, and four drug-nutrient interactions.
- Score surviving candidates across four independent scorers: clinical fit, contextual taste match, budget efficiency, and craving similarity.
- Compute the Pareto-optimal front and break ties lexicographically, starting with fewest safety warnings and ending with a stable dish-ID fallback.
- Generate a conflict log naming the winning dish, what was sacrificed, which runner-up lost and why, and which dishes were dropped for safety.
The telemetry step is where the system earns its "biometric-aware" label rather than just being a rules engine with a nutrition label attached. Poor sleep or low heart-rate recovery boosts the protein target and lowers the sugar ceiling for that meal slot; high stress raises fluid and electrolyte targets; a high step count widens the carb allowance. None of this is hardcoded per user. It falls out of the same envelope calculation every time telemetry changes.
Where the safety boundary actually lives
The interesting design choice in NutriPulse is not the scoring math. It is that the safety check does not trust the scoring math. A SafetyInterceptor sits at the framework level, applied to the resolver tool with a decorator, and re-evaluates every dish in the tool's own output against the user's profile before that output leaves the server. If a blocked dish is still present, the interceptor throws a hard error rather than letting the response through. That means a bug in the resolver's candidate assembly, the Pareto logic, or the tiebreaker cannot itself cause an unsafe dish to reach the user; a second, independent layer checks the finished answer rather than trusting the layer that produced it. In a demo run, a peanut allergy and a warfarin interaction were flagged and their conflicting dishes excluded before any of the remaining candidates were ranked, which is the interceptor and the rules engine doing their job in that order.
Where NitroStack fits
NutriPulse is built on the NitroStack TypeScript SDK, organized into seven domain modules (Profile, Clinical, Catalog, Telemetry, Context, Resolver, Prompts) with Zod schemas validating every tool input and domain type, so a malformed request never reaches the clinical logic. The SafetyInterceptor uses NitroStack's interceptor pattern to wrap the resolver tool without embedding the safety check inside the resolver's own code, which is what lets the two stay independent. The server runs both STDIO and HTTP SSE transport out of the box, and the team built it with a path to NitroCloud for deployment and a ChatGPT connector for a production-facing client, without maintaining separate integration code for either surface.
What the build demonstrates
NutriPulse is a hackathon project, built for the Amrita University MCP Hackathon 2026, and its catalog, user profiles, and telemetry currently live in in-repo JSON rather than a production database. It does not demonstrate commercial adoption or measured patient outcomes. What it does demonstrate is that a genuinely safety-sensitive workflow, one where an LLM's confident tone is the actual risk, can be structured so the model narrates a decision it is architecturally prevented from getting wrong. The clinical computation, the safety gate, and the conflict explanation all live in typed, testable server code; the model's only job is to read the trace and talk to the user about it.
The reusable pattern
The transferable idea here is not "add health data to a chatbot." It is separating an irreversible or high-stakes decision from the conversational layer entirely, then adding a second enforcement point that checks the first one's output instead of assuming it. Any MCP server exposing a scored or ranked recommendation, not just a nutrition one, can use the same shape: compute deterministically, score across independent objectives, and gate the final response with a check that does not share code with the logic it is checking.
Explore the NutriPulse repository to see the clinical rules engine and interceptor pattern in full, or check the NitroStack SDK docs to build a similar guarded MCP tool.