garmin-unofficial-mcp

garmin-unofficial-mcp

A local, read-only MCP server that gives Claude access to your Garmin health and training data, exposing tools for health snapshots, training status, run details, body metrics, and training analysis.

Category
Visit Server

README

garmin-unofficial-mcp

<!-- Banner is an AI-generated image; it is decorative and carries no data. --> garmin-unofficial-mcp

CI Python 3.12+ License: MIT

A local, single-user, read-only MCP server that gives Claude access to your own Garmin health and training data. It exposes composite tools — health snapshot, training status, recent runs, run detail, body metrics and training analysis — so Claude can ground recovery advice and workout recommendations in your actual sleep, HRV, training load and run history instead of guessing. Everything Garmin returns is cached day-by-day in a local SQLite database, so repeated questions don't repeatedly hit Garmin's servers.

Before you start

This uses the unofficial garminconnect library, which signs in with your Garmin password and MFA code — not OAuth. There is no scoped, read-only Garmin grant available to third-party apps today, so the token this stores is full-account access. It is stored only on your own machine.

The server never writes to your Garmin account: no set_, add_, delete_, upload_, import_, create_ or remove_ call exists anywhere in it, deliberately.

See docs/privacy.md for exactly what is stored and where.

Requirements

  • Python 3.12 or newer
  • A Garmin Connect account
  • Optionally, the claude CLI, so install.sh can register the server for you

Install

git clone https://github.com/danielsuri/garmin-unofficial-mcp.git
cd garmin-unofficial-mcp
./install.sh
.venv/bin/garmin-unofficial-mcp login you@example.com
.venv/bin/garmin-unofficial-mcp doctor

login prompts for your password and, if enabled, an MFA code. It needs a real terminal — getpass fails without a TTY. doctor then checks that the stored tokens are accepted, that a live call works, and that the cache directory is writable.

install.sh creates .venv, installs the package into it, and finds the claude CLI to register the server at user scope if found. It's idempotent — safe to re-run any time; it never clobbers an existing venv or registration it doesn't own. Pass --dev to also install the test extras, --dry-run to preview every action without doing it, or --help for the full rundown.

On success, tokens are written to ~/.garminconnect. They're good for roughly a year; after that (or if Garmin invalidates them) re-run login.

Check the install

.venv/bin/garmin-unofficial-mcp doctor

doctor checks three things: that the stored tokens are accepted, that a live call to Garmin actually succeeds, and that the cache directory (~/.garmin-unofficial-mcp/) is writable. Healthy output looks like:

auth: OK (tokenstore accepted)
live call: OK (userProfileId=12345678)
cache path: OK (/Users/you/.garmin-unofficial-mcp/cache.db)

Any FAIL line points at what to fix — usually re-running login if auth fails, or a permissions problem if the cache path fails.

Register with Claude Code

install.sh does this automatically when it can find the claude CLI. To do it by hand instead (or if it couldn't find claude):

claude mcp add -s user garmin-unofficial -- <your/install/dir>/garmin-unofficial-mcp/.venv/bin/garmin-unofficial-mcp serve

This runs the server over stdio. Once registered, a fresh Claude Code session will have the tools below available — MCP servers are loaded at startup, so the session you register from won't see them.

-s user matters: without it claude mcp add defaults to local scope, and the tools appear only when you're working inside this repo. User scope makes them available in every project, which is what you want for a personal health server.

Note the registration is an absolute path into this repo's venv, so moving or deleting the repo breaks it (re-run install.sh, or the command above, after moving it). Tokens (~/.garminconnect) and the cache (~/.garmin-unofficial-mcp/cache.db) live outside the repo and are unaffected by scope.

The tools

  • get_health_snapshot(days=7) — sleep (total/deep/light/REM/awake minutes, sleep score), HRV, body battery (the day's true high and low), resting heart rate, and stress over the window, plus rolling baselines. Use this to judge how recovered you are.
  • get_training_status(weeks=4) — acute and chronic training load, the acute:chronic load ratio, VO2max, and training readiness score per day. Use this to judge whether training load is ramping safely.
  • get_recent_runs(n=10) — your last n runs with pace (average, moving, grade-adjusted, best), heart-rate zones, training effect and training load, VO2max, fastest splits, PR flag, calories, body-battery cost and elevation, plus weekly mileage rollups and race-time predictions. Use this to spot trends and prescribe a specific run.
  • get_run_detail(activity_id=None) — one run in full: everything get_recent_runs reports for that run, plus running dynamics (ground contact time and balance, vertical oscillation and ratio, stride length), power (average/normalized/max watts and power zones), respiration (average/max/min breaths per minute), min/max temperature, lap count, step count, activity name, and run/walk/idle time detection (run_walk: seconds spent running vs. walking vs. stopped, from Garmin's typed splits). Omit activity_id for the most recent run found in the last 56 days. Use this to deep-dive a specific run. Costs one extra Garmin API call beyond get_recent_runs (typed splits, for run/walk detection) — each of the other fields comes from the same activity record get_recent_runs already fetches.
  • get_body_metrics(days=30) — weight, body fat, steps, calories, floors climbed, and intensity minutes, with simple weight and step trends.
  • get_training_analysis(weeks=8) — the marathon-block view: within-run form indicators (efficiency factor, aerobic decoupling, ground-contact-time drift, duty factor, split pattern) for every run in the window, plus across-block trends (weekly mileage, ramp rate with the base it was computed from, HR-zone polarization, efficiency and decoupling trend, recovery coupling, VO2max trend). It computes; it does not advise. Costs two extra Garmin API calls per run it hasn't seen before (laps and typed splits), both cached permanently.

Arguments are clamped to a range the server can actually serve — days to 1–365, weeks to 1–52, n to 1–100, activity_id to a non-negative 64-bit value — so an out-of-range value narrows or widens to the nearest bound (or, for an activity_id that matches nothing, yields a clean all-null result) rather than failing.

Every tool returns a partial: true flag when it couldn't be certain all of its data is fresh or complete — see Troubleshooting below.

Testing

.venv/bin/pytest

The suite covers the MCP tools, cache, fetcher and assemblers with fake Garmin clients — no network, no real account required.

Troubleshooting

  • Auth errors / "Garmin rejected the stored tokens" — your tokens expired or were revoked. Re-run garmin-unofficial-mcp login you@example.com from a real terminal.
  • partial: true in a tool's response — Garmin rate-limited a request or a connection failed transiently. The tool still returns whatever it has cached; treat the numbers as possibly incomplete or stale and try again later rather than assuming Garmin has no data.
  • Weight fields are null — this is normal on any day without a weigh-in. weight_kg, body_fat_pct, and the weight trend fields in get_body_metrics only populate on days you actually stepped on a connected scale.

Licence

MIT — see LICENSE.

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