garmin-mcp

garmin-mcp

This MCP server exposes Garmin Connect health data—sleep, heart rate, HRV, stress, VO2max, and activities—to Claude through local tools, enabling natural language queries, data syncing, and statistical analysis like correlations and night-out detection.

Category
访问服务器

README

garmin-mcp

A local MCP server that exposes Garmin Connect data — sleep, heart rate, HRV, stress, VO2max, activities — to Claude, plus the statistical analysis built on top of it.

It was written to answer one question: does how hard I train change how well I sleep? The short answer, on a year of one person's data, is no. What the analysis actually turned up is in Findings.

The data stays on your machine. Nothing is uploaded anywhere; the server runs as a local subprocess and reads a SQLite file in your home directory.

What it does

sync_garmin_data pulls a date range from Garmin Connect into a local SQLite cache (~/.garmin-mcp/data.db). Everything else reads from that cache, so queries are fast and you are not re-hitting Garmin's API — which rate-limits aggressively — every time you ask a question.

Tools

Tool What it returns
sync_garmin_data(start, end, force=False) Fetches and caches. Commits per day, so a failure partway through keeps what it already wrote. Skips days already synced unless force.
get_daily_summary(date) Everything for one day, merged
get_range_summary(start, end) The same across a range
get_sleep / get_activities / get_hrv / get_vo2max / get_stress One table each
detect_nights_out(start, end) Nights flagged as "was out late", inferred from physiology alone
log_night_out(date, note) Manual override
correlate(metric_a, metric_b, start, end) Spearman correlation between two metrics
moving_average(metric, window, start, end) Rolling mean

detect_nights_out compares four signals — bedtime, sleep duration, resting heart rate and HRV — against your own 30-day baseline, and flags a night when at least two of them deviate. It knows nothing about the calendar.

Setup

python3 -m venv venv && source venv/bin/activate    # Python 3.10+
pip install -r requirements.txt
cp .env.example .env                                 # then fill in your credentials
pytest -v

.env is gitignored. Credentials are read from the environment; they are never written to the database or logged.

Register with Claude Code

claude mcp add garmin -s user \
  -- /absolute/path/to/garmin-mcp/venv/bin/python /absolute/path/to/garmin-mcp/server.py

Restart Claude Code, then ask it to sync a range and query away.

No credentials go on that command line. server.py loads the .env sitting next to it, anchored to the script's own path rather than the working directory — an MCP client launches the server with a CWD you do not control. Passing -e GARMIN_PASSWORD=… would work too, but it writes your password in plaintext into the client's config file, so prefer the .env.

Register with Claude Desktop

Add to claude_desktop_config.json:

{
  "mcpServers": {
    "garmin": {
      "command": "/absolute/path/to/garmin-mcp/venv/bin/python",
      "args": ["/absolute/path/to/garmin-mcp/server.py"]
    }
  }
}

Credentials come from .env, for the same reason as above.

Both use stdio: the client launches the server as a subprocess and talks to it over stdin/stdout. No port is opened and no authentication layer is needed, because both processes run as you on your own machine. MCP also supports an HTTP transport, which you would need only to reach the server from another machine — not the case here.

Findings

One person, 349 nights, 175 workouts. Full write-up with charts: the analysis.

  • Training intensity barely touches sleep. Spearman ρ = −0.18 against the following night's sleep score — statistically detectable, but it explains 3% of the variance. Splitting by workout type (zone 2, threshold, HIIT, etc.) makes even that disappear (Kruskal-Wallis p = 0.58).
  • Bedtime is what moves the needle — ρ = −0.68 — but the mechanism is mundane: with a fixed alarm, a later bedtime is less sleep. The two are collinear at ρ = −0.945, so they cannot be separated statistically.
  • The threshold is worth knowing anyway. Crossing midnight (personal to a 06:40 wake time) takes the share of nights scoring under 60 from 1% to 29%.
  • The night-out detector rediscovered the weekend on its own. It flagged 37% of Fridays and 2% of Sundays (odds ratio 6.4, p = 4·10⁻⁸) using only heart rate, HRV, and sleep timing.

Three bugs worth knowing about

If you build something similar against the Garmin API, these will bite you:

  1. sleepStartTimestampLocal already has the UTC offset applied. Reading it with datetime.fromtimestamp() applies your machine's offset a second time — every bedtime lands 1h late in winter and 2h in summer.
  2. A sleep record is labelled with the wake-up date, not the evening you went to bed. Pair training on day D with sleep[D+1], or you will correlate a workout with the sleep that came before it.
  3. bodyBatteryMostRecentValue is the end-of-day reading, not the day's peak. That is bodyBatteryHighestValue.

A fourth mistake was mine, not the API's, and is the one I would warn hardest about: I concluded that bedtime mattered independently of hours slept, based on a partial correlation. With a fixed wake time those two variables are near-identical, and partial correlation in that regime is unstable. Every statistical check passed. The common-sense objection — "if I always get up at 6:40, how can going to bed later not mean less sleep?" — did not.

Layout

server.py           MCP tools (FastMCP, stdio)
garmin_client.py    Garmin API wrapper + response parsing
db.py               SQLite schema and queries
stats.py            Pure functions: correlation, moving average, night-out signals
analysis/           Standalone analysis scripts; report.html is the write-up
tests/              Unit tests, no network or credentials required

The analysis scripts read your local database and print to stdout. They are research code, kept as they were run, and the docstring at the top of each says what it was trying to find out.

Notes and limits

  • Uses garminconnect, which talks to Garmin's private API. It is not an officially supported interface and can break when Garmin changes something.
  • Garmin rate-limits by IP. Sync a long range in one go rather than in many small calls, and expect 429s if you retry too quickly.
  • Sleep score, stress, and body battery are Garmin's own algorithms, not measurements. Stress in particular is derived from HRV, so correlating the two mostly measures Garmin's formula rather than your physiology.
  • n = 1. These findings are one person's; the code is what generalises.

Licence

MIT

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选