Skip to main content
Glama

garmin-mcp

CI License: MIT Python 3.12+ MCP

A Model Context Protocol (MCP) server for Garmin Connect. Ask Claude (or any MCP client) about your sleep, HRV, Body Battery, stress, training readiness, VO2max, runs and rides, and have it build structured workouts that sync to your watch.

"How did I sleep last night, and am I ready for intervals today?" "Show the splits and heart-rate zones of my last run." "Plot my HRV and resting heart rate for September." "Build me 6 x 400 m at 3:50-4:05/km for Thursday." "Where did I lose time on the climbs in Sunday's ride?"

It works with any Garmin device that syncs to Garmin Connect (Forerunner, Fenix, Epix, Venu, Vivoactive, Edge, Index scales...). It runs locally on your computer, and your password never reaches the server.

Why this one

  • Everything, in 39 tools. It covers every one of the 153 tools in Taxuspt/garmin_mcp, mapped one by one in PARITY.md and enforced by a test. Related endpoints sit behind one tool with a metric or kind selector, so the whole server costs a quarter of the context in every chat.

  • A login that survives Garmin's changes. Built on python-garminconnect 0.3.x, which recovered from Garmin's March 2026 login change. You log in once in a terminal (MFA supported). The server only reuses the saved tokens and never retries a password login, because repeated attempts can lock an account for days.

  • Answers a model can use. Compact JSON with the unit in every key (distance_km, avg_hr_bpm, pace_min_km), in your account's time zone, with today's partial data marked. Garmin's unit quirks (grams, RPE ×10, Fahrenheit, knots, centimetres...) are converted in one place and were checked against Garmin Connect on a real account.

  • Deeper analysis. Decodes the original FIT file with Garmin's official FIT SDK: climbs and VAM, time on each grade, aerobic decoupling, power curve, NP/IF/TSS, R-R HRV, running and cycling dynamics, Di2 gear use.

  • Safe writes. Every tool carries MCP annotations, so clients know which ones change your account. Deletes always preview first. GARMIN_MCP_READ_ONLY=1 removes every write.

  • Windows-proof. A Cloudflare 403 on Windows 11 (python-garminconnect #444) is retried automatically through curl_cffi.

Related MCP server: garmin-mcp-bridge

Quick start

You need uv.

1. Log in once, in a terminal. It asks for your Garmin email, password and, if Garmin sends one, the verification code. Only the tokens are saved, to ~/.garmin-mcp/.

uvx --from git+https://github.com/wiande00/garmin-mcp garmin-mcp-auth login

2. Add the server to your MCP client.

Claude Code:

claude mcp add --scope user garmin -- uvx --from git+https://github.com/wiande00/garmin-mcp garmin-mcp

Claude Desktop (claude_desktop_config.json; quit Claude Desktop completely before editing, since it rewrites the file while running):

{
  "mcpServers": {
    "garmin": {
      "command": "uvx",
      "args": ["--from", "git+https://github.com/wiande00/garmin-mcp", "garmin-mcp"]
    }
  }
}

If Claude Desktop can't find uvx, use its full path (which uvx, or where uvx on Windows). Other MCP clients take the same command and arguments. To pin a release, add @v0.1.0 to the git URL.

3. Ask away. Start with "check my Garmin status".

git clone https://github.com/wiande00/garmin-mcp
cd garmin-mcp
uv sync
uv run garmin-mcp-auth login
claude mcp add --scope user garmin -- uv --directory "$PWD" run --quiet garmin-mcp

On Windows with the Microsoft Store edition of Claude Desktop, scripts\add-to-claude-desktop.cmd registers the server for you: double-click it from File Explorer, quit Claude Desktop from its tray icon, and it adds the entry and reopens Claude.

Tools

Tool

What it covers

garmin_status

Login, token lifetimes, last watch sync, time zone, last error

garmin_activities

Activities by date range and type, newest first; limit=0 for the count

garmin_activity

One activity: summary, laps, run/walk or interval splits, weather, HR and power zones, gear, strength sets, training effect, time series

garmin_activity_analysis

From the FIT file: climbs, grades, decoupling, power curve, NP/IF/TSS, R-R HRV, dynamics, Di2

garmin_fit_messages

Every decoded FIT message, paged

garmin_power_curve

Best power for 5 s to 60 min across recent activities, with an FTP estimate

garmin_wellness

One day: summary, sleep, HRV, stress, Body Battery, heart rate, steps, SpO2, respiration, floors, hydration, intensity minutes, events, lifestyle

garmin_training

Readiness, recovery time, training status, load balance, fitness age, VO2max, acclimation, running tolerance, race predictions, lactate threshold, FTP

garmin_trend

A metric over a date range: sleep, HRV, resting HR, steps, stress, calories, Body Battery, training load (acute, chronic, ACWR, form), VO2max, endurance and hill score, weight, totals by sport

garmin_body

Weigh-ins, body composition, blood pressure

garmin_profile

Profile, settings, heart-rate and power zones

garmin_devices

Devices, last sync, settings, primary device, solar intake, alarms

garmin_gear

Shoes and bikes, with distance and default activity types

garmin_achievements

Goals, personal records, badges, challenges

garmin_workouts

Saved workouts as readable steps (or raw JSON)

garmin_calendar

Scheduled workouts, races and events, Garmin Coach plans

garmin_nutrition / garmin_food_search

Garmin Connect food log, goals, energy balance; food search

garmin_courses

Courses and their waypoints

garmin_womens_health

Menstrual cycle and pregnancy tracking

garmin_reference

Activity types, event types, strength-exercise catalog, workout format

garmin_api_get

Read-only GET of any Garmin Connect API path

garmin_download

Save an activity (FIT, GPX, TCX, KML, CSV), workout or course to disk

garmin_create_workout

Build a structured workout from simple steps and schedule it

garmin_upload_workouts / garmin_schedule_workouts

Upload Garmin workout JSON; schedule workouts (never twice on a day)

garmin_update_activity / garmin_create_activity

Rename, retype, describe, RPE, feel, gear; add manual activities

garmin_add_weigh_in / garmin_add_body_composition / garmin_add_blood_pressure / garmin_add_hydration

Log body data

garmin_set_hr_zones / garmin_request_reload

Heart-rate zones; reprocess a day

garmin_set_nutrition_goals / garmin_save_custom_food / garmin_log_food

Garmin Connect nutrition

garmin_upload_course

Create a course from a GPX file

garmin_delete

Delete weigh-ins, blood pressure, workouts, scheduled workouts, foods, courses; always previews first

Five workout templates are also available as MCP resources (workout://templates/...).

Building workouts

garmin_create_workout takes plain steps and emits Garmin's exact workout JSON:

[
  {"type": "warmup", "duration": "10:00", "hr_zone": 2},
  {"repeat": 6, "steps": [
    {"type": "interval", "distance_km": 0.4, "pace_min_km": ["3:50", "4:05"]},
    {"type": "recovery", "duration": "1:30"}
  ]},
  {"type": "cooldown", "lap_button": true}
]

Each step ends one way (duration, distance_km, reps, lap_button) and has at most one target (hr_zone, hr_bpm, pace_min_km, speed_kmh, power_w, power_zone, cadence). Strength steps name an exercise from Garmin's catalog ("exercise": "Barbell Bench Press", "reps": 5, "weight_kg": 80). Running, cycling, swimming, walking, hiking, strength and more are supported.

Configuration

All optional, as environment variables on the server entry:

Variable

Default

Effect

GARMIN_MCP_HOME

~/.garmin-mcp

Where the tokens live; use one per account

GARMIN_MCP_TIMEZONE

the account's

IANA time zone for "today" and timestamps

GARMIN_MCP_READ_ONLY

off

1 removes every tool that changes the account

GARMIN_MCP_ENABLED_TOOLS / GARMIN_MCP_DISABLED_TOOLS

all

Comma-separated allow / deny lists

GARMIN_MCP_DOWNLOAD_DIR

~/Downloads/garmin-mcp

Where downloads go

GARMIN_MCP_HTTP

auto

curl_cffi forces the Windows workaround, requests disables it

GARMIN_MCP_CALL_TIMEOUT

90

Seconds per call; 0 disables

GARMIN_MCP_MAX_CHARS

40000

Cap on answer length

GARMIN_MCP_IS_CN

off

1 for garmin.cn accounts (untested)

garmin-mcp --transport streamable-http --port 8765 serves over HTTP on 127.0.0.1 instead of stdio. That transport has no authentication, so it never listens beyond loopback.

Troubleshooting

Symptom

What to do

"Not logged in" / "session has expired"

Run garmin-mcp-auth login again in a terminal

429 during login

Garmin is rate-limiting. The login command enforces a cooldown (15 min, 60 after a 429); wait it out rather than retrying

403 on every call

Usually Cloudflare. It is retried through curl_cffi automatically; set GARMIN_MCP_HTTP=curl_cffi, and avoid VPNs and datacenter networks

412 on a write

EU accounts must grant upload consent in Garmin Connect's privacy settings first

Training status, race predictions empty

A new watch needs a few weeks of activities first; the tools say so

Anything else

garmin-mcp-auth status tests the saved login; garmin_status shows the last error

How it works

src/garmin_mcp/
  server.py          the MCP server: tool names, parameters, descriptions, annotations
  cli.py             garmin-mcp-auth login | status | logout
  auth.py            tokens, the login and its cooldown, why a login doesn't work
  client.py          one Garmin client per process, error translation, cache
  transport.py       the Windows 403 fallback
  units.py           every Garmin unit quirk
  tools/             what each tool does, testable without MCP
    workout_dsl.py   the workout step language and checks for raw workout JSON
    fit.py           FIT decoding (Garmin FIT SDK) and analysis
  • The server logs in lazily on the first call, so the MCP handshake never waits on Garmin.

  • Calls run in a worker thread with a timeout, serialised so token refreshes stay safe.

  • Range views use Garmin's range endpoints where they exist: a 90-day sleep or HRV trend is a few requests, not 90.

  • Failures keep Garmin's status and text, and each kind gets its own message.

  • garmin_delete deletes only a request previewed in the last 15 minutes.

Development

uv sync
uv run pytest           # no network: a fake Garmin client and synthetic payloads
uv run ruff check
uv run python scripts/smoke.py   # read-only check of every tool against your real account

Issues and pull requests are welcome. For a bug, include the output of garmin_status (it holds no secrets).

Credits

  • python-garminconnect by cyberjunky does the hard part: logging in and talking to Garmin Connect.

  • Taxuspt/garmin_mcp defined the feature set, and its issue tracker was the best guide to what goes wrong. Endpoints that python-garminconnect doesn't wrap follow its source.

  • Garmin FIT SDK decodes activity files.

Disclaimer

This is an unofficial project, not affiliated with or endorsed by Garmin. It uses the same private API as the Garmin Connect apps, which Garmin can change at any time, and Garmin's terms of use don't cover third-party access. Use it with your own account, at your own risk.

License

MIT

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    B
    maintenance
    Enables 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.
    28
    206 PyPI
    3
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    Brings Garmin training data into Claude via Intervals.icu, enabling querying of activities, wellness, and planned workouts, as well as creating structured workouts that sync back to the Garmin watch.
    9
    1
    MIT