Skip to content

Feature/coach athlete - #150

Open
morritter wants to merge 28 commits into
mvilanova:mainfrom
morritter:feature/coach-athlete-id
Open

morritter wants to merge 28 commits into
mvilanova:mainfrom
morritter:feature/coach-athlete-id

Conversation

@morritter

Copy link
Copy Markdown

No description provided.

morritter and others added 28 commits September 26, 2026 20:14
…y volume

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ummary

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…and drift, hard days

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… eFTP trend

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…d URLs

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…tivity

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
[intervals-mcp-server] Add get_coach_report with deterministic coaching metrics
get_event_by_id called /athlete/{id}/event/{eventId}, which returns 404; the
Intervals.icu endpoint is /events/{eventId} like update and delete. Event
details now show category, sport, planned load and planned time, and detect
races from the RACE_A/B/C category.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The event list of the API has no workout object, so every event was shown as
"Type: Other". Summaries now use category, type, icu_training_load and
moving_time (N/A when missing), and detect races from RACE_A/B/C.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Updates sent description/moving_time/distance as null, always set category
WORKOUT and moved the event to today when no date was given, so renaming an
event cleared its description or turned a NOTE into a WORKOUT. None values are
now dropped, category/type/date/color are only sent on update when passed, and
workout_type is optional so an event can be renamed. add_or_update_event takes
an optional category (e.g. RACE_A).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A future end_date treated planned days as rest days (ACWR/deload/HRV false
alarms) and labelled CTL values that already included planned workouts as
recomputed_without_planned. In projection mode, sections from completed
training end at the last completed day, recovery refers to today, CTL/ATL/TSB
at the end date are reported as api_incl_planned with load.actual and
load.projected, and a plan section summarises planned workouts per week and
sport (planned load, rest days, missing loads, projected CTL/ramp/TSB, races).
New flags ramp_planned_high, tsb_planned_low and planned_rest_days_low; flags
carry basis actual/projection. Reports without a future end date are unchanged
apart from schema_version and period.mode (snapshot test).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
[intervals-mcp-server] Fix calendar tools and add planning projection to the coach report
MINS_KM, MINS_MILE, SECS_100M and SECS_500M were written as bare
numbers ("335"), which Intervals.icu does not parse as pace. They are
now written as "5:35/km Pace", with ranges like "5:30-5:40/km Pace".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
[intervals-mcp-server] Format absolute pace targets in workout steps
get_coach_report takes an optional athlete_id (default: ATHLETE_ID).
resolve_athlete_id now validates a passed ID before it reaches a URL
path and treats an empty ID as the default. get_activity_details looks
up gear with the athlete who owns the activity.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
futureweb added a commit to futureweb/intervals-mcp-server that referenced this pull request Oct 9, 2026
Fixes two bugs reported in
[mvilanova/intervals-mcp-server#150](mvilanova/intervals-mcp-server#150)
by @morritter that also exist here:

- **Event and note updates were destructive.** `add_or_update_event` /
`add_or_update_note` sent unset fields as null, always set the category
to WORKOUT and moved the event to today when no date was given. Renaming
an event therefore cleared its description, moved it, or turned a NOTE
into a WORKOUT. Updates now send only the fields that were passed;
creating still needs a name and gets the defaults. New optional
`category` (WORKOUT, RACE_A/B/C, TARGET, NOTE, HOLIDAY, SICK, INJURED).
- **Absolute pace targets** (MINS_KM, MINS_MILE, SECS_100M, SECS_100Y,
SECS_500M) were written as bare numbers that Intervals.icu does not
parse; now `5:35/km Pace`, ranges `5:30-5:40/km Pace`.

16 new regression tests; 375 tests, ruff, mypy, pylint gate green. Also
records the evaluation of the coach report from #150 in UPSTREAM_AUDIT
and credits the reporter.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
futureweb added a commit to futureweb/intervals-mcp-server that referenced this pull request Oct 9, 2026
… tools (after #150 by morritter) (#15)

Phase 4: evidence-based training load and intensity metrics as separate
read-only tools. The metric set, edge cases and quality filters come
from the coach report proposed by **@morritter in upstream
mvilanova/intervals-mcp-server#150**; it is re-implemented here as
reusable tools on pure metric modules, with sample sizes, coverage and
cited reference ranges, without flags or verdicts.

## New tools (all `read`, `output_format="json"`, `detail_level`
compact/standard/full)

| Tool | What it does | API calls |
| --- | --- | --- |
| `get_training_load` | Acute/chronic Intervals.icu load (7/28 d,
configurable), coupled ACWR, Foster monotony + strain over 7 days with
rest days as 0, 7-day load vs chronic weekly mean (deload-like <= 80 %),
per sport family with primary sport and its own monotony (cross-training
floor caveat), ISO week table, CTL/ATL/form/ramp (today recomputed
without planned-but-not-done load), device loads summed separately |
activities (field selection), wellness, custom items + sport settings
(cached) |
| `get_intensity_distribution` | Three-zone model from power/HR/pace
zones with a mapping by zone count, Treff et al. 2019 polarization index
(Z3 < 1 %, Z1 = 0, Z2 = 0 edge cases), class + zone order, hard
sessions/days, drift between the halves, per sport and ISO week,
coverage | 1 |
| `get_durability` | Aerobic decoupling of steady long sessions with a
quality filter (duration, pauses, HR, power/VI, temperature,
indoor/outdoor), excluded sessions per reason, median/IQR, count > 5 %,
last 7 d vs window with a +/- 1 pp band, EF trend (+/- 2 %), note for
several bikes | 1 |
| `get_load_projection` | CTL/ATL/form projected over planned WORKOUT
loads (42/7-day model), missing planned loads reported, race days,
Intervals.icu's own projection and a model check | 3 |
| `get_coach_context` | Compact weekly overview (~2k chars): load,
fitness, intensity 7/28 d, HRV/RHR/sleep vs 42-day baselines (numbers
only), durability, top sessions, plan of the next 7 days | 3 |

Plus the prompt `training_load_review`; `weekly_training_review` and
`intervals://guide` mention the new tools.

## Adopted from #150 / changed
- Adopted: ACWR (coupled), Foster monotony/strain with rest days,
primary-sport monotony, 80 % deload rule, PI formula and edge cases,
class rules, durability filter (60 min, moving/elapsed 0.85, VI 1.20, 25
°C) and 1 pp band, EF trend, top sessions (highest IF kept), recomputing
today's CTL/ATL without planned load, "no activity today -> windows end
yesterday".
- Changed: separate tools instead of one report; no alarm/warning flags
(reference ranges with sources and "inside/above/below" only); zone
mapping chosen by the athlete's zone count (power, HR, pace; unmapped
models reported, never counted as easy time); hard sessions defined on
the three-zone Z3 (>= 10 min) or IF >= 0.85 on >= 20 min; drift compares
the two halves of the period; all cycling/running types with the
steadiness filter deciding; device loads reported on their own scale;
projection computed by us with a model check and Intervals.icu's own
projection side by side; sport families from `utils.sports`; English
internals.

## Testing
- `pytest` 404 passed (new: `tests/test_load_metrics.py` pure metrics
with edge cases, `tests/test_load_tools.py` tool level incl. MCP layer),
`ruff`, `mypy src tests`, pylint gate exit 0 (no new duplicate-code
messages).
- Live read-only check against the account (activities, wellness,
events, custom items; GET only): all five tools in text and JSON; model
check reproduces the stored CTL/ATL within 0.00.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant