Strava MCP Server
# Strava MCP Server
A small, **read-only** [Model Context Protocol](https://modelcontextprotocol.io)
server that gives Claude live access to your personal Strava data — activities,
splits, heart-rate/pace streams, zones, and lifetime totals — so it can coach
off your real training instead of manual exports.
Runs in Strava "single-player mode": it only ever connects **your own** account.
## What it exposes
All tools are reads. The server never creates, edits, or deletes anything on Strava.
| Tool | What it returns |
|------|-----------------|
| `get_athlete` | Your profile (name, location, weight, FTP if set) |
| `get_stats` | Lifetime, YTD, and last-4-weeks totals per sport |
| `list_activities` | Recent activities, paginated, with optional date filters |
| `get_activity` | Full detail for one activity (splits, pace, HR, power, elevation) |
| `get_activity_streams` | Time-series streams for detailed analysis |
| `get_athlete_zones` | Configured heart-rate and power zones |
## Prerequisites
- Python 3.10+
- A Strava account **with an active subscription** (required to create an API app)
## Setup
### 1. Create a Strava API application
1. Go to <https://www.strava.com/settings/api>.
2. Create an app. Set **Authorization Callback Domain** to `localhost`.
3. Note your **Client ID** and **Client Secret** (keep the secret private).
### 2. Install
```bash
git clone <your-repo-url> strava-mcp-server
cd strava-mcp-server
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
```
### 3. Configure credentials
```bash
cp .env.example .env
# edit .env: set STRAVA_CLIENT_ID and STRAVA_CLIENT_SECRET
```
### 4. Authorize (one time) to get a refresh token
```bash
python scripts/get_token.py
```
This opens your browser, catches the redirect on `localhost`, exchanges the code,
and prints a `STRAVA_REFRESH_TOKEN=...` line. Paste it into `.env`.
### 5. Register the server with Claude
**Claude Code** — copy `mcp.example.json` to `.mcp.json` in the repo root (adjust
the `python` path to your venv, e.g. `.venv/bin/python`, if needed), then start
Claude Code from this directory. Approve the server when prompted.
**Claude Desktop** — add the `strava` block from `mcp.example.json` to your
`claude_desktop_config.json` under `mcpServers`, using absolute paths:
```json
{
"mcpServers": {
"strava": {
"command": "/absolute/path/to/strava-mcp-server/.venv/bin/python",
"args": ["-m", "strava_mcp.server"],
"cwd": "/absolute/path/to/strava-mcp-server"
}
}
}
```
Restart Claude Desktop. You should see the Strava tools appear.
### 6. Sanity check
```bash
python -m strava_mcp.server # should start and wait on stdio; Ctrl-C to exit
```
## Security model
- **No secrets in git.** `.env`, `.strava_tokens.json`, and key files are
gitignored. Only `.env.example` is tracked.
- **Read-only scope.** The app requests `read`, `activity:read_all`, and
`profile:read_all` — no write scopes. The server issues only GET requests to
data endpoints.
- **Token hygiene.** Access tokens (6-hour lifetime) are refreshed automatically
from your refresh token. Strava rotates the refresh token on each exchange; the
server persists the current one to `.strava_tokens.json` (chmod `600`,
gitignored).
- **Rate limits.** Honors Strava's 200 req / 15 min and 2,000 / day limits with
automatic backoff on HTTP 429.
- **Revoking access.** Remove the app anytime at
<https://www.strava.com/settings/apps>. Delete `.strava_tokens.json` and `.env`
to purge local credentials.
## Publish to GitHub
From the repo root (secrets are already gitignored, so this is safe):
The GitHub repo already exists at <https://github.com/esowder22/strava-mcp-server>,
so just wire up the remote and push:
```bash
git init
git add .
git status # confirm .env / .strava_tokens.json are NOT listed
git commit -m "Initial commit: read-only Strava MCP server"
git branch -M main
git remote add origin git@github.com:esowder22/strava-mcp-server.git
git push -u origin main
```
Prefer HTTPS over SSH? Use
`git remote add origin https://github.com/esowder22/strava-mcp-server.git` instead.
If the GitHub repo was created with a README/license and the push is rejected,
run `git pull --rebase origin main` once, then push again. Keep the repo
**private** — even though no secrets are committed, there's no reason to make it
public.
## Compatibility note
Written for the current `mcp` 2.x SDK (where `FastMCP` is `MCPServer`), with a
fallback import so it also runs on `mcp` 1.x. No code change needed either way.
## License
MIT — see [LICENSE](LICENSE).
TDQS
Scored across 6 tools
Each tool targets a clearly distinct resource or granularity: athlete profile, aggregate stats, activity list, activity detail, time-series streams, and zones. The only close pair, get_activity and get_activity_streams, is cleanly separated by summary detail vs. raw time-series data.
All tool names follow a consistent verb_noun snake_case pattern, with five using get_ and one using list_activities. The lone list_ is still a standard retrieval verb, so the naming stays predictable and coherent.
Six tools is a well-scoped size for a read-only Strava analytics server. Each tool covers a distinct part of the activity/athlete data surface without unnecessary duplication or bloat.
For the apparent purpose of retrieving athlete and activity data, the surface is complete: profile, aggregate stats, activity listing, detailed activity, streams, and zones. There are no dead ends for common queries like 'how fast was my last ride?' or 'what are my power zones?'