wellness-cgm-mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/LICENSE +21 -0
- package/README.md +151 -0
- package/SECURITY.md +21 -0
- package/dist/cli/commands.d.ts +2 -0
- package/dist/cli/commands.js +143 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/constants.d.ts +21 -0
- package/dist/constants.js +20 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +76 -0
- package/dist/index.js.map +1 -0
- package/dist/services/agent-manifest.d.ts +28 -0
- package/dist/services/agent-manifest.js +70 -0
- package/dist/services/agent-manifest.js.map +1 -0
- package/dist/services/capabilities.d.ts +9 -0
- package/dist/services/capabilities.js +31 -0
- package/dist/services/capabilities.js.map +1 -0
- package/dist/services/dexcom-client.d.ts +40 -0
- package/dist/services/dexcom-client.js +99 -0
- package/dist/services/dexcom-client.js.map +1 -0
- package/dist/services/glucose-engine.d.ts +65 -0
- package/dist/services/glucose-engine.js +168 -0
- package/dist/services/glucose-engine.js.map +1 -0
- package/dist/services/privacy-audit.d.ts +8 -0
- package/dist/services/privacy-audit.js +27 -0
- package/dist/services/privacy-audit.js.map +1 -0
- package/dist/tools/cgm-tools.d.ts +2 -0
- package/dist/tools/cgm-tools.js +151 -0
- package/dist/tools/cgm-tools.js.map +1 -0
- package/llms.txt +54 -0
- package/package.json +71 -0
- package/server.json +21 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [Unreleased]
|
|
4
|
+
|
|
5
|
+
## [0.1.0] - 2026-05-10
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Initial release. Local-first CGM MCP server with full Dexcom Developer API support (sandbox + production).
|
|
10
|
+
- 10 MCP tools: standard 5 (`cgm_agent_manifest`, `cgm_capabilities`, `cgm_connection_status`, `cgm_privacy_audit`, `cgm_data_inventory`) + CGM-specific 5 (`cgm_glucose_now`, `cgm_glucose_window`, `cgm_daily_summary`, `cgm_meal_response`, `cgm_authorize_url`).
|
|
11
|
+
- Glucose math: time-in-range (ADA diabetic 70-180 + Levels-style metabolic-health 70-140), GMI (Bergenstal 2018 formula), CV, mean/median/min/max/stdev.
|
|
12
|
+
- Meal response: baseline → peak → return-to-baseline with bands (excellent < 30 / good 30-49 / moderate 50-79 / poor ≥ 80).
|
|
13
|
+
- Dexcom OAuth flow scaffolded: `cgm_authorize_url` MCP tool + `wellness-cgm authorize` / `wellness-cgm exchange <code>` CLI helpers.
|
|
14
|
+
- **Mock mode by default** — without DEXCOM_ACCESS_TOKEN, every tool returns synthetic 5-minute-interval readings clearly tagged with `mock: true`. Lets agents demo the full surface without setup.
|
|
15
|
+
- One-line stderr community CTA on CLI commands (TTY-gated).
|
|
16
|
+
- `community` block on the agent manifest.
|
|
17
|
+
- FreeStyle Libre via LibreLink Up community proxy listed as v0.2 roadmap.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 David Batista
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
<!-- delx-wellness header v2 -->
|
|
2
|
+
<h1 align="center">Wellness CGM MCP</h1>
|
|
3
|
+
|
|
4
|
+
<h3 align="center">
|
|
5
|
+
Local-first continuous glucose monitor MCP for AI agents.<br>
|
|
6
|
+
Dexcom Developer API. <strong>Levels-killer pattern, agent-first, $0.</strong>
|
|
7
|
+
</h3>
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<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>
|
|
11
|
+
<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>
|
|
12
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/LICENSE-MIT-22C55E?style=for-the-badge&labelColor=0F172A" alt="License MIT" /></a>
|
|
13
|
+
<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>
|
|
14
|
+
</p>
|
|
15
|
+
|
|
16
|
+
<p align="center">
|
|
17
|
+
<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>
|
|
18
|
+
<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>
|
|
19
|
+
<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>
|
|
20
|
+
<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>
|
|
21
|
+
</p>
|
|
22
|
+
|
|
23
|
+
<p align="center">
|
|
24
|
+
<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.
|
|
25
|
+
</p>
|
|
26
|
+
|
|
27
|
+
> ⚡ **One-command install** — pick your runtime:
|
|
28
|
+
> - [Delx Wellness for Hermes](https://github.com/davidmosiah/delx-wellness-hermes): `npx -y delx-wellness-hermes setup`
|
|
29
|
+
> - [Delx Wellness for OpenClaw](https://github.com/davidmosiah/delx-wellness-openclaw): `npx -y delx-wellness-openclaw setup`
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
<!-- /delx-wellness header v2 -->
|
|
33
|
+
|
|
34
|
+
## Overview
|
|
35
|
+
|
|
36
|
+
Local MCP server that exposes Dexcom CGM data (and synthetic mock data when no token is set) to any MCP-aware agent. v0.1 ships full Dexcom Developer API support — sandbox + production. FreeStyle Libre via LibreLink Up is roadmapped for v0.2.
|
|
37
|
+
|
|
38
|
+
## Try It In 60 Seconds (mock mode, zero setup)
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npx -y wellness-cgm-mcp doctor # see env / mode
|
|
42
|
+
npx -y wellness-cgm-mcp status
|
|
43
|
+
|
|
44
|
+
# In Claude Desktop / Cursor / etc., add:
|
|
45
|
+
# {
|
|
46
|
+
# "mcpServers": {
|
|
47
|
+
# "wellness-cgm": {
|
|
48
|
+
# "command": "npx",
|
|
49
|
+
# "args": ["-y", "wellness-cgm-mcp"]
|
|
50
|
+
# }
|
|
51
|
+
# }
|
|
52
|
+
# }
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The agent now has 10 CGM tools. Without a Dexcom token, every tool returns synthetic readings tagged `mock: true` — perfect for prototyping.
|
|
56
|
+
|
|
57
|
+
## Live setup (Dexcom Developer)
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# 1. Sign up at https://developer.dexcom.com (sandbox is free)
|
|
61
|
+
# 2. Create an app, register your redirect URI
|
|
62
|
+
export DEXCOM_ENV=sandbox
|
|
63
|
+
export DEXCOM_CLIENT_ID=...
|
|
64
|
+
export DEXCOM_CLIENT_SECRET=...
|
|
65
|
+
export DEXCOM_REDIRECT_URI=https://your.callback/redirect
|
|
66
|
+
|
|
67
|
+
# 3. Get the OAuth URL, open it, grant access, copy the code from the redirect
|
|
68
|
+
npx -y wellness-cgm-mcp authorize
|
|
69
|
+
|
|
70
|
+
# 4. Swap code for tokens
|
|
71
|
+
npx -y wellness-cgm-mcp exchange <auth_code_from_redirect>
|
|
72
|
+
|
|
73
|
+
# 5. Set DEXCOM_ACCESS_TOKEN to the access_token, restart the MCP — flips from mock to live.
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Tools (10)
|
|
77
|
+
|
|
78
|
+
| Tool | Purpose |
|
|
79
|
+
|---|---|
|
|
80
|
+
| `cgm_agent_manifest` | Runtime contract |
|
|
81
|
+
| `cgm_capabilities` | Providers, metrics, privacy modes |
|
|
82
|
+
| `cgm_connection_status` | env, credentials, mode (live vs mock) |
|
|
83
|
+
| `cgm_privacy_audit` | Local storage + outbound destinations |
|
|
84
|
+
| `cgm_data_inventory` | Metric catalog + TIR ranges + GMI formula |
|
|
85
|
+
| **`cgm_glucose_now`** | **Most recent EGV + trend** |
|
|
86
|
+
| `cgm_glucose_window` | All EGVs over last N hours |
|
|
87
|
+
| **`cgm_daily_summary`** | **Mean / GMI / CV / 2 TIR profiles** |
|
|
88
|
+
| **`cgm_meal_response`** | **Baseline → peak → return + band** |
|
|
89
|
+
| `cgm_authorize_url` | Dexcom OAuth URL builder |
|
|
90
|
+
|
|
91
|
+
## Two Time-In-Range profiles in every summary
|
|
92
|
+
|
|
93
|
+
- **Diabetic** (70-180 mg/dL) — ADA standard for adults with diabetes.
|
|
94
|
+
- **Metabolic health** (70-140 mg/dL) — Levels-style for non-DM users.
|
|
95
|
+
|
|
96
|
+
Agents surface BOTH so the user picks the one that fits their context.
|
|
97
|
+
|
|
98
|
+
## Meal response bands
|
|
99
|
+
|
|
100
|
+
| Peak Δ from baseline | Band |
|
|
101
|
+
|---|---|
|
|
102
|
+
| < 30 mg/dL | excellent |
|
|
103
|
+
| 30-49 | good |
|
|
104
|
+
| 50-79 | moderate |
|
|
105
|
+
| ≥ 80 | poor |
|
|
106
|
+
|
|
107
|
+
Combine with `wellness-nourish` to compute "what did I eat → what happened" automatically.
|
|
108
|
+
|
|
109
|
+
## The killer combo
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
wellness-nourish: meal at 13:15 (rice + chicken)
|
|
113
|
+
↓
|
|
114
|
+
wellness-cgm-mcp.cgm_meal_response(meal_time)
|
|
115
|
+
↓
|
|
116
|
+
{ peak: 167, peak_delta: 72, band: "moderate", peak_time_minutes: 45 }
|
|
117
|
+
↓
|
|
118
|
+
whoop-mcp.recovery: 67%
|
|
119
|
+
↓
|
|
120
|
+
Agent: "That meal hit a moderate spike (peak +72 mg/dL at 45 min)
|
|
121
|
+
AND recovery is borderline. Try protein-first next time, or
|
|
122
|
+
swap white rice for lentils — should drop the peak ~30 mg/dL."
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Levels charges $199/mo for this. Here it is, free, local-first, MCP.
|
|
126
|
+
|
|
127
|
+
## Privacy
|
|
128
|
+
|
|
129
|
+
- ✅ **Tokens local only** — DEXCOM_ACCESS_TOKEN stays in env vars.
|
|
130
|
+
- ✅ **Mock mode by default** — every tool returns synthetic data with `mock: true` until a token is configured.
|
|
131
|
+
- ✅ **No third-party telemetry** — only outbound calls go to Dexcom.
|
|
132
|
+
|
|
133
|
+
Run `wellness-cgm-mcp doctor` to inspect.
|
|
134
|
+
|
|
135
|
+
## Roadmap
|
|
136
|
+
|
|
137
|
+
- **v0.2** — FreeStyle Libre via LibreLink Up community proxy. Refresh-token rotation. Cross-meal automation with wellness-nourish.
|
|
138
|
+
- **v0.3** — Per-meal historical browser (which foods spike YOU?).
|
|
139
|
+
- **v0.4** — Threshold alerts (agent notified when glucose holds > X mg/dL for Y minutes).
|
|
140
|
+
|
|
141
|
+
## What this is NOT
|
|
142
|
+
|
|
143
|
+
- Not medical advice or diagnosis.
|
|
144
|
+
- Not for insulin/medication dosing decisions — defer to clinician.
|
|
145
|
+
- Not affiliated with Dexcom or Abbott.
|
|
146
|
+
|
|
147
|
+
## License
|
|
148
|
+
|
|
149
|
+
MIT — see [LICENSE](LICENSE).
|
|
150
|
+
|
|
151
|
+
<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>
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Reporting
|
|
4
|
+
|
|
5
|
+
Email **mosiahdavid@gmail.com** with details and reproduction. Do not open a public issue.
|
|
6
|
+
|
|
7
|
+
## Scope
|
|
8
|
+
|
|
9
|
+
CGM data is medical-record sensitive. The relevant security surfaces:
|
|
10
|
+
|
|
11
|
+
- Dexcom OAuth tokens stored in env vars (never logged).
|
|
12
|
+
- Local cache directory under `~/.wellness-cgm`.
|
|
13
|
+
- HTTP transport (`--http`) bound to `127.0.0.1` by default.
|
|
14
|
+
- Outbound calls to sandbox-api.dexcom.com / api.dexcom.com.
|
|
15
|
+
|
|
16
|
+
If you find token leaks, unintended outbound destinations, or unexpected behaviors with sensitive data, please report.
|
|
17
|
+
|
|
18
|
+
## Out of scope
|
|
19
|
+
|
|
20
|
+
- Dexcom API vulnerabilities — report to Dexcom.
|
|
21
|
+
- LibreLink Up risks — that integration is not in v0.1.
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { NPM_PACKAGE_NAME, SERVER_VERSION } from "../constants.js";
|
|
2
|
+
import { DexcomClient } from "../services/dexcom-client.js";
|
|
3
|
+
import { buildCapabilities } from "../services/capabilities.js";
|
|
4
|
+
import { buildPrivacyAudit } from "../services/privacy-audit.js";
|
|
5
|
+
const COMMANDS = new Set(["status", "doctor", "setup", "authorize", "exchange"]);
|
|
6
|
+
function printCommunityCTA() {
|
|
7
|
+
if (process.env.WELLNESS_CGM_QUIET === "1")
|
|
8
|
+
return;
|
|
9
|
+
if (!process.stderr.isTTY)
|
|
10
|
+
return;
|
|
11
|
+
process.stderr.write(`\n✨ wellness-cgm-mcp v${SERVER_VERSION} — bringing CGM into the agent loop. A star ⭐ helps surface this to other AI builders.\n` +
|
|
12
|
+
` ⭐ https://github.com/davidmosiah/wellness-cgm-mcp\n` +
|
|
13
|
+
` 💬 https://github.com/davidmosiah/wellness-cgm-mcp/issues\n` +
|
|
14
|
+
` 🐦 https://x.com/delx369\n` +
|
|
15
|
+
` (silence with WELLNESS_CGM_QUIET=1)\n\n`);
|
|
16
|
+
}
|
|
17
|
+
export function isCliCommand(args) {
|
|
18
|
+
const command = args[0];
|
|
19
|
+
return command !== undefined && COMMANDS.has(command);
|
|
20
|
+
}
|
|
21
|
+
export async function runCliCommand(args) {
|
|
22
|
+
const [command, ...rest] = args;
|
|
23
|
+
try {
|
|
24
|
+
switch (command) {
|
|
25
|
+
case "status":
|
|
26
|
+
return printStatus();
|
|
27
|
+
case "doctor":
|
|
28
|
+
return doctor();
|
|
29
|
+
case "setup":
|
|
30
|
+
return setup(rest);
|
|
31
|
+
case "authorize":
|
|
32
|
+
return authorize();
|
|
33
|
+
case "exchange":
|
|
34
|
+
return await exchange(rest);
|
|
35
|
+
default:
|
|
36
|
+
return -1;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
console.error(err.message);
|
|
41
|
+
return 1;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function printStatus() {
|
|
45
|
+
const c = new DexcomClient();
|
|
46
|
+
console.log(JSON.stringify({
|
|
47
|
+
name: NPM_PACKAGE_NAME,
|
|
48
|
+
version: SERVER_VERSION,
|
|
49
|
+
env: c.env,
|
|
50
|
+
client_id_configured: Boolean(c.clientId),
|
|
51
|
+
access_token_configured: c.hasAuth(),
|
|
52
|
+
mode: c.hasAuth() ? "live" : "mock",
|
|
53
|
+
}, null, 2));
|
|
54
|
+
printCommunityCTA();
|
|
55
|
+
return 0;
|
|
56
|
+
}
|
|
57
|
+
function doctor() {
|
|
58
|
+
const c = new DexcomClient();
|
|
59
|
+
const checks = [
|
|
60
|
+
{ name: "node", ok: true, detail: process.version },
|
|
61
|
+
{ name: "dexcom_env", ok: true, detail: c.env },
|
|
62
|
+
{ name: "client_id", ok: Boolean(c.clientId), detail: c.clientId ? "set" : "missing" },
|
|
63
|
+
{ name: "redirect_uri", ok: Boolean(c.redirectUri), detail: c.redirectUri ?? "missing" },
|
|
64
|
+
{
|
|
65
|
+
name: "access_token",
|
|
66
|
+
ok: c.hasAuth(),
|
|
67
|
+
detail: c.hasAuth() ? "present" : "missing — running in mock mode",
|
|
68
|
+
},
|
|
69
|
+
];
|
|
70
|
+
console.log(JSON.stringify({
|
|
71
|
+
ok: true,
|
|
72
|
+
package: NPM_PACKAGE_NAME,
|
|
73
|
+
version: SERVER_VERSION,
|
|
74
|
+
mode: c.hasAuth() ? "live" : "mock",
|
|
75
|
+
checks,
|
|
76
|
+
privacy: buildPrivacyAudit(),
|
|
77
|
+
capabilities: buildCapabilities(),
|
|
78
|
+
}, null, 2));
|
|
79
|
+
printCommunityCTA();
|
|
80
|
+
return 0;
|
|
81
|
+
}
|
|
82
|
+
function setup(args) {
|
|
83
|
+
const optional = (args[0] ?? "generic").toLowerCase();
|
|
84
|
+
console.log(JSON.stringify({
|
|
85
|
+
client: optional,
|
|
86
|
+
config: {
|
|
87
|
+
mcpServers: {
|
|
88
|
+
"wellness-cgm": {
|
|
89
|
+
command: "npx",
|
|
90
|
+
args: ["-y", NPM_PACKAGE_NAME],
|
|
91
|
+
env: {
|
|
92
|
+
DEXCOM_ENV: "sandbox",
|
|
93
|
+
DEXCOM_CLIENT_ID: "${DEXCOM_CLIENT_ID}",
|
|
94
|
+
DEXCOM_CLIENT_SECRET: "${DEXCOM_CLIENT_SECRET}",
|
|
95
|
+
DEXCOM_REDIRECT_URI: "${DEXCOM_REDIRECT_URI}",
|
|
96
|
+
DEXCOM_ACCESS_TOKEN: "${DEXCOM_ACCESS_TOKEN:-}",
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
next_steps: [
|
|
102
|
+
"Sign up at https://developer.dexcom.com and create a project (sandbox is free).",
|
|
103
|
+
"Set DEXCOM_CLIENT_ID, DEXCOM_CLIENT_SECRET, DEXCOM_REDIRECT_URI in your env or MCP config.",
|
|
104
|
+
"Run `wellness-cgm authorize` to print the OAuth URL — open it, grant access, copy the auth code from the redirect.",
|
|
105
|
+
"Run `wellness-cgm exchange <auth_code>` to swap the code for an access_token.",
|
|
106
|
+
"Set DEXCOM_ACCESS_TOKEN and restart the MCP. Until then, all tools return mock data.",
|
|
107
|
+
],
|
|
108
|
+
}, null, 2));
|
|
109
|
+
printCommunityCTA();
|
|
110
|
+
return 0;
|
|
111
|
+
}
|
|
112
|
+
function authorize() {
|
|
113
|
+
const c = new DexcomClient();
|
|
114
|
+
try {
|
|
115
|
+
const url = c.buildAuthorizeUrl();
|
|
116
|
+
console.log(JSON.stringify({ ok: true, env: c.env, authorize_url: url }, null, 2));
|
|
117
|
+
return 0;
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
console.error(err.message);
|
|
121
|
+
return 1;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
async function exchange(args) {
|
|
125
|
+
const code = args[0];
|
|
126
|
+
if (!code) {
|
|
127
|
+
console.error("usage: wellness-cgm exchange <auth_code>");
|
|
128
|
+
return 1;
|
|
129
|
+
}
|
|
130
|
+
const c = new DexcomClient();
|
|
131
|
+
const tokens = await c.exchangeAuthCode(code);
|
|
132
|
+
console.log(JSON.stringify({
|
|
133
|
+
ok: true,
|
|
134
|
+
env: c.env,
|
|
135
|
+
access_token: tokens.access_token,
|
|
136
|
+
refresh_token: tokens.refresh_token,
|
|
137
|
+
expires_at: tokens.expires_at,
|
|
138
|
+
scope: tokens.scope,
|
|
139
|
+
next: "Set DEXCOM_ACCESS_TOKEN to the access_token above (and DEXCOM_REFRESH_TOKEN to the refresh_token).",
|
|
140
|
+
}, null, 2));
|
|
141
|
+
return 0;
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=commands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/cli/commands.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export declare const SERVER_NAME = "wellness-cgm-mcp";
|
|
2
|
+
export declare const SERVER_VERSION = "0.1.0";
|
|
3
|
+
export declare const NPM_PACKAGE_NAME = "wellness-cgm-mcp";
|
|
4
|
+
export declare const PINNED_NPM_PACKAGE = "wellness-cgm-mcp@0.1.0";
|
|
5
|
+
export declare const USER_AGENT = "wellness-cgm-mcp/0.1.0 (https://wellness.delx.ai/connectors/cgm; contact: david@delx.ai)";
|
|
6
|
+
export declare const DEFAULT_HOST = "127.0.0.1";
|
|
7
|
+
export declare const DEFAULT_PORT = 3012;
|
|
8
|
+
export declare const LOCAL_DIR_NAME = ".wellness-cgm";
|
|
9
|
+
export declare const DEXCOM_SANDBOX_BASE = "https://sandbox-api.dexcom.com";
|
|
10
|
+
export declare const DEXCOM_PRODUCTION_BASE = "https://api.dexcom.com";
|
|
11
|
+
export declare const DEXCOM_OAUTH_AUTHORIZE = "/v2/oauth2/login";
|
|
12
|
+
export declare const DEXCOM_OAUTH_TOKEN = "/v2/oauth2/token";
|
|
13
|
+
/** Time-in-range thresholds (mg/dL). ADA standard for adults with diabetes (also useful for non-DM users). */
|
|
14
|
+
export declare const TIR_LOW_MGDL = 70;
|
|
15
|
+
export declare const TIR_HIGH_MGDL = 180;
|
|
16
|
+
/** Tighter "metabolic health" range used for non-DM users (Levels-style). */
|
|
17
|
+
export declare const MH_LOW_MGDL = 70;
|
|
18
|
+
export declare const MH_HIGH_MGDL = 140;
|
|
19
|
+
export declare const SUPPORTED_PROVIDERS: readonly ["dexcom", "libre"];
|
|
20
|
+
export type CgmProvider = typeof SUPPORTED_PROVIDERS[number];
|
|
21
|
+
export type DexcomEnv = "sandbox" | "production";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const SERVER_NAME = "wellness-cgm-mcp";
|
|
2
|
+
export const SERVER_VERSION = "0.1.0";
|
|
3
|
+
export const NPM_PACKAGE_NAME = "wellness-cgm-mcp";
|
|
4
|
+
export const PINNED_NPM_PACKAGE = `${NPM_PACKAGE_NAME}@${SERVER_VERSION}`;
|
|
5
|
+
export const USER_AGENT = `${NPM_PACKAGE_NAME}/${SERVER_VERSION} (https://wellness.delx.ai/connectors/cgm; contact: david@delx.ai)`;
|
|
6
|
+
export const DEFAULT_HOST = "127.0.0.1";
|
|
7
|
+
export const DEFAULT_PORT = 3012;
|
|
8
|
+
export const LOCAL_DIR_NAME = ".wellness-cgm";
|
|
9
|
+
export const DEXCOM_SANDBOX_BASE = "https://sandbox-api.dexcom.com";
|
|
10
|
+
export const DEXCOM_PRODUCTION_BASE = "https://api.dexcom.com";
|
|
11
|
+
export const DEXCOM_OAUTH_AUTHORIZE = "/v2/oauth2/login";
|
|
12
|
+
export const DEXCOM_OAUTH_TOKEN = "/v2/oauth2/token";
|
|
13
|
+
/** Time-in-range thresholds (mg/dL). ADA standard for adults with diabetes (also useful for non-DM users). */
|
|
14
|
+
export const TIR_LOW_MGDL = 70;
|
|
15
|
+
export const TIR_HIGH_MGDL = 180;
|
|
16
|
+
/** Tighter "metabolic health" range used for non-DM users (Levels-style). */
|
|
17
|
+
export const MH_LOW_MGDL = 70;
|
|
18
|
+
export const MH_HIGH_MGDL = 140;
|
|
19
|
+
export const SUPPORTED_PROVIDERS = ["dexcom", "libre"];
|
|
20
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,WAAW,GAAG,kBAAkB,CAAC;AAC9C,MAAM,CAAC,MAAM,cAAc,GAAG,OAAO,CAAC;AACtC,MAAM,CAAC,MAAM,gBAAgB,GAAG,kBAAkB,CAAC;AACnD,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAG,gBAAgB,IAAI,cAAc,EAAE,CAAC;AAC1E,MAAM,CAAC,MAAM,UAAU,GAAG,GAAG,gBAAgB,IAAI,cAAc,oEAAoE,CAAC;AAEpI,MAAM,CAAC,MAAM,YAAY,GAAG,WAAW,CAAC;AACxC,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,CAAC;AACjC,MAAM,CAAC,MAAM,cAAc,GAAG,eAAe,CAAC;AAE9C,MAAM,CAAC,MAAM,mBAAmB,GAAG,gCAAgC,CAAC;AACpE,MAAM,CAAC,MAAM,sBAAsB,GAAG,wBAAwB,CAAC;AAC/D,MAAM,CAAC,MAAM,sBAAsB,GAAG,kBAAkB,CAAC;AACzD,MAAM,CAAC,MAAM,kBAAkB,GAAG,kBAAkB,CAAC;AAErD,8GAA8G;AAC9G,MAAM,CAAC,MAAM,YAAY,GAAG,EAAE,CAAC;AAC/B,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,CAAC;AACjC,6EAA6E;AAC7E,MAAM,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC;AAC9B,MAAM,CAAC,MAAM,YAAY,GAAG,GAAG,CAAC;AAEhC,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAU,CAAC"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import cors from "cors";
|
|
3
|
+
import express from "express";
|
|
4
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
7
|
+
import { DEFAULT_HOST, DEFAULT_PORT, SERVER_NAME, SERVER_VERSION } from "./constants.js";
|
|
8
|
+
import { isCliCommand, runCliCommand } from "./cli/commands.js";
|
|
9
|
+
import { registerCgmTools } from "./tools/cgm-tools.js";
|
|
10
|
+
function createServer() {
|
|
11
|
+
const server = new McpServer({ name: SERVER_NAME, version: SERVER_VERSION });
|
|
12
|
+
registerCgmTools(server);
|
|
13
|
+
return server;
|
|
14
|
+
}
|
|
15
|
+
async function runStdio() {
|
|
16
|
+
const server = createServer();
|
|
17
|
+
const transport = new StdioServerTransport();
|
|
18
|
+
await server.connect(transport);
|
|
19
|
+
}
|
|
20
|
+
async function runHttp() {
|
|
21
|
+
const app = express();
|
|
22
|
+
const host = process.env.WELLNESS_CGM_HOST ?? DEFAULT_HOST;
|
|
23
|
+
const port = Number(process.env.WELLNESS_CGM_PORT ?? DEFAULT_PORT);
|
|
24
|
+
const allowedOrigin = process.env.WELLNESS_CGM_ALLOWED_ORIGIN ?? `http://${host}:${port}`;
|
|
25
|
+
app.use(express.json({ limit: "1mb" }));
|
|
26
|
+
app.use(cors({ origin: allowedOrigin }));
|
|
27
|
+
app.get("/health", (_req, res) => res.json({ ok: true, name: SERVER_NAME, version: SERVER_VERSION }));
|
|
28
|
+
app.post("/mcp", async (req, res) => {
|
|
29
|
+
const server = createServer();
|
|
30
|
+
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined, enableJsonResponse: true });
|
|
31
|
+
res.on("close", () => {
|
|
32
|
+
transport.close().catch(() => undefined);
|
|
33
|
+
server.close().catch(() => undefined);
|
|
34
|
+
});
|
|
35
|
+
try {
|
|
36
|
+
await server.connect(transport);
|
|
37
|
+
await transport.handleRequest(req, res, req.body);
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
console.error("MCP HTTP request failed:", error);
|
|
41
|
+
if (!res.headersSent) {
|
|
42
|
+
res.status(500).json({ jsonrpc: "2.0", error: { code: -32603, message: "Internal server error" }, id: null });
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
app.listen(port, host, () => {
|
|
47
|
+
console.error(`${SERVER_NAME} HTTP transport listening on http://${host}:${port}/mcp`);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
const args = process.argv.slice(2);
|
|
51
|
+
if (args.includes("--http")) {
|
|
52
|
+
await runHttp();
|
|
53
|
+
}
|
|
54
|
+
else if (isCliCommand(args)) {
|
|
55
|
+
const code = await runCliCommand(args);
|
|
56
|
+
process.exit(code);
|
|
57
|
+
}
|
|
58
|
+
else if (args.length === 0 || args.includes("--stdio")) {
|
|
59
|
+
await runStdio();
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
console.error([
|
|
63
|
+
`${SERVER_NAME} v${SERVER_VERSION}`,
|
|
64
|
+
"",
|
|
65
|
+
"Usage:",
|
|
66
|
+
" wellness-cgm Run MCP stdio server (default).",
|
|
67
|
+
" wellness-cgm --http Run MCP Streamable HTTP server.",
|
|
68
|
+
" wellness-cgm doctor Local DX checks.",
|
|
69
|
+
" wellness-cgm status Show current configuration.",
|
|
70
|
+
" wellness-cgm setup [client] Print MCP client config snippet.",
|
|
71
|
+
" wellness-cgm authorize Print Dexcom OAuth authorize URL.",
|
|
72
|
+
" wellness-cgm exchange <code> Swap an auth code for an access_token.",
|
|
73
|
+
].join("\n"));
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { buildCapabilities } from "./capabilities.js";
|
|
2
|
+
import { buildPrivacyAudit } from "./privacy-audit.js";
|
|
3
|
+
export type CgmAgentClient = "claude" | "codex" | "cursor" | "windsurf" | "hermes" | "openclaw" | "generic";
|
|
4
|
+
export interface CgmAgentManifest {
|
|
5
|
+
name: string;
|
|
6
|
+
version: string;
|
|
7
|
+
client: string;
|
|
8
|
+
supported_clients: CgmAgentClient[];
|
|
9
|
+
install: {
|
|
10
|
+
command: string;
|
|
11
|
+
args: string[];
|
|
12
|
+
optional_env: string[];
|
|
13
|
+
};
|
|
14
|
+
recommended_first_calls: string[];
|
|
15
|
+
tools: ReadonlyArray<string>;
|
|
16
|
+
resources: string[];
|
|
17
|
+
agent_rules: string[];
|
|
18
|
+
community: {
|
|
19
|
+
repo: string;
|
|
20
|
+
issues: string;
|
|
21
|
+
twitter: string;
|
|
22
|
+
docs: string;
|
|
23
|
+
invite: string;
|
|
24
|
+
};
|
|
25
|
+
capabilities: ReturnType<typeof buildCapabilities>;
|
|
26
|
+
privacy: ReturnType<typeof buildPrivacyAudit>;
|
|
27
|
+
}
|
|
28
|
+
export declare function buildAgentManifest(client?: CgmAgentClient): CgmAgentManifest;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { PINNED_NPM_PACKAGE, SERVER_NAME, SERVER_VERSION } from "../constants.js";
|
|
2
|
+
import { buildCapabilities } from "./capabilities.js";
|
|
3
|
+
import { buildPrivacyAudit } from "./privacy-audit.js";
|
|
4
|
+
const SUPPORTED_CLIENTS = [
|
|
5
|
+
"claude",
|
|
6
|
+
"codex",
|
|
7
|
+
"cursor",
|
|
8
|
+
"windsurf",
|
|
9
|
+
"hermes",
|
|
10
|
+
"openclaw",
|
|
11
|
+
"generic",
|
|
12
|
+
];
|
|
13
|
+
const TOOLS = [
|
|
14
|
+
"cgm_agent_manifest",
|
|
15
|
+
"cgm_capabilities",
|
|
16
|
+
"cgm_connection_status",
|
|
17
|
+
"cgm_privacy_audit",
|
|
18
|
+
"cgm_data_inventory",
|
|
19
|
+
"cgm_glucose_now",
|
|
20
|
+
"cgm_glucose_window",
|
|
21
|
+
"cgm_daily_summary",
|
|
22
|
+
"cgm_meal_response",
|
|
23
|
+
"cgm_authorize_url",
|
|
24
|
+
];
|
|
25
|
+
export function buildAgentManifest(client = "generic") {
|
|
26
|
+
return {
|
|
27
|
+
name: SERVER_NAME,
|
|
28
|
+
version: SERVER_VERSION,
|
|
29
|
+
client,
|
|
30
|
+
supported_clients: SUPPORTED_CLIENTS,
|
|
31
|
+
install: {
|
|
32
|
+
command: "npx",
|
|
33
|
+
args: ["-y", PINNED_NPM_PACKAGE],
|
|
34
|
+
optional_env: [
|
|
35
|
+
"DEXCOM_ENV",
|
|
36
|
+
"DEXCOM_CLIENT_ID",
|
|
37
|
+
"DEXCOM_CLIENT_SECRET",
|
|
38
|
+
"DEXCOM_REDIRECT_URI",
|
|
39
|
+
"DEXCOM_ACCESS_TOKEN",
|
|
40
|
+
"DEXCOM_REFRESH_TOKEN",
|
|
41
|
+
"WELLNESS_CGM_LOCAL_DIR",
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
recommended_first_calls: ["cgm_connection_status", "cgm_capabilities", "cgm_glucose_now"],
|
|
45
|
+
tools: TOOLS,
|
|
46
|
+
resources: [
|
|
47
|
+
"wellness-cgm-mcp://agent-manifest",
|
|
48
|
+
"wellness-cgm-mcp://capabilities",
|
|
49
|
+
"wellness-cgm-mcp://privacy-audit",
|
|
50
|
+
],
|
|
51
|
+
agent_rules: [
|
|
52
|
+
"Call cgm_connection_status before any provider-backed tool.",
|
|
53
|
+
"Without DEXCOM_ACCESS_TOKEN, all glucose tools return mock data — clearly tagged.",
|
|
54
|
+
"Surface the time-in-range profile being used (diabetic 70-180 vs metabolic-health 70-140).",
|
|
55
|
+
"When meal_response band is 'poor', cross-reference the meal in wellness-nourish and suggest a swap.",
|
|
56
|
+
"Treat CGM data as medical-record sensitive. Defer insulin / medication dosing to clinician.",
|
|
57
|
+
"Never claim diagnostic accuracy.",
|
|
58
|
+
],
|
|
59
|
+
community: {
|
|
60
|
+
repo: "https://github.com/davidmosiah/wellness-cgm-mcp",
|
|
61
|
+
issues: "https://github.com/davidmosiah/wellness-cgm-mcp/issues",
|
|
62
|
+
twitter: "https://x.com/delx369",
|
|
63
|
+
docs: "https://wellness.delx.ai/connectors/cgm",
|
|
64
|
+
invite: "If wellness-cgm-mcp helps your agent close the metabolic-health loop, a star on the repository increases visibility for other AI builders.",
|
|
65
|
+
},
|
|
66
|
+
capabilities: buildCapabilities(),
|
|
67
|
+
privacy: buildPrivacyAudit(),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=agent-manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-manifest.js","sourceRoot":"","sources":["../../src/services/agent-manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,kBAAkB,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACpG,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAIvD,MAAM,iBAAiB,GAAqB;IAC1C,QAAQ;IACR,OAAO;IACP,QAAQ;IACR,UAAU;IACV,QAAQ;IACR,UAAU;IACV,SAAS;CACV,CAAC;AAEF,MAAM,KAAK,GAAG;IACZ,oBAAoB;IACpB,kBAAkB;IAClB,uBAAuB;IACvB,mBAAmB;IACnB,oBAAoB;IACpB,iBAAiB;IACjB,oBAAoB;IACpB,mBAAmB;IACnB,mBAAmB;IACnB,mBAAmB;CACX,CAAC;AAiBX,MAAM,UAAU,kBAAkB,CAAC,SAAyB,SAAS;IACnE,OAAO;QACL,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,cAAc;QACvB,MAAM;QACN,iBAAiB,EAAE,iBAAiB;QACpC,OAAO,EAAE;YACP,OAAO,EAAE,KAAK;YACd,IAAI,EAAE,CAAC,IAAI,EAAE,kBAAkB,CAAC;YAChC,YAAY,EAAE;gBACZ,YAAY;gBACZ,kBAAkB;gBAClB,sBAAsB;gBACtB,qBAAqB;gBACrB,qBAAqB;gBACrB,sBAAsB;gBACtB,wBAAwB;aACzB;SACF;QACD,uBAAuB,EAAE,CAAC,uBAAuB,EAAE,kBAAkB,EAAE,iBAAiB,CAAC;QACzF,KAAK,EAAE,KAAK;QACZ,SAAS,EAAE;YACT,mCAAmC;YACnC,iCAAiC;YACjC,kCAAkC;SACnC;QACD,WAAW,EAAE;YACX,6DAA6D;YAC7D,mFAAmF;YACnF,4FAA4F;YAC5F,qGAAqG;YACrG,6FAA6F;YAC7F,kCAAkC;SACnC;QACD,SAAS,EAAE;YACT,IAAI,EAAE,iDAAiD;YACvD,MAAM,EAAE,wDAAwD;YAChE,OAAO,EAAE,uBAAuB;YAChC,IAAI,EAAE,yCAAyC;YAC/C,MAAM,EACJ,4IAA4I;SAC/I;QACD,YAAY,EAAE,iBAAiB,EAAE;QACjC,OAAO,EAAE,iBAAiB,EAAE;KAC7B,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type CgmProvider } from "../constants.js";
|
|
2
|
+
export interface CgmCapabilities {
|
|
3
|
+
providers: ReadonlyArray<CgmProvider>;
|
|
4
|
+
configured: ReadonlyArray<CgmProvider>;
|
|
5
|
+
metrics: ReadonlyArray<string>;
|
|
6
|
+
privacy_modes: ReadonlyArray<"summary" | "structured" | "raw">;
|
|
7
|
+
notes: string[];
|
|
8
|
+
}
|
|
9
|
+
export declare function buildCapabilities(): CgmCapabilities;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { SUPPORTED_PROVIDERS } from "../constants.js";
|
|
2
|
+
export function buildCapabilities() {
|
|
3
|
+
const configured = [];
|
|
4
|
+
if (process.env.DEXCOM_ACCESS_TOKEN)
|
|
5
|
+
configured.push("dexcom");
|
|
6
|
+
// libre = LibreLink Up (community proxy); not in v0.1
|
|
7
|
+
return {
|
|
8
|
+
providers: SUPPORTED_PROVIDERS,
|
|
9
|
+
configured,
|
|
10
|
+
metrics: [
|
|
11
|
+
"current_glucose_mgdl",
|
|
12
|
+
"trend_arrow",
|
|
13
|
+
"time_in_range_70_180_pct",
|
|
14
|
+
"time_in_range_70_140_pct",
|
|
15
|
+
"mean_glucose_mgdl",
|
|
16
|
+
"median_glucose_mgdl",
|
|
17
|
+
"gmi_pct",
|
|
18
|
+
"cv_pct",
|
|
19
|
+
"meal_response_band",
|
|
20
|
+
],
|
|
21
|
+
privacy_modes: ["summary", "structured", "raw"],
|
|
22
|
+
notes: [
|
|
23
|
+
"Dexcom v0.1 ships against the official Developer API (sandbox + production).",
|
|
24
|
+
"FreeStyle Libre via LibreLink Up community proxy is roadmapped for v0.2.",
|
|
25
|
+
"Without a Dexcom token, all glucose tools return mock readings so agents can demo the surface.",
|
|
26
|
+
"Time-in-range uses two profiles: ADA diabetic (70-180 mg/dL) AND Levels-style metabolic-health (70-140 mg/dL).",
|
|
27
|
+
"GMI (estimated A1C) is computed via Bergenstal 2018 formula: 3.31 + 0.02392 × mean(mg/dL).",
|
|
28
|
+
],
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=capabilities.js.map
|