strava-mcp
Provides read-only access to a Strava athlete's activities: fetches the latest activity, lists and filters activities by date, sport, and limit, and retrieves individual activities with laps, splits, heart rate, workout structure, and power-vs-calorie checks for rides.
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
strava-mcp
A remote MCP server on Cloudflare Workers that lets an AI assistant read your Strava activities on demand. Tell it "I just ran" and it pulls the run: laps, splits, heart rate, and a one-line read of the workout's structure. For rides, it checks Strava's calorie number against the power data.
Built for Grok Bot; works with any MCP client that connects to remote servers with OAuth. You deploy your own copy, with your own Strava API app, and only your Strava account can connect to it.
2 mi WU · 4x3 mi (6:36, 6:31, 6:23, 6:16) w/ 1 mi @ 7:50-8:05 · 3.1 mi CD
2.5 mi WU · 3x1 mi (6:40, 6:32, 6:26) w/ 3:00 rec · 4x200m (41s, 40s, 39s, 38s) w/ 2:00 rec · 2.2 mi CDTools
Tool | What it returns |
| Your newest activity in full detail, plus |
| Compact rows (id, date, name, sport, miles, moving time, pace or watts, HR), filtered by |
| One activity by id. Runs: summary, laps, splits, workout structure, stops. Rides: summary plus a power-vs-calories check. |
All three are read-only. Units are miles, feet and min/mi. The full output format is in docs/DESIGN.md §5.
Related MCP server: strava-mcp
How it works
One Worker is both the OAuth server for your MCP client and the MCP endpoint at
/mcp. When you connect, it shows a consent page, sends you to Strava to
approve read access, and refuses any Strava account but yours. Strava tokens
stay in a Durable Object that refreshes them under a lock, because Strava
invalidates the old refresh token when it issues a new one. The design and its
reasoning are in docs/DESIGN.md.
Deploy your own
You need a Cloudflare account (the free plan works), a Strava account, and Node 22.13 or later.
1. Work out your Worker's URL
In the Cloudflare dashboard, open Workers & Pages and find your account's
workers.dev subdomain (create one if you don't have one). Your Worker will be
at https://strava-mcp.<subdomain>.workers.dev.
2. Create a Strava API app
At https://www.strava.com/settings/api:
Authorization Callback Domain: the host only, e.g.
strava-mcp.<subdomain>.workers.dev. Nohttps://, no path.Website: your Worker URL.
Copy the Client ID and Client Secret.
Use a separate app from anything else that reads your Strava data: two services sharing one app would rotate each other's refresh tokens.
Your athlete id is the number in your profile URL,
strava.com/athletes/<id>.
3. Configure
npm installcp wrangler.jsonc wrangler.local.jsoncIn wrangler.local.jsonc (gitignored), set:
"vars": {
"ALLOWED_ATHLETE_ID": "<your athlete id>",
"PUBLIC_URL": "https://strava-mcp.<subdomain>.workers.dev"
},4. First deploy
Wrangler won't create a new Worker without its required secrets, so the first deploy passes them in a file. Log in first:
npx wrangler loginCreate the secrets file, readable only by you, with a generated remember-consent secret:
(umask 077; printf 'STRAVA_CLIENT_ID=\nSTRAVA_CLIENT_SECRET=\nCONSENT_REMEMBER_SECRET=%s\n' "$(openssl rand -hex 32)" > .deploy-secrets)Open .deploy-secrets and paste your Client ID and Client Secret after the =,
with no quotes or spaces. Then deploy and delete the file:
npx wrangler deploy -c wrangler.local.jsonc --secrets-file .deploy-secretsrm .deploy-secretsThe deploy creates the OAUTH_KV namespace and prints its id. Pin it in
wrangler.local.jsonc, so later deploys reuse it:
"kv_namespaces": [{ "binding": "OAUTH_KV", "id": "<the id it printed>" }],From now on, deploy with:
npm run deploy:local5. Check it
curl https://strava-mcp.<subdomain>.workers.dev/healthIt should show {"ok":true,"athleteConfigured":true}.
6. Connect
Grok Bot: add a connector with URL https://strava-mcp.<subdomain>.workers.dev/mcp
and OAuth auth, then authorize. Any other MCP client works the same way.
On the consent page, check the app name and the host access goes to, then click Continue to Strava.
On Strava, keep "View data about your private activities" ticked.
To try it first in the MCP Inspector (Streamable HTTP transport, same URL):
npx @modelcontextprotocol/inspectorSuggested instructions for your assistant:
You have a Strava connector. When I say I ran, rode or worked out, call
get_latest_activitybefore responding. Ifended_minutes_agois large, tell me it may not have synced yet instead of reviewing an older activity. For a past activity, find it withlist_activities, then callget_activity. For runs, lead with thestructureline and use the laps to back it up. For rides, mentionenergy.checkif the calories look off.
Troubleshooting
What you see | What it means |
Strava says invalid redirect_uri | The app's callback domain doesn't exactly match your Worker's host. |
"Not allowed" after Strava | You signed in to a Strava account other than |
"Strava permission missing" | The private-activities box was unticked. Connect again with it ticked. |
"Can't continue" on the consent page | The page expired (10 minutes) or was reloaded. Start again from your client. |
The assistant says reconnect the Strava connector | The stored Strava tokens are missing or were revoked. Reconnect. |
|
|
Rotating secrets
To replace a secret, for example after regenerating the Client Secret on Strava:
npx wrangler secret put STRAVA_CLIENT_SECRET -c wrangler.local.jsoncIt takes effect immediately. If you change CONSENT_REMEMBER_SECRET, remembered
consents are forgotten and the consent page shows again. If Strava access breaks,
reconnecting from your client replaces the stored Strava tokens.
Develop
cp .dev.vars.example .dev.varsnpm run devnpm testnpm run typecheckAfter editing wrangler.jsonc, regenerate worker-configuration.d.ts:
npm run cf-typegenTests run in plain Node with a stubbed cloudflare:workers, node:sqlite in
place of the Durable Object's storage, and an in-memory KV. The test fixtures
are synthetic, built by test/fixtures/generate.mjs. For local OAuth against
real Strava, add localhost as a callback domain on your Strava app.
Limits
One athlete per deployment.
Workout structure is read from lap paces: with auto-laps, rep boundaries are only mile-accurate, and hill repeats can be mislabeled.
Read limits are Strava's: 100 requests per 15 minutes and 1,000 per day.
License
MIT. Not affiliated with or endorsed by Strava. Your use of Strava data is governed by the Strava API Agreement, which includes terms on using that data with AI models.
This server cannot be deployed
Maintenance
Related MCP Connectors
Ask your AI about your fitness: activities and data from Garmin, COROS, Strava, GPX and more
Your Garmin, Oura, WHOOP, Hevy and 35 more fitness apps, queryable from your own AI.
Your own cycling data for your AI assistant: rides, power, climbs and routes. Read-only.
Garmin data in Claude & ChatGPT via the Garmin Health API. OAuth sign-in, no password sharing.
Related MCP Servers
- AlicenseAqualityDmaintenanceEnables AI assistants to directly access and analyze Strava activity data, including runs, rides, and swims, through natural language queries.417 npmMIT
- AlicenseNot gradedqualityDmaintenanceExposes your Strava data to Claude, enabling training load analysis, activity comparison, and lap splits via conversation.7 npmMIT
- AlicenseAqualityBmaintenanceEnables AI assistants to read your Strava training data and answer plain-language questions about activities, splits, and totals across sports like running, skiing, and hiking.47 npmMIT
- AlicenseNot gradedqualityBmaintenanceEnables AI assistants such as Claude, ChatGPT and Gemini to read wearable health data — daily activity, sleep stages, heart rate, SpO2, HRV, workouts, weight and nutrition — through a self-hosted, OAuth-protected Cloudflare Worker endpoint. It also lets users log weight, body fat, water, food, exercises, mood and symptoms by voice or chat, while leaving sensor-recorded data read-only.MIT