MCP Servers

模型上下文协议服务器、框架、SDK 和模板的综合目录。

garmin_mcp for Garmin Connect with MFA

创建于 11/9/2025
更新于 about 1 month ago
Repository documentation and setup instructions

Garmin MCP Server

This Model Context Protocol (MCP) server connects to Garmin Connect and exposes your fitness and health data to OpenWebUI, Claude or any other MCP-compatible clients via Streamable HTTP transport.

Credits: https://github.com/Taxuspt/garmin_mcp

Features

  • 78+ MCP Tools covering:

    • Activity management (list, get details, splits, weather, gear)
    • Health & wellness metrics (steps, heart rate, sleep, stress, body battery)
    • Training & performance data (VO2 Max, HRV, training effect, fitness age)
    • Device management
    • Gear tracking
    • Weight management
    • Challenges & badges
    • Workouts
    • Women's health data
    • Data management (add body composition, blood pressure, hydration)
    • Personalised training and diet recommedations
  • Streamable HTTP Transport - Network-accessible MCP server for Kubernetes/container deployments

  • Token Persistence - OAuth tokens cached to avoid repeated MFA prompts

  • Non-interactive MFA - Supports containerised deployments with environment-based MFA codes

Prerequisites

  • Python 3.10+ (3.12 recommended)
  • Garmin Connect account credentials
  • For Kubernetes: kubectl access to your cluster

Deployment Options

1. Standalone (Local Development)

Installation

# Clone the repository
git clone <repository-url>
cd garmin_mcp

# Install dependencies
uv sync

Running

With stdio transport (for MCP clients like Claude Desktop):

export GARMIN_EMAIL="your-email@example.com"
export GARMIN_PASSWORD="your-password"
export GARMIN_MCP_TRANSPORT="stdio"

uv run garmin-mcp

With Streamable HTTP transport (for network access):

export GARMIN_EMAIL="your-email@example.com"
export GARMIN_PASSWORD="your-password"
export GARMIN_MCP_TRANSPORT="streamable-http"
export GARMIN_MCP_HOST="0.0.0.0"
export GARMIN_MCP_PORT="8000"

uv run garmin-mcp

The server will be accessible at http://localhost:8000/mcp

First-Time Setup (MFA - Only if enabled on Garmin Connect)

If you have MFA enabled on your Garmin Connect account, you'll need to provide the 2FA code on first run. You have two options:

Option A: Interactive (for local development)

  • The server will prompt for the MFA code in the terminal

Option B: Non-interactive (for automation)

export GARMIN_MFA_CODE="123456"  # Code from email/SMS
export GARMIN_MFA_WAIT_SECONDS="180"  # Optional: wait up to 180s for code to appear

Note: If MFA is not enabled on your Garmin Connect account, you can skip these environment variables.

After successful login, OAuth tokens are saved to ~/.garminconnect and future runs won't require MFA until tokens expire.

Configuration with Claude Desktop

Edit your Claude Desktop configuration:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json
{
  "mcpServers": {
    "garmin": {
      "command": "uv",
      "args": ["run", "garmin-mcp"],
      "env": {
        "GARMIN_EMAIL": "your-email@example.com",
        "GARMIN_PASSWORD": "your-password",
        "GARMIN_MCP_TRANSPORT": "stdio"
      }
    }
  }
}

Note: If you have MFA enabled on your Garmin Connect account, add "GARMIN_MFA_CODE": "123456" to the env section for the first run.

Restart Claude Desktop after making changes.

2. Docker Deployment

Build the Image

docker build -t garmin-mcp:latest .

Run the Container

Basic run (stdio transport):

docker run --rm -it \
  -e GARMIN_EMAIL="your-email@example.com" \
  -e GARMIN_PASSWORD="your-password" \
  -e GARMIN_MCP_TRANSPORT="stdio" \
  -v garmin_tokens:/root/.garminconnect \
  garmin-mcp:latest

Network-accessible (Streamable HTTP):

docker run --rm -it \
  -e GARMIN_EMAIL="your-email@example.com" \
  -e GARMIN_PASSWORD="your-password" \
  -e GARMIN_MCP_TRANSPORT="streamable-http" \
  -e GARMIN_MCP_HOST="0.0.0.0" \
  -e GARMIN_MCP_PORT="8000" \
  -e GARMIN_MFA_CODE="123456" \
  -e GARMIN_MFA_WAIT_SECONDS="180" \
  -p 8000:8000 \
  -v garmin_tokens:/root/.garminconnect \
  garmin-mcp:latest

