Skip to main content
Glama
README.md
# Garmin Coach MCP

A Model Context Protocol (MCP) server that lets an AI assistant **read your Garmin Connect
data and write structured training back into it**. It covers sleep, health metrics, activities
and training volume on the read side, and multi-sport structured workouts, calendar scheduling
and performance metrics on the write side.

Built to close a specific gap: most Garmin integrations only read. This one plans a training
block and puts it on the watch.

## Table of Contents

- [Overview](#overview)
- [Features](#features)
- [Setup](#setup)
- [Available Tools](#available-tools)
- [Usage Examples](#usage-examples)
- [Advanced Features](#advanced-features)
- [Development](#development)
- [Security](#security)
- [Attribution](#attribution)

## Overview

This MCP server connects your AI assistant (Claude Desktop, Claude Code, or any MCP-compatible client) directly to your Garmin Connect account, enabling:

- **Real-time Health Insights**: Access sleep, heart rate, steps, stress, and body battery data
- **Training Analytics**: Aggregate training volume by week, month, or custom date ranges
- **Activity Analysis**: Retrieve detailed activity data with filtering and pagination
- **Multi-metric Summaries**: Get comprehensive daily health overviews

**Tech Stack**: TypeScript, Node.js 20+, MCP SDK, garmin-connect library

## Features

### šŸŒ™ Sleep Analytics
- Detailed sleep stages (deep, light, REM, awake)
- Sleep scores and quality metrics
- Duration and timing analysis
- Summary and detailed modes

### šŸ’Ŗ Health Metrics
- **Steps**: Daily step counts, goals, and progress tracking
- **Heart Rate**: Resting HR, max HR, zones, and time-series data
- **Body Composition**: Weight tracking and body composition
- **Stress & Recovery**: Stress levels and body battery metrics

### šŸƒ Activity Tracking
- Recent activity lists with filtering
- Detailed activity information (splits, laps, metrics)
- Activity-specific data (distance, duration, pace, elevation)
- Pagination support for large datasets

### šŸ“Š Training Volume Analysis
- Weekly training aggregation (ISO week standards)
- Monthly training summaries
- Custom date range analysis (up to 365 days)
- Activity type filtering (running, cycling, swimming, etc.)
- Trend analysis (week-over-week, month-over-month)
- Sport-specific breakdowns

## Setup

### Prerequisites

1. **Garmin Connect Account**: Active account with data from a compatible Garmin device
2. **Node.js**: Version 20 or higher
3. **MCP Client**: Claude Desktop, Claude Code, or another MCP-compatible application

### Installation

#### Option 1: Use with npx (Recommended)

No installation required! Configure directly in your MCP client:

**Claude Desktop** (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS or `%APPDATA%\Claude\claude_desktop_config.json` on Windows):

```json
{
  "mcpServers": {
    "garmin-connect": {
      "command": "npx",
      "args": ["-y", "garmin-connect-mcp@latest"],
      "env": {
        "GARMIN_USERNAME": "your_username",
        "GARMIN_PASSWORD": "your_password"
      }
    }
  }
}
```

**Claude Code**:

Using the Claude Code CLI (recommended):
```bash
claude mcp add garmin-connect npx garmin-connect-mcp@latest \
  --env GARMIN_USERNAME=your_username \
  --env GARMIN_PASSWORD=your_password
```

Or manually configure (`.claude/mcp.json` in your project):
```json
{
  "mcpServers": {
    "garmin-connect": {
      "command": "npx",
      "args": ["-y", "garmin-connect-mcp@latest"],
      "env": {
        "GARMIN_USERNAME": "your_username",
        "GARMIN_PASSWORD": "your_password"
      }
    }
  }
}
```

The `-y` flag automatically accepts the npx prompt, ensuring smooth startup.

#### Option 2: Global Installation

Install globally via npm:

```bash
npm install -g garmin-connect-mcp@latest
```

Then configure without npx:

```json
{
  "mcpServers": {
    "garmin-connect": {
      "command": "garmin-connect-mcp",
      "env": {
        "GARMIN_USERNAME": "your_username",
        "GARMIN_PASSWORD": "your_password"
      }
    }
  }
}
```

#### Option 3: Local Development

For development or testing local changes:

```bash
git clone <repository-url>
cd garmin-connect-mcp
pnpm install
pnpm build
```

Configure with absolute path:

```json
{
  "mcpServers": {
    "garmin-connect": {
      "command": "node",
      "args": ["/absolute/path/to/garmin-connect-mcp/dist/index.js"],
      "env": {
        "GARMIN_USERNAME": "your_username",
        "GARMIN_PASSWORD": "your_password"
      }
    }
  }
}
```

Or use a `.env` file:

```json
{
  "mcpServers": {
    "garmin-connect": {
      "command": "node",
      "args": ["/absolute/path/to/garmin-connect-mcp/dist/index.js"],
      "envFile": "/absolute/path/to/garmin-connect-mcp/.env"
    }
  }
}
```

## Available Tools

### Overview Tools

#### `get_daily_overview`
Get a comprehensive daily summary including sleep, activities, and health metrics in one call.

**Parameters:**
- `date` (optional): Date in `YYYY-MM-DD` format (defaults to today)

**Example:**
```
Show me my daily overview for yesterday
```

**Response includes:**
- Sleep summary (duration, quality, stages)
- Activity summary (count, total duration, distance)
- Health metrics (steps, heart rate, stress, body battery)

---

### Sleep Tools

#### `get_sleep_data`
Get detailed sleep information including sleep stages, movements, and quality metrics.

**Parameters:**
- `date` (optional): Date in `YYYY-MM-DD` format (defaults to today)
- `summary` (optional): Return only summary data (default: false)
- `fields` (optional): Specific fields to include (e.g., `['dailySleepDTO', 'wellnessEpochSummaryDTO']`)

**Example:**
```
Get my detailed sleep data for 2025-01-15
Show me a sleep summary for last night
```

**Response includes:**
- Total sleep duration
- Sleep stages (deep, light, REM, awake) with durations
- Sleep scores and quality ratings
- Start/end times
- Movement data (when `summary: false`)

#### `get_sleep_duration`
Quick access to total sleep duration for a specific date.

**Parameters:**
- `date` (optional): Date in `YYYY-MM-DD` format (defaults to today)

**Example:**
```
How many hours did I sleep last night?
```

---

### Health Metrics Tools

#### `get_health_metrics`
Get aggregated health metrics for a specific date.

**Parameters:**
- `date` (optional): Date in `YYYY-MM-DD` format (defaults to today)
- `metrics` (optional): Array of specific metrics `['steps', 'weight', 'heart_rate', 'stress', 'body_battery']` (defaults to all)

**Example:**
```
What are my health metrics for today?
Show me just my steps and heart rate for yesterday
```

**Response includes:**
- Steps data (count, goal, distance)
- Heart rate (resting, max, zones)
- Stress levels
- Body battery percentage
- Weight and body composition

#### `get_steps_data`
Get detailed step count and activity data.

**Parameters:**
- `date` (optional): Date in `YYYY-MM-DD` format (defaults to today)
- `summary` (optional): Return only summary data (default: false)

**Example:**
```
Show me my step data for today
How many steps did I take yesterday?
```

**Response includes:**
- Total steps
- Daily goal and progress percentage
- Distance covered
- Active time
- Hourly breakdown (when `summary: false`)

#### `get_heart_rate_data`
Get detailed heart rate measurements and zone data.

**Parameters:**
- `date` (optional): Date in `YYYY-MM-DD` format (defaults to today)
- `summary` (optional): Return only summary data (default: false)

**Example:**
```
What was my heart rate today?
Show me my heart rate zones for yesterday
```

**Response includes:**
- Resting heart rate
- Maximum heart rate
- Average heart rate
- Heart rate zones and time in each zone
- Time-series measurements (when `summary: false`)

#### `get_weight_data`
Get weight and body composition data.

**Parameters:**
- `date` (optional): Date in `YYYY-MM-DD` format (defaults to today)

**Example:**
```
What's my current weight?
Show me my weight for last week
```

**Response includes:**
- Weight (kg/lbs)
- BMI
- Body fat percentage
- Muscle mass
- Body water percentage

---

### Activity Tools

#### `get_activities`
Get a list of recent activities with optional filtering and pagination.

**Parameters:**
- `start` (optional): Starting index for pagination (default: 0)
- `limit` (optional): Number of activities to return, max 50 (default: 20)
- `summary` (optional): Return compact summary format (default: false)

**Example:**
```
List my last 10 activities
Show me my recent runs
Get activities 20-40 (for pagination)
```

**Response includes:**
- Activity ID and name
- Activity type (running, cycling, swimming, etc.)
- Start time and duration
- Distance, pace, speed
- Calories and elevation gain
- Heart rate data
- Splits and laps (when `summary: false`)

#### `get_activity_details`
Get comprehensive information for a specific activity.

**Parameters:**
- `activityId` (required): The unique ID of the activity

**Example:**
```
Show me details for activity 12345678
Give me the full breakdown of my last run
```

**Response includes:**
- Complete activity metadata
- Detailed splits and laps
- Heart rate zones
- Cadence, power, and other sensor data
- GPS/route information
- Weather conditions

---

### Training Volume Tools

#### `get_weekly_volume`
Get aggregated training volume for a specific ISO week.

**Parameters:**
- `year` (optional): Year (defaults to current year)
- `week` (optional): ISO week number 1-53 (defaults to current week)
- `includeActivityBreakdown` (optional): Include per-sport breakdown (default: true)
- `includeTrends` (optional): Compare with previous week (default: false)
- `maxActivities` (optional): Max activities to process, up to 2000 (default: 1000)
- `activityTypes` (optional): Filter by activity types (e.g., `['running', 'cycling']`)

**Example:**
```
What was my training volume this week?
Show me week 42 of 2024 with trends
Compare my running volume this week vs last week
```

**Response includes:**
- Week number and date range
- Total metrics (duration, distance, calories, elevation)
- Activity count
- Breakdown by activity type
- Week-over-week trends (when `includeTrends: true`)

#### `get_monthly_volume`
Get aggregated training volume for a specific month.

**Parameters:**
- `year` (optional): Year (defaults to current year)
- `month` (optional): Month number 1-12 (defaults to current month)
- `includeActivityBreakdown` (optional): Include per-sport breakdown (default: true)
- `includeTrends` (optional): Compare with previous month (default: false)
- `maxActivities` (optional): Max activities to process, up to 2000 (default: 1000)
- `activityTypes` (optional): Filter by activity types

**Example:**
```
What was my training volume in January?
Show me this month's cycling volume
Compare my training this month vs last month
```

**Response includes:**
- Month name and date range
- Total metrics (duration, distance, calories, elevation)
- Activity count
- Breakdown by activity type
- Month-over-month trends

#### `get_custom_range_volume`
Get training volume for any custom date range (up to 365 days).

**Parameters:**
- `dateRange` (required): Date range as `YYYY-MM-DD/YYYY-MM-DD`
- `includeActivityBreakdown` (optional): Include per-sport breakdown (default: true)
- `includeDailyBreakdown` (optional): Include day-by-day breakdown (default: false)
- `maxActivities` (optional): Max activities to process, up to 2000 (default: 1000)
- `activityTypes` (optional): Filter by activity types

**Example:**
```
What was my training volume from 2025-01-01 to 2025-01-31?
Show me my running volume for the last 90 days
Give me a daily breakdown for the past 2 weeks
```

**Response includes:**
- Date range and period length
- Total metrics across the range
- Activity count
- Breakdown by activity type
- Daily breakdown (when `includeDailyBreakdown: true`)

### Performance Tools

Endpoints the underlying library does not type, reached through a raw GET helper.
All are read-only.

#### `get_vo2max`
VO2max series at one-decimal precision, plus first, last, min, max and the change
over the range.

**Parameters:** `startDate`, `endDate` (both optional, default: last 90 days)

Garmin's interface rounds VO2max to a whole number, which can sit unchanged for
months while the underlying value moves. Read `vo2MaxPreciseValue` before
concluding that fitness has plateaued.

#### `get_activity_laps`
Per-lap splits for one activity, with pace, HR and cadence per lap.

**Parameters:** `activityId` (required)

`get_activity_details` aggregates by step type, which collapses the individual
reps of an interval session. This is what makes rep-by-rep analysis possible —
whether a session held pace or faded. Laps under 300 m report no pace, since over
that distance the figure is noise.

#### `get_hr_zones`
The heart rate zones configured on the account, per sport, and the basis they are
calculated from.

**Parameters:** none

The basis matters more than the percentages. An identical 60-70% band maps to very
different bpm under max HR, heart rate reserve and lactate threshold, so a run can
be reported as too hard purely because the basis is wrong.

Garmin returns one entry per sport, each with its own threshold, and includes the
detected lactate threshold HR even when the zones are not based on it — so this is
also where to read the threshold itself. The untouched payload is returned
alongside the summary, since the field set is not contractual.

#### `get_activity_hr_zones`
Time spent in each heart rate zone during one activity, with each zone's share of
the session.

**Parameters:** `activityId` (required)

Average HR hides distribution: an evenly aerobic run and one that alternated too
hard and too soft can average the same number.

#### `get_race_predictions`
Predicted 5K, 10K, half marathon and marathon times, formatted and in seconds.

**Parameters:** none

Note that this reflects race history as well as current fitness, so it can lag a
real change in either direction.

#### `get_training_readiness`
The readiness score for a date, derived from sleep, recovery time, HRV and recent
load.

**Parameters:** `date` (optional, defaults to today)

#### `get_hrv`
Overnight heart rate variability: last night's average, weekly average, baseline
range and status.

**Parameters:** `date` (optional, defaults to today)

#### `get_training_status`
Aggregated training status for a date, including the most recent VO2max reading
and acute/chronic load balance.

**Parameters:** `date` (optional, defaults to today)

## Usage Examples

### Quick Health Check
```
What's my daily overview for today?
```

### Sleep Analysis
```
Show me my sleep quality for the past week
Compare my deep sleep from Monday vs Tuesday
```

### Training Insights
```
How much did I run this month?
Compare my weekly volume: this week vs last week
What's my total training time for Q1 2025?
```

### Activity Exploration
```
List my last 20 activities
Show me all my runs from January with heart rate data
What was my fastest 5K in the past 6 months?
```

### Advanced Queries
```
Get my weekly running volume with trends for week 15 of 2025
Show me daily breakdown of cycling for 2025-03-01/2025-03-31
What are my health metrics (just steps and heart rate) for yesterday?
```

## Advanced Features

### Pagination
For large activity lists, use pagination:
```javascript
// Get activities 0-49
get_activities({ start: 0, limit: 50 })

// Get activities 50-99
get_activities({ start: 50, limit: 50 })
```

### Activity Type Filtering
Filter training volume by specific sports:
```javascript
get_weekly_volume({
  activityTypes: ['running', 'cycling'],
  includeTrends: true
})
```

### Trend Analysis
Compare periods to track progress:
```javascript
// Week-over-week comparison
get_weekly_volume({ includeTrends: true })

// Month-over-month comparison
get_monthly_volume({ includeTrends: true })
```

### Summary vs Detailed Modes
Control response size and detail level:
```javascript
// Quick summary
get_sleep_data({ summary: true })

// Full detailed breakdown with time-series
get_sleep_data({ summary: false })
```

### Response Size Management
The server automatically validates response sizes and provides fallback summaries if data exceeds limits. For large date ranges, consider:
- Using `summary: true` mode
- Filtering by specific activity types
- Reducing date ranges
- Disabling detailed breakdowns

## Development

### Commands

```bash
# Development
pnpm install          # Install dependencies
pnpm build            # Build for production
pnpm dev              # Watch mode with auto-rebuild

# Quality Checks
pnpm typecheck        # Run TypeScript type checking
pnpm lint             # Lint code
pnpm lint:fix         # Auto-fix linting issues

# Testing
pnpm test             # Run tests in watch mode
pnpm test:run         # Run tests once
pnpm test:coverage    # Generate coverage report
```

### Project Structure

```
garmin-connect-mcp/
ā”œā”€ā”€ src/
│   ā”œā”€ā”€ client/           # Garmin Connect API client
│   ā”œā”€ā”€ tools/            # MCP tool implementations
│   │   ā”œā”€ā”€ overview-tools.ts
│   │   ā”œā”€ā”€ sleep-tools.ts
│   │   ā”œā”€ā”€ health-tools.ts
│   │   ā”œā”€ā”€ activity-tools.ts
│   │   └── activity-volume-tools.ts
│   ā”œā”€ā”€ types/            # TypeScript type definitions
│   ā”œā”€ā”€ utils/            # Helper functions
│   └── index.ts          # Main server entry point
ā”œā”€ā”€ dist/                 # Built output
└── __tests__/            # Test files
```

### Running Tests

```bash
# Run all tests
pnpm test:run

# Run with coverage
pnpm test:coverage

# Watch mode for development
pnpm test
```

## Security

### Credential Management

**Best Practices:**
- āœ… Use environment variables for credentials
- āœ… Use `.env` files (ensure `.env` is in `.gitignore`)
- āœ… Use MCP configuration `env` or `envFile` options
- āŒ Never hardcode credentials in configuration files
- āŒ Never commit credentials to version control

### Environment Variables

Create a `.env` file in the project root:
```bash
GARMIN_USERNAME=your_username
GARMIN_PASSWORD=your_password
```

### Testing Locally

For local development, use `.mcp.json` (gitignored):
```json
{
  "mcpServers": {
    "garmin-connect": {
      "command": "node",
      "args": ["./dist/index.js"],
      "envFile": ".env"
    }
  }
}
```

### API Rate Limits

The server includes automatic rate limiting and error handling for Garmin Connect API:
- Small delays between batch requests (100ms)
- Graceful error handling for failed requests
- Maximum activity limits to prevent overwhelming the API

## Troubleshooting

### Common Issues

**Authentication Failed**
- Verify credentials in `.env` file
- Check that MCP configuration points to correct `.env` or has correct `env` values
- Ensure Garmin account is active and accessible

**No Data Returned**
- Verify your Garmin device has synced recently
- Check that you're querying dates with actual data
- Ensure your Garmin account has the requested data types

**Response Too Large**
- Use `summary: true` for condensed results
- Reduce date ranges for volume queries
- Filter by specific activity types
- Disable detailed breakdowns (`includeActivityBreakdown: false`)

**Server Not Starting**
- Ensure Node.js version is 20 or higher
- Run `pnpm build` to rebuild after changes
- Check server logs for authentication errors

## Contributing

Contributions are welcome! Please ensure:
- All tests pass (`pnpm test:run`)
- Type checking passes (`pnpm typecheck`)
- Code follows existing style guidelines
- New features include tests

## Attribution

This project started from [garmin-connect-mcp](https://github.com/epodivilov/garmin-connect-mcp)
by **Evgenii Podivilov**, at version 0.4.0, used under the MIT License. That codebase provided the
MCP server scaffolding, the Garmin Connect client wrapper and the original read tools.

It is maintained here as an independent project rather than a fork, so it can diverge without
waiting on upstream. Work added since the import:

- Multi-sport structured workouts (cycling, swimming and other, not just running)
- VO2max readings at full precision, rather than the integer the Garmin UI rounds to
- Per-lap activity splits, so individual intervals can be analysed instead of type totals
- Race predictions, overnight HRV, and aggregated training status
- Configured HR zones and the basis they are derived from, which decides what every
  reported zone actually means
- Per-activity time in zone
- Training readiness

See [LICENSE](LICENSE) for the full notice, which retains the original copyright as the MIT
License requires.

## License

MIT - see [LICENSE](LICENSE).

TDQS

A3.6/5.0

Scored across 24 tools

Disambiguation4/5

Most tools target clearly distinct endpoints (sleep, HRV, VO2max, hydration, etc.) and descriptions do a good job clarifying boundaries. However, get_daily_overview overlaps with get_sleep_data, get_health_metrics, and get_activities, and the cluster of training metrics (status, readiness, TSB) could confuse without careful reading.

Naming Consistency5/5

All tools follow a consistent snake_case verb_noun pattern: get_*, create_*, schedule_*, unschedule_*, delete_*. The only mild oddity is create_running_workout despite supporting other sports, but the style remains uniform.

Tool Count3/5

24 tools sits in the heavy range and feels borderline for a coaching server. While most map to distinct Garmin endpoints, there is some redundancy (daily overview duplicates other getters) and potential to consolidate.

Completeness4/5

The surface covers activity data, health metrics, training analytics, and the full workout scheduling lifecycle (create, schedule, unschedule, delete, view details). Missing an update_workout operation and tools for courses/routes, but core coaching workflows are supported.

Maintenance

ActivityMaintained
ResponsivenessNo issues