Skip to main content
Glama
durely insights
by durely

durely garmin-insights-mcp

npm Node Docker CI License

Ask your AI assistant about your own Garmin data: your full training history, health and recovery, analysed.

durely insights is an MCP server that connects your Garmin Connect account to your AI assistant (Claude, Cursor, GitHub Copilot, Gemini CLI, Codex or any other MCP client) through Garmin's official Personal API. It keeps a copy of your history on your own machine, so you can ask about a single run or about years of training and get an answer right away.

Early development. Everything below works, but tools and options can still change before version 1.0.

Unofficial project: not affiliated with or endorsed by Garmin.

What you can ask

Activities

  • "What did I do this week?" / "Show my last 10 runs"

  • "Tell me about yesterday's run": splits, heart rate, training effect, load

  • "How did my power and heart rate evolve over the intervals of Tuesday's ride?"

  • "Compare the bike legs of my last two triathlons lap by lap"

  • "Am I getting fitter on my regular Tuesday run?"

Training

  • "How has my training load and fitness developed over the last 3 months?"

  • "Analyse Saturday's ride: time in zones, decoupling, best efforts"

  • "How many kilometres did I run, ride and swim per year?"

Health and recovery

  • "How did I sleep this week, and how was my HRV?"

  • "Does a hard training day affect my HRV the next morning?"

  • "Compare my resting heart rate and sleep score per month this year"

  • "Show my heart rate and Body Battery through yesterday"

Related MCP server: GarminSynapse

What makes it different

  • Official API, no password. It uses a Personal API key that you create in Garmin Connect and can revoke at any time. No scraping, nothing that breaks when Garmin changes its login.

  • Your whole history, instantly. Everything is synced once to a local database and kept up to date, so trend questions over months or years don't wait on Garmin.

  • Second-by-second detail. Laps, swim lengths, heart rate, power, cadence and more come from each activity's original FIT file, including VO2max, recovery time and lactate threshold when your device records them.

  • Analysis built in. Time in zones, training load and fitness/fatigue/form, aerobic decoupling, best efforts, recovery trends and comparisons of similar sessions. For anything else, your assistant can run read-only SQL over your data.

  • Private by default. Your data stays on your machine. Your name, account ID and GPS positions are kept out of answers unless you ask for a route.

  • Read-only. It can't change anything in your Garmin account.

Quick start

You need Garmin Connect, an MCP client (Claude Code, Claude Desktop, Cursor, …) and either Node.js 24+ or Docker.

1. Create your Personal API key

  1. In Garmin Connect on the web, open Settings → Personal API Token.

  2. Create a key and pick how long it stays valid (30 days to 1 year). Garmin shows it only once, so copy it.

  3. Save it by running this in your own terminal:

    npx -y @durely/garmin-insights-mcp auth

    It asks for the key (your input stays hidden), checks it with Garmin and saves it to ~/.config/garmin/connect-personal-api-key, readable only by you. If Garmin rejects the key, nothing is saved. The server reads the key from this file, so it doesn't go in your client config.

    With Docker (no Node.js), save it to that file yourself:

    mkdir -p ~/.config/garmin && chmod 700 ~/.config/garmin
    pbpaste > ~/.config/garmin/connect-personal-api-key    # macOS; elsewhere, paste it into this file with an editor
    chmod 600 ~/.config/garmin/connect-personal-api-key

Don't paste the key into a chat with your AI assistant.

2. Choose how to run it

With Node.js (npx): nothing to install. Your server command is:

npx -y @durely/garmin-insights-mcp

npx downloads the npm package on first use and picks up new releases by itself. Check your key with npx -y @durely/garmin-insights-mcp auth --check. Prefer a fixed install? npm install -g @durely/garmin-insights-mcp gives you the garmin-insights command.

Or with Docker (no Node.js needed; Docker must be running). Your server command is:

docker run -i --rm \
  -v ~/.config/garmin/connect-personal-api-key:/run/secrets/garmin_api_key:ro \
  -v garmin-mcp-data:/data \
  -e GARMIN_MCP_TIMEZONE=Europe/Brussels \
  ghcr.io/durely/garmin-insights-mcp

Replace Europe/Brussels with your own time zone. To check your key, run the same command with auth --check at the end. The image runs on Intel and ARM (Apple silicon, Raspberry Pi). More Docker tips are in How it works.

