garmin-mcp

garmin-mcp

A local MCP server that exposes your Garmin Connect data—sleep, HRV, training readiness, workouts, and more—to any MCP-compatible AI assistant. Runs entirely on your machine and keeps your Garmin credentials private.

Category
Visit Server

README

Garmin MCP

Your Garmin Connect data — sleep, HRV, recovery, training load, workouts — available to any AI that speaks MCP. Runs entirely on your own machine. Your Garmin credentials never leave it.

Works with Claude, ChatGPT, Perplexity, Le Chat, Cursor, Zed, or anything else that supports MCP. No subscription, no third-party server.


Setup

Open Terminal (press ⌘+Space, type "Terminal", hit Enter) and paste this:

cd ~/Downloads && git clone https://github.com/aayush-lunawat/garmin-mcp.git && cd garmin-mcp && bash install.sh

That installs the dependencies, signs you in to Garmin, verifies the connection and prints the config block to paste into your AI app. It is safe to re-run — it skips the sign-in step if you're already signed in.

If you'd rather see each step, or you want to know what the prompts mean before you hit them, SETUP.md is the same process written out in plain language with the failure modes explained.

<details> <summary>Or run the four steps by hand</summary>

1. Go to this folder. Don't type a path by hand — let Finder supply it:

  1. Type cd followed by one space. Don't press Enter yet.
  2. Drag the garmin-mcp folder from Finder onto the Terminal window and let go. The full path appears by itself.
  3. Now press Enter.

The finished line looks something like cd /Users/you/Downloads/garmin-mcp — the exact path depends on where the folder actually is on your Mac.

If Terminal says no such file or directory, the path is wrong. Drag the folder in again rather than retyping it; that method can't produce a typo.

To confirm you're in the right place, run ls. You should see setup.sh, login.py, doctor.py and README.md listed.

2. Install. Creates a self-contained Python environment inside this folder and installs the two dependencies. Nothing is installed system-wide. If you don't have a recent enough Python, this tells you how to get one.

bash setup.sh

3. Sign in to Garmin. Asks for your Garmin email and password, plus a verification code if you have two-factor enabled. Your password is exchanged for an access token and then discarded — it is never written to disk. The token lands in ~/.garmin-mcp/tokens and lasts about a year.

.venv/bin/python login.py

4. Check it works. Calls every endpoint against your real account and reports what came back.

.venv/bin/python doctor.py

Then get the config block for your AI app:

.venv/bin/python doctor.py --config

In Claude Desktop: Settings → Developer → Edit Config, paste it in, save, restart Claude. Other apps take the same command and args.

Ask it "how did I sleep this week?" to confirm.

</details>


What you can ask

The tools are designed around questions, not endpoints:

  • "Should I run today?" → readiness, HRV vs. baseline, yesterday's load
  • "Review my training week" → activities with load, effect and HR zones
  • "Is my sleep getting worse?" → 14 days of stages, score and overnight HRV
  • "Why did Saturday feel so hard?" → that workout's splits against recovery
  • "Am I building fitness or just tired?" → training status and acute:chronic load

Tools

Tool Returns
get_readiness Training readiness score and its contributing factors
get_hrv Overnight HRV against your personal baseline
get_body_battery Charge, drain, peak and trough per day
get_stress All-day stress average and peak
get_sleep Duration, stages, score, SpO₂, respiration, overnight HRV
get_training_status Status, VO₂ max, load balance, acute:chronic ratio
get_activities Workouts by date range or the most recent N
get_activity_detail Lap splits and HR zones for one workout
get_daily_summary Steps, resting HR, calories, intensity minutes
get_body_composition Weight and composition from smart-scale weigh-ins
get_race_predictions Predicted 5K / 10K / half / marathon times
get_profile Connection check — whose account, which timezone
get_raw Diagnostic: unshaped response from any read endpoint
refresh Clear the 5-minute cache after a fresh watch sync

Date arguments accept YYYY-MM-DD, today, or yesterday. Most tools also take days=N for a trailing window. Everything is read-only — nothing in this server writes to your Garmin account.


How it's built

Three layers, separated so the fragile part is isolated from the stable part:

server.py     MCP tools and their descriptions.  Transport-specific.
   |
client.py     Fan-out, caching, error tolerance.  Reusable as-is.
   |
auth.py       Where credentials live.            Swap this to go multi-user.

