Skip to content

gesteves/domestique

Repository files navigation

Domestique

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.

Features

  • Completed workouts from Intervals.icu with matched Whoop strain
  • Recovery, HRV, sleep, and strain from Whoop
  • Planned workouts from TrainerRoad and Intervals.icu
  • Sync TrainerRoad running workouts to Intervals.icu (for Zwift/Garmin)
  • Fitness trends (CTL/ATL/TSB) and detailed workout analysis with intervals, notes, and weather
  • Heat strain and heat adaptation score from a CORE Body Temperature sensor
  • Weather, AQI, and pollen forecasts up to 10 days out for Intervals.icu weather locations or any geocoded place
  • Generates activity descriptions for Strava

Note: Workouts imported from Strava can't be analyzed due to API restrictions. Sync them from Zwift, Garmin Connect, Dropbox, etc. instead.

Available Tools

Today's Data

  • get_todays_summary - Full snapshot of today: recovery, sleep, HRV, strain, fitness (CTL/ATL/TSB), wellness, completed and planned workouts, race, calendar annotations (sick/injured/holiday/notes), and weather.
  • get_todays_activities - Today's completed and planned workouts, race, and calendar annotations (sick/injured/holiday/notes). Leaner alternative to get_todays_summary.

Profile & Settings

  • get_athlete_profile - Athlete profile, unit preferences, age, and location.
  • get_sports_settings - FTP, zones, and thresholds per sport.

Historical/Trends

  • get_strain_history - Whoop strain and activities over a date range.
  • get_activity_history - Past completed workouts with matched Whoop strain and calendar annotations.
  • get_recovery_trends - HRV, sleep, and recovery patterns over time.
  • get_wellness_trends - Daily Intervals.icu wellness metrics over a date range.
  • get_activity_totals - Aggregated totals over a date range: duration, distance, training load, and zone distributions by sport.

Planning

  • get_upcoming_activities - Planned workouts (TrainerRoad + Intervals.icu) and races for a future date range, plus calendar annotations.

Workout Management

  • create_workout - Create a structured cycling/running/swimming workout from a plain-language description.
  • update_workout - Update a Domestique-created workout..
  • delete_workout - Delete a Domestique-created workout.
  • sync_trainerroad_runs - Sync running workouts from TrainerRoad to Intervals.icu, detecting changes and orphans.
  • set_workout_intervals - Set intervals on a completed activity.
  • update_activity - Update name and/or description of a completed activity.
  • regenerate_descriptions - Regenerate the descriptions of a day's completed activities, or a single activity by ID.

Calendar Annotations

  • create_annotation - Add a sick/injured/holiday/note annotation to the Intervals.icu calendar.
  • update_annotation - Update a Domestique-created annotation.
  • delete_annotation - Delete a Domestique-created annotation.

Analysis

  • get_training_load_trends - CTL, ATL, TSB, ramp rate, and ACWR over time.
  • get_workout_details - All details for one workout: intervals, notes, weather, zones, music, and matched Whoop strain.

Performance Curves

  • get_power_curve - Best watts at various durations with W/kg, estimated FTP, and period comparison.
  • get_pace_curve - Best running/swimming times at key distances.
  • get_hr_curve - Max sustained HR at various durations.

Weather

  • get_weather_forecast - Forecast for a date and optional location, with AQI and pollen.

Setup

Prerequisites

  • Node.js 20+
  • Intervals.icu account with API key
  • Whoop account with OAuth credentials
  • TrainerRoad account with calendar feed URL

Environment Variables

Copy .env.example to .env and fill in your credentials:

cp .env.example .env

Required variables:

  • MCP_AUTH_TOKEN - Secret token for MCP authentication. You can quickly generate one with:
openssl rand -hex 32

For Intervals.icu integration:

  • INTERVALS_API_KEY - Your Intervals.icu API key
  • INTERVALS_ATHLETE_ID - Your Intervals.icu athlete ID

For Whoop integration:

  • WHOOP_CLIENT_ID
  • WHOOP_CLIENT_SECRET
  • REDIS_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

For TrainerRoad integration:

  • TRAINERROAD_CALENDAR_URL - Private iCal feed URL

For weather forecasts (optional):

  • GOOGLE_API_KEY - Google Cloud API key with the Weather, Air Quality, Pollen, Elevation, Geocoding, and Time Zone APIs enabled.

For Last.fm integration (optional):

  • LASTFM_USERNAME - Last.fm username.
  • LASTFM_API_KEY - Last.fm API key.

When both are set, get_workout_details and get_todays_activities include tracks played during the workout. ("Why not use Spotify?" you may be wondering. Ingesting Spotify data into AI is against their developer policy.)

For Anthropic API integration:

  • ANTHROPIC_API_KEY - Required for create_workout and update_workout on cycling and running (server converts the plain-language structure field into Intervals.icu workout-doc syntax). Also enables, when set, Claude for TrainerRoad annotation categorization (Sick/Injured/Holiday/Note), triathlon race priority extraction (A/B/C from the umbrella description), auto-generated activity descriptions on workout.updated Whoop webhooks, and debug token counting.
  • ANTHROPIC_CLASSIFIER_MODEL - Optional override for the model used by the annotation and race-priority classifiers. Defaults to claude-haiku-4-5.
  • ANTHROPIC_DESCRIPTION_MODEL - Optional override for the model used by the activity-description generator. Defaults to claude-sonnet-4-6.
  • ANTHROPIC_WORKOUT_MODEL - Optional override for the model used by the create_workout / update_workout structure-to-syntax converter. Defaults to claude-sonnet-4-6.
  • ANTHROPIC_TOKEN_COUNTER_MODEL - Optional override for the model used by dev-only debug token counting. Defaults to claude-haiku-4-5.

