Labsco
Taxuspt logo

Garmin MCP Server

719

from Taxuspt

Connects to Garmin Connect to expose your fitness and health data to MCP-compatible clients.

🔥🔥🔥✓ VerifiedFreeAdvanced setup

Garmin MCP Server

This Model Context Protocol (MCP) server connects to Garmin Connect and exposes your fitness and health data to Claude and other MCP-compatible clients.

Garmin's API is accessed via the awesome python-garminconnect library.

Features

  • List recent activities with pagination support

  • Get detailed activity information

  • Edit activities: name, type, description/notes, event type, perceived effort (RPE), and feel

  • Access health metrics (steps, heart rate, sleep, stress, respiration)

  • View body composition data

  • Track training status and readiness

  • Access cycling FTP and lactate threshold metrics

  • Manage gear and equipment

  • Access workouts and training plans

  • Inspect detailed workout step structures, including repeat groups and swim pace targets

  • Weekly health aggregates (steps, stress, intensity minutes)

  • Advanced cycling analytics: power zones, FIT file analysis, DI2 electronic shift intelligence

  • Training load trend (CTL/ATL/TSB), HRV trend, VO2 max trend, respiration rate trend

  • Power Duration Curve, climb detection with VAM, cardiac drift (aerobic decoupling), W/kg calculations

Tool Coverage

This MCP server implements 110+ tools covering ~90% of the python-garminconnect library (v0.3.2):

  • ✅ Activity Management (20 tools) - includes write tools for type, description, event type, perceived effort, and feel

  • ✅ Health & Wellness (31 tools) - includes custom lightweight summary tools

  • ✅ Training & Performance (13 tools) - includes CTL/ATL/TSB, HRV, VO2 max, and respiration trends

  • ✅ Workouts (8 tools)

  • ✅ Devices (7 tools)

  • ✅ Gear Management (5 tools)

  • ✅ Weight Tracking (5 tools)

  • ✅ Challenges & Badges (10 tools)

  • ✅ Nutrition (8 tools) - food logs, meals, custom foods, and food logging

  • ✅ Women's Health (3 tools)

  • ✅ User Profile (3 tools)

  • ✅ High-Level Workout Builders (4 tools) - create and schedule workouts without writing JSON

  • ✅ Courses (3 tools) - list / upload GPX as course / delete course

  • ✅ Activity Analysis (2 tools) - FIT file parsing, Power Duration Curve; requires power meter and/or Di2

  • ✅ Activity File Downloads (2 tools) - download activity files in FIT, GPX, TCX, or CSV format

Note: Activity Analysis tools require a compatible power meter (e.g., Garmin Rally, Favero Assioma, PowerTap P1) and/or Shimano Di2 / SRAM eTap electronic shifting. The fitparse dependency is installed automatically.

Activity File Downloads

Two tools let you download a raw activity file to disk:

  • download_activity_file(activity_id, format="fit", output_dir=None) — downloads the activity and saves it to the configured directory. format accepts fit (default), gpx, tcx, or csv.

  • set_fit_download_dir(path) — sets and persists the default download directory (written to the config file).

Where files are saved (precedence):

  • output_dir argument — one-off override, not persisted.

  • GARMIN_FIT_DOWNLOAD_DIR environment variable.

  • Persisted config set via set_fit_download_dir.

First-run behavior: if no directory is configured, download_activity_file returns status: "needs_setup". The assistant will ask where you want to save files (suggesting the current directory as default), call set_fit_download_dir to persist your choice, and then retry the download automatically.

Intentionally Skipped Endpoints

Some endpoints are not implemented due to performance or complexity considerations:

High Data Volume:

  • get_activity_details() - Returns large GPS tracks and chart data (50KB-500KB). Use get_activity() for summaries instead.

Specialized Workout Formats:

  • upload_running_workout(), upload_cycling_workout(), upload_swimming_workout() - Sport-specific workout uploads. Use upload_workout() for general workouts.

Maintenance & Destructive Operations:

  • delete_activity(), delete_blood_pressure() - Destructive operations require careful consideration.

  • Internal/Auth methods: login(), resume_login(), connectapi(), download() - Handled automatically by the library.

If you need any of these endpoints, please open an issue.

Tool Filtering

This server registers 110+ tools by default, which can be a lot of context for an LLM to carry in every session. You can expose only the tools you need with two optional environment variables:

Env var Effect GARMIN_ENABLED_TOOLS Comma-separated allowlist — if set, only these tools are registered. GARMIN_DISABLED_TOOLS Comma-separated denylist — listed tools are skipped. Ignored if an allowlist is set.

Tool names are case-insensitive. With neither variable set, all tools register (unchanged default behaviour). Names that match no tool are ignored with a warning on stderr, which makes typos easy to spot.

Example — expose only sleep, stress, and recent activities:

Copy & paste — that's it
"env": {
 "GARMIN_ENABLED_TOOLS": "get_sleep_data,get_stress_summary,get_activities"
}

