A TypeScript MCP (Model Context Protocol) server that integrates with Intervals.icu, Whoop, and TrainerRoad to provide unified access to fitness data across all activities and sports.
- Query completed workouts from Intervals.icu with matched Whoop strain data
- Access sleep and recovery metrics (HRV, sleep, recovery score) from Whoop
- View planned workouts from TrainerRoad and Intervals.icu
- Sync TrainerRoad running workouts to Intervals.icu as structured workouts for Zwift/Garmin
- Analyze fitness trends (CTL/ATL/TSB)
- Comprehensive workout analysis with intervals, notes, and weather data
- Incorporates heat strain data recorded from a CORE Body Temperature sensor for analysis
Note: Due to Strava API restrictions, workouts imported from Strava to Intervals.icu cannot be analyzed. To work around this, ensure that workouts are synced to Intervals.icu from other sources (Zwift, Garmin Connect, Dropbox, etc.)
get_daily_summary- Complete snapshot of today including recovery, strain, fitness metrics (CTL/ATL/TSB), wellness, completed workouts, planned workouts, and today's race, if any
get_todays_recovery- Today's Whoop recovery, sleep, and HRV dataget_todays_strain- Today's Whoop strain data including strain score, heart rate, and logged activitiesget_todays_completed_workouts- Today's completed workouts from Intervals.icu with matched Whoop strain dataget_todays_planned_workouts- Today's scheduled workouts from both TrainerRoad and Intervals.icu calendars
get_athlete_profile- Athlete's profile including unit preferences (metric/imperial), age, and locationget_sports_settings- Sport-specific settings (FTP, zones, thresholds) for cycling, running, or swimming
get_strain_history- Whoop strain scores and activities for a date rangeget_workout_history- Historical workouts with matched Whoop strain dataget_recovery_trends- HRV, sleep, and recovery patterns over timeget_wellness_trends- Wellness data trends (weight) over a date rangeget_activity_totals- Aggregated activity totals over a date range, including duration, distance, training load, and zone distributions by sport
get_upcoming_workouts- Planned workouts for a future date range from both TrainerRoad and Intervals.icu calendarsget_upcoming_races- Upcoming races from the TrainerRoad calendar (only triathlons for now)
get_run_workout_syntax- Returns the Intervals.icu workout syntax documentation for creating structured running workoutscreate_run_workout- Creates a structured running workout in Intervals.icu from a plain English descriptionget_cycling_workout_syntax- Returns the Intervals.icu workout syntax documentation for creating structured cycling workoutscreate_cycling_workout- Creates a structured cycling workout in Intervals.icu from a plain English descriptionupdate_workout- Updates a Domestique-created workout in Intervals.icudelete_workout- Deletes a Domestique-created workout from Intervals.icusync_trainerroad_runs- Syncs running workouts from TrainerRoad to Intervals.icu, creating new workouts, detecting changes, and cleaning up orphansset_workout_intervals- Sets intervals on a completed activity
get_training_load_trends- Training load trends including CTL (fitness), ATL (fatigue), TSB (form), ramp rate, and ACWRget_workout_details- Get all the details for a single workout, including intervals, notes, weather, and zonesget_workout_intervals- Detailed interval breakdown for a specific workout including power, HR, cadence, and timing dataget_workout_notes- Notes and comments written by the athlete about a specific workout in Intervals.icuget_workout_weather- Weather conditions during a specific outdoor workoutget_workout_heat_zones- Heat zone analysis for a specific workout showing time spent in each heat strain zone
get_power_curve- Cycling power curve analysis showing best watts at various durations with W/kg, estimated FTP, and period comparisonget_pace_curve- Running/swimming pace curve analysis showing best times at key distancesget_hr_curve- Heart rate curve analysis showing max sustained HR at various durations
- Node.js 20+
- Intervals.icu account with API key
- Whoop account with OAuth credentials
- TrainerRoad account with calendar feed URL
Copy .env.example to .env and fill in your credentials:
cp .env.example .envRequired variables:
MCP_AUTH_TOKEN- Secret token for MCP authentication. You can quickly generate one with:
openssl rand -hex 32For Intervals.icu integration:
INTERVALS_API_KEY- Your Intervals.icu API keyINTERVALS_ATHLETE_ID- Your Intervals.icu athlete ID
For Whoop integration:
WHOOP_CLIENT_IDWHOOP_CLIENT_SECRETREDIS_URL- Required for token storage (e.g.,redis://localhost:6379)WHOOP_REDIRECT_URI- Optional. Auto-detected based on environment:- On Fly.io:
https://{FLY_APP_NAME}.fly.dev/callback - Otherwise:
http://localhost:3000/callback
- On Fly.io:
For TrainerRoad integration:
TRAINERROAD_CALENDAR_URL- Private iCal feed URL
Whoop uses OAuth 2.0, which requires a one-time authorization flow to obtain refresh tokens. The refresh tokens are single-use, so each time the server refreshes the access token, it receives a new refresh token that gets stored in Redis.
First-time setup:
-
Create a Whoop developer app at https://developer.whoop.com to get your
WHOOP_CLIENT_IDandWHOOP_CLIENT_SECRET -
Make sure Redis is running and
REDIS_URLis set in your.env -
Start the Docker environment:
docker compose up -d
-
Run the OAuth setup script:
docker compose exec domestique npm run whoop:auth -
The script will display an authorization URL. Open it in your browser and log in to Whoop
-
After authorizing, you'll be redirected to a callback page that displays your authorization code
-
Copy the authorization code and paste it into the script
-
The script exchanges the code for tokens and stores them in Redis. You're done!
The server will automatically refresh tokens as needed and store new refresh tokens in Redis.
All commands should be run in the Docker container:
# Start development server with hot reload
docker compose up
# Start in background
docker compose up -d
# View logs
docker compose logs domestique -f
# Restart container
docker compose restart domestique
# Stop containers
docker compose down
# Rebuild containers after dependency changes
docker compose build
# Run commands in container
docker compose exec domestique <command>
# Examples:
docker compose exec domestique npm run typecheck
docker compose exec domestique npm run whoop:authThe MCP Inspector is a useful tool for testing and debugging your MCP server:
# Install MCP Inspector globally (if not already installed)
npm install -g @modelcontextprotocol/inspector
# Run inspector pointing to your local server
npx @modelcontextprotocol/inspector --server-url "http://localhost:3000/mcp?token=YOUR_SECRET_TOKEN"
# Or with Authorization header
npx @modelcontextprotocol/inspector --server-url "http://localhost:3000/mcp" \
--header "{ \"Authorization\": \"Bearer YOUR_SECRET_TOKEN\" }"The inspector will open a web interface where you can:
- Browse available tools
- Test tool calls with different parameters
- View request/response payloads
- Debug connection issues
In development mode, tool responses include a _debug object with token count information. This helps you understand how many tokens each tool response would consume when passed to Claude.
To enable this feature:
-
Add your Anthropic API key to
.env:ANTHROPIC_API_KEY=your-anthropic-api-key
-
Restart the development server:
docker compose restart domestique
Tool responses will now include:
{
"response": { ... },
"field_descriptions": { ... },
"_debug": {
"token_count": 1234
}
}This feature is automatically disabled in production (when NODE_ENV is not development) or when ANTHROPIC_API_KEY is not set.
# Start development server with hot reload
docker compose up
# Server runs at http://localhost:3000# Install dependencies
npm install
# Run in development mode with hot reload
npm run dev
# Or build and run production
npm run build
npm startcurl -L https://fly.io/install.sh | sh
fly auth loginRedis is required for Whoop token storage. Deploy it first:
cd fly-redis
# Create the Redis app (first time only)
fly apps create domestique-redis
# Create a volume for persistence
fly volumes create redis_data --region iad --size 1
# Deploy Redis
fly deploy
cd ..# Create the app (first time only)
fly apps create domestique
# Set secrets
fly secrets set MCP_AUTH_TOKEN=your-secret-token
fly secrets set INTERVALS_API_KEY=your-api-key
fly secrets set INTERVALS_ATHLETE_ID=your-athlete-id
fly secrets set WHOOP_CLIENT_ID=your-client-id
fly secrets set WHOOP_CLIENT_SECRET=your-client-secret
fly secrets set REDIS_URL=redis://domestique-redis.internal:6379
# Deploy
fly deploy
# View logs
fly logsAfter deploying, run the OAuth setup to get initial Whoop tokens:
fly ssh console -C "npm run whoop:auth"The redirect URI is automatically set to https://{your-app}.fly.dev/callback when running on Fly.io. Make sure this URL is registered in your Whoop developer app settings.
Follow the prompts to authorize with Whoop and store the tokens in Redis.
Add this MCP server as a connector to your Claude configuration using this URL:
https://{FLY_APP_NAME}.fly.dev/mcp?token=YOUR_SECRET_TOKEN
Note: Replace YOUR_SECRET_TOKEN with your actual MCP_AUTH_TOKEN value and FLY_APP_NAME with the name of the Fly.io app (or the URL wherever you have it hosted).
Once connected, you can ask Claude:
- "How did my workout go today?"
- "What's my recovery like this morning?"
- "Show me my fitness trends for the last month"
- "What workouts do I have planned this week?"
- "How has my HRV trended compared to my training load?"
- "What workout do I have scheduled for next Wednesday?"
- "Show me my workouts from last Friday"
- "How many workouts did I complete in the last 2 weeks?"
- "What was my training load in the last 42 days?"
- "What's my power curve for the last 90 days?"
- "How has my 5-minute power improved compared to last quarter?"
- "Show me my running pace curve—what's my best 5km time?"
- "Compare my cycling power from the last 3 months vs the previous 3 months"
- "What's my FTP?"
- "What are my running zones?"
- "How has my weight changed over the last 30 days?"
- "What are my swimming, cycling and running totals for the past month?"
- "How much time did I spend in each power zone this month?"
- "Sync my TrainerRoad runs to Intervals.icu so they sync to Zwift"
This server has been tested with Claude and ChatGPT. However, there are some compatibility differences to be aware of:
-
Tool responses: ChatGPT uses
structuredContentfor JSON data and expects plain text or Markdowncontentfor narration in tools responses. Claude only usescontent. This server's tool responses return the same JSON payload in both fields for compatibility. -
MCP prompts: ChatGPT doesn't support prompts, and Claude inserts them as .txt files rather than as regular prompts. The
daily_summaryprompt is provided but may not work as expected on all clients. -
MCP resources: ChatGPT does not support resources; Claude does but doesn't seem to reliably use them while invoking tools. As an alternative, this server provides tools like
get_run_workout_syntaxandget_cycling_workout_syntaxthat return the resource contents directly. -
_metafields: ChatGPT provides_metafields in tool inputs, which could be used to identify that the request is coming from ChatGPT, and provides things like the location and locale of the user. Claude doesn't provide any hints.