Or from source: clone the repo and build it with npm or Docker. See How it works.

Updating: with npx, restart your client. With Docker, run docker pull ghcr.io/durely/garmin-insights-mcp. Your synced data is kept.

3. Add it to your AI client

Claude Code:

claude mcp add garmin --scope user -- npx -y @durely/garmin-insights-mcp

With Docker:

claude mcp add garmin --scope user -- docker run -i --rm \
  -v ~/.config/garmin/connect-personal-api-key:/run/secrets/garmin_api_key:ro \
  -v garmin-mcp-data:/data \
  -e GARMIN_MCP_TIMEZONE=Europe/Brussels \
  ghcr.io/durely/garmin-insights-mcp

--scope user makes it available in all your projects.

Claude Desktop: open Settings → Developer → Edit Config, add this, and restart Claude Desktop:

{
  "mcpServers": {
    "garmin": {
      "command": "/opt/homebrew/bin/npx",
      "args": ["-y", "@durely/garmin-insights-mcp"]
    }
  }
}

Desktop apps don't see your shell's PATH, so use the full path to npx (find it with which npx). If the log then says node isn't found, add "env": { "PATH": "/opt/homebrew/bin:/usr/bin:/bin" }, with the folder from which node first.

Claude Desktop with Docker:

{
  "mcpServers": {
    "garmin": {
      "command": "/usr/local/bin/docker",
      "args": [
        "run",
        "-i",
        "--rm",
        "-v",
        "/Users/you/.config/garmin/connect-personal-api-key:/run/secrets/garmin_api_key:ro",
        "-v",
        "garmin-mcp-data:/data",
        "-e",
        "GARMIN_MCP_TIMEZONE=Europe/Brussels",
        "ghcr.io/durely/garmin-insights-mcp"
      ]
    }
  }
}

Use full paths here; ~ isn't expanded in JSON configs.

Cursor: the same JSON as Claude Desktop, in ~/.cursor/mcp.json (global) or .cursor/mcp.json (project).

VS Code (GitHub Copilot agent mode): in .vscode/mcp.json, or via the command palette → MCP: Add Server:

{
  "servers": {
    "garmin": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@durely/garmin-insights-mcp"]
    }
  }
}

Gemini CLI:

gemini mcp add garmin npx -- -y @durely/garmin-insights-mcp

OpenAI Codex CLI:

codex mcp add garmin -- npx -y @durely/garmin-insights-mcp

Others (Windsurf, Cline, Zed, …): look for "MCP servers" in their settings. Any client that supports stdio servers works.

4. Try it

Start a new session and ask: "Check my Garmin connection." It should report connected.

The first question starts a sync in the background. Your activities are there within seconds; the rest of your history follows in a few minutes. After that, the data refreshes automatically when it's more than an hour old.

Good to know

  • Keep your key's expiry date in mind. Garmin's API doesn't tell the server when your key expires. Set GARMIN_API_KEY_EXPIRES (e.g. 2027-10-24) in your client config and garmin_status reminds you in time. With Docker, pass it to the container instead: add -e GARMIN_API_KEY_EXPIRES=2027-10-24 to the docker run arguments. Replacing the key file is enough; no restart is needed.

  • Sync at least every few months. Garmin deletes intraday detail (heart rate through the day, overnight HRV readings) after about 4–5 months. Whatever the server has synced stays in your local copy.

  • Desktop and CLI clients only, for now. The server runs on your own machine, so it doesn't work in claude.ai on the web or mobile yet.

  • Your data goes to your AI client. The server only talks to Garmin, but the answers it gives go to the MCP client and model you use.

Troubleshooting

Problem

Fix

Tools don't show up

Start a new session or restart the client. In Claude Code, check /mcp.

spawn npx ENOENT / server won't start (desktop apps)

Use the full path to npx or docker in the config.

"No Garmin Personal API key configured"

The key file is missing or not mounted. Check the path (and the -v mount for Docker).

"Garmin rejected the Personal API key"

The key expired or was revoked. Create a new one and replace the file (or run auth again). auth --check tests it.

Dates are off by a day

Set GARMIN_MCP_TIMEZONE to your time zone (especially in Docker). garmin_status warns when it differs from Garmin.

Want to see the tools without an AI client

npx @modelcontextprotocol/inspector npx -y @durely/garmin-insights-mcp

Tools

You don't need to call these yourself; your assistant picks them. For reference:

