feat: athlete context read tools (profile, sport-settings, summary) #2

Closed
opened 2026-07-20 19:26:39 +00:00 by Chris Farhood · 1 comment
Owner

Part of the 0.3.0 coaching-context milestone. Give the AI coach the athlete's
actual configuration and thresholds — currently a blind spot.

Tools

  • get_athlete_profileGET /athlete/{id} (+ /athlete/{id}/profile) — weight, sex, timezone, prefs
  • get_sport_settingsGET /athlete/{id}/sport-settings — FTP, W', LTHR, HR zones, pace zones, thresholds per sport
  • get_athlete_summaryGET /athlete/{id}/athlete-summary — fitness/fatigue/load overview over a range

Notes

  • Read-only. Resolve creds via resolve_caller_credentials(), format via utils/formatting.py.
  • get_sport_settings output enables readiness/intensity reasoning (issue #1).
  • Register in server.py + tools/__init__.py; tests (success + error).
Part of the **0.3.0** coaching-context milestone. Give the AI coach the athlete's actual configuration and thresholds — currently a blind spot. ### Tools - `get_athlete_profile` — `GET /athlete/{id}` (+ `/athlete/{id}/profile`) — weight, sex, timezone, prefs - `get_sport_settings` — `GET /athlete/{id}/sport-settings` — FTP, W', LTHR, HR zones, pace zones, thresholds per sport - `get_athlete_summary` — `GET /athlete/{id}/athlete-summary` — fitness/fatigue/load overview over a range ### Notes - Read-only. Resolve creds via `resolve_caller_credentials()`, format via `utils/formatting.py`. - `get_sport_settings` output enables readiness/intensity reasoning (issue #1). - Register in `server.py` + `tools/__init__.py`; tests (success + error).
Chris Farhood added this to the 0.3.0 milestone 2026-07-20 19:26:39 +00:00
Author
Owner

Implementation plan (0.3.0) — intended delivery model: Claude Opus 4.8

Branch: feat/coaching-context (shared across the 0.3.0 issues; one conventional feat: commit per issue so cz bump derives 0.3.0).

Endpoints (verified against the official OpenAPI spec at intervals.icu/api/v1/docs)

  • GET /athlete/{id} -> WithSportSettings (athlete object with embedded sport settings)
  • GET /athlete/{athleteId}/sport-settings -> SportSettings[]
  • GET /athlete/{id}/athlete-summary{ext} -> SummaryWithCats[]; query: start, end (dates), tags (array). Empty {ext} returns JSON (same pattern as the existing power-curves tool, which omits ext).

New module: tools/athlete.py

  1. get_athlete_profile()GET /athlete/{id}. Format: name, sex, weight, timezone, ICU IDs, units prefs. Do NOT dump the embedded sportSettings blob (that's get_sport_settings' job) — summarize as "N sports configured".
  2. get_sport_settings(sport: str | None = None)GET /athlete/{athleteId}/sport-settings, optional client-side filter by sport (each record's types list). Per record, format the coaching-relevant subset of the 61 props: types, ftp, indoor_ftp, w_prime, p_max, power_zones (+names), lthr, max_hr, hr_zones (+names), threshold_pace, pace_units, pace_zones, warmup_time/cooldown_time. Include each record's id — required input for update_sport_settings (#5).
  3. get_athlete_summary(start_date, end_date, tags=None) — date-validated via resolve_date_params; render totals/load per category.

Conventions (per CLAUDE.md)

  • Creds via await credentials.resolve_caller_credentials(); all HTTP through make_intervals_request; check the {"error": True} shape; return formatted str.
  • Formatters in utils/formatting.py (format_athlete_profile, format_sport_settings, format_athlete_summary), conditional-line style like format_wellness_entry (null hides the line).
  • Register in BOTH server.py E402 block + __all__ and tools/__init__.py (import-side-effect registration).

Tests (tests/test_athlete.py)

  • Patch make_intervals_request at module level (pattern from test_wellness.py); autouse conftest creds fixture gives ("i1","testkey").
  • Cover: request shape (URL/params), sport filter hit+miss, formatting of a realistic SportSettings fixture (zones arrays), empty result, error dict, CredentialError. Keep the >=90% coverage gate green.

Order & estimate

First issue to land on the branch (its output feeds #1 readiness and #5 writes). ~350 LOC incl. tests.

## Implementation plan (0.3.0) — intended delivery model: Claude Opus 4.8 **Branch:** `feat/coaching-context` (shared across the 0.3.0 issues; one conventional `feat:` commit per issue so `cz bump` derives 0.3.0). ### Endpoints (verified against the official OpenAPI spec at `intervals.icu/api/v1/docs`) - `GET /athlete/{id}` -> `WithSportSettings` (athlete object with embedded sport settings) - `GET /athlete/{athleteId}/sport-settings` -> `SportSettings[]` - `GET /athlete/{id}/athlete-summary{ext}` -> `SummaryWithCats[]`; query: `start`, `end` (dates), `tags` (array). Empty `{ext}` returns JSON (same pattern as the existing power-curves tool, which omits ext). ### New module: `tools/athlete.py` 1. **`get_athlete_profile()`** — `GET /athlete/{id}`. Format: name, sex, weight, timezone, ICU IDs, units prefs. Do NOT dump the embedded `sportSettings` blob (that's `get_sport_settings`' job) — summarize as "N sports configured". 2. **`get_sport_settings(sport: str | None = None)`** — `GET /athlete/{athleteId}/sport-settings`, optional client-side filter by sport (each record's `types` list). Per record, format the coaching-relevant subset of the 61 props: `types`, `ftp`, `indoor_ftp`, `w_prime`, `p_max`, `power_zones` (+names), `lthr`, `max_hr`, `hr_zones` (+names), `threshold_pace`, `pace_units`, `pace_zones`, `warmup_time`/`cooldown_time`. Include each record's `id` — required input for `update_sport_settings` (#5). 3. **`get_athlete_summary(start_date, end_date, tags=None)`** — date-validated via `resolve_date_params`; render totals/load per category. ### Conventions (per CLAUDE.md) - Creds via `await credentials.resolve_caller_credentials()`; all HTTP through `make_intervals_request`; check the `{"error": True}` shape; return formatted `str`. - Formatters in `utils/formatting.py` (`format_athlete_profile`, `format_sport_settings`, `format_athlete_summary`), conditional-line style like `format_wellness_entry` (null hides the line). - Register in BOTH `server.py` E402 block + `__all__` and `tools/__init__.py` (import-side-effect registration). ### Tests (`tests/test_athlete.py`) - Patch `make_intervals_request` at module level (pattern from `test_wellness.py`); autouse conftest creds fixture gives `("i1","testkey")`. - Cover: request shape (URL/params), sport filter hit+miss, formatting of a realistic SportSettings fixture (zones arrays), empty result, error dict, CredentialError. Keep the >=90% coverage gate green. ### Order & estimate First issue to land on the branch (its output feeds #1 readiness and #5 writes). ~350 LOC incl. tests.
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: farhoodlabs/intervalsicu-mcp#2