garmin-mcp

garmin-mcp

Enables users to analyze their own Garmin Connect data—activities, sleep, HRV, Body Battery, training readiness—directly inside Claude Desktop.

Category
Visit Server

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 activityIdinclude[]: 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.json with file mode 0600 (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 stderr only (never over the stdio MCP channel).

Architecture (layers)

  • src/http/impersonate.ts — TLS impersonation via cycletls (JA3 fingerprint) + cookie jar, so Garmin's bot protection lets us through.
  • src/http/smoke.ts — milestone test against sso/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 — generic connectapi() + 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.ts against the current python-garminconnect source.
  • JA3 / User-Agent — on a 403 or a "Just a moment …" Cloudflare page, update the JA3/User-Agent pair in src/http/impersonate.ts to a current Chrome (both together).
  • Client ids — the DI-OAuth2 client ids (GARMIN_CONNECT_MOBILE_ANDROID_DI_*) rotate; update the list in src/garmin/auth.ts on auth errors.
  • garth is discontinued — the previously common auth library garth is no longer maintained; the authoritative reference is python-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

  • 403 or a "Just a moment …" page — Garmin's Cloudflare is blocking the TLS fingerprint. Update the JA3/User-Agent pair in src/http/impersonate.ts to a current Chrome (both together), then re-run npm run smoke.
  • 🔒 Not logged in / session expired — run npm run login again.
  • rate limited (HTTP 429) — Garmin is throttling; wait a bit and retry.
  • First run is slow / cycletls errorscycletls downloads 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.

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured