feat: activity search + per-activity analytics tools #3

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. Richer activity access than the
current date-range get_activities.

Tools

  • search_activitiesGET /athlete/{id}/activities/search — query by name/type/filters
  • get_activity_best_effortsGET /activity/{id}/best-efforts — PRs/bests in an activity
  • get_activity_interval_statsGET /activity/{id}/interval-stats — deeper per-interval analytics

Notes

  • Read-only. Reuse patterns in tools/activities.py. Register + tests.
Part of the **0.3.0** coaching-context milestone. Richer activity access than the current date-range `get_activities`. ### Tools - `search_activities` — `GET /athlete/{id}/activities/search` — query by name/type/filters - `get_activity_best_efforts` — `GET /activity/{id}/best-efforts` — PRs/bests in an activity - `get_activity_interval_stats` — `GET /activity/{id}/interval-stats` — deeper per-interval analytics ### Notes - Read-only. Reuse patterns in `tools/activities.py`. Register + tests.
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; one conventional feat: commit for this issue.

Endpoints (verified against the official OpenAPI spec — note the REQUIRED params)

  • GET /athlete/{id}/activities/search — query q (required), limit (int) -> ActivitySearchResult[] (id, name, start_date_local, type, race, distance, moving_time, tags, description)
  • GET /activity/{id}/best-efforts — query stream (required, e.g. watts/hr/pace), optional duration, distance, count, minValue, excludeIntervals, startIndex, endIndex -> BestEfforts{efforts}
  • GET /activity/{id}/interval-stats — query start_index + end_index (both required) -> a single Interval object. This computes stats for an arbitrary stream index range; it is NOT a per-interval list (that's the existing get_activity_intervals).

Tools (extend tools/activities.py)

  1. search_activities(query: str, limit: int = 20) — validate non-empty query; compact one-line-per-result formatter (date, type, name, distance, moving_time, id) — results feed other tools by id.
  2. get_activity_best_efforts(activity_id, stream = "watts", duration = None, distance = None, count = None) — pass through only non-None params; format each effort (duration/distance, value, start index).
  3. get_activity_interval_stats(activity_id, start_index: int, end_index: int) — docstring must explain the indices come from streams/intervals output; render via a trimmed variant of the existing interval formatting in utils/formatting.py (reuse format_intervals' field set for a single interval).

Conventions

Same as the rest of the repo: creds resolve, single HTTP path, error-shape check, formatted str return, registration in server.py + tools/__init__.py, formatters in utils/formatting.py.

Tests (extend tests/test_activities.py or new tests/test_activity_analytics.py)

  • Request shape incl. required-param enforcement (missing/empty q returns a validation message, not an API call).
  • Param passthrough for best-efforts (None params omitted); success/empty/error branches per tool; coverage gate stays >=90%.

Estimate

~300 LOC incl. tests. No new module needed.

## Implementation plan (0.3.0) — intended delivery model: Claude Opus 4.8 **Branch:** `feat/coaching-context`; one conventional `feat:` commit for this issue. ### Endpoints (verified against the official OpenAPI spec — note the REQUIRED params) - `GET /athlete/{id}/activities/search` — query `q` **(required)**, `limit` (int) -> `ActivitySearchResult[]` (`id, name, start_date_local, type, race, distance, moving_time, tags, description`) - `GET /activity/{id}/best-efforts` — query `stream` **(required**, e.g. `watts`/`hr`/`pace`), optional `duration`, `distance`, `count`, `minValue`, `excludeIntervals`, `startIndex`, `endIndex` -> `BestEfforts{efforts}` - `GET /activity/{id}/interval-stats` — query `start_index` + `end_index` **(both required)** -> a single `Interval` object. This computes stats for an arbitrary stream index range; it is NOT a per-interval list (that's the existing `get_activity_intervals`). ### Tools (extend `tools/activities.py`) 1. **`search_activities(query: str, limit: int = 20)`** — validate non-empty query; compact one-line-per-result formatter (date, type, name, distance, moving_time, id) — results feed other tools by id. 2. **`get_activity_best_efforts(activity_id, stream = "watts", duration = None, distance = None, count = None)`** — pass through only non-None params; format each effort (duration/distance, value, start index). 3. **`get_activity_interval_stats(activity_id, start_index: int, end_index: int)`** — docstring must explain the indices come from streams/intervals output; render via a trimmed variant of the existing interval formatting in `utils/formatting.py` (reuse `format_intervals`' field set for a single interval). ### Conventions Same as the rest of the repo: creds resolve, single HTTP path, error-shape check, formatted `str` return, registration in `server.py` + `tools/__init__.py`, formatters in `utils/formatting.py`. ### Tests (extend `tests/test_activities.py` or new `tests/test_activity_analytics.py`) - Request shape incl. required-param enforcement (missing/empty `q` returns a validation message, not an API call). - Param passthrough for best-efforts (None params omitted); success/empty/error branches per tool; coverage gate stays >=90%. ### Estimate ~300 LOC incl. tests. No new module needed.
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#3