Intervals.icu MCP
by fm4tt0s
README.md
# Intervals.icu MCP
Minimal local stdio MCP server for [Intervals.icu](https://intervals.icu), generated from the official OpenAPI spec. Exposes every API operation as an MCP tool. Works with Claude Desktop, mcphost (Ollama / OpenRouter), Cline, and any other MCP client that speaks stdio; this repo is fully self-contained.
## Files
```bash
|____spec/intervals-openapi-spec.json # the Intervals.ICU API spec
|____README.md # that's me!
|____config.js.example # example config.js file
|____package.json # npm package dependency file
|____mcp-stdio.mjs # the MCP server
|____mcp-stdio.mjs # agent definition
```
## Requirements
- Node.js ≥ 18
- An Intervals.icu API key — get it from <https://intervals.icu/settings> (bottom of the page, "Developer settings")
## Install
Clone the repo and store it anywhere on your machine. Copy `config.js.example` to `config.js` and set `INTERVALS_TOKEN` to your Intervals.icu API key.
```bash
cp config.js.example config.js
npm install
```
## Test it
```bash
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | node mcp-stdio.mjs | python3 -mjson.tool | grep "name\"" | head
```
You should see on stderr:
```bash
[intervals-mcp] ready · 147 tools loaded
"name": "updateLastSeenMessageId",
"name": "updateMessage",
"name": "deleteMessage",
"name": "showWorkout",
"name": "updateWorkout",
"name": "deleteWorkout",
"name": "getRecord",
"name": "updateWellness",
"name": "updateWellnessBulk",
"name": "uploadWellness",
```
Press Ctrl-C to exit. The server is now ready to be driven by an MCP client.
## Shrinking the tool surface (for small/local models)
The full spec generates 147 tools. Smaller models (`qwen2.5:7b`, `llama3.1:8b`, ...) struggle to pick the right one out of so many. Edit `config.js` to filter:
```js
export const METHODS = ["GET"]; // read-only server
export const TAGS = ["Activity", "Wellness"]; // only these OpenAPI tags
export const INCLUDE = ["getActivities", "getWellness"]; // exact allow-list
export const EXCLUDE = ["someNoisyTool"];
export const DESCRIPTION_STYLE = "short"; // terse one-liners (vs "full")
```
Filters apply in order: `METHODS` → `TAGS` → `INCLUDE` → `EXCLUDE`. Restart the server after editing. Claude can handle all 147 tools comfortably; for Ollama/mcphost/Tome with small models, keep it under ~20 tools.
## Claude Desktop
Edit Claude Desktop config file (create it if missing):
- MacOS - `~/Library/Application Support/Claude/claude_desktop_config.json`
- Linux - `~/.config/Claude/claude_desktop_config.json`
- Windows - `%APPDATA%\Claude\claude_desktop_config.json`
```json
{
"mcpServers": {
"intervals-icu": {
"command": "node",
"args": ["/ABSOLUTE/PATH/TO/local/mcp-stdio.mjs"],
"env": { "INTERVALS_TOKEN": "your_api_key_here" }
}
}
}
```
The `env` block is optional if you already set `INTERVALS_TOKEN` in `config.js`. Restart Claude Desktop. The Intervals.icu tools are now available.
---
## Agent - The AI Sports Scientist Layer - aka 'coach'
The 'coach'. Load the `intervals-coach-agent.yaml` defintion into your agents/custom modes definition. Once connected to this MCP server, your LLM gains real-time, programmatic visibility into your structured training data. It is pre-conditioned to act as an advanced exercise physiologist and endurance data companion—translating granular on-bike telemetry into accessible, motivational, and actionable insights.
### Automated Ride & Stream Analysis
Every time the agent pulls a ride stream, it evaluates key metabolic and physiological markers:
* **Biomechanical Metrics:** Computes or extracts **NP** (Normalized Power), **IF** (Intensity Factor), **TSS** (Training Stress Score), and **VI** (Variability Index).
* **Aerobic Efficiency:** Monitors **Pw:Hr (Aerobic Decoupling)** to analyze cardiac drift and gauge metabolic penalty or heat stress during threshold efforts.
* **Advanced Diagnostics:** Identifies subtle performance trend signals, such as $VO_2\text{max}$ progressions, multi-day fatigue accumulation, or cadence decay over long durations.
### Training Load Modeling (CTL / ATL / TSB)
The agent references systemic training loads using industry-standard formulas to help optimize your training progression without risking chronic burnout:
* **Form Management:** Automatically tracks **TSB (Training Stress Balance)** to keep your training stimulus safely inside the *Optimal Training Zone* (TSB between -10 and -30) and flags risk factors when entering the *Danger Zone* (TSB below -30).
* **Progressive Overload Planning:** Uses automated calculations to model micro-periodization blocks, suggesting safe week-over-week **CTL Ramp Rates** (+3 to +5 points max) and customized weekly TSS targets for build vs. recovery cycles.
### Personalized Context & Asset Profiles
The agent maps your exact physiological baselines (**FTP, CP, W', LTHR, HR Zones, and weight**) and matches them contextually against your equipment profiles (e.g., Road vs. Gravel bike weights) and workout schemas. It gracefully handles structured step data, ensuring initial high-intensity intervals are never misclassified as warm-ups.
* You should edit `intervals-coach-agent.yaml:customInstructions` property with your own values. Some data might be available at intervals.icu already and the agent is capable of fetching it, but won't be any harm setting them up. Once configured, agent will be available thru `/intervals-coach` slug.
---
*Note: This tool is built to enhance data literacy, illuminate physiological correlations, and simplify deep data exploration—it is designed to act as an analytical companion layer, not a replacement for a human cycling coach.*
### Extra Context with Strava MCP
Now that [Strava has released their own MCP](https://support.strava.com/en-us/articles/15401531-strava-mcp-connector), you can bring additional context and data into this; altogether Strava and Intevals.icu data will provide a richier vision of your data.
## Ollama / OpenRouter (via mcphost)
[mcphost](https://github.com/mark3labs/mcphost) is a small CLI that bridges any MCP server to Ollama or OpenRouter models.
> Model suggestion for Ollama:
- qwen3 or qwen2.5-coder:32b (if your machine can run it)
- llama3.1:8b — surprisingly solid at tool calls
- mistral-small3.1 / mistral-nemo — trained for function calling
> Model suggestion for OpenRouter:
- anthropic/claude-3.5-sonnet, openai/gpt-4o-mini, google/gemini-2.0-flash
```bash
# install
go install github.com/mark3labs/mcphost@latest
# config: ~/.config/mcphost.intervals.json
{
"mcpServers": {
"intervals-icu": {
"command": "node",
"args": ["/ABSOLUTE/PATH/TO/local/mcp-stdio.mjs"],
"env": { "INTERVALS_TOKEN": "your_api_key_here" }
}
}
}
# run with Ollama
mcphost -m ollama:qwen2.5 --config ~/.config/mcphost.intervals.json
# or OpenRouter
mcphost -m openrouter:anthropic/claude-3.5-sonnet
```
> If you'd rather a Desktop app instead of CLI, you can use [Tome](https://github.com/runebookai/tome), which offers pretty straightforward configuration for adding MCP servers.
## Authentication
Intervals.icu uses HTTP Basic auth. The server sends:
```
Authorization: Basic base64("API_KEY:${INTERVALS_TOKEN}")
```
This matches the official `curl -u "API_KEY:TOKEN"` style. If your token already contains a colon (e.g. `myuser:mytoken`), it is used verbatim.
## Troubleshooting
- **`INTERVALS_TOKEN is not set`** — set it in `config.js` or export it as an environment variable.
- **`HTTP 401`** — token is wrong or expired. Regenerate at <https://intervals.icu/settings>.
- **Tool not found in Claude** — restart Claude Desktop fully after editing the config file.
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues