Wahoo MCP Server
A Model Context Protocol (MCP) server for interacting with the Wahoo Cloud API, focusing on reading workout information.
Features
- Workouts: List workouts with pagination and date filtering, get detailed workout information
- Routes: List and retrieve saved cycling/running routes
- Training Plans: Access and create training plans in your Wahoo account
- Power Zones: View power zone configurations for different workout types
- OAuth 2.0 Authentication: Secure authentication with automatic token refresh
- Comprehensive workout type support: 72 different workout types with location and family categorization
- Async/await implementation: High-performance async operations using httpx
- Automatic token management: Tokens are refreshed automatically when they expire
Installation
Using uv (recommended)
First, install uv if you haven't already:
curl -LsSf https://astral.sh/uv/install.sh | sh
Then install the project dependencies:
uv venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
uv pip install -e .
For development:
uv pip install -e ".[dev]"
Using pip (alternative)
If you prefer using pip:
python3.13 -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
pip install -e .
For development:
pip install -e ".[dev]"
Configuration
Getting an Access Token
-
Register your application at Wahoo's Developer Portal to get a Client ID and Client Secret.
-
Create a
.envfile from the example:cp .env.example .envThen edit
.envand add your credentials:WAHOO_CLIENT_ID=your_client_id_here WAHOO_CLIENT_SECRET=your_client_secret_here -
Set the token file path in your
.envfile:WAHOO_TOKEN_FILE=token.json -
Use the authentication helper:
make auth # or uv run python src/auth.pyThis will:
- Use credentials from
.env(or prompt if not set) - Open a browser for OAuth authentication
- Start a local server to receive the callback
- Save your tokens to the file specified by
WAHOO_TOKEN_FILE - Tokens will be automatically refreshed when needed
- Use credentials from
Configuration Options
The auth server can be configured via environment variables:
Server Configuration:
WAHOO_AUTH_HOST: Auth server bind address (default:localhost)WAHOO_AUTH_PORT: Auth server port (default:8080)
Redirect URL Configuration:
WAHOO_REDIRECT_HOST: OAuth callback host (default: usesWAHOO_AUTH_HOST)WAHOO_REDIRECT_PORT: OAuth callback port (default: usesWAHOO_AUTH_PORT)WAHOO_REDIRECT_SCHEME: URL scheme -httporhttps(default:http)
Credentials:
WAHOO_CLIENT_ID: Your Wahoo Client IDWAHOO_CLIENT_SECRET: Your Wahoo Client SecretWAHOO_TOKEN_FILE: Path to store OAuth tokens (required)
Example Configurations:
-
Local Development (default):
# Redirect URL will be: http://localhost:8080/callback -
Using ngrok:
WAHOO_AUTH_HOST=localhost WAHOO_AUTH_PORT=8080 WAHOO_REDIRECT_HOST=your-app.ngrok.io WAHOO_REDIRECT_PORT=443 WAHOO_REDIRECT_SCHEME=https # Redirect URL will be: https://your-app.ngrok.io:443/callback
Note: When registering your app with Wahoo, use the redirect URL that matches your configuration.
Usage
Running the MCP Server
uv run python -m src.server
Or if you've activated the virtual environment:
python -m src.server
Using with Claude Desktop
Add the following to your Claude Desktop configuration file:
Configuration file location:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json - Linux:
~/.config/Claude/claude_desktop_config.json
Example configuration:
{
"mcpServers": {
"wahoo": {
"type": "stdio",
"command": "/path/to/uv",
"args": [
"--project",
"/path/to/wahoo-mcp",
"run",
"python",
"-m",
"src.server"
],
"env": {
"WAHOO_TOKEN_FILE": "/path/to/wahoo-mcp/token.json"
}
}
}
}
Make sure to replace /path/to/ with your actual paths.
Available Tools
list_workouts
List workouts from your Wahoo account.
Parameters:
page(optional): Page number (default: 1)per_page(optional): Number of items per page (default: 30)start_date(optional): Filter workouts created after this date (ISO 8601 format)end_date(optional): Filter workouts created before this date (ISO 8601 format)
Example:
Use the list_workouts tool to show my recent workouts
get_workout
Get detailed information about a specific workout.
Parameters:
workout_id(required): The ID of the workout to retrieve
Example:
Use the get_workout tool to get details for workout ID 12345
list_routes
List routes from your Wahoo account.
Parameters:
external_id(optional): Filter routes by external ID
Example:
Use the list_routes tool to show my saved routes
get_route
Get detailed information about a specific route.
Parameters:
route_id(required): The ID of the route to retrieve
Example:
Use the get_route tool to get details for route ID 456
list_plans
List training plans from your Wahoo account.
Parameters:
external_id(optional): Filter plans by external ID
Example:
Use the list_plans tool to show my training plans
get_plan
Get detailed information about a specific plan.
Parameters:
plan_id(required): The ID of the plan to retrieve
Example:
Use the get_plan tool to get details for plan ID 789
list_power_zones
List power zones from your Wahoo account.
Parameters: None
Example:
Use the list_power_zones tool to show my power zones
get_power_zone
Get detailed information about a specific power zone.
Parameters:
power_zone_id(required): The ID of the power zone to retrieve
Example:
Use the get_power_zone tool to get details for power zone ID 321
create_plan
Create a new training plan in your Wahoo account.
Parameters:
plan(required): Complete workout plan structure containing:name(required): Name of the workout plandescription(optional): Description of the workoutintervals(required): List of workout intervals, each containing:duration(required): Duration in secondstargets(required): List of targets (power, heart_rate, speed, pace, rpe, cadence)name(optional): Name/description of the intervalinterval_type(optional): Type (work, rest, warmup, cooldown, tempo, threshold, recovery, active, or Wahoo types: wu, cd, lt, map, ac, nm, ftp, recover)
workout_type(optional): Type of workout (bike, run, swim) - defaults to "bike"estimated_duration(optional): Estimated total duration in secondsestimated_tss(optional): Estimated Training Stress Scoreauthor(optional): Author of the plan
external_id(required): Unique external ID for the planprovider_updated_at(required): External date/time the file was updated (ISO 8601 format)filename(optional): Name of the plan file
Example:
Use the create_plan tool to create a new training plan with intervals for power and heart rate zones
Development
Running Tests
uv run pytest
Or if you've activated the virtual environment:
pytest
Project Structure
wahoo-mcp/
├── src/
│ ├── __init__.py
│ ├── server.py # Main MCP server implementation
│ ├── auth.py # OAuth authentication helper
│ ├── token_store.py # Token storage and refresh logic
│ └── models.py # Pydantic models for API data structures
├── tests/
│ ├── __init__.py
│ ├── test_server.py # Server test suite
│ └── test_token_store.py # Token store tests
├── pyproject.toml # Project configuration
└── README.md # This file
API Reference
The server implements the following Wahoo Cloud API endpoints:
Workouts:
GET /v1/workouts- List workouts with pagination and date filteringGET /v1/workouts/{id}- Get detailed workout information
Routes:
GET /v1/routes- List saved routesGET /v1/routes/{id}- Get route details including GPS data
Training Plans:
GET /v1/plans- List training plansGET /v1/plans/{id}- Get plan detailsPOST /v1/plans- Create a new training plan
Power Zones:
GET /v1/power_zones- List power zone configurationsGET /v1/power_zones/{id}- Get specific power zone details
For full API documentation, see Wahoo Cloud API.
License
This project is licensed under the GNU General Public License v3.0 - see the LICENSE file for details.