Skip to main content

Model Context Protocol server for Coros with first-class running workout authoring, scheduling, and editing on top of training data, sleep, HRV, and activity exports.

Project description

coros-training-mcp

A Model Context Protocol (MCP) server for COROS with first-class running workout authoring and editing — pace-based intervals, distance targets, repeat groups, and clone-and-swap edits of scheduled runs — on top of the standard COROS training data, scheduling, strength, and activity export tools.

This project is built on top of cygnusb/coros-mcp and keeps that repository as an upstream reference.

Why this fork

Upstream is a solid read-oriented COROS MCP, but its authoring story is shaped around time-and-power workouts (cycling, strength). This fork is the one to pick when an agent needs to build or edit running workouts end-to-end:

  • Sport-specific run toolscreate_run_workout and update_run_workout speak distance/pace/HR targets natively, not just duration/power.
  • Shared run-step schemaget_run_workout_schema exposes the exact contract so agents don't guess which fields are valid on create vs. update.
  • Clone-and-swap edits — change a scheduled run's distance, pace band, or rep count without destroying the calendar entry.
  • Live builder catalog — enum values (step kinds, target types, intensity types) are extracted from the Training Hub builder itself, not hand-maintained.
  • Workout taxonomy docs — explicit disambiguation of library workouts, scheduled entries, and plan containers, so agents stop confusing list_workouts with the calendar.
  • Broader automated test suite covering the running-edit paths.

No API key required. This server authenticates directly with your Coros Training Hub credentials. Your token is stored securely in your system keyring (or an encrypted local file as fallback), never transmitted anywhere except to Coros.

For the distinction between library workouts, scheduled calendar entries, and plan containers, see docs/workout-taxonomy.md.

What You Can Do

Ask your AI assistant questions like:

