garmin-mcp
Provides read-only access to a user's Garmin Connect data, including sleep, HRV, heart rate, stress, Body Battery, daily activity, workouts, and weight. It offers tools for sleep and recovery analysis, trends, daily summaries, workout details, profile and weight data, and allow-listed raw API queries.
garmin-mcp
English | Русский
Unofficial. This project is not affiliated with, endorsed by or supported by Garmin Ltd. "Garmin" is a trademark of Garmin Ltd. or its subsidiaries; it is used here only to describe compatibility.
An MCP server that gives Claude (or any other MCP client) read-only access to your own Garmin Connect data: sleep, HRV, heart rate, stress, Body Battery, daily activity, workouts and weight. It uses the official Garmin Connect Personal API.
The main use case is personal advice on sleep and wellbeing. The server computes averages, sleep regularity, sleep shortfall, deviations from your own baseline and links between your day and your sleep; Claude turns that into concrete recommendations.
MCP (Model Context Protocol) is the standard an AI assistant uses to call external tools. Claude Code starts
garmin-mcpas a child process and talks to it over stdin/stdout.
Quick start
API key. Open https://connect.garmin.com/app/settings/personalApiToken → Create Key → accept the terms → choose an expiry (30, 90, 180 days or 1 year) → copy the key. The key is shown once. Garmin emails you about a week before it expires.
Install — pick the option that fits your client:
Client
How
Claude Desktop
Download
garmin-mcp.mcpbfrom the latest release and open it. Claude Desktop asks for the API key (stored securely) and time zone. Bundled for macOS (Intel and Apple Silicon), Windows x64 and Linux x64Claude Code (plugin)
/plugin marketplace add sasha-sup/garmin-mcp, then/plugin install garmin-mcp@garmin-mcp; Claude Code asks for the key. On first start the plugin downloads the release binary and verifies its SHA-256. macOS and LinuxClaude Code (binary)
Install the binary (row below), then
claude mcp add -s user garmin -- garmin-mcp -env-file /path/to/.envBinary
Archives for Linux, macOS and Windows on the releases page, or
go install github.com/sasha-sup/garmin-mcp/cmd/garmin-mcp@latestDocker
docker run -i --rm -e GARMIN_API_KEY -e GARMIN_TZ ghcr.io/sasha-sup/garmin-mcpas the MCP command (images for amd64 and arm64)Any MCP client
Listed in the MCP Registry as
io.github.sasha-sup/garmin-mcpFrom source
make build(Go 1.27.4+; Go 1.21+ downloads it automatically) →bin/garmin-mcpConfigure (binary and source installs; the plugin and the bundle ask for the values instead):
cp .env.example .env chmod 600 .env # only your user can read the key $EDITOR .env # set GARMIN_API_KEY and GARMIN_TZCheck:
garmin-mcp -env-file .env -check # auth ok: access token valid until 2026-11-13 10:33:12 # profile ok: Garmin time zone Europe/Berlin, server time zone Europe/Berlin (CET +01:00) # data ok: 7 of the last 7 days have a daily summaryClaude Desktop, in
claude_desktop_config.json, if you prefer the binary over the bundle:{ "mcpServers": { "garmin": { "command": "/absolute/path/garmin-mcp", "args": ["-env-file", "/absolute/path/.env"] } } }Ask Claude, for example:
"Analyse my sleep over the last month and give me recommendations"
"How recovered am I today? Is a hard workout a good idea?"
"How have my resting heart rate and HRV changed over six months?"
"What hurts my sleep more: stress or late workouts?"
Or run a ready-made prompt. In Claude Code they are
/mcp__garmin__sleep_review,/mcp__garmin__wellbeing_review(both take an optional number of days) and/mcp__garmin__readiness_check.
Related MCP server: garmin-mcp
Tools
Tool | Returns | Period |
| Sleep analysis: average duration vs Garmin's baseline sleep need and weekly shortfall, median sleep onset and wake time and their spread, weekday/weekend shift (social jetlag), stage shares, sleep score trend, sleep heart rate/HRV/SpO2/breathing, and factors from the previous day (stress above your usual, late or hard workouts, low step count, late sleep onset → score, HRV and sleep heart rate with and without; differences are shown only with ≥ 5 nights in each group) | up to 366 days, default 28 |
| Per night: bedtime, wake time, stages, score and sub-scores, heart rate, stress, SpO2, breathing, sleep need, Garmin's feedback | up to 366, 7 |
| One-day readiness snapshot: sleep; HRV, resting HR, sleep HR, breathing rate and yesterday's stress each compared with your own previous 28 days (mean, SD, z-score, days of history); overnight Body Battery charge; training load of the last 7 vs 28 days; plain-language signals and the method used. Baseline-relative signals need ≥ 14 days of history | 1 day |
| Weekly or monthly averages: resting HR, HRV, sleep, steps, stress, Body Battery, intensity minutes, workouts, training load, weight | up to 731 days, 84 |
| Per day: steps, distance, calories, heart rate, intensity minutes, stress, Body Battery, breathing, SpO2 | up to 366, 7 |
| Overnight HRV, 7-day average, personal baseline band, Garmin status | up to 366, 14 |
| Charged/drained, min/max, events (sleep, workouts), Garmin's feedback, optional intraday timeline | up to 31, 3 |
| Heart rate for one day in local-time intervals (5, 10, 15, 20, 30, 60, 120, 180, 240 min) | 1 day |
| Workouts: type, duration, distance, pace/speed, heart rate, time in zones, training load, training effect, VO2max | default 30 days, up to 100 items |
| Full summary of one workout | — |
| Time zone, heart rate zones, Fitness Age, latest weight | — |
| Weigh-ins: weight, BMI, body fat, muscle mass, body water | up to 731, 90 |
| Raw JSON from an allow-listed endpoint (hourly SpO2, daily stress and steps, hydration, …), truncated at 100 KB. Nutrition and menstrual cycle only with | — |
All dates are local calendar dates (YYYY-MM-DD). Sleep is filed under the wake-up date:
the night from 10 to 11 November is 2026-11-11.
Not in the API
The Personal API does not expose Training Readiness, Training Status, Acute Load or a VO2max trend.
garmin_recovery_status computes comparable signals itself (HRV, resting HR, sleep, the sum of
activityTrainingLoad over 7 and 28 days) and labels them as computed by this server, not by Garmin.
Configuration
Variable | Purpose | Default |
| Personal API key. Without it the server starts and lists its tools, but every call returns an error explaining how to set it | — (required for data) |
| Your IANA time zone, e.g. | system zone |
| Cache directory, or | user cache dir: |
|
|
|
|
|
|
|
| off |
Flags: -env-file PATH (the environment wins over the file; the file may only set GARMIN_*), -check, -clear-cache, -version.
How it works
Authentication. The key is exchanged for an access token (a JWT valid for 24 hours; there is no refresh token):
POST /personalapitoken-service/personal-api/personal-api-key/tokenwithAuthorization: ApiKey <key>. Every other request carriesAuthorization: Bearer <token>. The token lives only in memory and is renewed 5 minutes before expiry, and once on HTTP 401. One request performs the renewal; concurrent requests wait for it, but never longer than their own timeout.Range limits. Garmin answers
400when a period is too long: sleep and Body Battery 31 days, stress and steps 28, daily summaries 1 year. The dedicated tools split requests into chunks and run them in parallel (at most 4 at a time).garmin_api_getpasses dates through unchanged, so rawstress_daily/steps_dailyrequests must stay within 28 days.Cache. Responses are stored per day in
GARMIN_CACHE_DIR. A day with data that was fetched more than 72 hours after it ended is final and never requested again; more recent days are cached for 10 minutes because the watch keeps syncing. A day without data (watch not worn or not synced yet) is re-checked every 6 hours and becomes final only after 30 days. Re-analysing a year costs a handful of requests, not hundreds. If the watch synced very late and past days came in partially, rungarmin-mcp -clear-cache.Retries. On
429and5xx: up to 3 retries with exponential backoff, honouringRetry-After. Garmin does not document a rate limit.
Privacy and security
The server only reads: the API has no write methods and every tool is marked
readOnlyHint.The key and token are sent only to Garmin's host; redirects are not followed, so the key cannot leak to another address. Logs and error messages never contain the key or token.
The cache holds health data: the
v1directory insideGARMIN_CACHE_DIRgets mode0700, files0600; symlinks and oversized files in the cache are ignored. Clear it withgarmin-mcp -clear-cacheor by deleting the directory.-env-fileaccepts onlyGARMIN_*variables, so a tampered file cannot setHTTPS_PROXYorSSL_CERT_FILEto intercept the key.Nutrition and menstrual-cycle data never reach the model unless you set
GARMIN_ALLOW_SENSITIVE=1.Whatever the tools return is sent to the model provider (Anthropic for Claude) as part of the conversation.
.envand*.dbare in.gitignore. If the key leaks, delete it on the Personal API Key page; every token issued for it stops working too.Recommendations based on this data are not medical advice.
Development
make test # unit tests with -race against a synthetic fake Garmin API (no real data needed)
make lint # golangci-lint v2
make vuln # govulncheck
make live # every tool against the real API (needs .env); prints response sizes only
make all # lint + test + vuln + build
make ci-lint # validate the GitHub Actions workflows (actionlint)Releases
Push a tag vX.Y.Z from a commit where plugin/.claude-plugin/plugin.json has version X.Y.Z.
.github/workflows/release.yml then builds the archives and garmin-mcp.mcpb (GoReleaser),
pushes ghcr.io/sasha-sup/garmin-mcp:X.Y.Z (amd64 and arm64) and publishes server.json to the
MCP Registry with GitHub OIDC (no stored secrets).
CI
GitHub Actions (.github/workflows/ci.yml) runs on pushes to main, on pull requests, weekly
(new vulnerabilities appear without code changes) and on demand:
Job | What it does |
Lint |
|
Test |
|
Vulnerability scan |
|
Actions are pinned to commit SHAs, the workflow has only contents: read, and checkout does not
persist the token. Dependabot proposes Go module and action updates weekly. The live test against
the real API does not run in CI: no key is given to CI.
Layout:
cmd/garmin-mcp/ entry point: flags, config, stdio server
internal/config/ environment and .env loading
internal/garmin/ HTTP client (token, retries, limits), response models, cached range-aware service
internal/cache/ file cache with atomic writes
internal/tools/ MCP tools, sleep and recovery analysis, prompts
internal/civil/ calendar date without timeGarmin's official OpenAPI spec is embedded in
https://developer.garmin.com/gc-developer-program/personal/swagger-docs/; the response formats in
internal/garmin/models.go were checked against live API responses (November 2026).
License
Available Tools
13 toolsgarmin_activitiesARead-onlyIdempotent
List workouts in a date range, newest first: type, duration, distance, pace or speed, heart rate, time in HR zones, training load, training effect, VO2max, Body Battery impact, sets/reps. Default last 30 days, up to 100 items.
Inputs: days, limit, search, end_date, +2 more. Returns: to, from, count, truncated, +1 more
garmin_activityARead-onlyIdempotent
Full summary of one workout by activity_id (from garmin_activities): all measurements Garmin computed, including temperature, cadence, power, training effect messages.
Inputs: activity_id (required). Returns: name, type, units, summary, +2 more
garmin_api_getARead-onlyIdempotent
Raw JSON from a Garmin Personal API endpoint not covered by other tools (SpO2 detail, stress/steps series, hydration, nutrition, menstrual cycle, fitness age history, HRV readings). Responses are large and truncated at 100 KB: prefer the dedicated tools.
Inputs: endpoint (required), date, query, end_date, +2 more.
garmin_body_batteryARead-onlyIdempotent
Per-day Body Battery (Garmin's 0-100 energy estimate): charged, drained, min/max, events that changed it (sleep, activities, naps) and Garmin's feedback. Optional intraday timeline. Range up to 31 days, default last 3.
Inputs: days, end_date, start_date, include_timeline. Returns: to, days, from
garmin_daily_summaryARead-onlyIdempotent
Per-day wellness totals: steps, distance, calories, resting/min/max heart rate, intensity minutes, stress, Body Battery range, respiration, SpO2. Range up to 366 days, default last 7.
Inputs: days, end_date, start_date. Returns: to, days, from
garmin_heart_rateARead-onlyIdempotent
Intraday heart rate for one day, aggregated into local-time intervals (avg/min/max bpm per interval), plus resting, min, max and 7-day resting average.
Inputs: date, interval_minutes. Returns: date, series, max_hr_bpm, min_hr_bpm, +4 more
garmin_hrvARead-onlyIdempotent
Per-night heart rate variability (HRV, ms): last-night average, 5-minute high, 7-day average, personal baseline band and Garmin's status (BALANCED, UNBALANCED, LOW, POOR). Range up to 366 days, default last 14.
Inputs: days, end_date, start_date. Returns: to, from, nights
garmin_profileARead-onlyIdempotent
User context for interpreting other data: time zone and units, heart rate zones per sport, Fitness Age with its components, latest weight.
Returns: hr_zones, warnings, fitness_age, latest_weight, +3 more
garmin_recovery_statusARead-onlyIdempotent
Readiness snapshot for one day (default today): last night's sleep; HRV, resting HR, sleep HR, breathing rate and yesterday's stress each compared with the user's own previous 28 days (mean, SD, z-score, days of history); Body Battery recharge; training load of the last 7 vs 28 days; plain-language signals with the method used. Use this first for 'how recovered am I' questions.
Inputs: date. Returns: hrv, date, method, signals, +9 more
garmin_sleepARead-onlyIdempotent
Per-night sleep: bedtime, wake time, duration, deep/light/REM/awake minutes, sleep score with sub-scores, average heart rate, sleep stress, SpO2, respiration, sleep need and Garmin's feedback. Filed under the wake-up date. Range up to 366 days, default last 7.
Inputs: days, end_date, start_date. Returns: to, from, nights
garmin_sleep_analysisARead-onlyIdempotent
Sleep quality analysis over a period (default 28 days, up to 366): average duration vs Garmin's baseline sleep need and weekly shortfall, regularity (median sleep onset and wake time, spread, weekday-weekend shift), stage shares, sub-score counts, score trend, sleep heart rate, HRV, resting HR, SpO2, respiration, and factors from the previous day (stress above usual, late or hard workouts, low activity, late sleep onset) compared with sleep score, HRV and sleep HR. Use this first for sleep questions and recommendations.
Inputs: days, end_date, start_date. Returns: to, from, notes, factors, +9 more
garmin_trendsARead-onlyIdempotent
Weekly or monthly averages over a long period (default 12 weeks, up to 2 years): resting HR, HRV, sleep duration and score, steps, stress, Body Battery peak, intensity minutes, workout count, workout time, training load, weight. Small output; use it for progress and long-term patterns.
Inputs: days, period, end_date. Returns: to, from, note, period, +2 more
garmin_weightARead-onlyIdempotent
Daily weigh-ins: weight (kg), BMI, body fat, muscle mass, body water where available. Range up to 2 years, default last 90 days.
Inputs: days, end_date, start_date. Returns: to, from, entries
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 13 tools
Each tool targets a distinct Garmin data domain, and descriptions explicitly prioritize tools (e.g., garmin_recovery_status for recovery questions, garmin_sleep_analysis for sleep questions). Potential confusion exists between closely related analytical tools like garmin_daily_summary, garmin_trends, and garmin_recovery_status, but the descriptions clarify their boundaries well.
All tools consistently follow the garmin_ prefix with snake_case naming throughout. The singular/plural distinction between garmin_activity and garmin_activities is clear and intentional.
13 tools is well within the ideal 3-15 range and each covers a distinct, useful slice of Garmin health and fitness data. No tool feels redundant or excessive.
The surface covers a broad range of wellness domains with a strategic raw-API escape hatch for uncovered endpoints like hydration or nutrition. Minor gaps include dedicated structured workout or training plan tools, but the raw API fallback mitigates these.
Maintenance
Related MCP Connectors
Unofficial Garmin Connect access: sleep, HRV, training, activities, workouts and weight.
Garmin watch data for AI assistants: activities, sleep, HRV, recovery. Never writes to Garmin.
Your swim, bike and run training: readiness, HRV, sleep, workouts and race predictions. Read-only.
Read-only Apple Health data synced from your iPhone: sleep, heart rate, HRV, workouts and trends.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceExposes personal Garmin wellness data through MCP tools for accessing summary, sleep, HRV, heart rate, stress, body battery, and historical data.MIT
- AlicenseNot gradedqualityCmaintenanceProvides read access to Garmin Connect health and training data (sleep, heart rate, stress, HRV, activities) and supports writes like creating workouts, editing activities, and exporting files.854 npmMIT
- FlicenseAqualityDmaintenanceProvides read-only access to your Garmin Connect health data, including sleep, HRV, body battery, stress, training readiness, and activities, through an MCP server.214-
- AlicenseAqualityBmaintenanceGarmin Connect for Claude and any MCP client, on Garmin's official Personal API. Read-only access to sleep, HRV, Body Battery, stress, heart rate, activities and weight with a revocable key, no password or scraping.261,568 PyPI69MIT