Note: Only include GARMIN_MFA_CODE and GARMIN_MFA_WAIT_SECONDS if you have MFA enabled on your Garmin Connect account.

The server will be accessible at http://localhost:8000/mcp

Using Docker Compose:

Create docker-compose.yml:

version: '3.8'

services:
  garmin-mcp:
    build: .
    image: garmin-mcp:latest
    container_name: garmin-mcp
    restart: unless-stopped
    ports:
      - "8000:8000"
    environment:
      - GARMIN_EMAIL=${GARMIN_EMAIL}
      - GARMIN_PASSWORD=${GARMIN_PASSWORD}
      - GARMIN_MCP_TRANSPORT=streamable-http
      - GARMIN_MCP_HOST=0.0.0.0
      - GARMIN_MCP_PORT=8000
      # Only include MFA variables if MFA is enabled on your Garmin Connect account
      - GARMIN_MFA_CODE=${GARMIN_MFA_CODE}
      - GARMIN_MFA_WAIT_SECONDS=180
    volumes:
      - garmin_tokens:/root/.garminconnect

volumes:
  garmin_tokens:

Run with:

docker-compose up -d

3. Kubernetes Deployment

Prerequisites

  • Kubernetes cluster with kubectl configured
  • PersistentVolume support (for token storage)

Step 1: Create Secrets

Create a Kubernetes Secret with your Garmin credentials:

kubectl create namespace mcpo  # or your preferred namespace

kubectl create secret generic garmin-secrets \
  --from-literal=email='your-email@example.com' \
  --from-literal=password='your-password' \
  --from-literal=mfa='123456' \
  -n mcpo

Note: The mfa key is only needed if you have MFA enabled on your Garmin Connect account. If MFA is not enabled, omit the --from-literal=mfa line. The mfa key is only needed for the first run or when tokens expire. You can remove it after tokens are established.

Step 2: Create PersistentVolumeClaim

Create pvc.yaml:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: garmin-tokens
  namespace: mcpo
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi

Apply it:

kubectl apply -f pvc.yaml

Step 3: Deploy the Application

Create deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: garmin-mcp
  namespace: mcpo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: garmin-mcp
  template:
    metadata:
      labels:
        app: garmin-mcp
    spec:
      containers:
        - name: garmin-mcp
          image: garmin-mcp:latest  # Replace with your image registry
          imagePullPolicy: Always
          ports:
            - containerPort: 8000
              name: http
          env:
            - name: GARMIN_EMAIL
              valueFrom:
                secretKeyRef:
                  name: garmin-secrets
                  key: email
            - name: GARMIN_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: garmin-secrets
                  key: password
            - name: GARMIN_MCP_TRANSPORT
              value: "streamable-http"
            - name: GARMIN_MCP_HOST
              value: "0.0.0.0"
            - name: GARMIN_MCP_PORT
              value: "8000"
            # Only include MFA variables if MFA is enabled on your Garmin Connect account
            - name: GARMIN_MFA_CODE
              valueFrom:
                secretKeyRef:
                  name: garmin-secrets
                  key: mfa
            - name: GARMIN_MFA_WAIT_SECONDS
              value: "180"
          volumeMounts:
            - name: tokens
              mountPath: /root/.garminconnect
          readinessProbe:
            tcpSocket:
              port: 8000
            initialDelaySeconds: 5
            periodSeconds: 5
          livenessProbe:
            tcpSocket:
              port: 8000
            initialDelaySeconds: 10
            periodSeconds: 10
      volumes:
        - name: tokens
          persistentVolumeClaim:
            claimName: garmin-tokens

Apply it:

kubectl apply -f deployment.yaml

Step 4: Create Service

Create service.yaml:

apiVersion: v1
kind: Service
metadata:
  name: garmin-mcp
  namespace: mcpo
spec:
  selector:
    app: garmin-mcp
  ports:
    - name: http
      port: 80
      targetPort: 8000
  type: ClusterIP

Apply it:

kubectl apply -f service.yaml

Step 5: (Optional) Create Istio HTTPRoute

If using Istio, create httproute.yaml:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: garmin-mcp
  namespace: mcpo
