Garmin Cloud MCP
Provides tools to access personal Garmin Connect data, including activity summaries, daily health metrics, sleep, training readiness, training status, HRV, and VO2 max.
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 Cloud MCPHow was my 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 Cloud MCP
A personal, stateless Model Context Protocol server for Cloudflare Workers. The protected MCP endpoint exposes nine bounded, read-only tools for one securely linked personal Garmin Connect account.
The MCP endpoint uses Cloudflare's createMcpHandler() with a fresh MCP server for every request.
There are no MCP protocol sessions, SSE endpoints, or retained MCP transport state. Inbound OAuth
grants/codes/tokens and encrypted Garmin linking state are retained in separate Cloudflare KV
namespaces. A narrowly scoped, single-owner Durable Object coordinates one-time MFA consumption;
it is application security state, not MCP transport state.
Tools
All returned values are normalized into stable JSON shapes and also rendered as MCP text content.
Unavailable scalar metrics are null; fields Garmin adds later are omitted. GPS polylines, charts,
raw health/sleep samples, and other bulky arrays are never passed through.
Tool | Purpose | Input and bounds |
| Concise owner profile | No input |
| Newest-first activity summaries |
|
| One activity summary with at most 20 basic splits | Positive Garmin activity ID as a decimal string |
| Steps, heart rate, stress, body battery, intensity, and respiration | One real |
| Sleep total, stages, scores, SpO2, stress, and respiration | One real |
| Recovery score and bounded sleep, load, HRV, and stress factors | One real |
| Training status, acute/chronic load, workload ratio, and load focus | One real |
| Overnight HRV summary, status, and personal baseline | One real |
| Generic and cycling VO2 max and fitness-age estimates | One real |
The training/recovery category deliberately has four non-overlapping, single-date tools: readiness
explains immediate recovery factors, status describes training load, HRV describes overnight
variability against a personal baseline, and VO2 max reports aerobic-capacity estimates. The
single-date contracts keep calls bounded and match the current endpoint division verified against
python-garminconnect commit 414b540.
Examples:
{ "name": "list_activities", "arguments": { "start": 0, "limit": 10 } }
{ "name": "get_activity", "arguments": { "activityId": "1234567890" } }
{ "name": "get_daily_health", "arguments": { "date": "2026-08-12" } }
{ "name": "get_sleep", "arguments": { "date": "2026-08-12" } }
{ "name": "get_training_readiness", "arguments": { "date": "2026-08-12" } }
{ "name": "get_training_status", "arguments": { "date": "2026-08-12" } }
{ "name": "get_hrv", "arguments": { "date": "2026-08-12" } }
{ "name": "get_vo2_max", "arguments": { "date": "2026-08-12" } }Each normalized result is capped at 64 KiB. Garmin's upstream JSON is capped at 2 MiB before normalization; responses above either limit fail safely. Activity list results are capped to the requested page, activity splits to 20, and training-readiness snapshots to 8. This service sends the requested dates to Garmin Connect and stores encrypted Garmin authorization tokens in Cloudflare KV. It does not retain Garmin response data or MCP transport/session state. Training readiness, training status, HRV, and VO2 max are sensitive health and fitness data returned to the authorized MCP client. Raw HRV readings, device identifiers/details, user/profile identifiers, long-form feedback prose, and other unlisted upstream fields are omitted; concise Garmin feedback keys are retained where they explain a normalized metric.
Related MCP server: Garmin Open MCP
Authorization
/mcp is protected by the current
@cloudflare/workers-oauth-provider
OAuth 2.1 implementation. The Worker provides:
authorization code flow with S256 PKCE; implicit flow and plain PKCE are disabled;
rotating refresh tokens when
offline_accessis granted;exact resource/audience binding to the MCP URL;
RFC 9728 protected-resource metadata and RFC 8414 authorization-server discovery;
garmin:readandoffline_accessscopes, with unknown or excess scopes rejected;Client ID Metadata Documents (CIMD) as the preferred registration mechanism; and
constrained dynamic client registration (DCR) at
/registeronly as a fallback for older MCP clients.
CIMD clients are public clients: their metadata must select token_endpoint_auth_method: none, and
they must use S256 PKCE. The Worker sets clientIdMetadataDocumentEnabled: true; the matching
global_fetch_strictly_public compatibility flag is required to keep metadata fetches SSRF-safe.
The /authorize endpoint uses a reusable personal-owner sign-in and explicit consent screen. The
owner password is a Worker secret, never a client secret. Login and consent use CSRF protection,
signed Secure/HttpOnly/SameSite cookies, bounded request bodies, and generic authentication errors.
Link Garmin Connect
Open /garmin on the HTTPS Worker host and sign in with the same OWNER_PASSWORD used by the
authorization screen. The page shows whether the personal account is linked and supports:
entering the Garmin email and password to start or retry linking;
entering a Garmin multi-factor code in a second, one-time browser step;
verifying the resulting Garmin token with the Garmin profile endpoint before saving it; and
unlinking all locally stored Garmin tokens, MFA continuation, and rate-limit state.
Garmin credentials and MFA codes are submitted only over HTTPS and are never logged or written to
KV. The password exists only for the duration of the first Worker request. Garmin's widget flow
allows MFA to continue without the password: the Worker retains only Garmin cookies, an MFA CSRF
token, the sign-in context, a random state, and timestamps. That continuation is AES-GCM encrypted
in GARMIN_KV, expires after ten minutes, and is deleted before its single use.
KV does not offer atomic get-and-delete. To prevent two concurrent MFA submissions from both
reaching Garmin, GARMIN_MFA_COORDINATOR transactionally grants one claim for the SHA-256 hash of
the random continuation state. It never stores Garmin cookies, credentials, MFA codes, tokens, or
MCP session state. The encrypted continuation itself remains in GARMIN_KV.
Garmin DI access and refresh tokens are stored as one authenticated AES-GCM record in GARMIN_KV.
Non-secret owner and created/updated/expiry metadata is present on the versioned envelope; token
values remain encrypted. Refresh uses Garmin's DI OAuth token endpoint, and the complete replacement
pair is committed with one KV write only after Garmin accepts and verifies it. Records created by
the previous OAuth1-based flow remain readable and refreshable during migration.
Unlinking deletes local state. This project does not claim to revoke tokens at Garmin because no supported Garmin token-revocation endpoint was verified.
Requirements
Node.js 24 or newer
Corepack with pnpm 11.3.0
Local setup
corepack enable
pnpm install
pnpm typegen
GARMIN_KEY="$(openssl rand -base64 32 | tr '+/' '-_' | tr -d '=')"
printf 'OWNER_PASSWORD=replace-with-a-long-random-password\nGARMIN_TOKEN_ENCRYPTION_KEY=v1:%s\n' "$GARMIN_KEY" > .dev.vars
pnpm dev.dev.vars is ignored by Git. Use a long, unique password; never put it in wrangler.jsonc or
commit it. Local Wrangler supplies local KV namespaces for the configured OAUTH_KV and
GARMIN_KV bindings and local Durable Object storage for GARMIN_MFA_COORDINATOR. Vite starts the
Worker locally and prints its URL. Credential linking is deliberately rejected over plain HTTP;
use an HTTPS tunnel or deployed preview when exercising /garmin with a real account. Check the
public health endpoint with:
curl http://localhost:5173/healthAn unauthenticated request to /mcp returns a Bearer challenge pointing to:
/.well-known/oauth-protected-resource/mcpThe authorization-server metadata is at /.well-known/oauth-authorization-server.
Cloudflare configuration and deployment
The committed OAUTH_KV and GARMIN_KV IDs identify this Worker's production namespaces. For a
new Cloudflare account, create replacement namespaces, update those IDs, put the independent
secrets, generate fresh types, and deploy with Wrangler:
pnpm wrangler kv namespace create OAUTH_KV --binding OAUTH_KV
pnpm wrangler kv namespace create GARMIN_KV --binding GARMIN_KV
pnpm wrangler secret put OWNER_PASSWORD
pnpm wrangler secret put GARMIN_TOKEN_ENCRYPTION_KEY
pnpm typegen
pnpm wrangler deploySet GARMIN_TOKEN_ENCRYPTION_KEY to v1: followed by a base64url-encoded 32-byte random key, as in
the local setup example. It must be independent of OWNER_PASSWORD. Alternatively, add
--update-config to each KV command and review its wrangler.jsonc edit. Never commit a real
password, encryption key, or .dev.vars. Namespace IDs are non-secret configuration and are
intentionally committed for this private application.
Rotating the encryption key makes existing Garmin records unreadable. For recovery, restore the
old key or use the authenticated /garmin recovery button to discard unreadable state, then link
again. The current envelope/key version is v1; unsupported versions fail closed.
Unofficial Garmin integration and recovery
Garmin Connect does not provide a supported public API for this personal workflow. The endpoint
sequence is adapted from the MIT-licensed garth, garmin-connect, and python-garminconnect
projects (see NOTICE.md), uses an explicit cookie jar and Worker-native Web Crypto/fetch, and may
break when Garmin changes SSO HTML or private endpoints. There is no runtime dependency on those
projects.
Garmin and Cloudflare aggressively limit automated sign-in (commonly HTTP 429 with Cloudflare 1015
pages). The Worker never automatically retries a credential or MFA submission. It records generic
failure counts and increasing backoff in GARMIN_KV; an upstream limit enforces at least a one-hour
cooldown. If limited, stop retrying, wait for the displayed/reported cooldown, confirm Garmin's
service status, and sign in through Garmin's official site if account recovery is required.
If linking reports an account lock or required phone-number update, complete that recovery on Garmin's official site and retry later. If saved tokens stop refreshing, unlink and link again.
Connect from ChatGPT
ChatGPT needs the publicly reachable HTTPS Worker deployment; it cannot connect directly to the local Vite URL. Garmin account linking and ChatGPT app creation remain interactive owner steps.
In ChatGPT, enable developer mode for the workspace/account under Settings → Apps → Advanced settings (the exact controls depend on plan and workspace role).
Create a custom app/MCP connection and enter
https://YOUR_WORKER_HOST/mcpas the server URL.Choose OAuth and select Client ID Metadata Document (CIMD) for client registration when that choice is shown. CIMD is the preferred path for this server.
Request
garmin:read offline_access.offline_accessis required for ChatGPT to retain access by refreshing expired access tokens.Complete the owner-password sign-in, review the explicit consent page, authorize, and let ChatGPT scan the nine read-only tools.
The /register DCR endpoint remains advertised for older MCP clients that cannot use CIMD. It is a
compatibility fallback, not the registration option to select in ChatGPT. DCR accepts only supported
HTTPS/loopback redirects, authorization-code plus refresh-token grants, the code response type, and
supported token authentication methods.
See OpenAI's developer mode and custom MCP app guidance and the MCP authorization specification for the client-side discovery flow.
Checks
pnpm format:check
pnpm lint
pnpm typecheck
pnpm test
pnpm build
pnpm wrangler:dry-runpnpm test runs the endpoint tests inside the Cloudflare Workers runtime. The MCP tests issue
independent authenticated initialize, list, and call requests and assert that no Mcp-Session-Id
is returned. The suite also covers discovery, CIMD advertisement, DCR constraints, PKCE/scope/
resource rejection, login and consent CSRF, bearer challenges, refresh rotation, secret leakage,
public health, streamed body bounds, encrypted storage, concurrent one-time MFA, Garmin failure
mapping, endpoint contracts, response normalization and bounds, and atomic Garmin token refresh
persistence. Training/recovery coverage also fixes the upstream URL contracts, rejects malformed
roots and invalid dates, verifies strict normalized allowlists and explicit nulls, caps readiness
snapshots, and excludes raw HRV samples and device/profile identifiers.
After deployment, open https://YOUR_WORKER_HOST/garmin and link the Garmin account before using
the tools. If Garmin authorization expires, use the same page to unlink/relink. For code updates,
run all checks, push main, wait for GitHub Actions, then run pnpm wrangler deploy. For encryption
key rotation, retain the previous key until the existing record is deliberately discarded and the
account is linked again; changing the key alone makes the current record unreadable.
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-qualityCmaintenancePersonal MCP server for interacting with your Garmin Connect data. Exposes 62 tools across 11 domains including activities, health, training, and workouts.1MIT
- AlicenseAqualityBmaintenanceUnofficial MCP server bridging Garmin Connect to MCP clients and ChatGPT, providing tools to access health data, activities, and trends via a self-hosted API.11MIT
- AlicenseAqualityBmaintenanceMCP server for reading and querying Garmin Connect data, including activities, strength history, recovery, trends, and optionally creating workouts.12MIT
- Alicense-qualityCmaintenanceMCP server for Garmin Connect that enables users to access and manage their personal health and fitness data, including daily summaries, heart rate, sleep, HRV, stress, body composition, activities, and training readiness, with secure per-user authentication.Apache 2.0
Related MCP Connectors
Hosted remote MCP server for YNAB on Cloudflare Workers with OAuth
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.
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/RedlineTriad/garmin-connect-cloudflare-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server