Model Context Protocol (MCP) server for connecting Claude and ChatGPT with the Intervals.icu API. It provides tools for retrieving activities, events, wellness data, power curves, and more.
If you find the MCP server useful, please consider supporting its continued development with a donation.
Before you begin you'll need your Intervals.icu credentials:
- API Key — Log in to Intervals.icu, go to Settings → API, and generate a new API key.
- Athlete ID — Visible in the URL when you're logged in, e.g.
https://intervals.icu/athlete/i12345/...→i12345.
The fastest way to get started is to deploy the server to Render as a Docker Web Service. No local installation required.
- Go to render.com → New → Web Service
- Connect your GitHub repository (
intervals-mcp-serveror your fork) - Configure the service:
- Name:
intervals-mcp-server(or your preferred name) - Branch:
develop - Runtime: Docker
- Instance Type: Free tier works fine
- Name:
💤 Free tier cold starts: Render free-tier services sleep after 15 minutes of inactivity. The first request after sleeping may take 30–60 seconds while the container restarts. Subsequent requests are fast. To avoid this, upgrade to a paid instance or use an external cron/ping service to keep it awake.
In the Render dashboard under Environment, add:
| Key | Value | Description |
|---|---|---|
MCP_TRANSPORT |
http |
Enables the remote transport (streamable HTTP) |
FASTMCP_HOST |
0.0.0.0 |
Bind to all interfaces (required inside Docker) |
FASTMCP_PORT |
8000 |
Port the server listens on |
ATHLETE_ID |
your_athlete_id |
Your Intervals.icu athlete ID (e.g. i12345) |
API_KEY |
your_api_key |
Your Intervals.icu API key |
Optional — enable OAuth 2.0 to protect the endpoint (see Securing the endpoint):
| Key | Value | Description |
|---|---|---|
MCP_CLIENT_ID |
intervals-icu |
OAuth client ID; must match the connector config exactly |
MCP_CLIENT_SECRET |
<random secret> |
OAuth client secret / access token; must match the connector |
MCP_SERVER_URL |
https://your-service-name.onrender.com |
Public HTTPS URL (no /mcp); required for OAuth discovery |
- Click Create Web Service — Render will build the Docker image and deploy
- Wait for the build to complete (green status)
- Note your service URL:
https://your-service-name.onrender.com - Test by opening
https://your-service-name.onrender.com/mcpin a browser — you should get a response from the server
⚠️ Security Warning: Without OAuth (see below), your Render endpoint is publicly accessible — anyone who discovers the URL can query and mutate your Intervals.icu data. Either enable OAuth or do not share your service URL publicly. For full isolation, use the Local Setup (stdio).
Set MCP_CLIENT_ID, MCP_CLIENT_SECRET, and MCP_SERVER_URL (see table above) to require OAuth 2.0 (authorization code + PKCE) on all HTTP requests. Notes:
- The values on the server must match the connector's OAuth Client ID / Secret exactly (case-sensitive).
MCP_SERVER_URLmust be the public HTTPS URL without/mcp; the connector URL must end with/mcp.- Leave all three unset to keep the endpoint unauthenticated.
- Open Claude → Settings → Integrations (or MCP Servers)
- Click Add
- Fill in:
- Name:
Intervals.icu - URL:
https://your-service-name.onrender.com/mcp(must end with/mcp) - If OAuth is enabled, also set OAuth Client ID =
MCP_CLIENT_IDand OAuth Client Secret =MCP_CLIENT_SECRET(exact, case-sensitive match)
- Name:
Open a new conversation and ask "What MCP tools do you have available?" to confirm the connection.
- In ChatGPT, open Settings → Features → Custom MCP Connectors → Add
- Fill in:
- Name:
Intervals.icu - MCP Server URL:
https://your-service-name.onrender.com/mcp
- Name:
Save the connector and open a new chat.
Once connected, the following tools are available:
Activities
get_activities— Retrieve a list of activitiesget_activity_details— Get detailed information for a specific activityget_activity_intervals— Get interval data for a specific activityget_activity_streams— Get time-series stream data (power, HR, cadence, etc.)get_activity_histogram— Get a power, heart rate, pace, or gap histogramget_activity_messages— Get messages/comments on an activityadd_activity_message— Add a message/comment to an activityupdate_activity,delete_activity,create_manual_activity,bulk_create_manual_activities— Edit, remove, or create activities
Activity analysis
get_activity_curve,get_activity_best_efforts,get_activity_segments,get_activity_interval_stats,get_activity_map,get_activity_power_vs_hr,get_activity_hr_load_model,get_activity_power_spike_model,get_activity_time_at_hr,get_activity_weather_summary
Activity search & interval editing
search_activities,interval_search,get_activities_around,get_activities_by_ids,get_activity_tagsupdate_activity_intervals,update_activity_interval,delete_activity_intervals,split_activity_interval
Events
get_events— Retrieve upcoming events (workouts, races, etc.)get_event_by_id— Get detailed information for a specific eventadd_or_update_event— Create or update an eventdelete_event— Delete a specific eventdelete_events_by_date_range— Delete events within a date range
Wellness & Training
get_wellness_data— Fetch wellness dataget_training_summary— Get a training load summaryget_athlete_power_curves— Get best power output curves for selected durations and time periodsget_athlete_zones— Get athlete training zones (power, HR, pace, etc.)
Training Plans
get_training_plan,change_training_plan,apply_plan_changes,apply_plan_to_calendar,change_athlete_plans_bulk
Custom Items
get_custom_items— List custom itemsget_custom_item_by_id— Get a specific custom itemcreate_custom_item— Create a new custom itemupdate_custom_item— Update an existing custom itemdelete_custom_item— Delete a custom item
Workout Library
get_workout_folders— Get workout library folder metadata (IDs, names, types)list_workouts— List workouts in the library, optionally filtered by folderget_workout— Get full workout detail including step-by-step structurecreate_workout— Create a new workout in a library folderupdate_workout— Update an existing library workoutschedule_workout— Schedule a library workout onto the calendar
Structured workouts: pass steps via
workout_doc. The server renders them to Intervals.icu workout-builder text so the platform parses them and draws the step chart. Use renderable target units — power%ftp/w, HR%hr/%lthr, pace%paceor absolute pace (e.g.4:30/km); avoidpace_zone/power_zonefor pace runs.
- Service won't start — Check Render logs for build errors. Ensure all environment variables are set.
- Claude/ChatGPT can't connect — Verify the URL ends with
/mcpand is publicly accessible. Try opening it in a browser. - "Authorization failed" with OAuth —
MCP_CLIENT_ID/MCP_CLIENT_SECRETmust match the connector exactly (case-sensitive),MCP_SERVER_URLmust be the public HTTPS URL without/mcp, and the connector URL must end with/mcp. Remove and re-add the connector to clear cached credentials. - API errors — Double-check your
ATHLETE_IDandAPI_KEYvalues. Verify your Intervals.icu API key is valid. - Free tier cold starts — Render free-tier services sleep after inactivity. The first request may take 30–60 seconds to wake up.
Local Setup (alternative)
If you prefer to run the server on your own machine instead of Render, follow the steps below.
- Python 3.12 or higher
- uv (recommended package manager)
curl -LsSf https://astral.sh/uv/install.sh | shgit clone https://github.com/mvilanova/intervals-mcp-server.git
cd intervals-mcp-server
uv venv --python 3.12
source .venv/bin/activate # Windows: .venv\Scripts\activate
uv synccp .env.example .envEdit .env and fill in your credentials:
API_KEY=your_intervals_api_key_here
ATHLETE_ID=your_athlete_id_here
-
From the project directory, run:
mcp install src/intervals_mcp_server/server.py --name "Intervals.icu" --with-editable . --env-file .env
-
Your
claude_desktop_config.jsonshould look like:{ "mcpServers": { "Intervals.icu": { "command": "/Users/<USERNAME>/.cargo/bin/uv", "args": [ "run", "--with", "mcp[cli]", "--with-editable", "/path/to/intervals-mcp-server", "mcp", "run", "/path/to/intervals-mcp-server/src/intervals_mcp_server/server.py" ], "env": { "INTERVALS_API_BASE_URL": "https://intervals.icu/api/v1", "ATHLETE_ID": "<YOUR_ATHLETE_ID>", "API_KEY": "<YOUR_API_KEY>", "LOG_LEVEL": "INFO" } } } }Replace
/path/to/with the actual path. If you seespawn uv ENOENTerrors, use the full path fromwhich uv. -
Restart Claude Desktop.
-
Start the server in HTTP mode:
export FASTMCP_HOST=127.0.0.1 FASTMCP_PORT=8765 MCP_TRANSPORT=http FASTMCP_LOG_LEVEL=INFO python src/intervals_mcp_server/server.py -
ChatGPT needs a public URL, so forward the port (e.g.
ngrok http 8765). -
In ChatGPT, open Settings → Features → Custom MCP Connectors → Add:
- Name:
Intervals.icu - MCP Server URL:
https://<your-public-host>/mcp
- Name:
git checkout main && git pull
source .venv/bin/activate
uv syncIf Claude Desktop fails after an update, delete the entry in claude_desktop_config.json and re-run the mcp install command above.
Modify claude_desktop_config.json to redirect stderr to a log file:
{
"mcpServers": {
"Intervals.icu": {
"command": "/bin/bash",
"args": [
"-c",
"/Users/<USERNAME>/.local/bin/uv run --with 'mcp[cli]' --with-editable /path/to/intervals-mcp-server mcp run /path/to/intervals-mcp-server/src/intervals_mcp_server/server.py 2>> /path/to/intervals-mcp-server/mcp-server.log"
],
"env": {
"INTERVALS_API_BASE_URL": "https://intervals.icu/api/v1",
"ATHLETE_ID": "<YOUR_ATHLETE_ID>",
"API_KEY": "<YOUR_API_KEY>",
"LOG_LEVEL": "INFO"
}
}
}
}Then tail the log:
tail -f /path/to/intervals-mcp-server/mcp-server.logInstall development dependencies and run the test suite with:
uv sync --all-extras
pytest -v testsmcp run src/intervals_mcp_server/server.pyThe GNU General Public License v3.0