Local-first Garmin data warehouse with an analysis-grade MCP server. Sync once, analyze forever - even when the API is down.
garmin-local-mcp
以下摘录自该仓库的 README(片段) · 在 GitHub 读完整 README →
**Local-first Garmin data warehouse with an analysis-grade MCP server.**
Sync once, analyze forever, even when the API is down.
Try it without a Garmin account
If you don't own a Garmin, or just want to see what the tools return before
handing over credentials, seed a synthetic store:
That generates 180 days across every table, then serves them over MCP. No
login, no network, no account.
The data is generated rather than recorded, but it is not random. A latent
recovery factor drives HRV up while resting heart rate goes down, training
load raises the *next* day's resting heart rate, a six-day illness window sits
in the middle of the range, and a few sleep nights are deliberately missing.
Sleep timing carries a planted chronotype (MCTQ MSFsc 03:45, with alarm-pinned
work mornings and later, longer free nights), and the sleep score drops with
distance from that natural wake time. So the analysis tools have something real
to find. Over the full 180 days (exact figures shift a little with the end
date, because training days depend on the weekday):
| Ask | Returns |
|---|---|
| correlate(hrv, resting_hr) | −0.4 to −0.6, a genuine inverse relationship |
| correlate(training_load, resting_hr, scan_lags=True) | near 0 at lag 0, **+0.4 to +0.65 at lag 1** (90 to 100 training days, significant after correcting for the 15 lags scanned): the effect is next-day |
| anomalies() | the illness window, flagged across resting HR, HRV, skin temperature, sleep score and stress at once |
| gaps() | the missing sleep nights |
| circadian() | MSFsc within 10 minutes of the planted 03:45, a circadian wake around 07:45, and evidence supportive (the most that two holdout folds allow) |
The analysis tools default to the last 30 days, which misses the illness
window, so ask about the whole range (demo prints it). On a short window a
lag scan has little to work with; correlate says so in its note when the
strongest lag is not significant once all 15 lags are accounted for.
sync_status reports demo_store: true on these stores, so an assistant can
never present generated numbers as real measurements. The generator is
deterministic — --seed reproduces a store exactly, and --days changes the
range. demo refuses to overwrite a database it did not generate.
Why another Garmin MCP?
Most Garmin MCP servers are thin live wrappers around Garmin's unofficial API.
Every question your AI assistant asks becomes one or more live API calls
returning large raw JSON blobs (a single raw sleep response runs around 230 KB),
which makes multi-month questions like "how does my sleep correlate with
training load?" expensive to ask.
That design is no longer universal. Since Garmin's auth change in March 2026
broke the ecosystem for several weeks, a number of projects have added local
storage, and the largest server computes training-load and HRV trends
server-side. Data ownership and server-side analysis are both crowded ground
now. Three things are not:
- **Sleep-timing advice that is tested before it is given.** circadian
estimates your chronotype from your watch's sleep times (the Munich
Chronotype Questionnaire's sleep-debt-corrected mid-sleep) and the wake time
that fits it, with an 80% interval on each and a check of how much the answer
moves under other reasonable settings. Then it asks whether waking near that
time actually went with higher Garmin scores on nights the model never saw,
beyond what sleep duration and schedule regularity explain, and says plainly
when it didn't. Chronotype on its own is not new: other tools, including at
least one MCP server, report MCTQ-style chronotype from wearable sleep data.
I have not found another that puts uncertainty on the answer or validates it
out of sample. See Sleep timing and chronotype.
- **Ingest that needs no login.** A standalone decoder for Garmin's undocumented
wellness FIT messages (sleep score, HRV, skin temperature, sleep stages, naps)
reads manually exported bundles with no credentials at all. Other servers
parse FIT *activity* files; I have not found another that decodes the wellness
export. It is the only ingest path here that keeps working when Garmin auth