I Built an MCP Server So Claude Can Read My Strava Data
22 April 2026
I run a lot. I also use Claude Code for basically everything at this point. So it was only a matter of time before I connected the two.
Last week I built an MCP server that lets Claude query my Strava data directly. It’s open source, published on npm, and anyone with a Strava account can set it up in about 60 seconds. Here’s how I built it and what I learned.
Why bother?
I already have ukrunner.com for visualising my running data. But ukrunner is a website. When I’m working in the terminal and want to know my mileage this month or check how a recent run went, I don’t want to open a browser. I want to ask Claude.
MCP servers solve exactly this problem. They give Claude structured access to external data without you having to copy-paste anything. I wrote about the general approach in an earlier post. This time I wanted to build something real, open source it, and see if other people would actually use it.
What it does
The server exposes 7 tools that Claude can call:
- get_athlete and get_stats for profile info and running totals (all-time, this year, last 4 weeks)
- get_activities for listing recent runs with pagination and date filtering
- get_activity for full detail on a single run: splits, best efforts, heart rate, gear
- get_activity_streams for time-series data (heart rate, pace, cadence, altitude over the course of a run)
- get_personal_records for your best times across standard distances
- get_starred_segments for your favourite segments with personal bests
Once it’s set up, you just ask Claude natural questions. “What’s my 5K PB?” or “How was my heart rate during last Sunday’s long run?” and it picks the right tool automatically.
The OAuth problem
The hardest part wasn’t building the tools. It was the setup experience.
Strava uses OAuth 2.0. That means anyone who wants to use the server needs a client ID, client secret, and refresh token. Getting the refresh token requires crafting a URL, authorising in the browser, catching a redirect, and exchanging an authorisation code via curl. For a developer, it’s 5 minutes of mild annoyance. For anyone else, it’s a wall.
I built an interactive setup command that handles the whole flow:
npx @michaelhutchinson/strava-mcp-server setup
It asks for your client ID and secret, spins up a local HTTP server on port 8420, opens the Strava authorisation page in your browser, catches the OAuth callback, exchanges the code for tokens, and prints the exact claude mcp add command you need to paste. The whole thing takes about 30 seconds.
This was maybe 20% of the total code but 80% of the user experience improvement. Most MCP servers I’ve seen punt on this and tell you to “follow the OAuth guide.” That’s fine for developers, but I wanted non-technical friends who run to be able to set this up.
Formatting matters more than you’d think
An early version of the server returned raw JSON from the Strava API. Claude could parse it, but the responses were verbose and the context window filled up fast. A single activity detail response from Strava is several KB of JSON.
I switched to pre-formatted plain text. Each tool returns human-readable output with the key information pulled out and formatted sensibly. Distances in km, paces in min/km, durations as “1h 23m”, dates in a readable format. This cut the token usage significantly and made Claude’s responses much cleaner.
For activity streams (heart rate, pace over time), I added automatic downsampling. A 2-hour run might have 7,000+ data points. The server samples down to ~100 points, which is enough for Claude to analyse trends without burning through context.
What I’d do differently
The personal records tool is slow. It has to fetch activities in batches and check each one for best efforts. Strava’s API doesn’t have a dedicated “get all PRs” endpoint, so you’re stuck paginating through activities. Caching would help, but I kept the server stateless to keep it simple.
I also learned that Strava’s rate limits are tighter than I expected. 200 requests per 15 minutes sounds generous until you’re fetching detailed data for 50 activities to build a PR list. Something to be aware of if you’re building on their API.
The CI/CD setup
I added a GitHub Action that handles publishing automatically. When I push a version tag:
- GitHub Actions runs the build
- Publishes to npm
- Creates a GitHub release with auto-generated notes
So the release workflow is just npm version patch && git push && git push --tags. Two commands, everything else is automated. It’s a small thing but it removes enough friction that I actually publish updates instead of letting them sit in local commits.
Try it
If you use Claude Code and you run (or cycle, or swim), give it a go:
npx @michaelhutchinson/strava-mcp-server setup
The source is on GitHub and the package is on npm. Issues and PRs welcome.