Garmin Health Gateway
Provides read-only access to Garmin health and activity data, including daily health, readiness, runs, activities, VO2max, HRV, sleep, resting heart rate, training load, and sync status.
Click on "Deploy 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 Health GatewayWhat was my sleep quality and HRV 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 Health Gateway
A production-oriented Garmin health gateway for 64-bit Docker hosts, with read-only data queries and on-demand synchronization. It is optimized for Raspberry Pi but also runs on Linux servers, NAS devices, mini PCs, cloud hosts, and Docker Desktop. It incrementally collects normalized health and activity data into PostgreSQL, preserves each original FIT activity file, and exposes semantic MCP tools to ChatGPT and Codex through an outbound-only OpenAI Secure MCP Tunnel.
Versioned application images are published to ghcr.io/cara-labs/garmin-health-mcp-gateway for linux/amd64 and linux/arm64. Releases include build provenance and an SBOM. Contributors can still build the image locally with compose.build.yaml.
The gateway code in this repository was written independently. It uses the third-party, MIT-licensed garminconnect Python package as a runtime dependency; that package is installed from PyPI and its source is not copied into this repository. Our GarminProvider adapter is the only layer that calls it. Pinning version 0.3.11 prevents an untested update from being installed automatically, and the adapter converts failures into understandable sync errors while keeping the database and MCP layers independent of that dependency. Garmin Connect remains an unofficial and potentially changing personal-data interface.
Architecture
Garmin Connect ──HTTPS──> collector ──> PostgreSQL (internal network only)
│
└────────> FIT archive volume
ChatGPT phone <── OpenAI ── outbound Secure MCP Tunnel <── MCP queries <── PostgreSQLThe MCP server never receives Garmin credentials, Garmin tokens, or FIT-file access. Its separate PostgreSQL role has only SELECT privileges and database-enforced read-only transactions. PostgreSQL has no published port. The Docker host needs outbound HTTPS but no inbound firewall or router port.
Related MCP server: health-mcp
Docker host requirements
A 64-bit
arm64oramd64host. Raspberry Pi deployments should use a 64-bit OS.Docker Engine with Compose v2
At least 2 GB RAM and sufficient durable storage for PostgreSQL plus FIT files
A ChatGPT account/workspace with developer-mode app access and OpenAI Platform Secure MCP Tunnel permissions
Docker Desktop on macOS or Windows is suitable for development. On Windows, run the POSIX setup script through WSL or Git Bash. A native Linux Docker host is recommended for continuous production operation.
Installation
Follow the complete installation guide. It covers Docker prerequisites, OpenAI tunnel permissions, local secret creation, Garmin MFA, service verification, ChatGPT web setup, phone access, upgrades, and troubleshooting.
The first run backfills 90 days of daily health and 365 days of activities. With the default request delay this can take a while by design. Later cycles re-fetch the latest three days every hour to pick up overnight and post-activity Garmin corrections.
If Garmin has no original FIT file for a manual/imported activity, the activity is still stored with fit_download_status=error and an understandable error instead of blocking the entire collector. A manual backfill retries it.
MCP tools
get_daily_health(date)get_health_range(start_date, end_date)get_today_readiness()get_recent_runs(days=30)get_recent_activities(days=30, activity_type=null)get_activity(activity_id)get_vo2max_history(days=180)get_hrv_history(days=90)get_sleep_history(days=30)get_resting_hr_history(days=90)get_training_load(days=28)get_sync_status()request_sync()
The 12 query tools remain read-only. request_sync is annotated as mutating, non-destructive, idempotent, and open-world because it requests Garmin downloads and local database updates. There is no raw SQL tool or Garmin account mutation tool.
Ask in chat: “Sync my latest Garmin health and activities, wait for completion, then summarize today's recovery.” request_sync immediately returns a request ID and queued status. Check get_sync_status().ad_hoc until success or error, then query the refreshed data. A request refreshes today and the preceding two calendar days in the configured timezone, including FIT files for new activities. It cannot make the watch upload to Garmin Connect.
The collector checks for requests every two seconds while idle. Requests wait behind an active scheduled sync or unfinished initial backfill. Duplicate queued/running requests return the same ID; requests within five minutes of completion/failure return a cooldown and retry interval. Status includes timestamps, per-resource counts, and error types. Interrupted jobs are marked failed after collector restart. An offline collector leaves the request queued; queued is never a success indication.
Requests use a dedicated local sync_requests Docker volume; the MCP database role still has only SELECT privileges and no Garmin credentials. Collector and manual CLI syncs share a process lock to prevent concurrent Garmin requests.
Operations
Run an immediate incremental sync:
docker compose exec collector garmin-health syncRepeat the configured historical windows manually:
docker compose exec collector garmin-health sync --backfillInspect status and understandable errors:
docker compose logs --since=2h collector
docker compose exec collector garmin-health check --max-age-hours 3Test locally with MCP Inspector without publishing MCP on the LAN:
docker compose -f compose.yaml -f compose.local.yaml up -d mcp
npx @modelcontextprotocol/inspector@latestChoose Streamable HTTP and http://127.0.0.1:8000/mcp. Stop using the local override after testing.
Backups and restore
Back up the three data volumes: postgres_data, fit_archive, and garmin_tokens. The additional sync_requests volume stores only the latest refresh job and lock files; it can be recreated if losing pending job status is acceptable. Encrypt backups. The token volume grants long-lived Garmin access and is as sensitive as a password. A logical PostgreSQL dump plus a filesystem copy of the FIT archive is the most portable backup.
Do not restore PostgreSQL by copying a live data directory. Stop writes and use pg_dump/pg_restore, or use a snapshot mechanism designed for Docker volumes.
Upgrades
Review Garmin client release notes before changing the garminconnect pin. Run tests and a manual sync after an upgrade because Garmin endpoints are unofficial. Pin the exact TUNNEL_CLIENT_IMAGE tag in .env; review official tunnel-client releases before changing it.
Health and training interpretations are informational, not medical advice. Seek professional care for symptoms or health concerns.
Licensing
The original gateway code and documentation are licensed under Apache License 2.0. Third-party dependencies retain their own licenses and are not relicensed as Apache 2.0. See THIRD_PARTY_NOTICES.md and the release SBOM for the dependency inventory.
This server cannot be deployed
Maintenance
Related MCP Connectors
Garmin data in Claude & ChatGPT via the Garmin Health API. OAuth sign-in, no password sharing.
Garmin data in Claude: 135 tools — activities, sleep, HRV, training, workouts. Free, open source.
- SomviaOAuthapp.somvia
Apple Health training load, recovery, HRV and workout detail for Claude, ChatGPT and any MCP client.
Pace is a remote MCP server that exposes wearable and fitness data to Claude via the Model Context Protocol. It connects to Garmin, Oura, Whoop, Polar, Fitbit and 20+ devices and provides 15 tools for querying sleep, activity, recovery, and training data. Hosted on Google Cloud Run, OAuth 2.1 authentication, Streamable HTTP transport. Instructions: First you need to create an account at: https://pacetraining.co and connect your wearables. After that you can connect the remote Server via Custom Connector in Claude and OAuth 2.1 Flow startet.
Related MCP Servers
- AlicenseAqualityBmaintenanceEnables Claude to access and query your Garmin Connect data, including sleep, activities, training load, and health metrics, through a set of read-only MCP tools.28132 PyPI1MIT
- AlicenseNot gradedqualityDmaintenanceExposes personal Garmin wellness data through MCP tools for accessing summary, sleep, HRV, heart rate, stress, body battery, and historical data.MIT
- FlicenseNot gradedqualityBmaintenanceExposes Garmin Connect health data (steps, sleep, HR, HRV, stress, activities, etc.) as tools for MCP-compatible clients like Claude.-
- AlicenseBqualityBmaintenanceEnables retrieval of Garmin Connect data such as activities, sleep, heart rate, and body battery levels using unofficial API through MCP tools.4MIT