# Reported issues for intervals-icu-mcp

Pod holds 18 of 44 problems reported by people outside the maintainer team. Issues filed by the project's own owners, members and collaborators are excluded entirely — a maintainer's release checklist is not a warning to a prospective user.

Back to [intervals-icu-mcp](/mcp/intervals-icu-mcp).

## Most discussed

### ATP tool: any overlapping day note is surfaced as recovery_note — filter by for_week, use a neutral key

## Problem

`icu_get_annual_training_plan` (#73) attaches notes to ATP weeks like this: for each TARGET week, the first calendar event with category `NOTE` whose date range overlaps the week is returned under the response key `recovery_note`, and counted in `summary.recovery_week_count`.

The key name asserts a classification that never happened. Any day note that happens to fall in a target week — "Travel — Rest", "Rest Day", a medical appointment — comes back labeled as the week's recovery not

[Read the thread](https://github.com/hhopke/intervals-icu-mcp/issues/79) · 2026-07-05 · closed · 4 comments

### Workout syntax resource inaccurate distance field

### What happened?

Agent struggled to create swim workouts. Realized it was because the workout syntax resource has an error. 

> | `Xm` | `400m` | Meters (context-dependent, >200 = meters) |

Should be 

> | `Xmtr` | `400mtr` | Meters (context-dependent, >200 = meters) |

### What did you expect?

-

### How can we reproduce it?

-

### Error response / logs

```json

```

### MCP client

Claude Desktop

### Server version

-

### Python version

_No response_

### Operating system

-

[Read the thread](https://github.com/hhopke/intervals-icu-mcp/issues/75) · 2026-06-26 · closed · external user · 3 comments

### Fitness/Fatigue/Form (CTL/ATL/TSB) tools ignore athlete_id and always return the default profile's data

### What happened?

When I ask Claude to pull fitness/fatigue/form data (CTL/ATL/TSB) for one of my athletes using tools like icu_get_fitness_summary and icu_get_fitness_chart, the response comes back with the default account profile's numbers instead of the requested athlete's. This happens even when I explicitly tell Claude to use a specific athlete_id. No matter which athlete_id I specify, the returned CTL/ATL/TSB values don't change — they always match the default profile.

### What did you 

[Read the thread](https://github.com/hhopke/intervals-icu-mcp/issues/99) · 2026-07-30 · closed · external user · 2 comments

### icu_get_workouts_in_folder returns HTTP 405 for every folder

### What happened?

Calling icu_get_workouts_in_folder fails with a 405 Method Not Allowed for every folder tested (both regular folders and training plans), not just one specific case.

### What did you expect?

The tool should return the list of workouts contained in the folder, as described in its docstring.

### How can we reproduce it?

Call icu_get_workouts_in_folder with any valid folder_id returned by icu_get_workout_library.

### Error response / logs

```json
HTTP 405: {"timestamp":"2

[Read the thread](https://github.com/hhopke/intervals-icu-mcp/issues/95) · 2026-07-22 · closed · external user · 2 comments

### Token efficiency: add input_examples to high-complexity tools (Anthropic 72→90% accuracy gain)

## Context

Anthropic introduced a `input_examples` field for tool definitions in November 2025. Their internal testing reports **72% → 90% accuracy on complex parameter handling** when examples are added (cited in [Anthropic's tool-use docs](https://docs.anthropic.com/en/docs/agents-and-tools/tool-use/implement-tool-use)).

Token cost is small: ~20-50 tokens for simple examples, ~100-200 tokens for complex nested objects. For tools where Haiku already needed multiple attempts (see PR #19 testin

[Read the thread](https://github.com/hhopke/intervals-icu-mcp/issues/29) · 2026-04-26 · closed · 2 comments

### Token efficiency: INTERVALS_ICU_TOOLSET env var with core (default) and full tiers

## Context

The server currently exposes all 51 tools to every connected MCP client, costing **~8,869 tokens of tool descriptions per session** — paid once per conversation, regardless of how few tools are actually used.

A typical user prompt (*"show me my last ride"*, *"what's my CTL?"*, *"how was sleep this week?"*) only needs 1-2 tools. Power-user / coach prompts (*"apply this training plan"*, *"manage my gear retirement"*, *"create a custom power zone set"*) need the long tail.

**Proposal:

[Read the thread](https://github.com/hhopke/intervals-icu-mcp/issues/27) · 2026-04-26 · closed · 2 comments

### gear and sport_settings tools bypass middleware config injection

`gear.py` and `sport_settings.py` — 11 tools — bypass the middleware and load credentials themselves:

```python
config = load_config()
if not validate_credentials(config):
    return "Error: Intervals.icu credentials not configured. Run intervals-icu-mcp-auth to set up."
```

The other 12 tool modules use the documented pattern:

```python
config: ICUConfig = await ctx.get_state("config")
```

`CLAUDE.md` describes `middleware.py` as the component that "Validates config, injects `ICUConfig` int

[Read the thread](https://github.com/hhopke/intervals-icu-mcp/issues/103) · 2026-07-30 · open · 1 comment

### Remaining athlete-scoped tools still don't expose athlete_id

Follow-up to #99 / #100.

#99 reported that the fitness/fatigue/form tools ignored `athlete_id`. The root cause was not in the API client — `ICUClient` already routes every athlete-scoped endpoint through `athlete_id or config.intervals_icu_athlete_id`. The gap is purely at the tool layer: the parameter is never exposed, so a coach cannot pass it and silently gets the configured default athlete's data with no error.

#100 fixed that data family (fitness summary, athlete profile, wellness read/wr

[Read the thread](https://github.com/hhopke/intervals-icu-mcp/issues/101) · 2026-07-30 · closed · 1 comment

## Most recent

### session_rpe (S-RPE) is on the activity schema but no tool surfaces it

### What problem are you trying to solve?

`session_rpe` (S-RPE) is on the activity schema and the Intervals.icu web UI shows it next to RPE, but no tool surfaces it. An LLM asking "how much subjective load did I accumulate this week?" can read RPE and duration separately, but not the session load the platform itself computed — so it either does the arithmetic itself, or silently omits the metric.

This came up in #117: my activity showed RPE 7 and S-RPE 623 in the web UI while `icu_get_activity

[Read the thread](https://github.com/hhopke/intervals-icu-mcp/issues/123) · 2026-08-30 · open · external user · 0 comments

### Curve tools synthesize zones from a clipped echo of the configured max HR

`icu_get_hr_curves` and `icu_get_power_curves` each emit a zone block built from a hardcoded formula rather than the athlete's configured zones. The HR one is anchored to a value that is not max HR — and, as it turns out, is not a measurement at all.

Surfaced while investigating #116 / #118, which exposes the athlete's configured zones on `icu_get_sport_settings`. That change makes the divergence visible; it does not fix these tools.

## The anchor is circular, not merely wrong

`tools/curves.p

[Read the thread](https://github.com/hhopke/intervals-icu-mcp/issues/119) · 2026-08-30 · open · 0 comments

### Gear model reads invented field names: type, time, activities, retired mismatched

Follow-up to #107, found while live-testing its fix (PR #108): the \`Gear\` model itself also uses invented field names, so gear display is partly empty and gear writes likely silently drop fields.

### Evidence (live API, gear \`16276296\`)

Raw record:

\`\`\`json
{"id": "16276296", "athlete_id": "…", "type": "Bike", "name": "Rose Backroad CF",
 "distance": 47415.83, "time": 7280.0, "activities": 1,
 "use_elapsed_time": false, "reminders": [], "component": false}
\`\`\`

\`icu_get_gear_list\` 

[Read the thread](https://github.com/hhopke/intervals-icu-mcp/issues/110) · 2026-07-31 · closed · 0 comments

### Gear reminder tools call nonexistent endpoints with invented field names

\`icu_create_gear_reminder\` and \`icu_update_gear_reminder\` have never worked — the same never-worked class as #102 and the \`icu_get_workouts_in_folder\` HTTP 405 fixed in 4.3.2 — and reminder data shown by \`icu_get_gear_list\` is silently reduced to an id. Found by auditing every \`client.py\` endpoint call against \`openapi-spec.json\`.

### Three layers

**1. Wrong path (HTTP 404).** The client calls plural \`/reminders\`; the API only serves singular:

| Client calls | Spec + live API |

[Read the thread](https://github.com/hhopke/intervals-icu-mcp/issues/107) · 2026-07-30 · closed · 0 comments

### validate_credentials rejects the real athlete id i123456, blocking all tools

`validate_credentials()` treats the athlete id `i123456` as an unconfigured placeholder:

```python
if not config.intervals_icu_athlete_id or config.intervals_icu_athlete_id == "i123456":
    return False
```

`i123456` is a **valid Intervals.icu athlete id format**, not a reserved value — real ids look like `i368404`, `i492634`. It is simply the example used in `.env.example` and the README. Whoever actually owns `i123456` cannot use this server at all.

### Blast radius: all 61 tools, not just

[Read the thread](https://github.com/hhopke/intervals-icu-mcp/issues/104) · 2026-07-30 · closed · 0 comments

### icu_search_intervals always returns HTTP 422 (wrong query param names)

`icu_search_intervals` returns HTTP 422 on every call and appears never to have worked — the same class of defect as the `icu_get_workouts_in_folder` HTTP 405 fixed in 4.3.2.

### Cause

`ICUClient.search_intervals()` sends the wrong query parameter names and omits two required ones.

The API (`GET /athlete/{id}/activities/interval-search`, per `openapi-spec.json`) requires **four** query params:

| Param | Required | Client sends |
| --- | --- | --- |
| `minSecs` | ✅ | ❌ sends `minDuration` |
|

[Read the thread](https://github.com/hhopke/intervals-icu-mcp/issues/102) · 2026-07-30 · closed · 0 comments

### icu_update_sport_settings rejects every realistic swim CSS (swim threshold-pace unit bug)

**Summary:** `icu_update_sport_settings` / `icu_create_sport_settings` cannot set a realistic swim CSS/threshold pace — the API rejects every realistic value with HTTP 422 "Invalid threshold pace".

## Repro

`icu_update_sport_settings(sport_id=<swim>, swim_threshold=4.0)` (4:00/100m) → `HTTP 422: Invalid threshold pace: 240.0`. Same for 1:30, 2:30, 1:00/100m.

## Root cause

The write path multiplies the swim pace by 60, sending seconds:

```python
# sport_settings_format.py
payload["threshold_

[Read the thread](https://github.com/hhopke/intervals-icu-mcp/issues/88) · 2026-07-13 · closed · 0 comments

### client.get_activities() fetches entire date range, truncates client-side instead of passing limit as query param

## Problem

`ICUClient.get_activities()` (`src/intervals_icu_mcp/client.py`) sends only `oldest`/`newest` as query params, fetches the **entire** result set for the date range, then truncates in Python with `activities[:limit]`.

The OpenAPI spec's `listActivities` endpoint supports a native `limit` query param, so the truncation could happen server-side.

## Why it matters now

This predates #74, but that PR adds `icu_get_activities_by_date`, which is explicitly built for wide historical window

[Read the thread](https://github.com/hhopke/intervals-icu-mcp/issues/80) · 2026-07-05 · closed · 0 comments

### Analysis quality gaps: profile-readiness warnings, forward fitness projections, gear-name resolution

Three gaps where the server returns technically-correct data but leaves analysis quality or UX on the table. Grouped here; each is independently scoppable.

## 1. Profile-readiness warnings (guardrail — small, high value)

When a tool produces threshold- or zone-based output (power/HR/pace zones, curves), it should check whether the athlete actually has the relevant thresholds set (FTP / FTHR / pace thresholds) and surface a warning when they're missing — so the assistant can ask the user to set

[Read the thread](https://github.com/hhopke/intervals-icu-mcp/issues/72) · 2026-06-19 · open · 0 comments

### refactor: unify config loading — migrate gear/sport_settings to ctx middleware, drop redundant load_dotenv

## Context

Config loading is inconsistent across the codebase. Two tool modules bypass the middleware pattern, and `server.py` calls `load_dotenv()` twice at startup.

## Inconsistency 1: gear.py and sport_settings.py bypass ctx middleware

Every other tool module reads config via:
```python
config: ICUConfig = await ctx.get_state("config")
```

But [tools/gear.py](src/intervals_icu_mcp/tools/gear.py) (6 call sites) and [tools/sport_settings.py](src/intervals_icu_mcp/tools/sport_settings.py) (5

[Read the thread](https://github.com/hhopke/intervals-icu-mcp/issues/64) · 2026-05-20 · open · 0 comments

The remaining reports are on [the project's issue tracker](https://github.com/hhopke/intervals-icu-mcp/issues).