Running workouts (the fork's focus):

  • "Create a 5×1km threshold workout at 4:05–4:15/km with 2-minute jog recovery"
  • "Change my Tuesday VO2 workout to 6 reps instead of 5"
  • "Build a 90-minute long run with 4×8min at marathon pace in the middle"
  • "Move Thursday's tempo run to Friday"
  • "Replace my scheduled Sunday long run with a 16km progression at easy→steady pace"

Training data & recovery:

  • "How much deep sleep and REM did I get last week?"
  • "What was my HRV trend over the last 4 weeks?"
  • "Show me my resting heart rate and training load for last week"
  • "How many steps did I average per day this month?"

Activities, calendar, and other sports:

  • "List my rides from last month"
  • "Show me the details of my last long ride"
  • "Create a 90-minute sweet spot workout for me"
  • "What's on my training calendar next week?"
  • "Schedule my VO2 workout for Thursday"
  • "Create a 20-minute strength circuit with squats, lunges, and planks"

Features

Tool Description
authenticate_coros Log in with email and password — token stored securely in keyring
authenticate_coros_mobile Log in to the mobile API only (useful for sleep data troubleshooting)
check_coros_auth Check whether a valid auth token is present
get_daily_metrics Fetch daily metrics (HRV, resting HR, training load, VO2max, stamina, and more) for n weeks (default: 4)
get_sleep_data Fetch nightly sleep stages (deep, light, REM, awake) and sleep HR for n weeks (default: 4)
list_activities List activities for a date range with summary metrics
get_activity_detail Fetch full detail for a single activity (laps, HR zones, power zones)
export_activity_file Export a completed activity file in GPX, FIT, TCX, KML, or CSV and save it locally
list_workouts List all saved structured workout programs
get_workout_builder_catalog Return the checked-in enum registry and live builder catalog for workout construction
get_run_workout_schema Return the shared run-step schema used by both create_run_workout and update_run_workout
create_run_workout Create a running workout with explicit run-step kinds and run targets
update_run_workout Clone and edit a running workout using run-specific step updates
create_workout Create a new structured workout with named steps and power targets
delete_workout Delete a workout program from the library
list_planned_activities List planned workouts from the Coros training calendar
schedule_workout Schedule an existing workout on a calendar day
remove_scheduled_workout Remove a scheduled workout from the calendar
create_strength_workout Create a structured strength workout with sets, reps, or timed exercises
list_exercises Browse the Coros exercise catalogue, especially for strength workouts

Workout Taxonomy

Three distinct objects, often confused:

  • library workout — reusable program in your account. Queried by list_workouts.
  • scheduled entry — a calendar occurrence of a library workout on a specific day. Queried by list_planned_activities. May have different IDs than its source workout.
  • plan container — the higher-level COROS schedule/plan that owns scheduled entries.

GPX/FIT/TCX/KML export applies to completed activities only — structured library workouts use a separate share flow in the app. The MCP writes to COROS server state; device sync is still handled by the COROS app/watch.

See docs/workout-taxonomy.md for full detail, and docs/enum-extraction.md for how enum values are discovered from the live Training Hub builder.


Setup

Two commands — install and run the wizard:

uv tool install coros-training-mcp
coros-mcp setup

(pipx install coros-training-mcp also works if you don't have uv.)

The wizard asks for email, password, and region, verifies the credentials against the COROS API, stores them in your system keyring, detects which AI assistants you have installed (Claude Code, Claude Desktop, Codex CLI, Cursor), lets you pick which to install into, writes the MCP config for each picked one (preserving any other MCP entries you already have), and runs a post-install smoke test to confirm the server boots.

Lifecycle

coros-mcp setup                 # first-time setup
coros-mcp setup --reconfigure   # change credentials or add more assistants
coros-mcp uninstall             # remove from assistants, optionally clear keyring
coros-mcp auth-status           # check stored tokens

To upgrade: uv tool upgrade coros-training-mcp (or pipx upgrade coros-training-mcp).

Requirements

  • Python ≥ 3.11 (fetched automatically by uv tool install; already present on most systems otherwise)
  • A COROS Training Hub account
  • One of: Claude Code, Claude Desktop, Codex CLI, or Cursor (others can still use the server manually — see "Manual setup" below)

What gets stored and where

  • Credentials: system keyring (macOS Keychain / Windows Credential Manager / freedesktop Secret Service). If the keyring is unavailable (headless Linux, some VMs), the wizard falls back to an encrypted local file at ~/.coros-mcp/credentials.enc and tells you.
  • Assistant config entries: live in each assistant's own config file — we only ever add/replace a coros entry; we never touch other MCP entries.
  • The coros-mcp binary itself: in the uv tool / pipx isolated venv, at an absolute path that MCP clients pin to.

Manual setup (advanced)

If you're on a system none of the supported assistants run on, or you want full control, you can still run the server directly:

uv tool install coros-training-mcp
coros-mcp auth     # one-time interactive login, stores tokens in keyring

Then point any MCP client at the binary:

{ "mcpServers": { "coros": { "command": "/absolute/path/to/coros-mcp", "args": ["serve"] } } }

Find the absolute path with which coros-mcp.

Developer setup

For hacking on the code:

git clone https://github.com/dholliday3/coros-training-mcp.git
cd coros-training-mcp
python3 -m venv .venv && source .venv/bin/activate
pip install -e .[dev]
pytest

CLI reference

coros-mcp setup                 # first-time interactive setup
coros-mcp setup --reconfigure   # re-run wizard
coros-mcp uninstall             # remove from assistants
coros-mcp serve                 # start the MCP server (what MCP clients run)
coros-mcp auth                  # low-level: re-authenticate (web + mobile)
coros-mcp auth-web              # low-level: web token only (sleep data lazy-loads)
coros-mcp auth-mobile           # low-level: mobile token only
coros-mcp auth-status           # check stored tokens
coros-mcp auth-clear            # remove stored tokens

The mobile login logs you out of the COROS mobile app on your phone. Use auth-web (or let the wizard's default skip-mobile choice stand) to avoid this — the mobile token is fetched lazily on the first sleep-data request.


Tool Reference

Auth — authenticate_coros, authenticate_coros_mobile, check_coros_auth

You normally don't call these directly — credentials from Keychain or .env are picked up automatically. They exist for explicit login/reauth ({ "email", "password", "region" }) and status checks (check_coros_auth returns authenticated, expires_in_hours, mobile_authenticated, mobile_token_status). authenticate_coros_mobile is useful for restoring sleep-data access without redoing web auth.

get_daily_metrics

Fetch daily metrics for a configurable number of weeks (default: 4).

{ "weeks": 4 }

Returns: records (list), count, date_range

Each record includes:

Field Source Description
date Date (YYYYMMDD)
avg_sleep_hrv dayDetail Nightly HRV (RMSSD ms)
baseline dayDetail HRV rolling baseline
rhr dayDetail Resting heart rate (bpm)
training_load dayDetail Daily training load
training_load_ratio dayDetail Acute/chronic training load ratio
tired_rate dayDetail Fatigue rate
ati / cti dayDetail Acute / chronic training index
distance / duration dayDetail Distance (m) / duration (s)
vo2max analyse (merge) VO2 Max (last ~28 days)
lthr analyse (merge) Lactate threshold heart rate (bpm)
ltsp analyse (merge) Lactate threshold pace (s/km)
stamina_level analyse (merge) Base fitness level
stamina_level_7d analyse (merge) 7-day fitness trend

get_sleep_data

Fetch nightly sleep stage data for a configurable number of weeks (default: 4).

{ "weeks": 4 }

Returns: records (list), count, date_range

Each record includes:

Field Description
date Date (YYYYMMDD) — the morning after the sleep
total_duration_minutes Total sleep in minutes
phases.deep_minutes Deep sleep
phases.light_minutes Light sleep
phases.rem_minutes REM sleep
phases.awake_minutes Time awake during the night
phases.nap_minutes Daytime nap time (null if none)
avg_hr Average heart rate during sleep
min_hr Minimum heart rate during sleep
max_hr Maximum heart rate during sleep
quality_score Sleep quality score (null if not computed)

Note: Sleep data is fetched from the Coros mobile API (apieu.coros.com), which uses a separate token from the Training Hub web API. coros-mcp auth obtains both tokens, but doing so logs you out of the Coros mobile app. Use coros-mcp auth-web to skip mobile login — the mobile token is then obtained automatically on the first sleep data request. The token expires after ~1 hour but refreshes automatically on subsequent requests.

list_activities

List activities for a date range.

{ "start_day": "20260101", "end_day": "20260305", "page": 1, "size": 30 }

Returns: activities (list), total_count, page

Each activity includes: activity_id, name, sport_type, sport_name, start_time, end_time, duration_seconds, distance_meters, avg_hr, max_hr, calories, training_load, avg_power, normalized_power, elevation_gain

get_activity_detail

Fetch full detail for a single activity. Requires the sport_type from list_activities.

{ "activity_id": "469901014965714948", "sport_type": 200 }

Returns full activity data including laps, HR zones, power zones, and all sport-specific metrics.

Note: Large time-series arrays (graphList, frequencyList, gpsLightDuration) are stripped from the response to keep it manageable.

export_activity_file

Export a completed activity file and save it locally.

{
  "activity_id": "469901014965714948",
  "sport_type": 100,
  "file_type": "gpx",
  "output_path": "/tmp/morning-run.gpx"
}

file_type: gpx, fit, tcx, kml, or csv

Returns: activity_id, sport_type, file_type, file_url, output_path, downloaded

list_workouts

List all saved structured workout programs.

{}

Returns: workouts (list), count

Each workout includes: id, name, sport_type, sport_name, estimated_time_seconds, exercise_count, exercises (list of steps with name, duration_seconds, power_low_w, power_high_w)

create_run_workout

Create a running workout with run-native step kinds (warmup, training, rest, cooldown), distance or time targets, and optional pace / HR intensity ranges. Supports repeat groups for intervals.

5×1km threshold with jog recovery:

{
  "name": "Tuesday Threshold",
  "steps": [
    {"kind": "warmup",   "name": "Warm-up",  "target_type": "distance", "target_distance_meters": 2000},
    {"repeat": 5, "name": "Main Set", "steps": [
      {"kind": "training", "name": "Rep",      "target_type": "distance", "target_distance_meters": 1000,
       "intensity_type": 3, "intensity_value": 245, "intensity_value_extend": 255, "intensity_display_unit": 2},
      {"kind": "rest",     "name": "Recovery", "target_type": "time",     "target_duration_seconds": 120}
    ]},
    {"kind": "cooldown", "name": "Cool-down", "target_type": "distance", "target_distance_meters": 1500}
  ]
}

Pace targets use intensity_type: 3 with intensity_value / intensity_value_extend as seconds-per-km and intensity_display_unit: 2. For the full field vocabulary (HR zones, percent-of-LT, named intensity presets), call get_run_workout_schema or see run_workout_schema.py.

Returns: workout_id, sport_type, estimated_time_seconds, estimated_distance_meters, steps_count, message

update_run_workout

Clone-and-edit an existing running workout. Select each step to change by step_name, step_id, or step_index, and patch it with any run-step field used by create_run_workout. The original workout is preserved; a new workout ID is returned. If the original was scheduled, re-schedule the replacement with schedule_workout.

{
  "workout_id": "476023839273435149",
  "name": "Tuesday Threshold (6×1km)",
  "step_updates": [
    {"step_name": "Main Set", "repeat": 6},
    {"step_name": "Rep", "target_distance_meters": 1000, "intensity_value": 240, "intensity_value_extend": 250}
  ]
}

Returns: new_workout_id, original_workout_id, name, steps_count, message

get_run_workout_schema

Return the shared run-step schema used by both create_run_workout and update_run_workout, including allowed step kinds, target types, intensity presets pulled from the live Training Hub builder, and required vs. optional fields. Call this before authoring to avoid guessing which fields are valid.

{}

create_workout

Generic time-and-power workout builder (primarily cycling). For running, prefer create_run_workout. Supports plain steps and nested repeat groups for intervals.

{
  "name": "3×10min Sweet Spot",
  "sport_type": 2,
  "steps": [
    {"name": "Warmup",   "duration_minutes": 10, "power_low_w": 150, "power_high_w": 200},
    {"repeat": 3, "steps": [
      {"name": "Sweet Spot", "duration_minutes": 10, "power_low_w": 265, "power_high_w": 285},
      {"name": "Recovery",   "duration_minutes":  3, "power_low_w": 150, "power_high_w": 175}
    ]},
    {"name": "Cooldown", "duration_minutes": 11, "power_low_w": 150, "power_high_w": 200}
  ]
}

sport_type: 2 = Indoor Cycling (default), 200 = Road Bike. Returns: workout_id, name, total_minutes, steps_count, message.

delete_workout

Delete a workout program from the Coros account.

{ "workout_id": "476023839273435149" }

The workout_id comes from list_workouts.

Returns: deleted, workout_id, message

list_planned_activities

List planned activities from the Coros training calendar.

{ "start_day": "20260309", "end_day": "20260316" }

Returns: activities (list), count, date_range

schedule_workout

Add an existing workout from your library to the Coros training calendar.

{ "workout_id": "1234567890", "happen_day": "20260312", "sort_no": 1 }

Returns: scheduled, workout_id, happen_day

remove_scheduled_workout

Remove a scheduled workout from the Coros training calendar.

{
  "plan_id": "987654321",
  "id_in_plan": "1234567890",
  "plan_program_id": "1234567890"
}

plan_id, id_in_plan, and plan_program_id come from list_planned_activities. If plan_program_id is missing, you can usually reuse id_in_plan.

Returns: removed, plan_id, id_in_plan

create_strength_workout

Create a structured strength workout with repeated sets. Exercises must come from the Coros exercise catalogue.

{
  "name": "Leg Circuit",
  "sets": 3,
  "exercises": [
    {
      "origin_id": "54",
      "name": "T1061",
      "overview": "sid_strength_squats",
      "target_type": 3,
      "target_value": 12,
      "rest_seconds": 45
    },
    {
      "origin_id": "130",
      "name": "T1176",
      "overview": "sid_strength_plank",
      "target_type": 2,
      "target_value": 60,
      "rest_seconds": 30
    }
  ]
}

target_type: 2 = time in seconds, 3 = reps

Returns: workout_id, name, sets, exercise_count

list_exercises

List the Coros exercise catalogue for a sport type. Default sport_type=4 returns strength exercises.

{ "sport_type": 4 }

Returns: exercises (list), count, sport_type


Disclaimer

This project uses the unofficial COROS Training Hub API. The API may change at any time without notice. Use at your own risk.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

coros_training_mcp-0.2.0.tar.gz (65.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

coros_training_mcp-0.2.0-py3-none-any.whl (74.8 kB view details)

Uploaded Python 3

File details

Details for the file coros_training_mcp-0.2.0.tar.gz.

File metadata

  • Download URL: coros_training_mcp-0.2.0.tar.gz
  • Upload date:
  • Size: 65.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for coros_training_mcp-0.2.0.tar.gz
Algorithm Hash digest
SHA256 9976cd4f7a82726641afc69ea94e79e91a2303ce210a48a614d715561bc25cbb
MD5 b5e2ac548be3d9c73fac25097fa0ff25
BLAKE2b-256 21c86c2baff4976751333c2d3edf175eee51a8d4769d4bf9f6c30878fe63d720

See more details on using hashes here.

Provenance

The following attestation bundles were made for coros_training_mcp-0.2.0.tar.gz:

Publisher: release.yml on dholliday3/coros-training-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file coros_training_mcp-0.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for coros_training_mcp-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5ced51eaa41a2048ae120efa582c4e112b95df2db414b7809411f4248c033f7a
MD5 7cf28bb40b130aa15d845a8ea19b33d9
BLAKE2b-256 85b872d19d4694e5bb64a39ece5024468fe9f0a0b4cb72f7b53f03526592fb2d

See more details on using hashes here.

Provenance

The following attestation bundles were made for coros_training_mcp-0.2.0-py3-none-any.whl:

Publisher: release.yml on dholliday3/coros-training-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page