garmin-mcp
Enables users to analyze their own Garmin Connect data—activities, sleep, HRV, Body Battery, training readiness—directly inside Claude Desktop.
README
Garmin → Claude MCP server
Analyze your own Garmin Connect data — activities, sleep, HRV, Body Battery, training readiness — right inside Claude Desktop.
A fully local, read-only MCP server that lets you analyze your own Garmin Connect data in Claude Desktop. The server runs as a local child process of Claude Desktop and talks to it over stdio.
Guiding principles
- 100% local — nothing is hosted or exposed to the network (stdio).
- Read-only — there is no write endpoint to Garmin.
- No third party — the only network contact is Garmin. No telemetry, no external logging.
- Passwordless at runtime — the password is never stored; after the one-time login only local tokens are used.
Requirements
- Node.js ≥ 20 (tested with Node 22 LTS), npm.
- A Garmin Connect account.
Setup (4 steps)
# 1) Install dependencies
npm install
# 2) Milestone test: does the client get through Garmin's Cloudflare protection?
npm run smoke
# Expectation: "Status 200" and "Challenge: no".
# 3) One-time interactive login (prompts for email, hidden password, MFA if enabled)
npm run login
# Creates ~/.garmin-mcp/tokens.json (mode 0600). The password is NOT stored.
# 4) Compile TypeScript to build/
npm run build
Connecting Claude Desktop
Claude Desktop → Settings → Developer → Edit Config. Add the server with its
absolute path (claude_desktop_config.json):
{
"mcpServers": {
"garmin": {
"command": "node",
"args": ["/absolute/path/to/garmin-mcp/build/mcp/server.js"]
}
}
}
Then restart Claude Desktop. Try it in chat, e.g.:
"Am I logged in to Garmin? Use
whoami." "Show me my daily summary and my recent activities."
Available tools
13 read-only tools. Date parameters are YYYY-MM-DD (default = today); ranges
default to the last ~4 weeks. Several tools take a metrics[] / include[] selector,
so one tool covers many data types (this keeps the tool list small for good tool
selection). Long time series in responses are truncated to stay compact.
| Tool | Description |
|---|---|
whoami |
Connection check + account profile |
get_daily_health |
Daily wellness for a date — metrics[]: summary, sleep, stress, heart_rate, hrv, spo2, respiration, hydration, steps, floors, intensity_minutes, body_battery, body_battery_events, stats_and_body |
get_training |
Training for a date — metrics[]: readiness, morning_readiness, status, vo2max, fitness_age |
get_fitness |
Fitness/performance — metrics[]: race_predictions, cycling_ftp, lactate_threshold, personal_records, endurance_score, hill_score, resting_heart_rate, weekly_intensity_minutes (startDate/endDate) |
get_weight |
Body weight & composition over a range (include_raw also returns weigh-ins) |
get_steps_history |
Step totals over a range — granularity: daily or weekly |
get_activity |
One activity by activityId — include[]: summary, splits, weather, details, hr_zones, exercise_sets |
list_activities |
Recent activities, or by date range/type (limit/startDate/endDate/type) |
get_devices |
Paired Garmin devices |
get_user_profile |
User profile & settings (units, preferences) |
get_goals |
Goals (status: active/future/past) |
get_workouts |
Saved workouts (limit) |
get_scheduled_workouts |
Scheduled workouts / calendar (year/month) |
How it works
The server registers each Garmin data method as an MCP tool and talks to Claude
Desktop over stdio. Every call goes through a generic connectapi() request to
connectapi.garmin.com with a bearer token, sent via
cycletls so the TLS fingerprint looks
like a real browser (Garmin sits behind Cloudflare).
Authentication happens once (npm run login): a login cascade — mobile iOS JSON
login first, the classic widget/CSRF flow as fallback — yields a CAS service ticket,
which is exchanged in Garmin's DI-OAuth2 flow for an access + refresh token. The
tokens are cached locally and the access token is refreshed automatically on expiry;
your password is never stored.
Security model
- The password is never stored — it is only used for the one-time login.
- Tokens live at
~/.garmin-mcp/tokens.jsonwith file mode0600(only you can read them). - The access token is renewed automatically via the refresh token when it expires.
- Read-only: there is no code path that changes anything on Garmin.
- No telemetry, no external logging. Diagnostics go to
stderronly (never over the stdio MCP channel).
Architecture (layers)
src/http/impersonate.ts— TLS impersonation viacycletls(JA3 fingerprint) + cookie jar, so Garmin's bot protection lets us through.src/http/smoke.ts— milestone test againstsso/embed.src/garmin/auth.ts— login cascade (mobile iOS JSON login → widget/CSRF fallback) + MFA + DI-OAuth2 ticket exchange + refresh.src/garmin/tokens.ts— local token cache (0600).src/garmin/client.ts— genericconnectapi()+ typed, read-only data methods.src/mcp/server.ts— MCP server, tool registration, stdio transport.src/cli/login.ts— one-time interactive login.
Maintenance & risks (named honestly)
This uses Garmin's unofficial internal API, ported from the open-source Python
package python-garminconnect.
Expected breakage points and how to fix them:
- Unofficial API — endpoint paths can change. On errors, compare the paths in
src/garmin/client.tsagainst the currentpython-garminconnectsource. - JA3 / User-Agent — on a
403or a "Just a moment …" Cloudflare page, update the JA3/User-Agent pair insrc/http/impersonate.tsto a current Chrome (both together). - Client ids — the DI-OAuth2 client ids (
GARMIN_CONNECT_MOBILE_ANDROID_DI_*) rotate; update the list insrc/garmin/auth.tson auth errors. garthis discontinued — the previously common auth librarygarthis no longer maintained; the authoritative reference ispython-garminconnect.- Keep the token folder private —
~/.garmin-mcp/holds valid session tokens; don't share it, don't commit it. - Terms of service — access via the unofficial API may be in tension with Garmin's terms of service. Intended for private personal use with your own data.
Troubleshooting
403or a "Just a moment …" page — Garmin's Cloudflare is blocking the TLS fingerprint. Update the JA3/User-Agent pair insrc/http/impersonate.tsto a current Chrome (both together), then re-runnpm run smoke.🔒 Not logged in/ session expired — runnpm run loginagain.⏳rate limited (HTTP 429) — Garmin is throttling; wait a bit and retry.- First run is slow / cycletls errors —
cycletlsdownloads a small Go helper binary on first use; make sure it can execute and isn't blocked by the OS.
Re-login
If tools report 🔒 Not logged in (token expired/invalid):
npm run login
License
MIT © Marcel Fortmann
Disclaimer
This is an unofficial, community project. It is not affiliated with, endorsed by, or sponsored by Garmin. Garmin® and Garmin Connect™ are trademarks of Garmin Ltd. or its subsidiaries.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。