garmin-personal-mcp
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| GARMIN_BASE_URL | No | The Garmin Personal API host. | https://personal.connectapi.garmin.com |
| GARMIN_DATA_DIR | No | The data folder used by the server. | ~/workspace/garmin-data |
| GARMIN_API_KEY_FILE | No | The file containing the Garmin Personal API key. | ~/.config/garmin-mcp/api-key |
| GARMIN_PERSONAL_API_KEY | No | The Garmin Connect Personal API key. It has priority over the key file. The automatic dashboard refresh requires the key file. |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": true
} |
| prompts | {
"listChanged": true
} |
| resources | {
"listChanged": true
} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| listActivitiesA | List the user's Garmin activities (most recent first) as a compact summary: id, name, type, local start time, distance (km), moving duration, average pace, average/max heart rate, cadence, ascent; when Garmin has them: elapsed time (if longer than moving time), race flag, Garmin training load, aerobic/anaerobic training effect and label, seconds in HR zones 1-5, calories, moderate/vigorous intensity minutes, body battery change, steps, swim SWOLF, pool length, lengths and strokes, power, mountain-bike grit and flow, running form (stride, ground contact, vertical oscillation and ratio), grade-adjusted pace, best efforts (1k, mile, 5k, 10k, half, marathon) and run/walk segments. Dates are yyyy-MM-dd. Set raw=true for Garmin's full JSON. |
| getActivityA | Get the full detail of one activity (summary, metadata, splits/laps) as JSON. |
| getActivityWorkoutsA | The planned workout that an activity followed, as created in Garmin Connect: its steps in order, each with its intensity (warmup/active/rest/recovery/cooldown), its duration (time in seconds, reps, or open), its target (cadence, heart rate, pace, or open, with low and high values), the exercise for strength steps, and the coach's notes. A step with durationType REPEAT_UNTIL_STEPS_CMPLT repeats the steps from stepIndex durationValue up to it, targetValue times in all. Compare it with getActivitySets or getActivityLaps for plan against done. An empty list means the activity followed no planned workout. Null fields are left out. |
| downloadActivityFitA | Download the ORIGINAL .FIT file of an activity and save it on the user's computer. Returns the absolute path of the saved .fit file (second-by-second HR, cadence, speed, laps). |
| getActivityLapsA | Laps of an activity as recorded by the watch (manual laps, auto-lap every km, or workout steps), read from the original .FIT file. One row per lap: distance (km), timer time, pace (min/km, foot sports) or speed (km/h), average/max HR, cadence (spm for running), ascent/descent, average power, running form (gctMs, vertRatioPct, vertOscCm, strideCm), intensity (active, interval, rest, recovery, warmup, cooldown; the auto laps of a free run can be interval), lap trigger, and the step of the planned workout (step: the reps of one drill share it). Use it for intervals and per-km splits; getActivity does not have them. For a run: split (pace of the first and second half, kind, hrDriftPct) and, for a structured workout, work (reps, pace, avgHR, and the fade of each step with more than one rep, pct). null is n/a. |
| getActivitySetsA | Strength training sets of an activity, read from the original .FIT file. One entry per exercise, in the order first done, with its movement category, its primary and secondary muscles (from Garmin's exercise catalogue) and its active sets: reps, weight (kg, absent when none was recorded), set time in seconds and the rest after the set (restSec). Each exercise with weight also has top (the heaviest set of 1 to 12 reps, then the most reps; without one, the heaviest set) and e1rmKg (the best estimated 1RM, Epley, of its sets of 1 to 12 reps; null when it has none). Exercise names are stable across sessions, so they can be used for per-exercise trends. Edits made later in Garmin Connect (renamed exercises, corrected reps or weights) are not in the .FIT file. |
| getHrvRangeC | Daily HRV (overnight) summaries for a date range, yyyy-MM-dd. |
| getDailyHeartRateB | Daily heart rate for one date (includes resting heart rate), yyyy-MM-dd. |
| getSleepA | Sleep for one night (date: the morning you woke up), or for up to 14 nights (fromDate and toDate), yyyy-MM-dd, as one compact row per night: bedtime and wake (local HH:mm), hours of sleep, deep, light, REM and awake, the sleep score and its quality, the sleep need (hours), average heart rate, average and lowest SpO2, average respiration, awake count, average sleep stress and Garmin's feedback. Missing values are left out, and a night without sleep data has only its date. Set raw=true for Garmin's JSON. |
| getWeightA | Weight for a date range, yyyy-MM-dd: the latest weight of each day (with body fat, muscle and bone mass, BMI and body water when a scale records them), the average of the range, and the last weight before and the first after the range. Set includeAll=true for every entry of each day. Weight, muscle mass and bone mass are in grams. |
| getBodyBatteryA | Body Battery for up to 14 days, yyyy-MM-dd: for each day how much it charged and drained, its readings through the day, and the events that changed it (sleep, activities, stress, naps) with their impact. |
| getHeartRateZonesB | Heart rate zones currently configured in Garmin Connect. |
| getFitnessAgeB | Garmin fitness age for a date, yyyy-MM-dd: fitness age vs chronological age, the achievable fitness age, and its components (body fat, vigorous days and minutes per week, resting HR) with the targets Garmin suggests. |
| getDailySummaryA | Daily user summary (steps, calories, stress, body battery, resting HR) for one date, or for up to 14 days (fromDate and toDate; the range answer has Garmin's v2 field names), yyyy-MM-dd. |
| garminGetA | Generic read-only GET on the Garmin Connect Personal API, for endpoints not covered by the other tools or when a query parameter name has to be adjusted. 'path' is relative and starts with '/', e.g. /fitnessage-service/fitnessage/v2/2026-10-25 . 'query' is an optional URL query string without '?', e.g. date=2026-10-25&foo=bar . |
| getDashboardDataA | All the data of the training dashboard in one call: HR zones, fitness age, the activities of the
last |
| writeDashboardA | Writes the training dashboard page (default /garmin-dashboard.html) with the data of the
last |
| editDashboardA | Changes the layout, the custom widgets or the languages of the dashboard page (a page from the
template; call writeDashboard first). Without arguments it only reads: the reply has the layout, the
custom widgets, the missing texts of each language, the catalogue of built-in widgets and |
| showDashboardA | Shows the training dashboard in the chat, in assistants that show MCP Apps: the page of |
| scheduleDashboardRefreshA | Turns on, changes or stops the automatic refresh of a dashboard page. Use it only when the user
asks for it or says yes to the question of a dashboard prompt. A job in the scheduler of the
computer (launchd, cron or Task Scheduler) replaces the data of the page at the given times, also
with no assistant open. The refresh removes the coach notes, because old notes do not agree
with new data. The computer must be on at that time. The page must exist (writeDashboard first).
|
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
| training-dashboard | Build a personal training + recovery dashboard (volume, load, HRV, sleep) from Garmin data |
| training-dashboard-update | Update the training dashboard with the new Garmin data; it keeps the layout, widgets and languages |
| dashboard-widget | Make a new widget for the training dashboard from your idea |
| dashboard-setup | Set up a dashboard: title, sports, the data it shows (e.g. for a coach), languages, themes, file |
| dashboard-language | Add a language to the training dashboard (the assistant translates its texts) |
| run-review | Review a run as a coach: planned workout against the laps, rep fade, HR drift, recovery |
| strength-review | Review a strength session as a coach: planned sets against done, top sets, rest, recovery |
| race-readiness | Check if you are ready for a race: training block, taper, predicted time, recovery, race plan |
| train-today | Tell if you should train hard today: the dashboard's readiness verdict, its inputs, fitness and form |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
| Training dashboard | The dashboard page that showDashboard fills |
TDQS
Scored across 20 tools
Most tools have clearly distinct targets, and the activity sub-tools (getActivity, getActivityLaps, getActivitySets, getActivityWorkouts) are explicitly differentiated with cross-references ("getActivity does not have them"). There is mild overlap between getDailySummary and the individual getters (getDailyHeartRate, getBodyBattery), and between getDashboardData and the per-domain readers, but descriptions largely resolve it. The garminGet escape hatch is clearly framed as a fallback.
Names are uniformly camelCase with a predictable verb_noun pattern: get*, list*, download*, write*, edit*, show*, schedule*. The main deviation is garminGet, which uses a noun-prefix style inconsistent with everything else, but overall the convention is readable and predictable.
Twenty tools is on the heavier side, but the Garmin domain is genuinely broad (activities, sleep, HRV, HR, weight, body battery, zones, fitness age) and each reader covers a distinct endpoint. The five dashboard tools form a coherent subsystem (read, write, edit, show, schedule) rather than redundant surface.
Read coverage is extensive across health, activity, and training domains, and the dashboard lifecycle plus a generic garminGet fallback make most workflows achievable. The main gap is the absence of writes back to Garmin (logging weight, uploading activities), though this appears to be a read-focused personal server by design.