spec:
  parentRefs:
    - name: your-gateway
      namespace: istio-system
  hostnames:
    - garmin-mcp.example.com
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /mcp
      backendRefs:
        - name: garmin-mcp
          port: 80

Apply it:

kubectl apply -f httproute.yaml

Environment Variables

| Variable | Description | Default | Required | |----------|-------------|---------|----------| | GARMIN_EMAIL | Garmin Connect email address | - | Yes | | GARMIN_PASSWORD | Garmin Connect password | - | Yes | | GARMIN_MFA_CODE | 2FA code (only if MFA is enabled) | - | No* | | GARMIN_MFA_WAIT_SECONDS | Seconds to wait for MFA code | 0 | No | | GARMINTOKENS | Path to token storage directory | ~/.garminconnect | No | | GARMIN_MCP_TRANSPORT | Transport type: stdio or streamable-http | http | No | | GARMIN_MCP_HOST | Bind host for HTTP transport | 0.0.0.0 | No | | GARMIN_MCP_PORT | Port for HTTP transport | 8000 | No |

*Required only if MFA is enabled on your Garmin Connect account, and only on first run or when tokens expire

Token Management

OAuth tokens are automatically saved to ~/.garminconnect (or path specified by GARMINTOKENS) after successful login. These tokens persist across restarts, eliminating the need for MFA on subsequent runs until they expire.

For Kubernetes: Tokens are stored in the PersistentVolumeClaim, so they persist across pod restarts and deployments.

Troubleshooting

Login Issues

  1. Invalid credentials: Verify your email and password are correct
  2. MFA required: If you have MFA enabled, ensure GARMIN_MFA_CODE is set for first run
  3. Token expired: Delete the token directory and re-authenticate

Network Issues

  1. Can't connect to server: Verify the server is binding to 0.0.0.0 (not 127.0.0.1)
  2. Connection refused: Check firewall rules and port exposure
  3. 404 errors: Ensure you're using the correct transport type (Streamable HTTP for network access)

Kubernetes Issues

  1. Pod crash loops: Check logs with kubectl logs -n mcpo deployment/garmin-mcp
  2. Service not accessible: Verify Service selector matches Deployment labels
  3. Token persistence: Ensure PVC is properly mounted and has storage available

Viewing Logs

Docker:

docker logs <container-id>

Kubernetes:

kubectl logs -n mcpo deployment/garmin-mcp -f

Available Tools

This server provides 78+ MCP tools. See TOOLS.md for a complete list organised by category.

Example Queries

Once connected to the MCP server, you can query your Garmin fitness data using natural language. Here are some example queries you can make:

Activity Queries

  • "Show me my recent activities"

    • Uses: list_activities
  • "What running activities did I do between September 1st and November 6th?"

    • Uses: get_activities_by_date with activity_type="running"
  • "Get details for activity ID 204592654"

    • Uses: get_activity
  • "Show me the splits for my last run"

    • Uses: get_activity_splits
  • "What was the weather during my activity 204592654?"

    • Uses: get_activity_weather

Health & Wellness Queries

  • "How many steps did I take on November 6th?"

    • Uses: get_steps_data
  • "Show me my sleep data for November 5th"

    • Uses: get_sleep_data
  • "What was my heart rate on November 6th?"

    • Uses: get_heart_rates
  • "Get my body battery data from November 1st to November 6th"

    • Uses: get_body_battery
  • "What was my stress level on November 5th?"

    • Uses: get_stress_data or get_all_day_stress
  • "Show me my resting heart rate for November 6th"

    • Uses: get_rhr_day
  • "Get my body composition data for November 6th"

    • Uses: get_body_composition
  • "What was my training readiness on November 6th?"

    • Uses: get_training_readiness
  • "Show me my hydration data for November 6th"

    • Uses: get_hydration_data
  • "Get my SpO2 (blood oxygen) data for November 6th"

    • Uses: get_spo2_data
  • "What was my respiration rate on November 6th?"

    • Uses: get_respiration_data

Training & Performance Queries

  • "What's my VO2 Max and fitness age for November 6th?"

    • Uses: get_max_metrics
  • "Get my HRV (Heart Rate Variability) data for November 6th"

    • Uses: get_hrv_data
  • "Show me my fitness age data for November 6th"

    • Uses: get_fitnessage_data
  • "What was my training effect for activity 204592654?"

    • Uses: get_training_effect
  • "Get my hill score from September 1st to November 6th"

    • Uses: get_hill_score
  • "Show me my endurance score between September 1st and November 6th"

    • Uses: get_endurance_score

