Skip to content

MAnders333/garmin-mcp-server

Repository files navigation

Garmin MCP Server

A semantic MCP server for Garmin Connect data access optimized for fitness, health, and coaching applications.

Overview

The Garmin MCP Server provides intelligent tools that combine Garmin Connect data into useful abstractions for personal coach agents and fitness applications. Built with FastMCP framework and leveraging the python-garminconnect library.

Key Features

  • Semantic Coaching Tools: Combines multiple API calls into coaching-relevant abstractions
  • 8 Specialized Tools: Daily overview, recovery status, activity analysis, training zones, fitness assessment, progress tracking, and wellness monitoring
  • Agent-Optimized: Designed for LLM agent consumption with consistent response formats
  • Production Ready: FastMCP framework with streamable_http transport
  • OAuth Authentication: Secure token management with MFA support

🚀 Quick Start

🐳 Docker (Recommended)

Get started in under 2 minutes:

git clone https://github.com/MAnders333/garmin-mcp-server.git
cd garmin-mcp-server

# Automated setup with validation
./scripts/docker-dev-setup.sh

# Or manual setup:
cp .env.example .env
# Edit .env with your Garmin credentials
docker-compose up -d

For comprehensive Docker instructions, see Docker Quick Start Guide.

Option 1: Docker (Full Details)

Quick start with Docker Compose:

git clone https://github.com/MAnders333/garmin-mcp-server.git
cd garmin-mcp-server

# Copy and configure environment
cp .env.example .env
# Edit .env with your Garmin credentials (GARMIN_EMAIL and GARMIN_PASSWORD)

# Start server with Docker Compose
docker-compose up -d

# View logs
docker-compose logs -f

Using Docker directly:

# Build the image
docker build -t garmin-mcp-server .

# Run with environment file
docker run -d \
  --name garmin-mcp-server \
  --env-file .env \
  -p 8000:8000 \
  -v garmin-tokens:/app/.garmin \
  garmin-mcp-server

Option 2: Local Python Installation

  1. Clone and Setup

    git clone https://github.com/MAnders333/garmin-mcp-server.git
    cd garmin-mcp-server
    
    # Quick setup (recommended)
    make quick-start
    source .venv/bin/activate  # Linux/macOS
    
    # Or manual setup:
    # uv venv --python 3.11
    # source .venv/bin/activate
    # uv pip install -e ".[dev]"
  2. Configuration

    # Copy the example configuration
    cp .env.example .env
    
    # Edit .env with your Garmin Connect credentials
    # Required:
    #   [email protected]
    #   GARMIN_PASSWORD=your_secure_password
    # Optional:
    #   GARMIN_TOKENS_PATH=~/.garminconnect
    #   MCP_HOST=127.0.0.1
    #   MCP_PORT=8000
  3. Run Server

    # Start the MCP server (streamable_http transport on port 8000)
    python scripts/start_server.py
    
    # Or using the server module directly
    python src/garmin_mcp/server.py

First Run Authentication

On first run, you'll authenticate with Garmin Connect (MFA required):

Starting Garmin MCP Server on 127.0.0.1:8000
Enter MFA code: 123456
✅ Successfully authenticated and saved tokens to ~/.garminconnect

For Docker users: OAuth tokens are automatically persisted in the garmin-tokens Docker volume, so you only need to authenticate once.

For detailed authentication setup and troubleshooting, see docs/authentication.md.

Available Tools

Core Coaching Tools

  • get_daily_coaching_overview - Complete daily health and activity summary
  • get_recovery_status - Sleep, HRV, body battery, and stress analysis
  • get_recent_activities - Recent workouts with coaching-relevant metrics
  • analyze_activity - Deep performance analysis with splits and zones

Advanced Tools

  • get_training_zones - Training zones based on lactate threshold and VO2 max
  • get_fitness_assessment - VO2 max, training status, and performance trends
  • get_progress_summary - Long-term trends and goal progress
  • get_wellness_snapshot - Comprehensive health and wellness status

Architecture

Minimally Complex Design

Following the principle of "Minimal Viable Complexity", this server uses a clean, modular architecture:

  • Direct Integration: Uses garminconnect library directly without unnecessary wrappers
  • Server-Level Authentication: OAuth token management integrated into the main server module
  • Simple Dependencies: Only essential libraries (FastMCP, garminconnect, pydantic)
  • Clear Data Flow: Authentication → API calls → Response formatting with consistent error handling

Semantic Tool Design

This server provides semantic tools that:

  • Combine multiple API calls into coaching-relevant data structures
  • Filter data by returning only coaching-relevant metrics
  • Optimize for agents with consistent response formats and graceful degradation
  • Use coaching terminology instead of technical API field names

Example Tool Response

