Content
# strava-mcp-server
An MCP (Model Context Protocol) server that connects AI agents to the [Strava API v3](https://developers.strava.com/docs/reference/). Built with TypeScript, communicates over stdio.
This lets any MCP-compatible client (Claude Desktop, OpenCode, etc.) fetch your Strava activities, stream data, routes, stats, and more through natural language.
## Tools
| Tool | Description |
|------|-------------|
| `list_activities` | List recent activities with distance, time, elevation, speed, and heart rate |
| `get_activity` | Full activity detail including splits, laps, segment efforts, gear, and calories |
| `get_activity_streams` | Raw time-series data (heart rate, altitude, cadence, power, speed, GPS) |
| `get_activity_zones` | Heart rate and power zone distribution |
| `get_route` | Decoded GPS route as coordinate array or GeoJSON |
| `get_athlete` | Authenticated athlete's profile |
| `get_athlete_stats` | All-time and recent ride/run/swim totals |
## Prerequisites
- Node.js 20+
- A Strava API application ([create one here](https://www.strava.com/settings/api))
- An access token (and optionally refresh token credentials)
### Getting Strava tokens
1. Create an app at https://www.strava.com/settings/api
2. Note your **Client ID** and **Client Secret**
3. Authorize your app to get an access token and refresh token. The quickest way:
```
https://www.strava.com/oauth/authorize?client_id=YOUR_CLIENT_ID&response_type=code&redirect_uri=http://localhost&scope=read_all,activity:read_all,profile:read_all
```
4. After authorizing, grab the `code` parameter from the redirect URL and exchange it:
```bash
curl -X POST https://www.strava.com/oauth/token \
-d client_id=YOUR_CLIENT_ID \
-d client_secret=YOUR_CLIENT_SECRET \
-d code=AUTHORIZATION_CODE \
-d grant_type=authorization_code
```
This returns `access_token`, `refresh_token`, and `expires_at`.
## Setup
```bash
git clone https://github.com/JohanBellander/StravaMCP.git
cd StravaMCP
npm install
npm run build
```
## Environment variables
| Variable | Required | Description |
|----------|----------|-------------|
| `STRAVA_ACCESS_TOKEN` | Yes* | Bearer token for Strava API requests |
| `STRAVA_CLIENT_ID` | No | OAuth2 client ID (enables auto-refresh) |
| `STRAVA_CLIENT_SECRET` | No | OAuth2 client secret (enables auto-refresh) |
| `STRAVA_REFRESH_TOKEN` | No | OAuth2 refresh token (enables auto-refresh) |
\*If you provide all three OAuth credentials (`CLIENT_ID`, `CLIENT_SECRET`, `REFRESH_TOKEN`), the access token will be refreshed automatically when it expires. You can omit `STRAVA_ACCESS_TOKEN` entirely in that case.
## Usage
### With OpenCode
Add to your `opencode.json`:
```json
{
"mcp": {
"strava": {
"type": "local",
"command": ["node", "/path/to/StravaMCP/dist/index.js"],
"environment": {
"STRAVA_ACCESS_TOKEN": "{env:STRAVA_ACCESS_TOKEN}",
"STRAVA_CLIENT_ID": "{env:STRAVA_CLIENT_ID}",
"STRAVA_CLIENT_SECRET": "{env:STRAVA_CLIENT_SECRET}",
"STRAVA_REFRESH_TOKEN": "{env:STRAVA_REFRESH_TOKEN}"
}
}
}
}
```
### With Claude Desktop
Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
```json
{
"mcpServers": {
"strava": {
"command": "node",
"args": ["/path/to/StravaMCP/dist/index.js"],
"env": {
"STRAVA_ACCESS_TOKEN": "your-token-here",
"STRAVA_CLIENT_ID": "your-client-id",
"STRAVA_CLIENT_SECRET": "your-client-secret",
"STRAVA_REFRESH_TOKEN": "your-refresh-token"
}
}
}
}
```
### Direct
```bash
export STRAVA_ACCESS_TOKEN=your-token-here
node dist/index.js
```
## Tool details
### list_activities
List the authenticated athlete's activities.
**Parameters:**
- `page` (number, default: 1) — Page number
- `per_page` (number, default: 30, max: 200) — Activities per page
- `after` (number) — Epoch timestamp, only activities after this time
- `before` (number) — Epoch timestamp, only activities before this time
**Returns:** Activity summaries with id, name, type, sport_type, start_date, distance (km), moving_time (h:m:s), elapsed_time, elevation gain (m), average/max speed (km/h), average/max heart rate, suffer score.
### get_activity
Get full details for a single activity.
**Parameters:**
- `id` (number, required) — Activity ID
**Returns:** Everything from `list_activities` plus splits (metric), laps, segment efforts (name, time, distance, PR rank), gear, calories, description, perceived exertion.
### get_activity_streams
Get raw time-series stream data for an activity.
**Parameters:**
- `id` (number, required) — Activity ID
- `stream_types` (string array, default: `["time", "distance", "heartrate", "altitude", "cadence", "watts", "velocity_smooth", "latlng"]`)
**Returns:** Stream data keyed by type, each with a data array and resolution.
### get_activity_zones
Get heart rate and power zone distribution.
**Parameters:**
- `id` (number, required) — Activity ID
**Returns:** Zone distributions with time-in-zone breakdowns.
### get_route
Get the GPS route for an activity as decoded coordinates.
**Parameters:**
- `id` (number, required) — Activity ID
- `format` (`"summary"` or `"geojson"`, default: `"summary"`)
**Returns:**
- `summary`: Array of `{lat, lng}` coordinates decoded from the summary polyline
- `geojson`: GeoJSON FeatureCollection with a LineString geometry (full-resolution polyline)
### get_athlete
Get the authenticated athlete's profile. No parameters.
**Returns:** id, name, city, state, country, sex, weight, FTP, date_preference, measurement_preference.
### get_athlete_stats
Get all-time and recent activity statistics.
**Parameters:**
- `id` (number, required) — The athlete's Strava ID
**Returns:** Recent, year-to-date, and all-time totals for rides, runs, and swims (count, distance, moving time, elevation gain). Also includes biggest ride distance and biggest climb elevation.
## Rate limits
Strava enforces **100 requests per 15 minutes** and **1,000 requests per day**. The server tracks usage via the `X-RateLimit-Limit` and `X-RateLimit-Usage` response headers and includes warnings in tool responses when usage exceeds 80% of either limit. If you hit the limit, you'll get a clear error message with the current usage and limits.
## Project structure
```
src/
index.ts # MCP server setup, tool registration, stdio transport
strava-client.ts # HTTP client, OAuth2 token refresh, rate limit tracking
tools/
list-activities.ts
get-activity.ts
get-activity-streams.ts
get-activity-zones.ts
get-route.ts
get-athlete.ts
get-athlete-stats.ts
utils/
polyline.ts # Google encoded polyline decoder
formatting.ts # Unit conversions and time formatting
```
## Development
```bash
npm run dev # Watch mode — recompiles on changes
npm run build # One-time build
npm start # Run the server
```
## License
MIT