AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
MCP verified MIT Self-run

Coros Mcp

mcp-cygnusb-coros-mcp · by cygnusb

MCP server for AI assistants to read and manage Coros fitness data: sleep, HRV, daily metrics, activities, and structured workouts via the unofficial Coros API

No reviews yet
0 installs
32 views
0.0% view→install

Install

$ agentstack add mcp-cygnusb-coros-mcp

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets Used
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/mcp-cygnusb-coros-mcp)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
3mo ago

Declared compatibility

Claude CodeClaude DesktopCursorWindsurf

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Coros Mcp? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

coros-mcp

[](https://github.com/cygnusb/coros-mcp/actions/workflows/ci.yml) [](https://www.python.org/) [](LICENSE) [](https://github.com/astral-sh/ruff)

A Model Context Protocol (MCP) server that fetches sleep, HRV, and training data from the unofficial Coros API and exposes them to AI assistants like Claude.

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.

What You Can Do

Ask your AI assistant questions like:

  • "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?"
  • "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) | | list_workout_templates | List reusable workout templates saved in the library | | save_workout_template | Save a reusable cycling/intervals workout template (named steps, power targets) | | save_strength_workout_template | Save a reusable strength workout template (sets, reps, or timed exercises) | | delete_workout_template | Delete a saved workout template from the library | | list_planned_activities | List planned workouts from the Coros training calendar | | schedule_workout | Schedule a one-off cycling/intervals workout for a date (no library entry) | | schedule_strength_workout | Schedule a one-off strength workout for a date (no library entry) | | schedule_workout_template | Schedule an existing library template on a calendar day | | remove_scheduled_workout | Remove a scheduled workout from the calendar | | list_exercises | Browse the Coros exercise catalogue, especially for strength workouts | | sync_coros_data | Backfill all data into the local SQLite cache for a date range | | get_cache_status | Show coverage (record counts and date ranges) of the local cache |


Setup

Option A: Auto-Setup with Claude Code

If you have Claude Code, paste this prompt:

Set up the Coros MCP server from https://github.com/cygnusb/coros-mcp — clone it, create a venv, install it with pip install -e ., add it to my MCP config, then tell me to run 'coros-mcp auth' in my terminal to authenticate.

Claude will handle the installation and guide you through configuration.

Option B: Manual Setup

Step 1: Install
git clone https://github.com/cygnusb/coros-mcp.git
cd coros-mcp
python3 -m venv .venv
source .venv/bin/activate  # Windows: .venv\Scripts\activate
pip install -e .

Or with uv:

uv pip install -e .
Step 2: Add to Claude Code
claude mcp add coros -- /path/to/coros-mcp/.venv/bin/coros-mcp serve

To limit the MCP to a specific project only (recommended):

cd /path/to/your/project
claude mcp add --scope project coros -- /path/to/coros-mcp/.venv/bin/coros-mcp serve

Or add to Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json):

{
  "mcpServers": {
    "coros": {
      "command": "/path/to/coros-mcp/.venv/bin/coros-mcp",
      "args": ["serve"]
    }
  }
}
Step 3: Authenticate

Option A — .env file (recommended for project-scoped setups):

Create a .env file in your project directory:

COROS_EMAIL=you@example.com
COROS_PASSWORD=yourpassword
COROS_REGION=eu

The server authenticates automatically on the first request and re-authenticates transparently whenever the token expires. No manual auth step needed.

Option B — Manual authentication:

Run the following command in your terminal — outside of any Claude session:

coros-mcp auth

You will be prompted for your email, password, and region (eu, us, or asia). This stores both the Training Hub web token and the mobile API token (used for sleep data). Your credentials are sent directly to Coros and the tokens are stored securely in your system keyring (or an encrypted local file as fallback). You only need to do this once — the tokens persist across restarts.

> Note: The mobile login (apieu.coros.com) will log you out of the Coros mobile app on your phone. If you want to avoid this, use coros-mcp auth-web instead — it stores only the web token, and the mobile token will be obtained automatically when you first request sleep data.

Other auth commands:

coros-mcp serve         # Start the MCP server (used by Claude Code / Claude Desktop)
coros-mcp auth-web      # Web API only — skips mobile login (sleep data obtained lazily)
coros-mcp auth-mobile   # Mobile API only (sleep data)
coros-mcp auth-status   # Check if authenticated
coros-mcp auth-clear    # Remove stored tokens

Tool Reference

authenticate_coros

Log in with your Coros credentials. The auth token is stored securely in your system keyring (or an encrypted file as fallback).

{ "email": "you@example.com", "password": "yourpassword", "region": "eu" }

Returns: authenticated, user_id, region, message

authenticate_coros_mobile

Authenticate with the Coros mobile API only. This is mainly useful if you need to restore sleep-data access without redoing full web authentication.

{ "email": "you@example.com", "password": "yourpassword", "region": "eu" }

Returns: authenticated, user_id, region, message

check_coros_auth

Check whether valid web and mobile tokens are stored and how long the web token remains valid.

