Skip to main content
Glama
HamzaOuadid

MCP Health Coach

by HamzaOuadid
README.md
# MCP Health Coach

[![CI](https://github.com/HamzaOuadid/mcp-health-coach/actions/workflows/ci.yml/badge.svg)](https://github.com/HamzaOuadid/mcp-health-coach/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/downloads/)

An MCP server that turns any MCP-compatible LLM client (Claude Desktop, etc.) into a wellness coach grounded in verified public data.

**Core design principle:** the coaching conversation lives in the LLM, but every factual claim — a nutrient value, a supplement warning, a screening recommendation — is backed by a real tool call to a government or verified source, not generated from the model's training data.

## Demo

No API keys needed to see the core logic run — `demo/run_example.py` calls the local-computation tools directly (the same functions the MCP server exposes) using the persona from the [coaching session example](#5-try-a-coaching-session) below:

```bash
python demo/run_example.py
```

<details>
<summary>Sample output</summary>

```
============================================================
1. calculate_daily_needs — establish calorie/macro targets
============================================================
{
  "bmr": 1422,
  "tdee": 2204,
  "target_calories": 1704,
  "macro_split": {
    "protein_g": 122,
    "carbs_g": 189,
    "fat_g": 51
  },
  "goal": "lose",
  "goal_label": "Fat loss (~0.5 kg/week deficit)",
  "activity_level": "moderate",
  "note": "~500 kcal/day deficit; expect ~0.5 kg loss per week",
  "coaching_context": "For a 32-year-old female at 68 kg / 170 cm with moderate activity, targeting lose. Daily target: 1704 kcal (122g protein / 189g carbs / 51g fat)."
}

============================================================
2. calculate_bmi — quick screening metric
============================================================
{
  "bmi": 23.5,
  "category": "Normal weight",
  "healthy_weight_range_kg": { "min": 53.5, "max": 72.0 },
  "weight_kg": 68,
  "height_cm": 170,
  "note": "BMI is a population-level screening tool. It does not account for muscle mass, bone density, age, or fat distribution. Body fat percentage and waist circumference provide a fuller picture."
}

============================================================
3. calculate_water_intake — daily hydration target
============================================================
{
  "daily_water_target_ml": 2780,
  "range_ml": { "min": 2502, "max": 3058 },
  "in_liters": 2.78,
  "in_cups_8oz": 12,
  "activity_level": "moderate",
  "climate": "temperate",
  "note": "This is an estimate for total fluid intake (water, beverages, food moisture). Pure water needs are roughly 70-80% of this figure. Urine color is the simplest real-time hydration check: pale yellow = well hydrated."
}

============================================================
4. calculate_calories_burned — factor in a planned workout
============================================================
{
  "activity": "running (5mph / 12 min/mile)",
  "duration_minutes": 30,
  "weight_kg": 68,
  "met_value": 8.3,
  "intensity": "vigorous",
  "calories_burned": 282,
  "calories_per_minute": 9.4,
  "source": "2011 Compendium of Physical Activities (Ainsworth et al.)",
  "note": "MET-based estimates assume average metabolic efficiency. Individual variation (fitness level, terrain, pace consistency) typically puts real burn within ±15% of this estimate."
}
```

</details>

The full experience — chaining in `suggest_recipe`, `build_workout_plan`, and `get_preventive_care_recommendations` against live external APIs — runs through an MCP client like Claude Desktop; see [Setup](#setup) below.

## Tools (31 total)

### Core coaching

| Tool | Source | What it does |
|------|--------|-------------|
| `calculate_daily_needs` | Local (Mifflin-St Jeor) | BMR, TDEE, calorie + macro targets |
| `get_food_nutrition` | USDA FoodData Central → Open Food Facts | Unified nutrition facts by name or barcode |
| `suggest_recipe` | Edamam | Recipes matching calorie/macro/dietary targets |
| `get_exercise` | Wger + ExerciseDB | Exercises by muscle group, equipment, difficulty |
| `build_workout_plan` | Wger + program-design logic | Full weekly plan with sets/reps/rest |
| `get_health_topic_summary` | MedlinePlus (NLM/NIH) | Plain-language topic summaries + citations |
| `check_supplement_interaction` | openFDA | FDA-reported warnings and interactions |
| `get_preventive_care_recommendations` | ODPHP MyHealthfinder | Age/sex-appropriate screenings + vaccines |

### Body metrics & calculations

| Tool | What it does |
|------|-------------|
| `calculate_bmi` | Body mass index + category |
| `calculate_body_fat` | Body fat % (Navy method) |
| `calculate_water_intake` | Daily hydration target |
| `estimate_healthy_weight_range` | Healthy weight range for a given height |
| `calculate_calories_burned` | Calories burned for an activity + duration |
| `list_activities` | Browse the local calorie-burn activity table |

### Nutrition utilities

| Tool | Source | What it does |
|------|--------|-------------|
| `get_meal_totals` | Local (composes `get_food_nutrition`) | Aggregate macros/calories across a meal's items |
| `compare_foods` | USDA FoodData Central → Open Food Facts | Side-by-side nutrition comparison |
| `find_foods_high_in_nutrient` | USDA FoodData Central | Foods ranked by a target nutrient |
| `get_vitamin_mineral_info` | Local reference data | RDA, deficiency/toxicity signs, food sources |
| `search_nutrition_research` | PubMed | Peer-reviewed research summaries + citations |

### Biometrics & device integrations

Pulls live data from the user's own wearables/scales rather than static reference data. All integrations are optional and env-var gated — tools degrade gracefully with setup instructions when nothing is configured. See [Wearable Integrations](#wearable-integrations) below.

| Tool | What it does |
|------|-------------|
| `get_available_integrations` | Reports which wearable/device integrations are configured and ready |
| `get_biometric_summary` | Unified resting HR, HRV, sleep, steps, active calories, body composition |
| `get_sleep_analysis` | Per-night sleep breakdown, averages, sleep-debt and quality coaching flags |
| `get_recovery_score` | 0–100 recovery score (Oura readiness or computed from HRV/RHR/sleep) |
| `get_hrv_trends` | Daily HRV, 7-day rolling average, trend direction |
| `get_activity_summary` | Daily steps/active minutes vs. WHO 150 min/week guideline |
| `get_body_composition_trend` | Weight/body-fat trend, rate of change, aggressive-loss flag |
| `get_strain_score` | TRIMP-based training load from workouts / HR zones |
| `log_behavior_entry` | Journal lifestyle factors (sleep, alcohol, stress, caffeine, etc.) |
| `get_behavior_correlations` | Pearson correlations between journaled behaviors and next-day biometrics |
| `scan_ble_devices` | Discover nearby BLE health devices |
| `read_live_heart_rate` | Stream live HR + RMSSD HRV from a BLE heart rate monitor |

## Workflow

A typical coaching session chains tools in order:

```
1. calculate_daily_needs   → establishes calorie/macro targets
2. get_food_nutrition      → checks specific foods against those targets
3. suggest_recipe          → finds meals that fit the daily plan
4. build_workout_plan      → creates a training program for the goal
5. get_health_topic_summary → answers background health questions with citations
6. check_supplement_interaction → safety-checks supplements being considered
7. get_preventive_care_recommendations → surfaces relevant screenings
```

## Wearable Integrations

Each integration is activated purely by setting its env vars in `.env` — no code changes needed. `get_available_integrations` reports what's active and what's missing, including setup links.

| Integration | Env vars | Extra install |
|-------------|----------|----------------|
| Oura Ring | `OURA_PAT` | — |
| Polar (Accesslink) | `POLAR_ACCESS_TOKEN`, `POLAR_USER_ID` | — |
| Withings | `WITHINGS_ACCESS_TOKEN` | — |
| Fitbit | `FITBIT_ACCESS_TOKEN` | — |
| Garmin Connect | `GARMIN_EMAIL`, `GARMIN_PASSWORD` | `pip install garminconnect>=0.2.0` |
| Direct BLE (Polar H10, Bangle.js 2, PineTime, ESP32+MAX30102) | `BLE_DEVICE_ADDRESS` (optional default) | `pip install bleak>=0.21.0` |

Aggregation tools (`get_biometric_summary`, `get_sleep_analysis`, etc.) use whatever sources are active and label each metric with its source, generally preferring Oura > Withings > Garmin > Fitbit > Polar depending on the metric.

## Safety

- **Emergency hard-guard:** All tool inputs are checked against a hardcoded list of emergency phrases (chest pain, suicidal ideation, overdose, etc.) *before* any tool logic runs. Matching inputs return a fixed emergency-services redirect — this is not LLM-dependent.
- **Structural disclaimers:** Every Health Reference tool (MedlinePlus, openFDA, ODPHP) wraps its payload with a source citation and standard disclaimer before returning to the model. The LLM cannot forget to include it.
- **Scope boundary:** This tool covers general wellness, nutrition, and fitness. It does not diagnose, does not recommend medication dosages, and does not override physician guidance.

## Setup

### 1. Install dependencies

```bash
cd mcp-health-coach
pip install -r requirements.txt
```

### 2. Configure API keys

```bash
cp .env.example .env
# Edit .env with your keys
```

Required keys:
- `EDAMAM_APP_ID` + `EDAMAM_APP_KEY` — free tier at developer.edamam.com

Optional (free-tier or keyless):
- `USDA_FDC_API_KEY` — works without a key at low volume
- `EXERCISEDB_RAPIDAPI_KEY` — enables GIF links in exercise results

### 3. Test locally

```bash
pytest tests/ -v
```

### 4. Connect to Claude Desktop

Copy `config/claude_desktop_config.example.json` to your Claude Desktop config and update the path:

**macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`  
**Windows:** `%APPDATA%\Claude\claude_desktop_config.json`

```json
{
  "mcpServers": {
    "health-coach": {
      "command": "python",
      "args": ["/absolute/path/to/mcp-health-coach/server.py"],
      "env": {
        "EDAMAM_APP_ID": "your-id",
        "EDAMAM_APP_KEY": "your-key"
      }
    }
  }
}
```

Restart Claude Desktop. You should see the health-coach tools available.

### 5. Try a coaching session

Prompt example:
> "I'm a 32-year-old woman, 68kg, 170cm, moderately active. I want to lose weight. Help me build a meal and workout plan."

Claude will chain: `calculate_daily_needs` → `suggest_recipe` → `build_workout_plan` → `get_preventive_care_recommendations`.

## Project Structure

```
mcp-health-coach/
├── server.py                      # MCP entrypoint, tool registration (31 tools)
├── tools/
│   ├── nutrition.py               # get_food_nutrition (USDA + OFF)
│   ├── recipes.py                 # suggest_recipe (Edamam)
│   ├── exercise.py                # get_exercise (Wger + ExerciseDB)
│   ├── workout_plan.py            # build_workout_plan (composition)
│   ├── daily_needs.py             # calculate_daily_needs (pure logic)
│   ├── health_topics.py           # get_health_topic_summary (MedlinePlus)
│   ├── supplement_interactions.py # check_supplement_interaction (openFDA)
│   ├── preventive_care.py         # get_preventive_care_recommendations (ODPHP)
│   ├── body_metrics.py            # BMI, body fat, water intake, weight range
│   ├── calorie_burn.py            # calculate_calories_burned, list_activities
│   ├── meal_totals.py             # get_meal_totals, compare_foods
│   ├── nutrient_search.py         # find_foods_high_in_nutrient
│   ├── vitamin_info.py            # get_vitamin_mineral_info
│   ├── research.py                # search_nutrition_research (PubMed)
│   └── biometrics.py              # wearable aggregation, recovery/strain/HRV, behavior journal
├── integrations/
│   ├── oura.py                    # Oura Ring API v2
│   ├── polar.py                   # Polar Accesslink API v3
│   ├── withings.py                # Withings Health API
│   ├── fitbit.py                  # Fitbit Web API
│   ├── garmin.py                  # Garmin Connect (via garminconnect lib)
│   └── ble_gatt.py                # Direct BLE Heart Rate Service (via bleak)
├── lib/
│   ├── cache.py                   # Shared TTL cache (1hr, 512 entries)
│   ├── http_client.py             # Retry/timeout HTTP client
│   ├── normalization.py           # Unified NutritionFact schema
│   ├── disclaimers.py             # Structural disclaimer wrapper
│   └── safety_filters.py         # Hardcoded emergency redirect
├── tests/
│   ├── test_daily_needs.py        # BMR/TDEE formula correctness
│   ├── test_normalization.py      # Cross-source schema consistency
│   ├── test_disclaimer_wrapper.py # Disclaimer always present
│   ├── test_safety_filters.py    # Emergency patterns 100% blocked
│   ├── test_biometrics.py         # Wearable aggregation, recovery/strain/HRV, journal
│   └── test_integrations.py      # Per-device auth/parsing + degradation paths
├── config/
│   └── claude_desktop_config.example.json
├── demo/
│   └── run_example.py             # Key-free walkthrough of the local-logic tool chain
├── .env.example
└── requirements.txt
```

## Data Sources

All free-tier. No paid infrastructure required to run locally.

| Source | What | Key required? |
|--------|------|---------------|
| USDA FoodData Central | Whole food nutrition | No (optional for higher rate limits) |
| Open Food Facts | Branded/packaged foods | No |
| Edamam | Recipes | Yes (free tier) |
| Wger | Exercise database | No |
| ExerciseDB | Exercise GIFs | Yes (free tier via RapidAPI) |
| MedlinePlus (NLM) | Health topic summaries | No |
| openFDA | Drug/supplement labels | No |
| ODPHP MyHealthfinder | Preventive care guidelines | No |
| PubMed | Nutrition research | No |
| Oura / Polar / Withings / Fitbit / Garmin | Wearable biometrics | Yes, per-platform (see [Wearable Integrations](#wearable-integrations)) |
| Direct BLE hardware | Live heart rate / HRV | No (local Bluetooth only) |