{
  "date": "2025-10-11",
  "activity_summary": {
    "data": {
      "steps": 8542,
      "distance_meters": 6200,
      "calories_burned": 2145,
      "active_minutes": 67
    },
    "api_method": "get_user_summary",
    "status": "success"
  },
  "sleep_data": {
    "data": {
      "sleep_score": 82,
      "total_sleep_hours": 7.5,
      "deep_sleep_minutes": 89
    },
    "api_method": "get_sleep_data",
    "status": "success"
  },
  "body_battery": {
    "data": [
      {"timestamp": "2025-10-11T06:00:00", "level": 85},
      {"timestamp": "2025-10-11T12:00:00", "level": 67}
    ],
    "api_method": "get_body_battery",
    "status": "success"
  }
}

Docker Deployment

Docker Compose (Recommended)

The easiest way to run the Garmin MCP Server is with Docker Compose:

# 1. Copy environment template
cp .env.example .env

# 2. Edit .env with your credentials
[email protected]
GARMIN_PASSWORD=your_secure_password

# 3. Start the server
docker-compose up -d

# 4. Check logs
docker-compose logs -f garmin-mcp-server

# 5. Health check
curl http://localhost:8000/health

Docker Services

The docker-compose.yml provides two services:

Production Service (default):

docker-compose up -d
  • Optimized production build
  • Minimal attack surface
  • Persistent OAuth token storage
  • Health monitoring

Development Service (optional):

docker-compose --profile dev up -d garmin-mcp-dev
  • Hot code reload with volume mounts
  • Debug logging enabled
  • Runs on port 8001 by default

Manual Docker Commands

Build and run:

# Build image
docker build -t garmin-mcp-server .

# Run container
docker run -d \
  --name garmin-mcp-server \
  --env-file .env \
  -p 8000:8000 \
  -v garmin-tokens:/app/.garmin \
  garmin-mcp-server

Container management:

# View logs
docker logs -f garmin-mcp-server

# Stop and remove
docker stop garmin-mcp-server
docker rm garmin-mcp-server

# Clean up volumes (removes OAuth tokens)
docker volume rm garmin-tokens

Docker Environment Variables

All environment variables from .env.example are supported in Docker:

# Required
[email protected]
GARMIN_PASSWORD=your_password

# Optional Docker-specific settings
MCP_HOST=0.0.0.0                     # Docker internal host (don't change)
MCP_PORT=8000                        # Internal container port
GARMIN_TOKENS_PATH=/app/.garmin      # Token storage path in container

# Optional general settings
GARMIN_IS_CN=false                   # China region support
LOG_LEVEL=INFO                       # Logging level
RATE_LIMIT_PER_MINUTE=60            # API rate limiting
CACHE_TIMEOUT_SECONDS=300           # Cache duration

Docker Security Features

  • Non-root user: Container runs as garmin user (non-root)
  • Minimal base image: Uses Python slim image with minimal packages
  • Token persistence: OAuth tokens stored in secure Docker volume
  • Health monitoring: Built-in health check endpoint
  • Network isolation: Runs in dedicated Docker network

Docker Troubleshooting

Container won't start:

# Check logs for errors
docker-compose logs garmin-mcp-server

# Verify environment variables
docker-compose config

Authentication issues:

# Remove token volume to force re-authentication
docker-compose down
docker volume rm garmin-mcp-server_garmin-tokens
docker-compose up -d

Port conflicts:

# Change port in .env file
echo "MCP_PORT=8001" >> .env
docker-compose down && docker-compose up -d

Health check failures:

# Check if server is responding
docker exec garmin-mcp-server curl -f http://localhost:8000/health

# Check container health status
docker inspect garmin-mcp-server --format='{{.State.Health.Status}}'

Configuration

Environment Variables

# Required
[email protected]
GARMIN_PASSWORD=your_password

# Optional
GARMIN_TOKENS_PATH=~/.garminconnect  # OAuth token storage (local) / /app/.garmin (Docker)
MCP_HOST=127.0.0.1                   # Server bind address (0.0.0.0 for Docker)
MCP_PORT=8000                        # Server port
GARMIN_IS_CN=false                   # China region support
LOG_LEVEL=INFO                       # Logging level
RATE_LIMIT_PER_MINUTE=60            # API rate limiting
CACHE_TIMEOUT_SECONDS=300           # Cache timeout

Security

  • OAuth tokens stored locally (1-year lifetime)
  • No Garmin data stored permanently
  • Secure credential handling with environment variables
  • MFA support for enhanced account security
  • Docker containers run as non-root user

Development

Project Structure

src/garmin_mcp/
├── __init__.py         # Package initialization
├── server.py           # Main MCP server with 8 tools and authentication
├── models.py           # Pydantic response models for structured responses
└── utils.py            # Utility functions for data processing and error handling

# Docker files
├── Dockerfile          # Multi-stage Docker build
├── docker-compose.yml  # Docker Compose configuration
├── .dockerignore       # Docker build exclusions
├── .env.docker         # Docker-optimized environment template
└── scripts/
    ├── docker-dev-setup.sh    # Automated Docker setup
    └── validate-docker.sh     # Docker validation script