{}

Returns: authenticated, user_id, region, expires_in_hours, mobile_authenticated, mobile_token_status

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 (in cal — divide by 1000 for kcal), 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.

list_workout_templates

List reusable workout templates saved in the Coros library.

{}

Returns: workouts (list), count

Each entry includes: id, name, sport_type, sport_name, estimated_time_seconds, exercise_count, exercises (list of steps with name, duration_seconds, intensity_low, intensity_high, sets)

save_workout_template

Save a reusable cycling/intervals workout template to the Coros library. The template appears in the Coros app and can be synced to the watch. Steps can be plain steps or repeat groups for intervals.

> ⚠️ This persists to the library indefinitely. For a one-off workout for a specific date, use [schedule_workout](#schedule_workout) instead — it builds the workout inline and leaves no library entry.

Plain steps:

{
  "name": "Sweet Spot 90min",
  "sport_type": 2,
  "steps": [
    {"name": "15:00 Warmup",     "duration_minutes": 15, "intensity_low": 148, "intensity_high": 192},
    {"name": "20:00 Sweet Spot", "duration_minutes": 20, "intensity_low": 260, "intensity_high": 275},
    {"name": "5:00 Rest",        "duration_minutes":  5, "intensity_low": 100, "intensity_high": 150},
    {"name": "20:00 Sweet Spot", "duration_minutes": 20, "intensity_low": 260, "intensity_high": 275},
    {"name": "30:00 Cooldown",   "duration_minutes": 30, "intensity_low": 100, "intensity_high": 192}
  ]
}

With repeat groups (intervals):

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

sport_type: 2 = Indoor Cycling (default), 200 = Road Bike

Returns: workout_id, name, total_minutes, steps_count, message

save_strength_workout_template

Save a reusable strength workout template to the Coros library. Exercises must come from the Coros exercise catalogue.

> ⚠️ This persists to the library indefinitely. For a one-off workout for a specific date, use [schedule_strength_workout](#schedulestrengthworkout) instead — it builds the workout inline and leaves no library entry.

{
  "name": "Leg Circuit",
  "sets": 3,
  "exercises": [
    {
      "origin_id": "54",
      "name": "T1061",
      "overview": "sid_strength_squats",
      "target_type": 3,
      "target_value": 12,
      "rest_seconds": 45,
      "sets": 3,
      "weight_kg": 80
    },
    {
      "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

sets (per exercise, optional): consecutive sets of that exercise before moving on. Use this instead of duplicating the exercise entry. Defaults to 1.

sets (top-level): full-circuit repetitions — repeats the entire exercise list. Defaults to 1.

Weight fields (per exercise, optional):

| Field | Description | |-------|-------------| | weight_kg | Prescribed weight in kg (e.g. 80) | | weight_lbs | Prescribed weight in pounds (e.g. 176.4) — mutually exclusive with weight_kg |

  • Omitting both fields renders the exercise as Bodyweight in the Coros app.
  • Setting weight_kg: 0 renders as "0.00 kg" — distinct from Bodyweight.
  • For dumbbell exercises, the weight is per hand by convention (the app shows a single value).
  • kg and lbs can be mixed across exercises within the same workout.

Returns: workout_id, name, sets, exercise_count

delete_workout_template

Delete a saved workout template from the Coros library.

{ "workout_id": "476023839273435149" }

The workout_id comes from list_workout_templates.

Returns: deleted, workout_id, message

list_planned_activities

List planned activities from the Coros training calendar.

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

Returns: schedule (dict with entities and programs sub-lists), count (number of scheduled entities), date_range

schedule_workout

Schedule a one-off cycling/intervals workout for a specific date. Builds the workout inline and posts straight to the calendar — does not create a library entry. This is the common case.

Same parameters as [save_workout_template](#saveworkouttemplate), plus happen_day (YYYYMMDD) and an optional sort_no (order within the day).

{
  "name": "Z2 Recovery",
  "happen_day": "20260312",
  "sport_type": 2,
  "steps": [
    {"name": "Easy spin", "duration_minutes": 60, "intensity_low": 150, "intensity_high": 200}
  ]
}

Returns: scheduled, name, happen_day, total_minutes, steps_count, response

schedule_strength_workout

Schedule a one-off strength workout for a specific date. Builds the workout inline and posts straight to the calendar — does not create a library entry.

Same parameters as [save_strength_workout_template](#savestrengthworkout_template), plus happen_day.

{
  "name": "Push Day",
  "happen_day": "20260312",
  "sets": 1,
  "exercises": [ ... ]
}

Returns: scheduled, name, happen_day, sets, exercise_count, response

schedule_workout_template

Schedule an existing library template on a calendar day.

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

The workout_id comes from list_workout_templates. For one-off workouts that don't need a library entry, use [schedule_workout](#scheduleworkout) or [schedule_strength_workout](#schedulestrength_workout) instead.

Returns: scheduled, workout_id, happen_day, response

remove_scheduled_workout

Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.