For the /api endpoints (optional):

  • API_SECRET - Authenticates the /api endpoints via Authorization: Bearer. Generate with openssl rand -hex 32.

For error reporting (optional):

  • BUGSNAG_API_KEY - Reports upstream API failures (Intervals.icu, Whoop, TrainerRoad) to Bugsnag.

Whoop OAuth Setup

Whoop uses OAuth 2.0 with single-use refresh tokens stored in Redis. One-time setup:

  1. Create a Whoop developer app at https://developer.whoop.com to get your WHOOP_CLIENT_ID and WHOOP_CLIENT_SECRET.
  2. Ensure Redis is running and REDIS_URL is set in .env.
  3. Start Docker: docker compose up -d
  4. Run the OAuth script: docker compose exec domestique npm run whoop:auth
  5. Open the displayed URL, authorize, and paste the resulting code back into the script.

The server refreshes tokens automatically thereafter.

Whoop Webhooks (optional)

When Whoop is configured, Domestique exposes POST /webhooks/whoop and uses it to:

  • Sync the day's Whoop strain to Intervals.icu wellness.
  • Set per-workout Whoop strain on the matching Intervals.icu activity.
  • Auto-generate a description for the completed activity (requires ANTHROPIC_API_KEY).

One-time setup in Intervals.icu — create these custom fields:

  • Wellness: WhoopStrain (Number)
  • Activity: WhoopWorkoutStrain (Number)

One-time setup in Whoop — in your Whoop developer dashboard, add the webhook URL https://{your-host}/webhooks/whoop and select Model Version: v2.

/api endpoints (optional)

When API_SECRET is set, Domestique exposes two HTTP endpoints for callers like iOS Shortcuts, authenticated with Authorization: Bearer <API_SECRET>:

  • PUT /api/location — sets the athlete's current location from a JSON body { "latitude", "longitude" } (also requires GOOGLE_API_KEY). Same as the update_location tool.
  • POST /api/activities/descriptions — regenerates a day's activity descriptions. Optional JSON body { "date": "YYYY-MM-DD" } (defaults to today) or { "activity_id": "..." } to regenerate a single activity (takes precedence over date). Same as the regenerate_descriptions tool.

Local Development

Recommended: docker compose up (hot reload, server at http://localhost:3000). Use docker compose exec domestique <command> to run commands inside the container (e.g., npm run typecheck, npm run whoop:auth).

Without Docker:

npm install
npm run dev          # hot reload
npm run build && npm start

Testing with MCP Inspector

npx @modelcontextprotocol/inspector --server-url "http://localhost:3000/mcp?token=YOUR_SECRET_TOKEN"

Debug Token Counting

In development mode, set ANTHROPIC_API_KEY to include a token_count field in _meta on tool responses. _meta is out-of-band per the MCP spec, so it isn't shown to the model.

MCP Request Logging

Set LOG_MCP_REQUESTS=true to log incoming JSON-RPC requests on /mcp.

Deployment to Fly.io

  1. Install Fly CLI and log in: curl -L https://fly.io/install.sh | sh && fly auth login
  2. Deploy Redis (required for Whoop tokens):
    cd fly-redis
    fly apps create domestique-redis
    fly volumes create redis_data --region iad --size 1
    fly deploy
    cd ..
  3. Deploy Domestique:
    fly apps create domestique
    fly secrets set MCP_AUTH_TOKEN=... INTERVALS_API_KEY=... INTERVALS_ATHLETE_ID=... \
      WHOOP_CLIENT_ID=... WHOOP_CLIENT_SECRET=... REDIS_URL=redis://domestique-redis.internal:6379
    fly deploy
  4. Whoop tokens (if using Whoop): fly ssh console -C "npm run whoop:auth". The redirect URI is auto-set to https://{your-app}.fly.dev/callback — register it in your Whoop app.

Connecting to Claude

Add as a connector with: https://{FLY_APP_NAME}.fly.dev/mcp?token=YOUR_SECRET_TOKEN (substitute your MCP_AUTH_TOKEN and host).

Example Queries

  • "How did my workout go today?"
  • "What's my recovery like this morning?"
  • "Show me my fitness trends for the last month"
  • "How has my HRV trended compared to my training load?"
  • "What's my power curve for the last 90 days?"
  • "Show me my running pace curve—what's my best 5km time?"
  • "How has my weight changed over the last 30 days?"
  • "Sync my TrainerRoad runs to Intervals.icu"
  • "What's the weather like for my race in Boulder next Saturday?"

MCP Client Compatibility Notes

Tested with Claude and ChatGPT. Notes:

  • Tool responses: Every tool declares an outputSchema per the 2025-11-25 MCP spec. The payload is returned as structuredContent and also serialized into content for older clients. Field descriptions are delivered via tools/list, not embedded per response.
  • _meta fields: ChatGPT provides _meta (location, locale, etc.) on tool inputs; Claude doesn't.

About

MCP server for swimming, cycling and running workout and fitness data

Topics

Resources

Stars

Watchers

Forks

Contributors