Tool

What it does

garmin_status

Connection check: key, token, time zone, key expiry

garmin_get_profile

Profile settings, heart rate zones per sport, fitness age

garmin_list_activities

Search your activities by date, sport, name, distance or duration

garmin_get_activity

One activity in full: summary metrics, splits, structured workout

garmin_get_activity_laps

Laps from the FIT file: pace, HR, power, cadence, swim lengths, VO2max, recovery time

garmin_get_activity_streams

Second-by-second HR, power, speed, cadence, altitude, …; zoom into any part

garmin_analyze_activity

Time in zones, NP/IF/TSS, aerobic decoupling, best efforts, intervals

garmin_compare_activities

2–6 activities side by side

garmin_training_summary

Per week or month: volume per sport, load, intensity, fitness/fatigue/form

garmin_recovery_trends

Sleep, HRV, resting HR, Body Battery and stress against your baseline, and their link with load

garmin_get_daily_health

Per day, week or month: steps, resting HR, stress, Body Battery, calories, sleep, HRV

garmin_get_sleep

Nightly sleep stages, times and sleep score

garmin_get_hrv

Nightly HRV, 7-day average, baseline and status

garmin_get_intraday

One day hour by hour: heart rate, Body Battery, overnight HRV, SpO2

garmin_get_body_composition

Weigh-ins, body composition, weight goal

garmin_get_nutrition

Food and hydration logged in Garmin Connect or a linked app

garmin_get_womens_health

Menstrual cycle tracking

garmin_query

Read-only SQL over your synced data

garmin_download_activity_fit

Save an activity's original FIT file, e.g. to import elsewhere

garmin_sync

Sync now, go further back in time, or download all FIT files

garmin_sync_status

What's synced, and sync progress

In clients that support prompts (often as slash commands), weekly_review, activity_deep_dive and recovery_check start a guided analysis.

Configuration

All optional; set them as environment variables in your client config.

Variable

Default

Purpose

GARMIN_API_KEY_EXPIRES

Your key's expiry date (yyyy-MM-dd), for reminders

GARMIN_MCP_TIMEZONE

system time zone

Your time zone (IANA name, e.g. Europe/Brussels)

GARMIN_PERSONAL_API_KEY

The key itself, instead of the key file

GARMIN_PERSONAL_API_KEY_FILE

~/.config/garmin/connect-personal-api-key

Where the key file is

GARMIN_MCP_DATA_DIR

~/.garmin-insights-mcp

Where your synced data is kept

GARMIN_MCP_AUTO_SYNC

true

Refresh automatically when the data is over an hour old

GARMIN_MCP_DETAIL_DAYS

150

Days of intraday detail (HR, overnight HRV, SpO2) to keep synced

GARMIN_MCP_REQUESTS_PER_SECOND

4

Request throttle; Garmin publishes no limits

GARMIN_MCP_LOG_LEVEL

info

debug, info, warn or error (logs go to stderr)

In Docker, the key file and data directory are /run/secrets/garmin_api_key and /data.

