MCP server that plans cycling workouts in intervals.icu (and onwards to Zwift and Garmin) and reads the outcomes back
Strong tool definitions with excellent descriptions (avg 250+ chars) and comprehensive parameter documentation. All 7 tools have clear, action-oriented names (check_, upload_, list_, delete_, review_, activity_, power_) and detailed descriptions explaining WHAT, WHEN, and WHY. Input schemas are well-structured with type constraints and field descriptions. Key strength: descriptions guide LLM behavior explicitly (e.g., 'ALWAYS in ENGLISH' repeated 4 times in upload_block). Weakness: no output schemas documented, no error handling guidance, no pagination support for list_block despite potentially large result sets, and missing tool annotations (readOnlyHint/destructiveHint).
Fetches the full details of one completed activity, including per-interval data if available. Use this to dig into a single ride — power curve, heart rate, cadence, interval-by-interval breakdown, and the pairing to the planned workout if one exists.
Verifies the connection and fetches the athlete's threshold values. Run this before building a workout. FTP, threshold heart rate and zone boundaries come from the account, so intensities are computed against the right numbers instead of a guess.
Deletes all workouts in a block from the calendar. This is the only way to remove a block — individual workouts cannot be deleted, only whole blocks. Re-run upload_block with the same block_name to correct; it updates instead of duplicating.
Lists the workouts in a block, with their calendar dates and external_ids. Use this to check what's in the calendar before deleting or re-uploading.
Fetches the power curve for a completed activity. Returns the classic landmarks: 5s, 15s, 30s, 1min, 2min, 5min, 10min, 20min, 30min, 1h. Use this to see how the ride stacked up against the athlete's fitness.
No output schemas documented. LLMs cannot plan downstream calls or extract required fields (e.g., activity_id from review_period to pass to activity_details). Responses are unstructured.
Missing tool annotations. upload_block and delete_block are destructive/write operations but lack destructiveHint=true. check_connection, list_block, review_period, activity_details, power_curve are read-only but lack readOnlyHint=true. Agents cannot distinguish safe from risky calls.
list_block lacks pagination (limit, offset, page_size). If a user has many blocks, the response could be very large, wasting tokens and risking context window exhaustion. No guidance on result limits.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 63 | 2026-07-28+ | v2 |
Reviews a training period: planned workouts paired with their outcomes. Start with this to see what happened. It reports numbers; the judgement is yours. Missed workouts are flagged. A workout with an unpaired activity on the same day is flagged as a possible pairing failure — pairing is never guaranteed, and manual pairing in intervals.icu may be needed. The window defaults to the last 7 days. Specify start_date and/or end_date to review a different period (ISO format YYYY-MM-DD). The plan side lists stamped workouts inside the window; the outcome side lists every activity inside the window, paired or not.
Puts a block of planned workouts in the intervals.icu calendar. From there the workouts sync to Zwift (Workouts -> Custom -> Intervals.icu) and Garmin. Write the block name, the workout names and the descriptions in ENGLISH, even when talking to the user in another language. The calendar, Zwift and Garmin are English, and these workouts outlive the conversation. After the upload, the server's interpretation of each workout is compared with the duration you stated. That catches DSL mistakes that would otherwise be silent — above all nested repeats, which make the workout shorter than intended without the server complaining. Read the warnings: a workout can upload without errors and still be wrong. Re-run with the same block_name to correct; it creates no duplicates.
No error handling guidance. Tools return raw IntervalsError or API failures with no recovery hints. E.g., 'Connection failed' tells the LLM nothing about whether to retry, ask the user for credentials, or give up.
upload_block description warns about nested repeats failing silently but provides no validation or confirmation step. Agents cannot dry-run or preview before committing destructive changes.