Device & Gear Queries

  • "List all my Garmin devices"

    • Uses: get_devices
  • "What's my primary training device?"

    • Uses: get_primary_training_device
  • "Show me the gear I used for activity 204592654"

    • Uses: get_activity_gear

Challenges & Goals Queries

  • "What are my active goals?"

    • Uses: get_goals with goal_type="active"
  • "Show me my personal records"

    • Uses: get_personal_record
  • "What badges have I earned?"

    • Uses: get_earned_badges
  • "Get my race predictions"

    • Uses: get_race_predictions

User Profile Queries

  • "What's my full name?"

    • Uses: get_full_name
  • "What unit system do I use?"

    • Uses: get_unit_system
  • "Show me my user profile"

    • Uses: get_user_profile

Data Management Queries

  • "Add a weight measurement: 75.5 kg"

    • Uses: add_weigh_in
  • "Get my weight measurements from November 1st to November 6th"

    • Uses: get_weigh_ins
  • "Add body composition data for November 6th"

    • Uses: add_body_composition

Training and Diet Recommedations

  • "Prepare me for a marathon with training and diet recommendations"

    • Uses: get_training_and_diet_recommendations
  • "I want to improve my running performance, give me training and diet recommendations"

    • Uses: get_training_and_diet_recommendations
  • "I want to lose weight - what should I do?"

    • Uses: get_training_and_diet_recommendations

Complex Queries

The MCP server can handle complex, multi-step queries:

  • "Analyze my training week: show me my activities, sleep quality, and body battery from November 1st to November 6th"

    • Combines: get_activities_by_date, get_sleep_data, get_body_battery
  • "Compare my running performance: get my activities, training effect, and heart rate zones for my last 5 runs"

    • Combines: list_activities, get_training_effect, get_activity_hr_in_timezones
  • "Give me a complete health summary for November 6th: steps, sleep, stress, heart rate, and body battery"

    • Combines: get_steps_data, get_sleep_data, get_stress_data, get_heart_rates, get_body_battery

One‑Pane Summaries and Insights

  • "Show my weekly single-pane summary for last week"

    • Uses: get_period_summary with period="weekly", anchor_date="last week"
  • "Fetch a monthly dashboard summary including activities and readiness"

    • Uses: get_period_summary with period="monthly"
  • "What are my trends over the last 4 weeks?"

    • Uses: get_trends with start_date, end_date, include=["rhr","hrv","sleep","steps","body_battery"]
  • "Detect any recovery red flags this week"

    • Uses: detect_anomalies with heuristic thresholds (defaults sensible)
  • "Give me a readiness breakdown for today"

    • Uses: get_readiness_breakdown
  • "How complete is my data this month?"

    • Uses: get_data_completeness
  • "Hydration target for a 60‑minute run at 28°C, weight 75 kg"

    • Uses: get_hydration_guidance with weight_kg=75, training_minutes=60, temperature_c=28
  • "Coach cues for this week"

    • Uses: get_coach_cues with period="weekly"

Tip: These tools accept natural timeframe phrases like today, yesterday, last week, this week, last month, last 28 days. Ranges automatically clamp to today so mid-week requests never reach into the future.

Accessing the Server

When deployed with Streamable HTTP transport, the MCP server is accessible at:

  • Local: http://localhost:8000/mcp
  • Kubernetes with Istio: https://garmin-mcp.example.com/mcp
  • Docker: http://<container-ip>:8000/mcp

Configure your MCP client (OpenWebUI, Claude, etc.) to connect to the /mcp endpoint for Streamable HTTP transport.

Security Notes

  • Never commit credentials: Use environment variables or Kubernetes Secrets
  • Token storage: Tokens are stored locally/on PVC - ensure proper access controls
  • Network security: For production, use TLS/HTTPS (terminate at Ingress/Gateway)
  • Secret rotation: Rotate Garmin password regularly and update secrets accordingly

License

See LICENSE file for details.

Contributing

Contributions are welcome! Please open an issue or submit a pull request.

快速设置
此服务器的安装指南

安装包 (如果需要)

uvx garmin_mcp

Cursor 配置 (mcp.json)

{ "mcpServers": { "k8sautom8-garmin-mcp": { "command": "uvx", "args": [ "garmin_mcp" ] } } }