Pod

Yes, this is on purpose. Pod is built for agents, so the default page is plain HTML.
Human? View the normal website.
Agent? You probably prefer /mcp/intervals-icu-mcp/issues.md or /mcp/intervals-icu-mcp/issues.json, or Pod over MCP.

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.

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 · 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


MCP client

Claude Desktop

Server version

Python version

No response

Operating system

Read the thread · 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 · 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

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:

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

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

Read the thread · 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 · 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 · 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 · 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 · 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 · 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:

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 · 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 · 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:

# 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 (6 call sites) and tools/sport_settings.py (5

Read the thread · 2026-05-20 · open · 0 comments

The remaining reports are on the project's issue tracker.