Charlotte, NC
ProjectsApril 9, 2026

Garmin MCP Server

Tap image to enlarge
The Garmin MCP Server is an open-source Model Context Protocol server that connects your Garmin wearable data to any AI assistant. It exposes 34 tools covering daily health metrics, workout history, training readiness, body composition, sleep trends, and multi-day comparative reports. Ask your AI "how did I sleep last night?" or "compare my stress this week vs last week" and get real answers from real data. The project started because I wanted my AI tools to have access to my health data without building custom integrations for every client. MCP is the protocol that makes this work. One server, any compatible client. Garmin Connect has a mobile app and a web dashboard, but neither is designed for the kind of questions I actually want to ask. Things like "what's my HRV trend over the last two weeks and how does it correlate with my training load?" or "show me every strength training session this month with sets and reps" require clicking through multiple screens and mentally assembling the picture yourself. The Garmin Connect API exists, but it's undocumented and session-based. The garminconnect Python library wraps it nicely, but that still leaves you writing scripts for every question. MCP turns the API into something any AI assistant can use conversationally. I build data pipelines that make information queryable. This is the same pattern scaled down to a single person's health data: take a data source that's hard to query directly, wrap it in an interface that an AI can navigate, and let natural language do the rest. The server is a Python application built on FastMCP that authenticates with Garmin Connect via OAuth and exposes health data as MCP tools over Streamable HTTP. Garmin Connect uses OAuth 1.0a tokens that persist for roughly a year. The server handles auth in four tiers:
  1. Stored tokens: Checks ~/.garminconnect/ for existing OAuth tokens from a previous session
  2. Environment variables: Falls back to GARMIN_EMAIL and GARMIN_PASSWORD for non-interactive auth
  3. Interactive script: garmin_auth.py handles first-time setup with MFA support
  4. Browser-assisted fallback: garmin_browser_auth.py for when Cloudflare blocks the scripted login
That fourth tier exists because Garmin's Cloudflare configuration now rate-limits the POST /sso/signin endpoint that the garminconnect library uses for scripted login. The GET to the same endpoint still returns 200, but the credential POST gets a 429 regardless of TLS fingerprint or user agent. The browser-assisted flow sidesteps this entirely: you sign in through a real browser (which Cloudflare allows), grab the SSO ticket from the post-login redirect, and the script exchanges it for OAuth tokens via connectapi.garmin.com, a different host that isn't behind the same Cloudflare rules. The result is the same token pair the MCP server already expects. Once authenticated, the token file gets mounted into the Docker container as a read-only volume. No credentials in the image, no secrets in environment variables after initial setup. The 34 tools fall into four categories: Daily health (14 tools): Steps, heart rate, sleep, stress, body battery, HRV, SpO2, respiration, hydration, floors, goals progress, and detailed stress timelines. Each accepts an optional date parameter (defaults to today). Activities and training (11 tools): Recent workouts, activity details with splits, strength training exercise sets (actual sets, reps, and weight), activity search with filters, training status, training readiness, personal records, badges, and step streaks. Body and composition (2 tools): Current body composition and weight history with trend analysis over configurable time ranges. Trends and reports (7 tools): Weekly summaries, date range queries across selectable metrics, sleep quality trends, recovery metrics, period-over-period comparisons, and a comprehensive weekly health report that returns everything in a single call. The Garmin Connect library is synchronous, which means naive implementations make serial API calls. For a weekly health report that needs 7 days of sleep, HRV, stress, body battery, heart rate, steps, plus weight history, training status, recent activities, and exercise sets, that's 80+ sequential requests. The server wraps these in a thread pool executor with 5 concurrent workers. A _parallel_fetch helper takes a list of (function, *args) tuples and runs them concurrently, returning results in order. Tasks that fail return None instead of killing the batch. This brings the weekly health report from minutes of serial fetching down to a few seconds. The thread pool is capped at 5 to avoid hammering the Garmin API and triggering rate limits. All distance values are automatically converted from metric to imperial (miles). This is a small thing, but it means the AI doesn't need to know that Garmin stores everything in kilometers and meters. The response just says "3.2 miles" instead of requiring a follow-up conversion. Point your client at http://localhost:8000/mcp using Streamable HTTP transport. That's it. Claude Desktop, LiteLLM, or anything else that speaks MCP. Streamable HTTP over stdio. Most MCP examples use stdio transport, which ties the server's lifecycle to the client process. HTTP means the server runs independently as a long-lived service, multiple clients can connect simultaneously, and it deploys naturally in Docker. Thread pool, not async rewrite. The garminconnect library is synchronous. Rather than forking it or wrapping every call in asyncio.to_thread, a shared thread pool executor handles the concurrency. It's simpler, it works, and the Garmin API's rate limits are the bottleneck anyway, not the server's threading model. Read-only token mount. The Docker container never writes to the token directory. Auth happens outside the container, tokens get mounted read-only. This keeps the container stateless and means you can rebuild it without losing authentication. 34 tools instead of a generic query interface. Each tool has specific parameters, return types, and docstrings that help the AI understand what's available. A single "query Garmin" tool would technically work but would require the AI to know the API schema. Explicit tools mean the AI can browse capabilities and pick the right one.
  • Python with FastMCP for the MCP server framework
  • garminconnect (<0.3, garth-backed) for Garmin Connect API access (OAuth 1.0a)
  • garth for browser-assisted OAuth ticket exchange against connectapi.garmin.com
  • Streamable HTTP transport for client-agnostic connectivity
  • concurrent.futures thread pool for parallel API fetching
  • Docker with health checks and compose for deployment
  • MIT licensed and open source
The project is on GitHub under the MIT license. If you wear a Garmin and use AI tools, it's a docker compose up away from connecting the two.

Related projects

blakemccarn.dev

Portfolio and blog built with Next.js 16, deployed to AWS via SST v4 with Cloudflare DNS, staging environments, and full IaC.

Charlotte Wire & Cable

Full-stack business website for a specialty wire distributor: searchable catalog, admin dashboard, and serverless AWS infrastructure via SST.

Paperless OCR Enhanced

Open-source Paperless-ngx companion service that uses LLM vision models to repair weak OCR and improve downstream document search.

Paperless Knowledge Graph

Document intelligence system that combines Paperless-ngx, Neo4j, pgvector, Strands Agents, cited answers, and an interactive graph UI.

RapidEPR

Founded and built an AI SaaS product that helps service members across five military services write evaluations, performance statements, and award narratives.