io.github.davidmosiah/wellness-cgm-mcp
Local-first CGM MCP for AI agents: Dexcom Developer API + FreeStyle Libre via LibreLink Up.
Open source Open in the app JSON README (API)
About
Local-first CGM MCP for AI agents: Dexcom Developer API + FreeStyle Libre via LibreLink Up.
Details
- Kind
- MCP servers
- Topic
- No topic detected
- Publisher
- davidmosiah
- Origin
- official
- Category
- ferramentas
- Transport
- local
- Version
- 0.4.0
- Stars
- 1
- Last push
- 2026-08-29T10:34:55Z
- Repository state
- ativo
- Language
- TypeScript
- License
- MIT
- Added
- 2026-08-29 03:02:41
- Updated
- 2026-08-29 03:02:41
- Origin id
io.github.davidmosiah/wellness-cgm-mcp
README
<!-- delx-wellness header v2 -->
<h1 align="center">Wellness CGM MCP</h1>
<h3 align="center">
Local-first continuous glucose monitor MCP for AI agents.<br>
Dexcom Developer API. <strong>Levels-killer pattern, agent-first, $0.</strong>
</h3>
<p align="center">
<a href="https://www.npmjs.com/package/wellness-cgm-mcp"><img src="https://img.shields.io/npm/v/wellness-cgm-mcp?style=for-the-badge&labelColor=0F172A&color=10B981&logo=npm&logoColor=white" alt="npm version" /></a>
<a href="https://www.npmjs.com/package/wellness-cgm-mcp"><img src="https://img.shields.io/npm/dm/wellness-cgm-mcp?style=for-the-badge&labelColor=0F172A&color=0EA5A3&logo=npm&logoColor=white" alt="npm downloads" /></a>
<a href="LICENSE"><img src="https://img.shields.io/badge/LICENSE-MIT-22C55E?style=for-the-badge&labelColor=0F172A" alt="License MIT" /></a>
<a href="https://wellness.delx.ai/connectors/cgm"><img src="https://img.shields.io/badge/SITE-wellness.delx.ai-0EA5A3?style=for-the-badge&labelColor=0F172A" alt="Site" /></a>
</p>
<p align="center">
<a href="https://github.com/davidmosiah/wellness-cgm-mcp/stargazers"><img src="https://img.shields.io/github/stars/davidmosiah/wellness-cgm-mcp?style=for-the-badge&labelColor=0F172A&color=FBBF24&logo=github" alt="GitHub stars" /></a>
<a href="https://modelcontextprotocol.io"><img src="https://img.shields.io/badge/BUILT_FOR-MCP-7C3AED?style=for-the-badge&labelColor=0F172A" alt="Built for MCP" /></a>
<a href="https://github.com/davidmosiah/delx-wellness-hermes"><img src="https://img.shields.io/badge/HERMES-one--command_setup-10B981?style=for-the-badge&labelColor=0F172A" alt="Hermes" /></a>
<a href="https://github.com/davidmosiah/delx-wellness-openclaw"><img src="https://img.shields.io/badge/OPENCLAW-one--command_setup-FB923C?style=for-the-badge&labelColor=0F172A" alt="OpenClaw" /></a>
</p>
<p align="center">
<strong>๐ฉธ Why this exists:</strong> Levels charges $199/mo to do exactly this โ read your CGM, correlate with meals, flag spikes. <code>wellness-cgm-mcp</code> is the same game as a free local-first MCP. Stelo OTC + Dexcom developer API + your agent + <code>wellness-nourish</code> = the full metabolic loop.
</p>
> โก **One-command install** โ pick your runtime:
> - [Delx Wellness for Hermes](https://github.com/davidmosiah/delx-wellness-hermes): `npx -y delx-wellness-hermes setup`
> - [Delx Wellness for OpenClaw](https://github.com/davidmosiah/delx-wellness-openclaw): `npx -y delx-wellness-openclaw setup`
---
## HTTP (v2 stateless)
Default is **stdio**. Optional Streamable HTTP โ no session id, JSON responses, loopback only:
```bash
npx -y wellness-cgm-mcp --http
# GET http://127.0.0.1:3000/health
# POST http://127.0.0.1:3000/mcp (sessionless)
```
Env: `WELLNESS_CGM_HOST`, `WELLNESS_CGM_PORT`, `WELLNESS_CGM_TRANSPORT=http`.
<!-- /delx-wellness header v2 -->
## Overview
Local MCP server that exposes CGM data (and synthetic mock data when nothing is configured) to any MCP-aware agent. Two real backends are supported: **Dexcom** (Developer API, sandbox + production) and **FreeStyle Libre** (the OTC sensor โ Libre 2 / Libre 3) via **LibreLink Up**. Pick the backend with `CGM_PROVIDER`; it auto-detects Libre when only Libre credentials are set. Both feed the same ADA time-in-range / GMI / hypo / meal-response engine.
## Try It In 60 Seconds (mock mode, zero setup)
```bash
npx -y wellness-cgm-mcp doctor # see env / mode
npx -y wellness-cgm-mcp status
# In Claude Desktop / Cursor / etc., add:
# {
# "mcpServers": {
# "wellness-cgm": {
# "command": "npx",
# "args": ["-y", "wellness-cgm-mcp"]
# }
# }
# }
```
The agent now has 10 CGM tools. Without a Dexcom token, every tool returns synthetic readings tagged `mock: true` โ perfect for prototyping.
## Live setup (Dexcom Developer)
```bash
# 1. Sign up at https://developer.dexcom.com (sandbox is free)
# 2. Create an app, register your redirect URI
export DEXCOM_ENV=sandbox
export DEXCOM_CLIENT_ID=...
export DEXCOM_CLIENT_SECRET=...
export DEXCOM_REDIRECT_URI=https://your.callback/redirect
# 3. Get the OAuth URL, open it, grant access, copy the code from the redirect
npx -y wellness-cgm-mcp authorize
# 4. Swap code for tokens
npx -y wellness-cgm-mcp exchange <auth_code_from_redirect>
# 5. Set DEXCOM_ACCESS_TOKEN to the access_token, restart the MCP โ flips from mock to live.
```
## Live setup (FreeStyle Libre โ the OTC sensor)
No developer program, no app to build โ just the **same email/password you use in the LibreLinkUp follower app** (the OTC Libre 2 / Libre 3 sensor works). In the LibreLink app, share your readings; in the LibreLinkUp app, accept the invite. Then:
```bash
export CGM_PROVIDER=libre # or just set the creds below and let it auto-detect
export LIBRELINKUP_EMAIL=you@example.com
export LIBRELINKUP_PASSWORD=...
# Optional: region shard if you're not on EU/global, and a pinned sensor:
export LIBRELINKUP_REGION=us # eu (default) | us | de | fr | au | jp ...
# export LIBRELINKUP_PATIENT_ID=<id> # only if you follow more than one sensor
# Verify credentials + list the sensor(s) you follow (never prints the token):
npx -y wellness-cgm-mcp libre-login
```
Once logged in, every glucose tool (`cgm_glucose_now`, `cgm_daily_summary`, `cgm_time_in_range`, `cgm_meal_response`, `cgm_hypo_events`, โฆ) reads from Libre and returns the same ADA TIR / GMI / hypo / meal-response metrics โ each response carries a `provider` field so you always know the source. Without any credentials, everything returns synthetic `mock: true` data.
### Libre history limit: ~12h per read
LibreLink Up's graph endpoint takes **no start/end parameter** โ it always answers with its own fixed trailing window of roughly **12 hours**. Asking for 24h or 72h does not widen it, so on Libre those extra hours simply do not exist.
Every windowed payload therefore reports what it actually covered:
```jsonc
// cgm_daily_summary({ hours: 72 }) on live Libre
{
"window_hours": 72, // what you asked for
"hours_covered": 12, // what the numbers below are ACTUALLY computed over
"observed_window": { "start": "โฆ", "end": "โฆ", "hours": 12 },
"window_truncated_by_provider": true,
"notes": ["LibreLink Up returns ~12h of graph data per read and ignores wider spans; requested 72h, covered 12h. โฆ"]
}
```
Read `hours_covered`, never the requested `hours` / `window_hours`. A GMI (estimated A1C), CV or time-in-range built on 12h is not a 3-day result. **For multi-day metrics use Dexcom**, whose v3 API takes an explicit start/end and honours the request. Mock mode synthesises the full requested span, so it is never truncated.
The same applies to `cgm_hypo_events`, which takes an explicit `from`/`to`: "no hypoglycemia events" is only a claim about `hours_covered`. A 3-day question answered from a live Libre read is a 12-hour answer, and the payload says so in `hours_covered`, `observed_window.hours`, `window_truncated_by_provider` and `notes`. (`events_per_day` is safe either way โ its denominator is the observed span, not the requested one โ but the frame around it is not.)
#### `window_truncated_by_provider` is structural, not empirical
It answers *"can this provider cover a span this wide?"* โ never *"did this particular read come back short?"*. A sensor applied two hours ago answers `cgm_daily_summary({ hours: 12 })` with `hours_covered: 2`, `window_truncated_by_provider: false` and an empty `notes`, because nothing is broken and warning there would be a false alarm. That is deliberate:
> **An empty `notes` means "no known provider ceiling was hit", not "the window was fully covered".** `hours_covered` is the only number that states the real span โ compare it against `hours_requested` before reporting any window.
## Tools (19)
| Tool | Purpose |
|---|---|
| `cgm_agent_manifest` | Runtime contract |
| `cgm_capabilities` | Providers, metrics, privacy modes |
| `cgm_connection_status` | env, credentials, mode (live vs mock) |
| `cgm_privacy_audit` | Local storage + outbound destinations |
| `cgm_data_inventory` | Metric catalog + TIR ranges + GMI formula |
| **`cgm_glucose_now`** | **Most recent EGV + trend** |
| `cgm_glucose_window` | All EGVs over last N hours (+ `hours_covered` โ see the Libre ~12h limit) |
| **`cgm_daily_summary`** | **Mean / GMI / CV / 2 TIR profiles โ over `hours_covered`, not the requested window** |
| **`cgm_meal_response`** | **Baseline โ peak โ return + band** |
| `cgm_authorize_url` | Dexcom OAuth URL builder |
| **`cgm_hypo_events`** | **Hypo event detection (ADA Level 1 < 70, Level 2 < 54) โ "no events" applies to `hours_covered` only** |
| **`cgm_libre_status`** | **FreeStyle Libre (LibreLink Up) config + region + mode โ v0.4** |
| **`cgm_libre_login`** | **Log in to LibreLink Up + list followed sensors โ v0.4** |
> The table omits the shared profile/onboarding/quickstart/demo helpers (`cgm_profile_get`, `cgm_profile_update`, `cgm_onboarding`, `cgm_quickstart`, `cgm_demo`) for brevity โ call `cgm_agent_manifest` for the full, always-current list.
## Two Time-In-Range profiles in every summary
- **Diabetic** (70-180 mg/dL) โ ADA standard for adults with diabetes.
- **Metabolic health** (70-140 mg/dL) โ Levels-style for non-DM users.
Agents surface BOTH so the user picks the one that fits their context.
## Meal response bands
| Peak ฮ from baseline | Band |
|---|---|
| < 30 mg/dL | excellent |
| 30-49 | good |
| 50-79 | moderate |
| โฅ 80 | poor |
Combine with `wellness-nourish` to compute "what did I eat โ what happened" automatically.
## The killer combo
```
wellness-nourish: meal at 13:15 (rice + chicken)
โ
wellness-cgm-mcp.cgm_meal_response(meal_time)
โ
{ peak: 167, peak_delta: 72, band: "moderate", peak_time_minutes: 45 }
โ
whoop-mcp.recovery: 67%
โ
Agent: "That meal hit a moderate spike (peak +72 mg/dL at 45 min)
AND recovery is borderline. Try protein-first next time, or
swap white rice for lentils โ should drop the peak ~30 mg/dL."
```
Levels charges $199/mo for this. Here it is, free, local-first, MCP.
## Privacy
- โ
**Credentials local only** โ `DEXCOM_ACCESS_TOKEN` / `LIBRELINKUP_*` stay in env vars; the LibreLink Up auth token is never returned in tool output.
- โ
**Mock mode by default** โ every tool returns synthetic data with `mock: true` until a provider is configured.
- โ
**No third-party telemetry** โ outbound calls go only to your CGM provider (Dexcom or, for Libre, Abbott's LibreLink Up API).
Run `wellness-cgm-mcp doctor` to inspect.
## Roadmap
- โ
**v0.4** โ FreeStyle Libre via LibreLink Up (the OTC sensor). _Shipped._
- **next** โ Refresh-token rotation. Per-meal historical browser (which foods spike YOU?). Threshold alerts (agent notified when glucose holds > X mg/dL for Y minutes). Cross-meal automation with wellness-nourish.
## What this is NOT
- Not medical advice or diagnosis.
- Not for insulin/medication dosing decisions โ defer to clinician.
- Not affiliated with Dexcom or Abbott.
## ๐ง Contact & Support
- ๐จ **support@delx.ai** โ general questions, integration help, partnerships
- ๐ **Bug reports / feature requests** โ [GitHub Issues](https://github.com/davidmosiah/wellness-cgm-mcp/issues)
- ๐ฆ **Updates** โ [@delx369](https://x.com/delx369) on X
- ๐ **Site** โ [wellness.delx.ai](https://wellness.delx.ai)
## License
MIT โ see [LICENSE](LICENSE).
<sub>wellness-cgm-mcp is independent open-source software. Dexcom and FreeStyle Libre are trademarks of their respective owners. Neither company is affiliated with or endorses this project.</sub>
## Skill or MCP
Same package, two doors. MCP registers tools on stdio/HTTP. The [skill](skill/SKILL.md) can drive the **same** tools through the CLI when the client has no MCP:
```bash
npx -y wellness-cgm call cgm_connection_status --json '{}'
```
Copy `skill/SKILL.md` into your agent skills dir.