auth.py is the only file that knows about token storage. It exposes get_client(user_id). A hosted version implements that same method against an encrypted per-user store, and client.py, shaping.py and dates.py carry over untouched. server.py changes one line — transport="stdio" becomes transport="streamable-http" — plus an auth middleware.

Two decisions worth knowing about:

Payloads are compacted before the model sees them. A raw Garmin sleep response is several thousand tokens, mostly nulls, internal IDs and per-minute movement arrays. shaping.py reduces it to about twenty fields. Three unshaped calls would otherwise crowd out the conversation.

Missing data stays missing. A field Garmin didn't send is absent from the output rather than defaulted to zero, because "no HRV reading" and "HRV of zero" mean very different things and a model will happily average the latter.


The fragility you should know about

Garmin has no public API for this data. Everything here talks to the same private endpoints the Garmin Connect website uses.

In March 2026 Garmin enabled Cloudflare TLS fingerprinting, which blocks ordinary HTTP clients. This broke garth, the library nearly every Garmin integration depended on, and it was deprecated as a result.

This server depends on python-garminconnect 0.3.x, which survived by rebuilding authentication on curl_cffi — it impersonates a real browser's TLS handshake and falls back across four different login strategies.

It works today. It is also cat-and-mouse: Garmin can tighten the check again. The same risk applies to every paid service in this space; the difference is that you can patch this one yourself. If logins start failing, check the python-garminconnect issues and update:

.venv/bin/pip install --upgrade garminconnect

Troubleshooting

"No Garmin tokens found" — run .venv/bin/python login.py.

Login fails with 429 or Cloudflare — Garmin is rate limiting. Wait 15–30 minutes. Retrying immediately extends the block.

A tool returns nothing — usually means your watch doesn't record that metric, or the day predates it. doctor.py shows which endpoints have data.

Claude doesn't see the server — the config needs absolute paths, which is what doctor.py --config generates. Restart the app fully after saving.

A tool returns less than it should — Garmin renamed a field. Ask your AI to call get_raw with the matching method (e.g. get_sleep_data) and a date, then fix the field name in shaping.py.

More than one account

A token authenticates exactly one Garmin account, but nothing stops you running one server per account and pointing the same AI at all of them — useful for a couple, or a coach with a few athletes.

Both login.py and the server read GARMIN_MCP_TOKENS, so a second account is one command. Log in as them:

GARMIN_MCP_TOKENS=~/.garmin-mcp/tokens-partner .venv/bin/python login.py

It prints the destination before asking for credentials, and warns before replacing an existing token.

Then add a second entry to your AI config, identical to the first apart from the name and the env block:

"garmin-partner": {
  "command": "/absolute/path/to/garmin-mcp/.venv/bin/python",
  "args": ["-m", "garmin_mcp.server"],
  "env": { "GARMIN_MCP_TOKENS": "/Users/you/.garmin-mcp/tokens-partner" }
}

Tools are namespaced per server, so both sets stay distinct and you can ask things like "compare our sleep this week". Each account revokes independently — delete just that token directory.

Their health data reaches whichever AI you've connected, exactly as yours does. Worth them agreeing to that explicitly.

Tests

No Garmin account needed, and no tokens are read. Paste the whole block from inside the garmin-mcp folder:

.venv/bin/pip install -q -r requirements-dev.txt   # one time: installs pytest
.venv/bin/python -m pytest tests/ -q               # date maths and payload shaping
.venv/bin/python tests/test_protocol.py            # full MCP handshake over stdio

pytest is deliberately kept out of requirements.txt so that running the server doesn't drag in test tooling. If you skip the first line you'll get No module named pytest.

Privacy

Your password is used once, in memory, and discarded. Tokens are stored at ~/.garmin-mcp/tokens with owner-only permissions. Health data flows from Garmin to your machine to whichever AI app you connect — never through anyone else's server. Delete the token folder to revoke access:

rm -rf ~/.garmin-mcp

Note that when you ask a cloud AI a question, the data it fetches does go to that provider as part of the conversation, the same as anything else you type. Only a fully local model avoids that.

License

MIT — see LICENSE. Use it, change it, redistribute it. No warranty: Garmin's private endpoints can change without notice and this will break when they do.

This project is not affiliated with, endorsed by, or connected to Garmin Ltd.

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
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
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
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
E2B

E2B

Using MCP to run code via e2b.

Official
Featured