GarminMCP
Integrates with Garmin Connect telemetry to provide coaching tools for daily readiness briefings, workout and activity analysis, health and training-load trends, biometrics, and structured workout prescription/upload.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@GarminMCP Should I do my hard interval workout today based on my readiness?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Garmin Coach MCP Server (garmin-coach-mcp)
A Personal Health, Running, and Fitness Coach Model Context Protocol (MCP) server powered by Garmin Connect telemetry. Built on the official Python mcp SDK (FastMCP), garminconnect, garth, and Pydantic v2, this server transforms noisy, deeply nested Garmin Connect API responses into token-efficient, coach-ready Markdown and structured JSON for both local AI clients (stdio for Claude Desktop, Cursor, Antigravity, and Gemini CLI) and remote web/cloud clients (streamable-http over HTTPS with Bearer token authentication for gemini.google.com Connected Apps, Gemini Mobile, and Gemini Enterprise on Google Cloud Run).
Core Coaching Use Cases
Daily Readiness & Morning Check-in
Combines Training Readiness, Body Battery (current, charged, drained), Overnight HRV (last night average, 5-min high, 7-day average vs. baseline), Sleep stages & score, Resting Heart Rate (RHR), Stress, and yesterday's training load in a single aggregated call (
get_coach_daily_briefing) to answer: "Should I execute my hard interval session today, modify the pace, or swap for a recovery jog?"
Deep Workout Analysis & Post-Activity Debrief
Inspects lap/split pacing evenness, Heart Rate Zone distribution, Power/Pace Zone compliance, elevation-adjusted effort, cadence, weather impact, and cardiac drift (%) (
get_activity_details,list_recent_activities) to evaluate execution quality.
Longitudinal Trend & Load Management
Tracks Acute-to-Chronic Workload Ratio (ACWR), aerobic/anaerobic load focus, VO2 Max, Endurance/Hill scores (
get_training_status_and_load), and 7-to-90 day rolling averages & first-half vs. second-half deltas for RHR, HRV, sleep, steps, stress, and training volume (get_health_trends) to detect overreaching early.
Adaptive Training Plans & Structured Workout Prescription
Synthesizes athlete biometrics, Lactate Threshold HR/Pace, FTP, and zone boundaries (
get_body_composition_and_biometrics,garmin://athlete/profile) with current recovery state to build periodized training plans and generate Garmin-compatible structured workouts (create_structured_workout_json) with optional direct upload to Garmin Connect Calendar.
Related MCP server: claude-garmin
System Architecture
flowchart LR
subgraph LocalClients["Local AI Clients"]
IDEs["Claude Desktop / Cursor / Antigravity"]
GeminiCLI["Gemini CLI"]
Inspector["MCP Inspector"]
end
subgraph RemoteClients["Remote Web & Mobile Clients"]
GeminiWeb["gemini.google.com<br/>(Connected Apps & Mobile)"]
GeminiEnt["Gemini Enterprise"]
end
subgraph Server["garmin-coach-mcp (FastMCP / Cloud Run)"]
AuthMiddleware["BearerAuthMiddleware<br/>(Authorization: Bearer + /healthz)"]
Tools["8 Coaching Tools<br/>(briefing, health, activities, workouts)"]
Resources["2 MCP Resources<br/>(athlete/profile, coach/context/today)"]
Prompts["3 Coaching Prompts<br/>(morning, debrief, weekly-plan)"]
Normalizers["Normalizers & Pydantic v2 Models<br/>(Metric/Imperial + Hybrid Formatter)"]
Client["GarminCoachClient"]
Cache[("SQLite TTLCache<br/>~/.cache/garmin-coach-mcp/cache.db")]
Auth["GarminAuthManager<br/>(garth OAuth1/OAuth2)"]
end
subgraph Upstream["Token Stores & External Upstream"]
Tokens[("Local Token Store<br/>~/.garminconnect (0700)")]
TokenB64[("GARMIN_TOKEN_BASE64<br/>(Cloud Secret Manager)")]
GarminAPI["Garmin Connect API"]
end
IDEs <-->|"MCP (stdio)"| Tools
GeminiCLI <-->|"MCP (stdio)"| Tools
Inspector <-->|"MCP (stdio / HTTP)"| AuthMiddleware
GeminiWeb <-->|"HTTPS streamable-http (/mcp)<br/>+ Bearer Auth"| AuthMiddleware
GeminiEnt <-->|"HTTPS streamable-http (/mcp)<br/>+ Bearer Auth"| AuthMiddleware
AuthMiddleware --> Tools
IDEs -.-> Resources
IDEs -.-> Prompts
Tools --> Normalizers
Resources --> Normalizers
Normalizers --> Client
Client <-->|"Parameterized SQL"| Cache
Client --> Auth
Auth <-->|"Resume / Auto-Refresh"| Tokens
TokenB64 -.->|"Stateless Hydration (0600)"| Tokens
Auth <-->|"HTTPS"| GarminAPIInstallation & Setup
Prerequisites
Python 3.11+
uv(recommended) or standardpython3 -m venv+pipA Garmin Connect account
Option A: One-Click Install (./install.sh or .\install.ps1) — Recommended
Run the interactive one-click installer from the repository root:
Linux / macOS / WSL (Bash):
cd /path/to/GarminMCP
./install.shWindows (PowerShell 5.1+ / PowerShell 7+):
cd C:\path\to\GarminMCP
.\install.ps1Both ./install.sh and .\install.ps1 automate the complete 5-stage setup workflow:
[1/5]System Requirements Check: Verifies Python>= 3.11(viapython3/python/py -3oruv).[2/5]Virtual Environment & Dependencies: Creates.venvand installsgarmin-coach-mcpin editable mode with dev/test dependencies (uv pip install -e ".[dev]"or.venv/bin/pip/.venv\Scripts\pip.exe install -e ".[dev]"), then verifiesgarmin-coach-mcp --help.[3/5]Interactive Garmin Connect Login (MFA/2FA): Runsgarmin-coach-mcp auth(or prompts whether to reuse existing tokens if already present in~/.garminconnect).[4/5]OAuth Token & Permission Verification: Confirms non-empty OAuth token files exist in your token directory (with strict0700directory /0600file permissions on POSIX systems).[5/5]Optional Live Account Verification & MCP Config Output: Optionally runsgarmin-coach-mcp verifyto fetch your Garmin athlete profile and prints ready-to-copy MCP registration snippets for Claude Desktop / Cursor / Antigravity (~/.gemini/config/mcp_config.json) and Gemini CLI (gemini mcp add garmin-coach ...) using your machine's resolved paths.
Installer CLI Flags
Bash ( | PowerShell ( | Description |
|
| Custom directory for Garmin OAuth tokens (defaults to |
|
| Run non-interactively without |
|
| Skip the live Garmin athlete profile read check in Step 5. |
|
| Skip interactive Garmin login and token checks (Steps 3–5) — ideal for CI or automated testing. |
|
| Show usage and available flags. |
Option B: Manual Installation (uv or pip)
Linux / macOS / WSL:
cd /path/to/GarminMCP
# Using uv (recommended):
uv venv
uv pip install -e ".[dev]"
# Or using standard Python venv + pip:
python3 -m venv .venv
.venv/bin/pip install --upgrade pip
.venv/bin/pip install -e ".[dev]"Windows (PowerShell):
cd C:\path\to\GarminMCP
# Using uv (recommended):
uv venv
uv pip install -e ".[dev]"
# Or using standard Python venv + pip:
python -m venv .venv
.\.venv\Scripts\Activate.ps1
.\.venv\Scripts\pip.exe install --upgrade pip
.\.venv\Scripts\pip.exe install -e ".[dev]"Verify the CLI entrypoint is installed and responsive:
# Cross-platform via uv:
uv run garmin-coach-mcp --help
# Or directly via virtualenv binary (Linux / macOS / WSL):
.venv/bin/garmin-coach-mcp --help
# Or directly via virtualenv binary (Windows PowerShell):
.\.venv\Scripts\garmin-coach-mcp.exe --helpAuthentication & MFA / 2FA Bootstrap Guide
Garmin Connect uses garth (OAuth1 + OAuth2) and frequently challenges logins with 2FA / MFA or Cloudflare protections. Because MCP servers run headlessly over stdio, interactive MFA prompts during an active agent tool call would hang stdio transport.
garmin-coach-mcp solves this with a two-stage authentication lifecycle:
Step 1: One-Time Interactive Token Bootstrap (Recommended for All Users & MFA Accounts)
Run the built-in interactive authentication command once in your terminal:
uv run garmin-coach-mcp authOr specify a custom email and token directory:
uv run garmin-coach-mcp auth --email your-email@example.com --token-dir ~/.garminconnectYou will be prompted for your Garmin Connect email and password (hidden input) if not already set in the environment.
If your account has 2FA / MFA enabled, you will be prompted interactively in your terminal for the one-time verification code sent via SMS or email.
Upon success,
garthsavesoauth1_token.jsonandoauth2_token.jsonto~/.garminconnect(with0700directory permissions).During all future headless MCP server runs,
GarminAuthManagerautomatically resumes from~/.garminconnectand refreshes short-lived OAuth2 tokens using the long-lived OAuth1 token (~1 year validity) without storing your raw password or re-prompting for MFA.
Step 2: Verify Saved OAuth Tokens & Account Access (garmin-coach-mcp verify)
After authenticating, verify your saved tokens and perform a live read check of your Garmin athlete profile:
uv run garmin-coach-mcp verify
# or with a custom token directory:
uv run garmin-coach-mcp verify --token-dir ~/.garminconnectgarmin-coach-mcp verify checks that your token files exist in ~/.garminconnect, connects to Garmin Connect, and prints a summary of your connected athlete name, configured unit system (metric / imperial), and key physiological anchors (Max HR, Resting HR, VO2 Max).
Step 3: Headless Environment Variable Fallback (Non-MFA Accounts Only)
If your account does not require interactive 2FA, you can optionally provide credentials via environment variables (see .env.example). Upon first login, tokens are still persisted to GARMIN_TOKEN_DIR for subsequent calls.
Environment Variables Reference
Variable | Default | Description |
|
| Directory storing |
| (unset) | Base64-encoded token bundle produced by |
| (unset) | Optional Garmin Connect email for headless fallback login. |
| (unset) | Optional Garmin Connect password for headless fallback login. |
|
| Directory for the SQLite TTL cache ( |
|
| Default cache TTL in seconds ( |
|
| Unit system for distances, paces, elevations, and weights ( |
|
| MCP transport mode: |
| (unset) | Bearer token secret ( |
|
| HTTP port when running in |
Ready-to-Copy MCP Client Configurations
Pre-built configuration files are available in examples/:
1. Claude Desktop / Cursor / Antigravity (uv Configuration)
Add the garmin-coach entry to your MCP client configuration (e.g., ~/.gemini/config/mcp_config.json or claude_desktop_config.json; see examples/uv_mcp_config.json):
{
"mcpServers": {
"garmin-coach": {
"command": "uv",
"args": [
"--directory",
"/path/to/GarminMCP",
"run",
"garmin-coach-mcp"
],
"env": {
"GARMIN_TOKEN_DIR": "~/.garminconnect",
"GARMIN_CACHE_DIR": "~/.cache/garmin-coach-mcp",
"GARMIN_CACHE_TTL": "900",
"GARMIN_UNITS": "metric"
}
}
}
}2. Gemini CLI (~/.gemini/settings.json)
Merge the following block into your ~/.gemini/settings.json (see examples/gemini_cli_settings.json):
{
"mcpServers": {
"garmin-coach": {
"command": "uv",
"args": [
"--directory",
"/path/to/GarminMCP",
"run",
"garmin-coach-mcp"
],
"env": {
"GARMIN_TOKEN_DIR": "~/.garminconnect",
"GARMIN_CACHE_DIR": "~/.cache/garmin-coach-mcp",
"GARMIN_CACHE_TTL": "900",
"GARMIN_UNITS": "metric"
},
"timeout": 30000,
"trust": false
}
}
}3. Direct Virtualenv Binary Configuration (examples/mcp_config.json)
If you prefer invoking the virtualenv entrypoint binary directly without uv run:
{
"mcpServers": {
"garmin-coach": {
"command": "/path/to/GarminMCP/.venv/bin/garmin-coach-mcp",
"args": [],
"env": {
"GARMIN_TOKEN_DIR": "~/.garminconnect",
"GARMIN_CACHE_DIR": "~/.cache/garmin-coach-mcp",
"GARMIN_CACHE_TTL": "900",
"GARMIN_UNITS": "metric"
}
}
}
}Using with gemini.google.com (Web Client) & Google Cloud Run
1. How It Works
Unlike local desktop agents (Claude Desktop, Cursor, Antigravity, or Gemini CLI) that spawn an MCP subprocess on your workstation over stdio, gemini.google.com (Connected Apps) and Gemini Enterprise execute in Google's cloud and cannot spawn local processes on your laptop. Instead, they connect to remote MCP servers over HTTPS using the MCP streamable-http transport (/mcp endpoint) protected by authentication headers.
garmin-coach-mcp includes first-class support for stateless cloud deployments:
garmin-coach-mcp export-tokens&GARMIN_TOKEN_BASE64: Packages your locally bootstrappedoauth1_token.jsonandoauth2_token.jsoninto a single Base64 secret string so stateless containers (such as Google Cloud Run) can hydrate your Garmin session in memory/tmpfs without interactive MFA prompts or persistent disks.--transport streamable-http: Serves the MCP Streamable HTTP protocol at/mcpalongside an unauthenticated container liveness endpoint atGET /healthz.BearerAuthMiddleware(--auth-token/GARMIN_MCP_AUTH_TOKEN): Protects your telemetry endpoint with constant-time (hmac.compare_digest) Bearer token verification (Authorization: Bearer <token>).
2. Step 1 — Bootstrap MFA Locally & Export Portable Token Bundle
First, complete interactive Garmin login (including any SMS/email MFA challenge) on your local machine and export the resulting garth OAuth1/OAuth2 tokens into a Base64 bundle:
# 1. Authenticate interactively once on your workstation
garmin-coach-mcp auth
# 2. Export the OAuth1 + OAuth2 token bundle as a Base64 string
garmin-coach-mcp export-tokens > garmin_token_b64.txtSecurity Note:
garmin_token_b64.txtgrants access to your Garmin Connect account for up to ~1 year (the lifetime of the OAuth1 refresh token). Store it in Secret Manager and delete the local text file once uploaded.
3. Step 2 — Option A: Deploy to Google Cloud Run (Recommended)
Deploying to Google Cloud Run gives you a serverless, auto-scaling HTTPS endpoint with zero idle cost (scales to zero when not in use) backed by the included Dockerfile.
1. Store Secrets in Google Cloud Secret Manager
export PROJECT_ID="your-gcp-project-id"
export REGION="us-central1"
# Generate a strong random Bearer token for MCP client authentication
openssl rand -hex 32 > mcp_auth_token.txt
# Create secrets in Google Cloud Secret Manager
gcloud secrets create garmin-token-b64 \
--project="$PROJECT_ID" \
--replication-policy="automatic" \
--data-file=garmin_token_b64.txt
gcloud secrets create garmin-mcp-auth-token \
--project="$PROJECT_ID" \
--replication-policy="automatic" \
--data-file=mcp_auth_token.txt
# Clean up temporary local secret files
rm -f garmin_token_b64.txt2. Build & Deploy to Cloud Run
gcloud run deploy garmin-coach-mcp \
--project="$PROJECT_ID" \
--region="$REGION" \
--source . \
--port 8080 \
--allow-unauthenticated \
--min-instances=0 \
--max-instances=2 \
--set-env-vars="GARMIN_MCP_TRANSPORT=streamable-http,GARMIN_UNITS=metric,GARMIN_TOKEN_DIR=/tmp/.garminconnect,GARMIN_CACHE_DIR=/tmp/.cache/garmin-coach-mcp" \
--set-secrets="GARMIN_TOKEN_BASE64=garmin-token-b64:latest,GARMIN_MCP_AUTH_TOKEN=garmin-mcp-auth-token:latest"(Note: --allow-unauthenticated at the Cloud Run ingress layer allows gemini.google.com to reach the HTTPS URL directly, while BearerAuthMiddleware inside garmin-coach-mcp enforces Authorization: Bearer <token> on every MCP request.)
3. Verify Your Cloud Run Deployment
export SERVICE_URL="https://garmin-coach-mcp-xxxxxxxxxx-uc.a.run.app"
export GARMIN_MCP_AUTH_TOKEN="$(cat mcp_auth_token.txt)"
# 1. Verify container health check (returns 200 OK JSON without auth)
curl -sS "$SERVICE_URL/healthz"
# -> {"status":"ok","service":"garmin-coach-mcp","version":"0.1.0"}
# 2. Verify unauthenticated requests to /mcp are rejected (returns 401 Unauthorized)
curl -i -sS "$SERVICE_URL/mcp"
# 3. Verify authenticated MCP request passes Bearer auth
curl -sS -X POST "$SERVICE_URL/mcp" \
-H "Authorization: Bearer $GARMIN_MCP_AUTH_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"curl-check","version":"1.0"}}}'4. Step 2 — Option B: Quick Personal Testing via HTTPS Tunnel (Tailscale Funnel / Cloudflare Tunnel)
If you want to test gemini.google.com against your local workstation before deploying to Cloud Run, run garmin-coach-mcp locally in streamable-http mode bound to 127.0.0.1 and expose it via an HTTPS tunnel:
# 1. Start the MCP server locally on 127.0.0.1:8000 with Bearer auth enabled
export GARMIN_MCP_AUTH_TOKEN="$(openssl rand -hex 32)"
echo "Bearer Token: $GARMIN_MCP_AUTH_TOKEN"
garmin-coach-mcp \
--transport streamable-http \
--host 127.0.0.1 \
--port 8000 \
--auth-token "$GARMIN_MCP_AUTH_TOKEN"In a second terminal, expose 127.0.0.1:8000 over HTTPS using Tailscale Funnel or Cloudflare Tunnel:
# Option 1: Tailscale Funnel
tailscale funnel 8000
# -> Exposes https://<your-machine>.<your-tailnet>.ts.net/mcp
# Option 2: Cloudflare Quick Tunnel
cloudflared tunnel --url http://127.0.0.1:8000
# -> Exposes https://<random-subdomain>.trycloudflare.com/mcp5. Step 3 — Connect in gemini.google.com
Once your HTTPS endpoint (https://<your-service-url>/mcp) is live:
Open gemini.google.com in your desktop browser.
Navigate to Settings → Connected Apps (or Extensions / MCP Servers in Gemini Advanced / Gemini Enterprise).
Click Add Custom MCP Server (or Connect App) and enter:
Name:
Garmin CoachServer URL / Endpoint:
https://<your-service-url>/mcpTransport:
Streamable HTTPAuthentication:
Bearer Token(paste yourGARMIN_MCP_AUTH_TOKENsecret)
Save and enable the connection.
Automatic Mobile Propagation: Once registered under your Google account on
gemini.google.com, your connectedGarmin CoachMCP server automatically propagates to the Gemini mobile app (Android & iOS), allowing you to ask "How was my sleep and HRV last night, and what workout should I run today?" or "Create a 4x1km threshold workout and upload it to my Garmin watch" directly from your phone.
MCP Reference: Tools, Resources & Prompts
Every tool supports response_format: "hybrid" | "markdown" | "json" (default "hybrid"), returning both a concise human/coach-readable Markdown executive summary and a normalized Pydantic v2 JSON block.
8 MCP Tools
Tool Name | Parameters | Coaching Purpose & Returned Telemetry |
|
| Single-call morning check-in aggregator. Fetches Training Readiness score & factor breakdown, Body Battery ( |
|
| Multi-day sleep architecture & autonomic recovery. Returns deep/light/REM/awake durations & percentages, sleep score, overnight HRV average vs. baseline, RHR, Body Battery recharge, SpO2, respiration rate, and sleep need over a date range (defaults to last 7 days). |
|
| Physiological load & fitness status. Returns Training Status ( |
|
| Workout log browser. Lists recent workouts filtered by sport ( |
|
| Deep-dive workout execution analysis. Breaks down lap/split pacing, time & percentage in Heart Rate Zones and Power/Pace Zones, elevation gain/loss, cadence, weather conditions, cardiac drift (%), and pacing evenness assessment. |
|
| Longitudinal baseline & overtraining detector. Aggregates |
|
| Body composition & threshold zone settings. Tracks weight trends ( |
|
| Workout prescription & Garmin Calendar uploader. Validates warmup, interval, recovery, rest, cooldown, and nested repeat blocks ( |
Example Tool Call: create_structured_workout_json
{
"workout_name": "Threshold Cruise Intervals 4x1600m",
"sport_type": "running",
"description": "Warm up thoroughly, run 4x1600m at Lactate Threshold HR (Zone 4) with 90s jog recovery, then cool down.",
"upload_to_garmin": false,
"steps": [
{
"step_type": "warmup",
"duration_type": "time",
"duration_value": 900,
"target_type": "heart.rate.zone",
"target_zone_number": 2
},
{
"step_type": "repeat",
"duration_type": "iterations",
"duration_value": 4,
"repeat_steps": [
{
"step_type": "interval",
"duration_type": "distance",
"duration_value": 1600,
"target_type": "heart.rate.zone",
"target_zone_number": 4,
"description": "Threshold pace effort"
},
{
"step_type": "recovery",
"duration_type": "time",
"duration_value": 90,
"target_type": "no.target",
"description": "Easy recovery jog"
}
]
},
{
"step_type": "cooldown",
"duration_type": "time",
"duration_value": 600,
"target_type": "heart.rate.zone",
"target_zone_number": 1
}
]
}2 MCP Resources
Resource URI | Type | Description |
| Static / Cached | Complete athlete profile including display name, unit preferences, Max HR, Resting HR, Lactate Threshold HR & Pace, FTP, VO2 Max, configured Heart Rate / Pace / Power Zones, and Personal Records (PRs). |
| Dynamic | Real-time Markdown coaching snapshot for today combining readiness, overnight recovery, body battery, and yesterday's training load for instant context injection. |
3 MCP Prompts
Prompt Name | Arguments | Coaching Workflow |
|
| Calls |
|
| Inspects the specified activity (or latest activity via |
|
| Analyzes 28-day trends ( |
Testing & Verification
The test suite uses 100% offline, deterministic pytest fixtures in tests/—zero live Garmin credentials or network calls are required to run unit or integration tests.
Run the Full Test Suite
uv run pytest -vRun with Coverage Report
uv run pytest --cov=garmin_coach_mcp --cov-report=term-missingVerify Live Garmin Connect Session (garmin-coach-mcp verify)
To confirm that your local OAuth tokens in ~/.garminconnect are valid and can read your Garmin Connect athlete profile:
uv run garmin-coach-mcp verify --token-dir ~/.garminconnectInteractive Smoke Testing with MCP Inspector
You can inspect tools, resources, and prompts interactively using the official MCP CLI / Inspector:
npx @modelcontextprotocol/inspector uv --directory /path/to/GarminMCP run garmin-coach-mcpOr run the server in local streamable-http or sse mode (bound strictly to 127.0.0.1 by default):
uv run garmin-coach-mcp --transport streamable-http --host 127.0.0.1 --port 8000 --auth-token "local-dev-secret"Security & Privacy Architecture
Health and biometric telemetry is sensitive personal data. garmin-coach-mcp enforces strict security boundaries by design:
Restricted Token & Cache Permissions (
0700/0600):GARMIN_TOKEN_DIR(~/.garminconnect) andGARMIN_CACHE_DIR(~/.cache/garmin-coach-mcp) are created with0o700directory permissions, and OAuth token files (including those hydrated fromGARMIN_TOKEN_BASE64) / SQLite cache files are restricted to owner-only access (0o600).
Constant-Time Bearer Authentication for Remote HTTP (
BearerAuthMiddleware):When running with
--transport streamable-httpor--transport sseand--auth-token/GARMIN_MCP_AUTH_TOKEN, all MCP requests requireAuthorization: Bearer <token>verified viahmac.compare_digest(returning401 UnauthorizedwithWWW-Authenticate: Beareron missing/invalid tokens), whileGET /healthzremains available for Cloud Run liveness probes.
Zero Credential & Token Logging:
GarminAuthManagerandGarminCoachClientnever log passwords, MFA verification codes,GARMIN_TOKEN_BASE64payloads, or raw OAuth1/OAuth2 session tokens in stack traces, stdout, stderr, or MCP error payloads.
Parameterized SQLite Queries:
TTLCacheinsrc/garmin_coach_mcp/cache.pyexclusively uses parameterized SQL queries (?placeholders) to prevent SQL injection across cache lookups, insertions, and prefix invalidations.
Local-Only Network Binding (
127.0.0.1) & Non-Root Container:Default transport is
stdio. When running HTTP transports locally,--hostbinds exclusively to127.0.0.1by default (never0.0.0.0), preventing unintended exposure on local networks. The Cloud RunDockerfileruns as an unprivileged user (garmincoach,uid 10001).
Opt-In Upstream Writes:
All tools are strictly read-only except
create_structured_workout_json, which defaults toupload_to_garmin=Falseso workouts are only pushed to Garmin Connect when explicitly requested by the user.
Available Tools
8 toolscreate_structured_workout_jsonA
Validate, build, and optionally upload a structured Garmin Connect workout.
Coaching Purpose:
Translates a coach's workout prescription (warmups, intervals, recovery jogs,
nested repeat blocks, cooldowns with time/distance/lap-button durations and
HR/pace/power/cadence targets) into a schema-validated Garmin Connect
ExecutableStepDTO / RepeatGroupDTO JSON payload. Optionally uploads the
workout directly to the athlete's Garmin Connect calendar/device queue when
upload_to_garmin=True.
Args:
workout_name: Descriptive title for the workout (e.g., "Threshold 4x1km Cruise Intervals").
sport_type: Target sport ("running", "cycling", "swimming", "strength", "cardio").
steps: List of step dictionaries matching WorkoutStepInput (step_type, duration_type,
duration_value, target_type, target_zone_number, target_low, target_high,
description, repeat_steps). If omitted, generates a standard 3-step template.
description: Optional coaching notes or focus cues attached to the workout.
upload_to_garmin: If True, uploads the generated JSON to Garmin Connect via
client.upload_structured_workout(...). Defaults to False.
response_format: Output representation: "markdown", "json", or "hybrid" (default).
Returns:
Formatted workout summary and validated Garmin Connect workout JSON payload
(including uploaded status and uploaded_workout_id when uploaded).
Inputs: workout_name (required), steps, sport_type, description, +2 more. Returns: result
get_activity_detailsA
Perform a granular post-workout debrief for a specific Garmin activity ID.
Coaching Purpose: Deep-dives into a single completed workout to evaluate interval execution, lap-by-lap pacing stability, time spent in Heart Rate and Power/Pace zones, elevation gain/loss, running/cycling cadence, first-half vs. second-half cardiac drift percentage, and environmental weather stress.
Args:
activity_id: Numeric or string Garmin Connect activity ID (obtainable via list_recent_activities).
response_format: Output representation: "markdown", "json", or "hybrid" (default).
Returns: Formatted deep-dive workout analysis including lap/split breakdowns, HR zone distribution, power/pace zone distribution, cardiac drift %, pacing assessment, and weather conditions.
Inputs: activity_id (required), response_format. Returns: result
get_body_composition_and_biometricsA
Fetch weight trends, body fat %, muscle mass, hydration, and physiological thresholds (LTHR, Max HR, FTP).
Coaching Purpose: Tracks body composition shifts (weight, body fat percentage, skeletal muscle mass, body water, BMI), daily hydration intake, and physiological threshold anchors (Lactate Threshold Heart Rate & Pace, Max HR, Resting HR, Cycling FTP, VO2 Max) used to calibrate training zones and race power/pacing targets.
Args:
start_date: Start ISO date (YYYY-MM-DD). Defaults to a 30-day window ending on end_date.
end_date: End ISO date (YYYY-MM-DD). Defaults to today.
response_format: Output representation: "markdown", "json", or "hybrid" (default).
Returns: Formatted body composition and biometrics report with weight delta, weigh-in history, hydration averages, and Lactate Threshold / Max HR / FTP configurations.
Inputs: end_date, start_date, response_format. Returns: result
get_coach_daily_briefingA
Fetch a complete morning coaching briefing for a single date (defaults to today).
Coaching Purpose:
High-level morning readiness aggregator designed to be the first tool called
in a daily check-in. Synthesizes Training Readiness score & contributors,
Body Battery (current, charged, drained), overnight Sleep quality & stages,
Heart Rate Variability (HRV) status vs. personal baseline, Resting Heart Rate
(RHR vs. 7-day baseline), daily Stress, and yesterday's completed workouts into
an actionable training intensity prescription (high_intensity, moderate_steady,
easy_recovery, or rest).
Args:
target_date: Target ISO calendar date (YYYY-MM-DD). Defaults to today if omitted.
response_format: Output representation: "markdown" for human-readable summary,
"json" for compact normalized JSON, or "hybrid" (default) for Markdown + JSON.
Returns: Formatted coaching briefing containing readiness score, autonomic recovery metrics, sleep architecture, yesterday's training load, and today's recommended workout intensity.
Inputs: target_date, response_format. Returns: result
get_health_trendsA
Compute longitudinal rolling averages, first-half vs. second-half deltas, and training volume over N days.
Coaching Purpose: Detects multi-week physiological trends and early overtraining signals across Resting Heart Rate (RHR), overnight HRV, sleep duration, daily steps, daily stress, and total training volume (hours, distance, activity breakdown) over a configurable window (e.g., 7, 14, 28, 30, or 90 days).
Args:
days: Number of days to aggregate (default 28). Clamped to a positive integer.
end_date: End ISO date (YYYY-MM-DD) of the analysis window. Defaults to today.
response_format: Output representation: "markdown", "json", or "hybrid" (default).
Returns: Formatted longitudinal trend report with biomarker averages, min/max, 1st-half vs. 2nd-half deltas, trend directions, training volume totals, and diagnostic coach flags.
Inputs: days, end_date, response_format. Returns: result
get_sleep_and_recoveryA
Analyze overnight sleep architecture, HRV vs. baseline, SpO2, respiration, and Body Battery recharge.
Coaching Purpose: Evaluates multi-day or single-night sleep quality and autonomic nervous system recovery. Useful for diagnosing cumulative sleep debt, suppressed nocturnal HRV, elevated sleeping heart rate/respiration (early illness or overreaching markers), and poor Body Battery recharge.
Args:
start_date: Start ISO date (YYYY-MM-DD). If omitted, defaults to a 7-day window
ending on end_date (or today). If provided and end_date is omitted, fetches
that single day.
end_date: End ISO date (YYYY-MM-DD). Defaults to start_date when start_date is
explicitly provided, or today when start_date is omitted.
response_format: Output representation: "markdown", "json", or "hybrid" (default).
Returns: Formatted sleep & recovery analysis including deep/light/REM/awake sleep stages, sleep score, SpO2, respiration rate, overnight HRV + baseline, and Body Battery recharge.
Inputs: end_date, start_date, response_format. Returns: result
get_training_status_and_loadA
Retrieve VO2 Max, Acute/Chronic Load (ACWR), Training Status, Recovery Time, Fitness Age, and Endurance/Hill scores.
Coaching Purpose: Quantifies macro- and meso-cycle training load progression and aerobic fitness adaptations. Assesses whether the athlete is Productive, Peaking, Maintaining, Strained, or Overreaching, inspects Acute-to-Chronic Workload Ratio (ACWR) for injury-risk management, and checks low/high aerobic vs. anaerobic load balance.
Args:
target_date: Target ISO date (YYYY-MM-DD). Defaults to today if omitted.
response_format: Output representation: "markdown", "json", or "hybrid" (default).
Returns: Formatted report of VO2 Max (running/cycling), Acute Load, Chronic Load, ACWR load ratio, Training Status, Recovery Time remaining, Fitness Age, Endurance Score, and Hill Score.
Inputs: target_date, response_format. Returns: result
list_recent_activitiesA
List recent workouts with distance, duration, HR, pace/power, elevation, calories, and Training Effect.
Coaching Purpose: Provides a scannable training log across running, cycling, swimming, strength, and cardio sessions. Supports filtering by workout type and date range to inspect training consistency, session load distribution, and recent activity IDs for deep-dives.
Args:
limit: Maximum number of activities to return (default 10).
activity_type: Optional sport filter (e.g., "running", "cycling", "strength").
start_date: Optional start ISO date (YYYY-MM-DD) filter.
end_date: Optional end ISO date (YYYY-MM-DD) filter.
response_format: Output representation: "markdown", "json", or "hybrid" (default).
Returns: Formatted activity log containing activity IDs, distances, durations, average/max HR, pace/power, elevation gain/loss, calories, and aerobic/anaerobic Training Effect.
Inputs: limit, end_date, start_date, activity_type, +1 more. Returns: result
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 8 tools
Each tool targets a distinct analysis slice (daily readiness, sleep/recovery, training load, activity list, activity deep-dive, longitudinal trends, body composition, workout creation), so misselection risk is low. The only real overlap is get_coach_daily_briefing, which re-synthesizes HRV, sleep, Body Battery, and yesterday's workouts that dedicated tools also return, but it is explicitly framed as the entry-point aggregator, which mitigates confusion.
All eight tools follow a clean verb_noun snake_case convention with consistent prefixes (get_*, list_*, create_*) and domain-descriptive nouns. Requests are parameterized uniformly with target_date/start_date/end_date and response_format across the read tools.
Eight tools is well-scoped for a Garmin coaching/analytics server: one aggregator, five focused health/training readers, one activity deep-dive, and one workout builder. Each tool occupies a distinct analytical role without redundant entries.
The read surface is comprehensive for a coaching domain (readiness, sleep, load, activity list+details, trends, biometrics), and workout creation is covered. The notable gap is workout lifecycle management: there is no way to list scheduled/uploaded workouts, update or delete one after create_structured_workout_json, nor any calendar/planned-workout visibility, which could force workarounds.
Maintenance
Related MCP Connectors
AI coach for Garmin: builds training plans and structured workouts, synced straight to your watch.
Adaptive running coach MCP server — training data, plans, and recovery for AI assistants.
Garmin watch data for AI assistants: activities, sleep, HRV, recovery. Never writes to Garmin.
Ask your AI about your fitness: activities and data from Garmin, COROS, Strava, GPX and more
Related MCP Servers
- FlicenseBqualityDmaintenanceA Model Context Protocol server that integrates Garmin Connect data with LLMs to provide personalized running analysis and training plans. It enables users to monitor performance metrics, manage training loads, and receive data-driven workout suggestions based on health indicators like VO2 Max and recovery status.435-
- AlicenseNot gradedqualityCmaintenanceMCP server that connects Garmin Connect data to Claude, enabling training analysis, recovery checks, and personalized plans based on real metrics like HRV, training load, and activities.24MIT
- AlicenseAqualityBmaintenanceA lightweight Garmin Model Context Protocol server with 12 curated endpoints for endurance athletes, enabling natural-language queries of activity, training, health, device, and goal data from Garmin Connect.28MIT
- AlicenseNot gradedqualityBmaintenanceAn AI training coach MCP server that integrates with Garmin Connect to provide science-based load management, code-enforced injury gates, and persistent coaching memory, keeping data local.325 PyPI3MIT