High-level workout tools

These builder tools let an LLM create and schedule workouts without writing raw Garmin JSON.

create_walk_run_workout

Creates a walk/run interval workout with optional heart-rate zone target.

Copy & paste — that's it
{
 "name": "W3 Mié 2:2",
 "run_seconds": 120,
 "walk_seconds": 120,
 "repeats": 9,
 "warmup_min": 10,
 "cooldown_min": 8,
 "hr_zone": "Z3"
}

Returns: {"status": "success", "workout_id": 1234567890, ...}

create_z2_walk_workout

Creates a steady Z2 walking workout.

Copy & paste — that's it
{
 "name": "Z2 Walk 45m",
 "duration_min": 45,
 "hr_min": 110,
 "hr_max": 130
}

Returns: {"status": "success", "workout_id": 1234567890, ...}

create_strength_workout

Creates a strength workout from a list of exercises. Unknown names fall back to a generic step with the original name preserved.

Copy & paste — that's it
{
 "name": "Full Body A",
 "exercises": [
 {"name": "Sentadillas", "sets": 3, "reps": 12, "rest_seconds": 90},
 {"name": "Flexiones", "sets": 3, "reps": 15, "rest_seconds": 60},
 {"name": "Peso muerto", "sets": 3, "reps": 10, "rest_seconds": 90}
 ]
}

Returns: {"status": "success", "workout_id": 1234567890, ...}

schedule_week

Schedules multiple workouts in one call.

Copy & paste — that's it
{
 "week": [
 {"date": "2026-05-12", "workout_id": 1234567890},
 {"date": "2026-05-14", "workout_id": 1234567891}
 ]
}

Returns: {"status": "complete", "scheduled": [...]}

Full flow example

Copy & paste — that's it
create_walk_run_workout(name="W3 Mié 2:2", run_seconds=120, walk_seconds=120,
 repeats=9, warmup_min=10, cooldown_min=8)
 → workout_id = 1560092011

schedule_workout(workout_id=1560092011, date="2026-05-06")
 → OK

After syncing your watch, the workout appears on the Forerunner 965 calendar.

Raw upload_workout end conditions

When building custom workout JSON for upload_workout or upload_workouts, the endCondition.conditionTypeId and endCondition.conditionTypeKey must match Garmin's canonical mapping. Garmin treats the numeric conditionTypeId as the source of truth; if the key and ID conflict, Garmin stores the condition that matches the ID.

For example, this is invalid for a heart-rate end condition because ID 4 is calories, not heart.rate:

Copy & paste — that's it
{
 "endCondition": {
 "conditionTypeId": 4,
 "conditionTypeKey": "heart.rate"
 },
 "endConditionValue": 145
}

Use ID 6 for heart rate:

Copy & paste — that's it
{
 "endCondition": {
 "conditionTypeId": 6,
 "conditionTypeKey": "heart.rate"
 },
 "endConditionValue": 145
}

Common end-condition IDs:

ID Key 1 lap.button 2 time 3 distance 4 calories 5 power 6 heart.rate 7 iterations 8 fixed.rest 9 fixed.repetition 10 reps 11 training.peaks.tss

Raw upload_workout target types

When building raw Garmin workout JSON, targetType.workoutTargetTypeId and targetType.workoutTargetTypeKey must use Garmin's canonical mapping. Garmin treats the numeric ID as authoritative: a mismatched payload such as {"workoutTargetTypeId": 6, "workoutTargetTypeKey": "heart.rate"} is stored as pace.zone, because ID 6 means pace.zone.

For a custom heart-rate range, use target type ID 4 with heart.rate.zone and put the bpm range in targetValueOne / targetValueTwo:

Copy & paste — that's it
{
 "targetType": {
 "workoutTargetTypeId": 4,
 "workoutTargetTypeKey": "heart.rate.zone"
 },
 "targetValueOne": 143,
 "targetValueTwo": 157
}

For a named Garmin HR zone, use the same target type with zoneNumber instead:

Copy & paste — that's it
{
 "targetType": {
 "workoutTargetTypeId": 4,
 "workoutTargetTypeKey": "heart.rate.zone"
 },
 "zoneNumber": 3
}

Testing

This project includes comprehensive tests for all MCP tools. All tests are currently passing (100%).

Running Tests

Copy & paste — that's it
# Run all integration tests (default - uses mocked Garmin API)
uv run pytest tests/integration/

# Run tests with verbose output
uv run pytest tests/integration/ -v

# Run a specific test module
uv run pytest tests/integration/test_health_wellness_tools.py -v

# Run end-to-end tests (requires real Garmin credentials)
uv run pytest tests/e2e/ -m e2e -v

Test Structure

  • Integration tests (200+ tests): Test all MCP tools using FastMCP integration with mocked Garmin API responses

  • End-to-end tests (4 tests): Test with real MCP server and Garmin API (requires valid credentials)