Strava MCP

Strava MCP — Strava MCP for AI agents

Give your AI agent your Strava activities, streams, segments and routes — locally.
Local-first MCP server — tokens never leave your machine.

npm version npm downloads License MIT Site

GitHub stars Built for MCP Hermes one-command setup Strava

> ⚡ **One-command install** with [Delx Wellness for Hermes](https://github.com/davidmosiah/delx-wellness-hermes): > `npx -y delx-wellness-hermes setup` — preconfigures this connector and the other 8 in a dedicated Hermes profile. > > Or wire it standalone into Claude Desktop / Cursor / ChatGPT Desktop — see the install section below. --- ## HTTP (v2 stateless) Default is **stdio**. Optional Streamable HTTP — no session id, JSON responses, loopback only: ```bash npx -y strava-mcp-unofficial --http # GET http://127.0.0.1:3000/health # POST http://127.0.0.1:3000/mcp (sessionless) ``` Env: `STRAVA_MCP_HOST`, `STRAVA_MCP_PORT`, `STRAVA_MCP_TRANSPORT=http`. **Local-first MCP server that connects AI agents to your Strava activities, routes, streams and training context.** > **Unofficial project.** Not affiliated with, endorsed by or supported by Strava, Inc. Strava is a trademark of its respective owner. Use this only with your own Strava account and in line with Strava's API agreement. Built by [David Mosiah](https://github.com/davidmosiah) for people who use Claude, Cursor, Hermes, OpenClaw or other MCP-compatible agents to think about training, endurance and performance — without copy-pasting numbers from Strava. Part of [Delx Wellness](https://github.com/davidmosiah/delx-wellness), a registry of local-first wellness MCP connectors. > If this connector helps your agent workflow, please star the repo. Stars make the project easier for other AI builders to discover and help Delx keep shipping local-first wellness infrastructure. ## Why this exists Strava holds the long memory of your training — every ride, run, swim, segment, route and stream. But it lives behind an OAuth API with strict rate limits (200 req/15min, 2k/day per app) and GPS data that's privacy-sensitive by default. This package does the OAuth dance locally, throttles under Strava's per-app limits, redacts GPS lat/lng unless you explicitly opt in, and exposes Strava through the Model Context Protocol. Any MCP-compatible agent gets your training context with one config snippet. Tokens never leave your machine. ## Quickstart From zero to your first agent call in about a minute. You only need a Strava app ([create one here](https://www.strava.com/settings/api)) with redirect URI `http://127.0.0.1:3000/callback`. **1. Paste your app's client id + secret** (interactive, stored at `~/.strava-mcp/config.json` with `0600`): ```bash npx -y strava-mcp-unofficial setup ``` **2. Authorize Strava.** `auth` opens your browser; `--no-open` prints the URL so you can paste it yourself (handy on a headless box). Tokens are saved locally — the command never prints them: ```console $ npx -y strava-mcp-unofficial auth --no-open Strava MCP · Authorization Open this URL manually: https://www.strava.com/oauth/authorize?client_id=12345&redirect_uri=http%3A%2F%2F127.0.0.1%3A3000%2Fcallback&response_type=code&approval_prompt=auto&scope=read%2Cactivity%3Aread_all%2Cprofile%3Aread_all&state=aa38f29b Steps 1. Approve access in the browser tab that opens. 2. Strava will redirect to the local callback. 3. Tokens are saved locally; this command never prints them. Waiting for callback... ``` **3. Verify you're ready** — `doctor` confirms scopes and setup without calling Strava: ```console $ npx -y strava-mcp-unofficial doctor Strava MCP · Doctor Status: READY ✓ Checks ✓ Node.js >=20 ✓ Env vars ✓ Local config ✓ Automatic auth redirect ✓ Token file ✓ Token permissions ✓ Refresh token ✓ OAuth scopes · Privacy mode · Cache Next steps 1. Ready. Add this MCP server to your agent and start with strava_daily_summary. ``` If `OAuth scopes` shows a `✗`, re-run `auth` and approve `activity:read_all profile:read_all read`. **4. Make a first call — no live account required.** Ask your agent to run `strava_demo`. It returns realistic, synthetic payloads (tagged `is_demo: true`) so you can wire prompts before connecting real data: ```text > Call strava_demo and summarize my week. # Strava Demo - **is_demo**: true - **recent_sessions**: 3 - **load_classification**: moderate - **primary_signal**: Recent Strava load is manageable; use intent and consistency as the main lever. - **recommended_handoff**: exercise_catalog_recommend_session ``` Call it with `response_format: "json"` for the full shapes. The `sample` block mirrors `strava_daily_summary`, `strava_training_context` and `strava_list_activities` key-for-key — `npm run test:demo-contract` runs the real builders over `fixtures/strava-activities.mjs` and fails the build if the demo ever invents a field or omits one. Swap `strava_demo` for the real tool and the same shape arrives filled with your Strava data. **5. Wire it into your MCP client:** ```json { "mcpServers": { "strava": { "command": "npx", "args": ["-y", "strava-mcp-unofficial"] } } } ``` For Claude Desktop, run `setup --client claude` and the snippet is written for you. For Hermes, see [Hermes / remote setup](#hermes--remote-setup) below. ## Try it with your agent Three things to ask first: ```text Use strava_connection_status to check setup, then run strava_daily_summary. Tell me what my training context looks like in 5 lines. ``` ```text Call strava_weekly_summary with response_format=json. Find my biggest load/intensity bottleneck and give me a next-week endurance plan. ``` ```text Use the strava_activity_stream_investigator prompt for activity_id=. Don't expose GPS unless I explicitly ask for it. ``` ## Data availability This package uses the official Strava API v3. When this README says `raw`, it means the upstream Strava JSON for a supported endpoint — not continuous device telemetry. | Data | Available | Notes | |---|:---:|---| | Activities (runs, rides, swims, walks, workouts) | ✓ | All recorded activities | | Activity details + zones + splits | ✓ | HR, power, cadence, elevation, gear | | Activity streams (HR / cadence / watts / altitude) | ✓ | Per-second samples for the activity | | GPS lat/lng streams | opt-in | Hidden by default; requires `include_gps=true` or `raw` mode | | Athlete profile + zones + aggregate stats | ✓ | Authenticated athlete | | Routes + clubs + gear | ✓ | Route geometry redacted in summary/structured modes | | Live device telemetry / continuous HR | — | Not exposed by Strava's public API | ## Tools **Start with these:** - `strava_connection_status` — verify local setup, scopes and readiness before calling Strava - `strava_data_inventory` — inventory supported data domains, scopes, privacy modes and recommended first calls without calling Strava APIs. - `strava_daily_summary` — latest activity, weekly load and intensity context for today - `strava_weekly_summary` — scorecard, comparison vs prior week, next-week training plan **Auth & diagnostics** - `strava_capabilities`, `strava_agent_manifest`, `strava_privacy_audit`, `strava_cache_status` - `strava_get_auth_url`, `strava_exchange_code`, `strava_revoke_access` **Athlete & training** - `strava_get_athlete`, `strava_get_zones`, `strava_get_athlete_stats` **Activities & streams** - `strava_list_activities`, `strava_get_activity`, `strava_get_activity_zones` - `strava_activity_series`, `strava_get_activity_streams` — GPS lat/lng requires `include_gps=true` or `raw` mode **Routes & context** - `strava_list_routes`, `strava_get_route`, `strava_list_clubs`, `strava_get_gear` ## Prompts - `strava_daily_training_director` — practical daily training brief - `strava_weekly_endurance_review` — week comparison + next-week endurance plan - `strava_activity_stream_investigator` — investigate one activity using streams (GPS-aware) Each accepts `timezone` (IANA, default `UTC`). ## Resources - `strava://capabilities`, `strava://agent-manifest` - `strava://athlete` - `strava://latest/activity` - `strava://summary/daily`, `strava://summary/weekly` ## Privacy & security - OAuth tokens are stored in `~/.strava-mcp/tokens.json` with `0600` permissions and are never returned by tools. - Write/upload scopes are **not** requested by default — read-only by design. - GPS lat/lng and route geometry are recursively removed in `summary` and `structured` modes, and only included with explicit `include_gps=true` for stream calls or `raw` mode. - Structured mode otherwise preserves complete upstream physiological records and future Strava fields. - Activity `after` / `before` filters retain instant semantics when converted from timezone-aware ISO date-times to Strava epoch seconds; invalid ranges fail before HTTP. - Route geometry is also redacted unless raw mode is explicitly requested. - The MCP client never sees access or refresh tokens. - This is **not medical advice**. The server exposes user-authorized data for personal AI workflows, not diagnosis or training prescription. ## Configuration `setup` writes most of these into `~/.strava-mcp/config.json` (`0600`). Manual env override is supported: ```bash STRAVA_CLIENT_ID=… STRAVA_CLIENT_SECRET=… STRAVA_REDIRECT_URI=http://127.0.0.1:3000/callback # Optional STRAVA_SCOPES="read activity:read_all profile:read_all" STRAVA_PRIVACY_MODE=structured # summary | structured | raw STRAVA_CACHE=sqlite # optional read-through cache ``` ## Hermes / remote setup ```bash npx -y strava-mcp-unofficial setup --client hermes --no-auth npx -y strava-mcp-unofficial auth # run locally if browser auth is needed npx -y strava-mcp-unofficial doctor --client hermes hermes mcp test strava ``` Hermes commonly exposes Strava tools with a prefix: - `mcp_strava_strava_agent_manifest` - `mcp_strava_strava_connection_status` - `mcp_strava_strava_daily_summary` - `mcp_strava_strava_weekly_summary` - `mcp_strava_strava_get_activity_streams` After Hermes config changes, use `/reload-mcp` or `hermes mcp test strava`. Don't restart the gateway for normal data access. If browser OAuth has to happen on a different machine than Hermes, run `auth` locally and copy `~/.strava-mcp/tokens.json` to the server with `chmod 600`. The token must include `activity:read_all profile:read_all read` for activity history and streams. ## Requirements - Node.js 20+ - A Strava app with redirect URI `http://127.0.0.1:3000/callback` Why these scopes: - `read` — public profile, routes and public Strava resources - `activity:read_all` — your activities, including private activities visible to your app - `profile:read_all` — fuller authenticated athlete profile fields No write scope is requested by default. ## Development ```bash git clone https://github.com/davidmosiah/strava-mcp.git cd strava-mcp npm install npm test npm run build ``` Test with MCP Inspector: ```bash npx @modelcontextprotocol/inspector node dist/index.js ``` ## Links - npm: - Docs site: - Legacy docs: - GitHub Pages mirror: - Delx Wellness registry: - Connector quality standard: - Strava API docs: - Strava auth docs: ## See also The full [Delx Wellness](https://wellness.delx.ai) connector library: | Provider | Package | Repo | |---|---|---| | WHOOP | [`whoop-mcp-unofficial`](https://www.npmjs.com/package/whoop-mcp-unofficial) | [whoop-mcp](https://github.com/davidmosiah/whoop-mcp) | | Oura | [`oura-mcp-unofficial`](https://www.npmjs.com/package/oura-mcp-unofficial) | [ouramcp](https://github.com/davidmosiah/ouramcp) | | Garmin | [`garmin-mcp-unofficial`](https://www.npmjs.com/package/garmin-mcp-unofficial) | [garminmcp](https://github.com/davidmosiah/garminmcp) | | Strava | [`strava-mcp-unofficial`](https://www.npmjs.com/package/strava-mcp-unofficial) | [strava-mcp](https://github.com/davidmosiah/strava-mcp) | | Fitbit | [`fitbit-mcp-unofficial`](https://www.npmjs.com/package/fitbit-mcp-unofficial) | [fitbitmcp](https://github.com/davidmosiah/fitbitmcp) | | Withings | [`withings-mcp-unofficial`](https://www.npmjs.com/package/withings-mcp-unofficial) | [withingsmcp](https://github.com/davidmosiah/withingsmcp) | | Apple Health | [`apple-health-mcp-unofficial`](https://www.npmjs.com/package/apple-health-mcp-unofficial) | [apple-health-mcp](https://github.com/davidmosiah/apple-health-mcp) | | Polar | [`polar-mcp-unofficial`](https://www.npmjs.com/package/polar-mcp-unofficial) | [polarmcp](https://github.com/davidmosiah/polarmcp) | | Nourish (nutrition) | [`wellness-nourish`](https://www.npmjs.com/package/wellness-nourish) | [wellness-nourish](https://github.com/davidmosiah/wellness-nourish) | **One-command setup for Hermes** — preconfigures every connector above plus wellness skills + onboarding: [`delx-wellness-hermes`](https://github.com/davidmosiah/delx-wellness-hermes). ## 📧 Contact & Support - 📨 **support@delx.ai** — general questions, integration help, partnerships - 🐛 **Bug reports / feature requests** — [GitHub Issues](https://github.com/davidmosiah/strava-mcp/issues) - 🐦 **Updates** — [@delx369](https://x.com/delx369) on X - 🌐 **Site** — [wellness.delx.ai](https://wellness.delx.ai) ## License MIT — see [LICENSE](LICENSE). ## Disclaimer This software is provided as-is. It is not a medical device, does not provide medical advice, and should not be used for diagnosis, treatment or training prescription. Always consult qualified professionals for medical or training concerns. ## Skill or MCP Same package, two doors. MCP registers tools on stdio/HTTP. The [skill](skill/SKILL.md) can drive the **same** tools through the CLI when the client has no MCP: ```bash npx -y strava-mcp-unofficial call strava_connection_status --json '{}' ``` Copy `skill/SKILL.md` into your agent skills dir.