Privacy and security

  • Your key stays on your machine. It's exchanged for a 24-hour token, cached with owner-only permissions. The server never writes the key itself. To revoke access, delete the key in Garmin Connect.

  • No telemetry. The server only talks to Garmin's Personal API.

  • Your synced data is sensitive. The local database and FIT files hold your health and activity history, including GPS tracks. The data directory is readable only by you (in Docker it lives in the garmin-mcp-data volume).

  • Identity and location stay out of answers. Your name, profile picture and account ID are removed before anything is stored. GPS positions are only returned when you ask for route or location analysis (or query an activity's start and end point with SQL).

More

  • How it works: the local cache and sync, FIT files, how the numbers are calculated, Docker details, development

License

Apache-2.0. See LICENSE and NOTICE.

Data provided by Garmin devices via Garmin Connect; the analyses are insights derived in part from Garmin device-sourced data. Garmin and Garmin Connect are trademarks of Garmin Ltd. or its subsidiaries. This project is not affiliated with or endorsed by Garmin.

Available Tools

21 tools
garmin_analyze_activityAnalyze an activityA
Read-onlyIdempotent

Compute training metrics for one activity from its second-by-second FIT data:

  • time in heart rate zones (the user's Garmin zones for that sport) and, with an FTP, in power zones (Coggan)

  • power: average, normalized power (NP), variability index, intensity factor and TSS (needs FTP)

  • aerobic decoupling: drift of power or pace per heartbeat between the first and second half (<5% is good for steady efforts of 45+ min)

  • first vs second half (negative/positive split)

  • best efforts: power over 5 s–60 min, heart rate over 1–60 min, fastest 400 m–marathon (running), 5–90 km (cycling), 100 m–3.8 km (swimming)

  • Edwards TRIMP (zone-weighted minutes) and detected work intervals with their consistency

FTP comes from ftp_w, else from the activity's own record (Garmin cycling FTP). Works for multisport legs too.

Inputs: activity_id (required), ftp_w, response_format.

garmin_compare_activitiesCompare activitiesA
Read-onlyIdempotent

Compare 2–6 activities side by side: date, type, duration, distance, pace/speed, heart rate, power (avg/NP), elevation, training effect and load, cadence, and efficiency factor (speed or NP per heartbeat; higher means more output for the same heart rate, a classic way to track aerobic progress on similar sessions).

With deep=true, also analyzes each one's FIT data (downloads files if needed): aerobic decoupling, time in low/moderate/high HR zones and best efforts.

Example: "Am I getting fitter on my Tuesday interval run?" → list those runs, then compare them.

Inputs: activity_ids (required), deep, response_format.

garmin_download_activity_fitExport activity FIT fileA
Idempotent

Save the original FIT file of an activity to the server's exports folder and return its path, e.g. to import it into another tool (Intervals.icu, Golden Cheetah, a spreadsheet converter).

The file is exactly what the device recorded. Saved as /exports/<activity_id>.fit; an existing export is kept, not overwritten. For a multisport leg, exports the whole multisport file.

Inputs: activity_id (required).

garmin_get_activityGet Garmin activity detailsA
Read-onlyIdempotent

Get full details for one Garmin activity by ID.

Returns: name, description, type, start time and time zone, device, summary metrics (duration, distance, speeds, HR min/avg/max, power avg/max/NP/20-min, FTP, cadence, stride, calories, training effect + message, training load, TSS, IF, temperature, intensity minutes), split summaries (e.g. run/walk/stand intervals), the structured workout if one was followed, and multisport parent/child IDs.

Get IDs from garmin_list_activities. Example: "Tell me about yesterday's run" -> list activities for that date, then call this with its id.

Inputs: activity_id (required), response_format.

garmin_get_activity_lapsGet activity lapsA
Read-onlyIdempotent

Get the laps (and sessions, swim lengths and Garmin's physiological metrics) of one activity, from its original FIT file.

Returns per lap: start offset, duration (elapsed and timer), distance, pace/speed, avg/max HR, avg/max/normalized power, cadence, ascent/descent, intensity (active/rest/warmup/cooldown) and lap trigger; for swims, stroke and lengths. Also: device, multisport sessions, and FIT-only metrics when the device recorded them (VO2max, recovery time, lactate threshold HR/power).

Use it for interval sessions, splits and pacing. Downloads the FIT file on first use (one request), then it's cached. For a multisport leg ID, returns only that leg's laps.

Inputs: activity_id (required), lap_offset, response_format.

garmin_get_activity_streamsGet activity time seriesA
Read-onlyIdempotent

Get second-by-second data from one activity's FIT file, downsampled to fit in context.

Returns per field min/avg/max over the window, plus columnar series: t (seconds from the start of the activity, or of the leg for a multisport leg) and one array per field. Units: heart_rate bpm, power W, speed m/s, cadence steps/min (running) or rpm (cycling) or strokes/min, altitude m, distance m (cumulative), temperature °C, respiration_rate breaths/min, performance_condition (Garmin's -20..+20 score), vertical_oscillation mm, stance_time ms. Cadence averages leave out zeros.

Defaults to heart_rate, power, speed, cadence, altitude (whichever the activity has) at 10 s resolution, at most 500 points; the resolution coarsens automatically for long activities. Zoom in with start_s/end_s (e.g. one interval from garmin_get_activity_laps' start offsets) and a smaller resolution_s.

"position" adds GPS latitude/longitude and reveals where the user was: only request it when the user asks for location or route analysis. Developer fields (e.g. running power from a Connect IQ app) can be requested by the name listed in available_fields, like "dev_RP_Power".

Inputs: activity_id (required), end_s, fields, start_s, +3 more.

garmin_get_body_compositionWeight and body compositionA
Read-onlyIdempotent

Get weigh-ins from the local cache: weight (kg), BMI, body fat %, body water %, muscle and bone mass (kg), visceral fat, and where each came from (scale, manual, linked app). Includes the change over the period and the weight goal if one is set.

Only days with a weigh-in appear. Defaults to the last 365 days.

Inputs: end_date, start_date, response_format.

garmin_get_daily_healthDaily health and recoveryA
Read-onlyIdempotent

Get day-by-day wellness and recovery data, from the local cache (all history).

Per day: steps (+ goal), floors, resting/min/max heart rate, stress (average, max, % of the day at rest/low/medium/high), Body Battery (min, max, charged, drained), intensity minutes (moderate, vigorous), calories (total, active), respiration, hydration, plus that night's sleep (duration, score) and HRV (last-night average, status).

group_by="week" (Monday-based) or "month" aggregates: averages per day, minimums/maximums for min_hr/max_hr/stress_max, and totals plus per-day averages for steps and intensity minutes.

Defaults to the last 14 days. Daily rows are limited to 92 days per call; use group_by="week" or "month" for longer periods (e.g. a year by week). Today's values are partial.

Examples: "How's my recovery this week?" → defaults. "Resting HR trend this year" → start_date="2026-01-01", group_by="week".

Inputs: end_date, group_by, start_date, response_format.

garmin_get_hrvHeart rate variabilityA
Read-onlyIdempotent

Get nightly HRV (heart rate variability, in ms) from the local cache: last-night average, highest 5-minute value, 7-day average, the personal baseline range (balanced low/high) and Garmin's status (BALANCED, UNBALANCED, LOW, POOR).

With readings_date, also returns that night's 5-minute HRV readings (fetched from Garmin if older than the synced detail window).

Defaults to the last 28 nights, at most 366 per call. HRV needs a compatible watch worn overnight; history starts when that began.

Inputs: end_date, start_date, readings_date, response_format.

garmin_get_intradayIntraday health dataA
Read-onlyIdempotent

Get one day's health data through the day, averaged into intervals: heart rate (bpm), Body Battery (0-100), overnight HRV readings (ms) and SpO2 (pulse ox, %).

Returns per metric: min/avg/max, the day's summary values (e.g. resting HR, Body Battery charged/drained, SpO2 averages), and a columnar series (local times "HH:MM" + values). Synced for the last 150 days; older days are fetched from Garmin on demand. Garmin itself keeps intraday heart rate and HRV readings for only about 4–5 months (Body Battery longer), so older days may have just the day summary.

Defaults to today, all four metrics, 15-minute intervals. HRV and SpO2 need a compatible watch (SpO2 also needs pulse ox enabled).

Inputs: date, metrics, interval_min, response_format.

garmin_get_nutritionNutrition and hydrationA
Read-onlyIdempotent

Get food logged in Garmin Connect (or a linked app such as MyFitnessPal) per day: calories, carbs, protein and fat against the day's goals, and meals; plus hydration (ml) from the daily summary.

Food logs are fetched from Garmin on demand and cached. Defaults to the last 7 days, at most 31 per call.

Inputs: end_date, start_date, response_format.

garmin_get_profileGet Garmin profile and zonesA
Read-onlyIdempotent

Get the user's Garmin profile settings, heart rate zones per sport, and fitness age.

Returns: units and locale, time zone, preferred training days, HR zone floors per sport (with the max HR / LTHR / resting HR used to compute them), and the latest fitness age.

Use this before interpreting heart rate data (to know the user's zones), or when the user asks about their zones, max HR, lactate threshold or fitness age.

Inputs: response_format.

garmin_get_sleepSleepA
Read-onlyIdempotent

Get nightly sleep from the local cache: bed and wake time, total sleep, deep/light/REM/awake time, naps, sleep score with quality and Garmin's feedback, awake count, average respiration and sleep stress. JSON mode adds the sleep score's components (duration, stress, deep/light/REM %, restlessness, awake count) with Garmin's rating each.

A night belongs to the date you woke up. Defaults to the last 14 nights, at most 92 per call.

Inputs: end_date, start_date, response_format.

garmin_get_womens_healthMenstrual cycleA
Read-onlyIdempotent

Get menstrual cycle tracking from Garmin Connect for a date: cycle day, current phase and days until the next, period and cycle length, predicted next cycle, fertile window and ovulation date, plus that day's logged symptoms and notes (day view). Fetched live from Garmin.

Returns a clear "not tracked" message when cycle tracking isn't set up. Defaults to today.

Inputs: date, response_format.

garmin_list_activitiesList Garmin activitiesA
Read-onlyIdempotent

Search the user's Garmin activities (all history, from the local cache), newest first by default.

Returns one page of compact summaries plus the total number of matches: id, date, type, name, duration, distance, pace/speed, HR, power, elevation, training effect, training load, TSS/IF and VO2max where available, plus time in HR/power zones (JSON mode).

Examples:

  • "My runs this month" -> activity_type="running", start_date="2026-10-01"

  • "Longest rides ever" -> activity_type="cycling", sort_by="distance"

  • "Zwift sessions" -> activity_type="virtual_ride"

  • "Triathlon bike legs" -> include_multisport_legs=true, activity_type="road_biking"

The cache refreshes automatically when it's more than an hour old. Use garmin_get_activity for one activity's full details (splits, FTP, workout).

Inputs: limit, offset, search, sort_by, +10 more.

garmin_queryQuery the Garmin cache with SQLA
Read-onlyIdempotent

Run one read-only SQLite query against the local Garmin cache, for analyses the other tools don't cover (custom aggregations, correlations, records, comparisons across years).

Views (one row per item; use these first):

  • activities(id, parent_id, is_multisport_parent, type_key, sport, name, start_local, start_date, duration_s, moving_s, distance_m, elevation_gain_m, avg_speed_mps, avg_hr, max_hr, avg_power_w, norm_power_w, calories, aerobic_te, anaerobic_te, training_load, tss, vo2max) sport = Garmin parent type (running, cycling, swimming, walking, ...); type_key = exact type (road_biking, virtual_ride, lap_swimming, ...). Multisport: parent rows have is_multisport_parent=1, legs have parent_id; to count volume use is_multisport_parent = 0.

  • daily(date, steps, step_goal, walk_distance_m, floors_up, resting_hr, min_hr, max_hr, stress_avg, stress_max, stress_rest_pct, stress_low_pct, stress_medium_pct, stress_high_pct, body_battery_min, body_battery_max, body_battery_charged, body_battery_drained, intensity_moderate_min, intensity_vigorous_min, calories_total, calories_active, respiration_avg, hydration_ml)

  • sleep(date, start_local, end_local, sleep_s, deep_s, light_s, rem_s, awake_s, nap_s, score, quality, feedback, awake_count, respiration_avg, sleep_stress_avg) -- date = the morning you woke up

  • hrv(date, last_night_avg, last_night_5min_high, weekly_avg, baseline_low, baseline_high, status)

  • weight(date, weight_kg, bmi, body_fat_pct, body_water_pct, muscle_mass_kg, bone_mass_kg, visceral_fat, source, entries) Raw JSON (for fields not in the views, via json_extract):

  • activity(id, ..., raw_json) -- Garmin's full activity summary, e.g. json_extract(raw_json, '$.maxAvgPower_1200'), '$.hrTimeInZone_2', '$.fastestSplit_5000', '$.avgStrideLength'

  • daily_raw(domain, date, json) -- domains: daily_summaries, sleep, hrv, weight, body_battery, hrv_readings, heart_rate, spo2, food_logs Dates are 'yyyy-MM-dd' text in the user's local calendar; durations in seconds, distances in meters, speeds in m/s. SQLite dialect: strftime('%Y-%W', date) for weeks, date(date, '-7 days'), window functions (AVG(x) OVER (ORDER BY date ROWS 6 PRECEDING)) are supported.

Rules: one SELECT or WITH statement; at most max_rows rows returned (default 200); 10-second timeout. Prefer aggregating in SQL over returning many rows. Example: SELECT strftime('%Y', start_date) AS year, sport, ROUND(SUM(distance_m)/1000) AS km FROM activities WHERE is_multisport_parent = 0 GROUP BY 1, 2 ORDER BY 1, 3 DESC

Inputs: sql (required), max_rows, response_format.

garmin_statusGarmin connection statusA
Read-onlyIdempotent

Check that the Garmin Personal API connection works and report its configuration.

Reports: where the API key comes from, whether a token could be obtained and when it expires, the API key's expiry (if GARMIN_API_KEY_EXPIRES is set), the time zone used for calendar dates, and the server version.

Use this first when another garmin_* tool fails with an authentication error, or when the user asks whether their Garmin connection is set up.

Inputs: response_format.

garmin_syncSync Garmin dataA
Idempotent

Start downloading Garmin data into the local cache. Runs in the background and returns immediately; follow progress with garmin_sync_status.

You rarely need this: other tools sync automatically on first use and when the cache is over an hour old. Use it to:

  • fetch the latest data right now (e.g. "I just finished a run"),

  • extend history further back (from="2019-01-01"),

  • re-download everything (mode="full"), e.g. after editing many activities in Garmin Connect.

Domains (default: all except fit_files): activities, daily_summaries, sleep, hrv, weight, body_battery, hrv_readings, heart_rate, spo2. "fit_files" downloads the original FIT file of every activity (one request each, ~2 activities/s). That's optional: FIT files are otherwise fetched on demand by garmin_get_activity_laps and garmin_get_activity_streams.

Inputs: from, mode, domains.

garmin_sync_statusGarmin sync statusA
Read-only

Show what's in the local Garmin cache and the progress of the current or last sync.

Returns: the running/last sync (progress, errors), and per data domain the date range covered, number of days, whether all history is in, and the last successful sync.

Inputs: response_format.

garmin_training_summaryTraining summaryA
Read-onlyIdempotent

Summarize training per week or month: sessions, hours, distance, elevation and training load, per sport; intensity distribution (share of time in low = HR zones 1–2, moderate = zone 3, high = zones 4–5, with a rough pattern: polarized, pyramidal, threshold-heavy); and the load trend at the end of each period: fitness (CTL, 42-day), fatigue (ATL, 7-day), form (TSB = CTL − ATL) and the acute:chronic ratio (ACWR; above ~1.5 is a commonly cited risk zone).

Load is Garmin's training load per activity; activities without it (older devices, some third-party uploads) get an estimate from average heart rate (Banister TRIMP) scaled to Garmin's load using the user's own activities. The response says how much is estimated. Multisport legs count toward their sport; walking is included as its own sport.

Defaults to the last 12 weeks by week. Filter with sport (e.g. running, cycling, or a type like virtual_ride).

Inputs: sport, end_date, group_by, start_date, +1 more.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4.1/5.0

    Scored across 21 tools

    Disambiguation4/5

    Most tools target clearly distinct Garmin resources or analysis layers: daily health vs. sleep vs. HRV vs. intraday, and activity summary vs. laps vs. streams vs. computed analysis. There is some overlap because daily health includes sleep/HRV summaries, and several analysis tools operate on activities, but descriptions make the intended boundaries mostly clear.

    Naming Consistency4/5

    All tools use a consistent garmin_ snake_case prefix, which makes the set predictable. Most names follow verb_noun patterns (get_*, list_*, analyze_*, compare_*), though a few are noun phrases or slightly irregular (garmin_status, garmin_training_summary, garmin_recovery_trends, garmin_download_activity_fit).

    Tool Count3/5

    At 21 tools, the surface is on the heavy side for an MCP server and includes several closely related health and activity-analysis tools that could be consolidated. The breadth of Garmin domains justifies many of them, but the count is borderline rather than clearly well-scoped.

    Completeness5/5

    The set covers the Garmin insights domain thoroughly: activity listing/detail/laps/streams/FIT export, daily wellness, sleep, HRV, intraday health, body composition, nutrition, women's health, profile zones, training analysis, recovery trends, sync management, and a SQL escape hatch. No obvious core lifecycle gap exists for a read-only analytics server.

    Maintenance

    ActivityMaintained
    ResponsivenessOutdated data

    Related MCP Connectors

    Related MCP Servers

    • F
      license
      Not graded
      quality
      C
      maintenance
      Enables AI assistants to access and query Garmin Connect health and fitness data, including sleep, HRV, training load, and activities, with an optional coaching plugin for personalized training plans.
      9 npm
      4
      -
    • F
      license
      Not graded
      quality
      B
      maintenance
      Enables AI assistants to retrieve and analyze Garmin Connect health data, including daily summaries, sleep, HRV, workouts, and raw FIT files, while also supporting SQL queries on the underlying database.
      10
      -
    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables AI assistants to retrieve and discuss personal fitness activities, training splits, heart-rate zones, and daily health metrics from a Garmin Connect account.
      MIT