garmin-mcp
Provides tools for accessing Garmin Connect data, including activities, sleep, heart rate, body battery, training status, performance metrics, personal records, and threshold history.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@garmin-mcpHow did I sleep last night?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Garmin MCP Server
Connect Claude (or any MCP client) to your Garmin Connect data — activities, sleep, heart rate, body battery, and training status.
What you can ask
"How did I sleep this week?"
"What was my average pace on my last 5 runs?"
"Is my resting heart rate trending down?"
"Should I train hard today based on my body battery?"
"Summarize my training status and VO2 max trend."
"What's my lactate threshold heart rate and pace right now?"
"What 10k time does Garmin predict for me?"
"What are my running personal records?"
"Has my threshold pace improved over the last three months?"
Related MCP server: Garmin MCP Server
Quick Start
1. One-time login
uvx --from git+https://github.com/Sinfjell/garmin-mcp garmin-mcp-authPrompts for your Garmin Connect email and password (or reads GARMIN_EMAIL /
GARMIN_PASSWORD from the environment), handles MFA if your account uses it,
and caches a session token locally so you won't be prompted again.
2. Add it to your MCP client
Claude Desktop — add to claude_desktop_config.json:
{
"mcpServers": {
"garmin": {
"command": "uvx",
"args": ["--from", "git+https://github.com/Sinfjell/garmin-mcp", "garmin-mcp"]
}
}
}Claude Code:
claude mcp add garmin -- uvx --from git+https://github.com/Sinfjell/garmin-mcp garmin-mcpCursor — add to .cursor/mcp.json:
{
"mcpServers": {
"garmin": {
"command": "uvx",
"args": ["--from", "git+https://github.com/Sinfjell/garmin-mcp", "garmin-mcp"]
}
}
}3. Ask a question
Restart your client and ask something like "How did I sleep last night?"
Tools
Tool | Returns |
| Recent activities: date, type, name, distance, duration, avg HR, pace |
| Full detail for one activity, incl. explicit elapsed/timer/moving/stopped timing (stripped of bulky sample/GPS data) |
| Per-lap breakdown: distance, durations, pace, HR, power, cadence, interval intensity (WARMUP/ACTIVE/REST) |
| Activity summaries in a date range |
| Steps, calories, resting HR, stress for one day |
| Sleep stages, sleep score, overnight HRV and resting HR |
| Min/max/resting HR and 7-day average resting HR for one day |
| Body Battery charged/drained/highest/lowest per day |
| Training status, load, VO2 max, heat/altitude acclimation |
| Running fitness/threshold snapshot: lactate threshold (LTHR + pace), VO2 max + fitness age, and 5k/10k/half/marathon race predictions |
| Personal records / PBs: fastest 1km/1mile/5km/10km, longest run (labeled), plus raw type/value for any other record |
| Lactate-threshold HR + pace as a dated trend series over a range |
All dates are "YYYY-MM-DD". All tools return compact JSON.
Authentication
garmin-mcp-auth is the recommended way to log in — it supports MFA and
caches a token so the server doesn't need your password on every run.
Environment variable alternative: set GARMIN_EMAIL and GARMIN_PASSWORD
(see .env.example) and the server will log in non-interactively if no
cached token is found. This does not support MFA — accounts with MFA enabled
should use garmin-mcp-auth instead.
Token cache location: ~/.garminconnect by default, or the path in
GARMIN_TOKENS if set.
Remote / hosted mode (use from Claude mobile & web)
By default this server speaks stdio and is meant to run as a local subprocess of your MCP client. You can instead run it as a long-lived HTTP service and add it to claude.ai as a custom connector, which also makes it reachable from Claude on mobile.
1. Log in once, on the host
garmin-mcp-authRun this on the machine that will host the server (interactively, so it can
handle MFA). The cached token in ~/.garminconnect (or GARMIN_TOKENS) is
reused on every request — the server does not log in again per request.
Avoid triggering fresh logins often: Garmin rate-limits and sometimes blocks
sign-ins from datacenter IPs, so a stable, long-lived cached session is the
goal, not routine re-auth.
2. Run the server in streamable-http mode
garmin-mcp --transport streamable-http --host 127.0.0.1 --port 8765 --path /<random-secret>/mcp--transport streamable-httpswitches from stdio to an HTTP endpoint.--host/--portcontrol what the server binds to (default127.0.0.1:8765— bind to127.0.0.1and put a reverse proxy in front rather than exposing the process directly).--pathsets the URL path the MCP endpoint mounts at (default/mcp). Set it to a long random value (e.g./8f2c1a9e7b.../mcp) and treat it as a secret — see below.
3. Put TLS and a reverse proxy in front
This mode has no built-in authentication. The security model is:
The path is the secret. Anyone with the full URL — including the random path segment — can call every tool and read your Garmin data. Anyone without it gets a 404. This is possession-of-URL security, not real authentication — good enough for a personal deployment you control, not for anything shared or high-stakes.
TLS is required in practice. Run the server behind a reverse proxy (Caddy, nginx, Cloudflare Tunnel, etc.) that terminates HTTPS, so the secret path isn't sent in the clear. Point the proxy at
127.0.0.1:<port>and expose only the proxy's HTTPS URL.Don't log the URL, commit it, or paste it anywhere public — it's effectively a credential.
4. Add it to claude.ai as a custom connector
In claude.ai: Settings → Connectors → Add custom connector, then paste
your full HTTPS URL (e.g. https://mcp.example.com/8f2c1a9e7b.../mcp). Once
added, it's available from Claude on web and mobile, not just Claude Code or
Desktop.
5. Optional: serve several people from one process (multi-tenant)
A single hosted server can serve more than one Garmin account. Set
GARMIN_MULTI_TENANT_ROOT to a directory of per-user token stores:
GARMIN_MULTI_TENANT_ROOT=/srv/garmin-tenants \
garmin-mcp --transport streamable-http --port 8766 --path /uEach subdirectory is one user's token store, and its name is the user ID that appears in that user's endpoint URL:
/srv/garmin-tenants/<user-id>/ -> https://host/u/<user-id>/mcpUser IDs are 32–128 chars of
[a-z0-9-]and must be generated randomly (≥128 bits). As in single-tenant hosting, possession of the URL is the authentication, so a guessable ID is a leaked account.An unknown or malformed ID gets a 404. A request never falls back to another user's token store, and never to the
GARMIN_EMAIL/GARMIN_PASSWORDenvironment credentials — those belong to the host, not to a tenant.Requests are served statelessly, so a session cannot outlive the URL that created it.
Leaving
GARMIN_MULTI_TENANT_ROOTunset changes nothing: the server runs exactly as it did before, single-tenant, on the--pathyou give it.
A token store can be populated by hand — run garmin-mcp-auth with
GARMIN_TOKENS pointed at that user's directory — but the point of the
onboarding app below is that nobody has to.
Stores hold a Garmin session token, never a password.
6. Optional: let people onboard themselves
GARMIN_MULTI_TENANT_ROOT=/srv/garmin-tenants \
GARMIN_CONNECTOR_BASE_URL=https://host.example.com \
garmin-mcp-onboarding --port 8767Put it behind the same reverse proxy. Someone opens the page, logs in with their own Garmin account (Garmin's one-time code included), and the page hands them their personal connector URL. Their token store is created for them; you are not involved.
The password is passed straight to Garmin and dropped from memory in the same call — it is never written to disk, never logged, and is not held across the one-time-code wait. The consent text on the first page says so, in Norwegian.
Expect the login itself to be slow, and sometimes blocked. Garmin treats
datacenter IPs far more harshly than home connections. A single login attempt
runs five strategies with 12–20s waits between them — a minute or two of real
time — and from a hosted IP some or all of those strategies can come back 429
or with a Cloudflare challenge. Give the reverse proxy a generous
proxy_read_timeout (300s), and don't add retries on top: garminconnect
already backs off internally, so retrying just spends more attempts against an
IP that is already refusing them.
Measured from both sides on 2026-08-11, same code and same throwaway account:
a residential IP reached the credential check (401 Invalid Username or Password) in 27s, while the hosted IP got a Cloudflare 403 after 1m45s. The
library and the flow are fine; the location of the login is the problem.
So when server-side login is blocked, move that one step to where it works:
# on their own machine, where the IP is fine and the password never leaves
garmin-mcp-auth
# on the host, with the ~/.garminconnect directory they send you
garmin-mcp-tenant import ./their-garminconnect
# -> Imported as 3f9a... https://host.example.com/u/3f9a.../mcpimport copies (never moves), clamps the directory to 0700 and the token to
0600, mints the random ID, and prints the finished connector URL. No new unit,
no nginx change — that is what multi-tenant hosting buys even when self-service
can't complete.
To remove someone:
garmin-mcp-tenant list
garmin-mcp-tenant delete <user-id>Their URL 404s immediately afterwards. Deletion is a command rather than a web endpoint deliberately: with possession-of-URL auth, a delete endpoint would let anyone who ever saw a URL wipe that person's access.
docs/e2e-onboarding.md has the full verification sequence.
Security & privacy
Your credentials never leave your machine and are never stored by this server — only a session token is cached locally.
This server is read-only: it never writes to, modifies, or deletes anything in Garmin Connect.
Nothing is sent anywhere except Garmin's own API — no third-party telemetry, analytics, or logging.
Troubleshooting
"Garmin authentication expired" / auth errors — run garmin-mcp-auth
again to refresh your session.
Stuck in an MFA loop — make sure you're running garmin-mcp-auth
interactively (not through a client that swallows stdin); the MFA code
prompt needs a real terminal.
Wrong Python version — this package requires Python 3.10+. Check with
python3 --version; uvx will otherwise fail to build the environment.
Disclaimer
This project uses the unofficial, community-maintained
garminconnect
library to talk to Garmin Connect. It is not affiliated with, endorsed by,
or supported by Garmin. Garmin Connect's API is not public, and it can
change or break without notice.
License
MIT — see LICENSE.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- Alicense-qualityCmaintenanceConnects Garmin Connect data to MCP-compatible clients, providing access to fitness activities, health metrics, and training plans. It supports advanced features like headless 2FA and automated MFA retrieval to enable seamless health data interaction through natural language.1MIT
- Alicense-qualityDmaintenanceConnects to Garmin Connect and exposes fitness and health data (activities, steps, heart rate, sleep, body composition) to MCP-compatible clients like Claude.MIT
- AlicenseBqualityCmaintenanceExposes personal Garmin Connect data to MCP-capable clients like Claude and Gemini. Enables querying daily stats, heart rate, sleep, activities, and managing workouts.16MIT
- Flicense-qualityBmaintenanceExposes Garmin Connect health and activity data (steps, sleep, heart rate, etc.) via MCP tools, with built-in login and MFA support.
Related MCP Connectors
MCP server for Withings health data — sleep, activity, heart, and body metrics.
Garmin data in Claude & ChatGPT via the Garmin Health API. OAuth sign-in, no password sharing.
MCP server wrapping the Tesla Fleet API and TeslaMate API
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/Sinfjell/garmin-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server