Adding New Tools

  1. Define tool in server.py:

    @mcp.tool()
    def my_coaching_tool(param: str) -> Dict[str, Any]:
        """Tool description for agents"""
        # Use handle_api_call for consistent error handling
        result = handle_api_call(
            lambda: _garmin_client.get_some_data(param),
            "get_some_data",
            "No data available for this request"
        )
        return result
  2. Tool is automatically registered when decorated with @mcp.tool()

  3. Update tool documentation in docs/tools-reference.md

Testing

# Setup development environment (if not done already)
make quick-start
source .venv/bin/activate

# Run all tests with coverage
make test

# Or run specific test categories
make test-unit      # Unit tests only
make test-cov       # Tests with detailed coverage
make ci             # Full CI pipeline

For comprehensive testing instructions, see docs/testing.md.

Design Philosophy

This server takes a coaching-first approach to data presentation:

  • Semantic Abstraction: Rather than exposing raw API endpoints, tools combine related data points into coaching-relevant structures
  • Intelligent Filtering: Responses include only actionable metrics, reducing noise for LLM agents
  • Advanced Metrics: Includes training zones, VO2 max, HRV, lactate threshold, and other performance indicators
  • Graceful Degradation: Tools return partial data when some metrics are unavailable
  • Coaching Insights: Where appropriate, tools provide contextual recommendations

Use Cases

Personal Coach Agent

# Get daily coaching overview
overview = await call_tool("get_daily_coaching_overview")
# Returns: structured response with status indicators for each API call

# Analyze recent workout
analysis = await call_tool("analyze_activity", {"activity_id": "12345"})
# Returns: activity details, splits data, HR zones with status indicators

# Check training readiness
recovery = await call_tool("get_recovery_status") 
# Returns: sleep analysis, HRV, body battery, resting HR with status indicators

Fitness Dashboard

# Get comprehensive fitness assessment
fitness = await call_tool("get_fitness_assessment")
# Returns: fitness metrics, training status, race predictions with status indicators

# Track progress over time
progress = await call_tool("get_progress_summary", {
    "start_date": "2025-09-11", 
    "end_date": "2025-10-11"
})
# Returns: progress data, goals, personal records with status indicators

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes with tests
  4. Submit a pull request

Guidelines

  • Follow semantic tool design principles
  • Include comprehensive error handling
  • Add coaching insights where valuable
  • Maintain response format consistency
  • Test with real Garmin data

License

MIT License - see LICENSE file for details.

Troubleshooting

Common Issues

Authentication Fails

Local installation:

# Clear existing tokens and retry
rm -rf ~/.garminconnect
python scripts/start_server.py

Docker:

# Remove token volume to force re-authentication
docker-compose down
docker volume rm garmin-mcp-server_garmin-tokens
docker-compose up -d

MFA Code Required

  • Check email/SMS for Garmin Connect MFA code
  • Code expires quickly - enter immediately when prompted
  • If authentication still fails, verify credentials in .env
  • For Docker: Monitor logs with docker-compose logs -f during first authentication

No Data Available

  • Some metrics require specific Garmin devices (Body Battery, HRV, etc.)
  • Recent activities might not appear immediately after syncing
  • Historical data access depends on your Garmin Connect account history

Server Won't Start

Local installation:

# Check if port 8000 is already in use
lsof -i :8000

# Or specify different port in .env
echo "MCP_PORT=8001" >> .env

Docker:

# Check if port is in use
docker ps | grep 8000

# Change port in .env and restart
echo "MCP_PORT=8001" >> .env
docker-compose down && docker-compose up -d

Docker-Specific Issues

Container Health Check Failing

# Check container logs
docker-compose logs garmin-mcp-server

# Manual health check
docker exec garmin-mcp-server curl -f http://localhost:8000/health

# Restart unhealthy container
docker-compose restart garmin-mcp-server

Container Won't Start

# Check Docker configuration
docker-compose config

# View detailed error logs
docker-compose up --no-daemonize

# Check Docker daemon is running
docker info

Environment Variable Issues

# Validate environment file
docker-compose config

# Check variables are loaded correctly
docker-compose run --rm garmin-mcp-server env | grep GARMIN

Tools Return No Data Status

  • Verify Garmin Connect account has activity data
  • Some tools require recent activities within specified date ranges
  • Check tool responses - they include status indicators for each API call
  • Tools return status "no_data" instead of failing when data is unavailable

Documentation

Support

Acknowledgments

  • Built with FastMCP framework
  • Uses python-garminconnect library
  • Thanks to the MCP and Garmin Connect communities for their excellent tools and libraries

About

Garmin MCP Server designed for agentic coaching. Combines several API calls into semantic tools.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published