strava-mcp
by zurfyx
README.md
# strava-mcp
MCP server for the [Strava API v3](https://developers.strava.com/docs/reference/). Exposes Strava data — activities, segments, routes, clubs, gear, and streams — as tools that any MCP client (Claude Desktop, Claude Code, etc.) can call.
**Read-only by default.** Pass `--write` to enable mutating tools (create/update activities, star segments).
OpenClaw-specific onboarding and sidecar setup: [`OPENCLAW.md`](OPENCLAW.md).
## Getting started
### 1. Create a Strava API application
Go to [https://www.strava.com/settings/api](https://www.strava.com/settings/api) and create an application. Note your **Client ID** and **Client Secret**.
### 2. Set up credentials
```bash
cp .env.example .env
```
Add your `STRAVA_CLIENT_ID` and `STRAVA_CLIENT_SECRET` to `.env`.
### 3. Build
```bash
pnpm install
pnpm build
```
### 4. Authenticate
```bash
pnpm auth
```
This opens your browser to authorize with Strava. After you approve, it automatically exchanges the code for tokens and saves `STRAVA_ACCESS_TOKEN` and `STRAVA_REFRESH_TOKEN` to your `.env` file. All scopes are requested (`read_all`, `profile:read_all`, `activity:read_all`, `activity:write`).
### 5. Test locally
```bash
pnpm inspect
```
Opens the MCP Inspector web UI where you can browse tools and call them interactively. Credentials are loaded from `.env` automatically.
You can also run the server directly:
```bash
pnpm start
```
### 6. Configure your MCP client
#### Claude Desktop
Add to your `claude_desktop_config.json`:
```json
{
"mcpServers": {
"strava": {
"command": "node",
"args": ["/absolute/path/to/strava-mcp/build/index.js"],
"env": {
"STRAVA_ACCESS_TOKEN": "your-access-token"
}
}
}
}
```
To enable write tools (create/update activities, star segments), add `"--write"` to `args`:
```json
{
"mcpServers": {
"strava": {
"command": "node",
"args": ["/absolute/path/to/strava-mcp/build/index.js", "--write"],
"env": {
"STRAVA_ACCESS_TOKEN": "your-access-token"
}
}
}
}
```
#### Claude Code
```bash
claude mcp add strava node /absolute/path/to/strava-mcp/build/index.js
```
#### Automatic token refresh
Strava access tokens expire. To enable automatic refresh, provide all four env vars:
```json
{
"env": {
"STRAVA_ACCESS_TOKEN": "your-access-token",
"STRAVA_REFRESH_TOKEN": "your-refresh-token",
"STRAVA_CLIENT_ID": "your-client-id",
"STRAVA_CLIENT_SECRET": "your-client-secret"
}
}
```
When a request returns 401 and refresh credentials are available, the server will automatically request a new access token from Strava's OAuth endpoint and retry the request.
## Environment variables
| Variable | Required | Description |
|---|---|---|
| `STRAVA_ACCESS_TOKEN` | Yes | Strava OAuth2 bearer token |
| `STRAVA_REFRESH_TOKEN` | No | Enables automatic token refresh on 401 |
| `STRAVA_CLIENT_ID` | No | Required alongside refresh token |
| `STRAVA_CLIENT_SECRET` | No | Required alongside refresh token |
## Tools
### Read tools (always available)
#### Athlete
| Tool | Description |
|---|---|
| `get_athlete` | Get the authenticated athlete's profile |
| `get_athlete_stats` | Get athlete stats (totals, recent, YTD, all-time) |
| `get_athlete_zones` | Get heart rate and power zones |
#### Activities
| Tool | Description |
|---|---|
| `list_activities` | List the athlete's activities (filterable by `before`/`after` timestamps) |
| `get_activity` | Get full details of a specific activity |
| `list_activity_comments` | List comments on an activity |
| `list_activity_kudoers` | List athletes who kudoed an activity |
| `list_activity_laps` | List laps of an activity |
| `get_activity_zones` | Get heart rate/power zones for an activity |
| `get_activity_streams` | Get time-series data (heartrate, power, cadence, GPS, etc.) |
#### Segments
| Tool | Description |
|---|---|
| `get_segment` | Get details of a specific segment |
| `explore_segments` | Find popular segments within a bounding box |
| `list_starred_segments` | List the athlete's starred segments |
| `get_segment_effort` | Get details of a segment effort |
| `list_segment_efforts` | List efforts on a segment, optionally filtered by date |
| `get_segment_streams` | Get data streams for a segment |
| `get_segment_effort_streams` | Get data streams for a segment effort |
#### Routes
| Tool | Description |
|---|---|
| `get_route` | Get details of a specific route |
| `list_athlete_routes` | List routes created by an athlete |
| `get_route_streams` | Get data streams for a route |
#### Clubs
| Tool | Description |
|---|---|
| `get_club` | Get details of a specific club |
| `list_athlete_clubs` | List the athlete's clubs |
| `list_club_members` | List members of a club |
| `list_club_activities` | List recent activities in a club |
#### Gear
| Tool | Description |
|---|---|
| `get_gear` | Get details of a piece of equipment |
### Write tools (requires `--write` flag)
| Tool | Description |
|---|---|
| `create_activity` | Create a manual activity |
| `update_activity` | Update an existing activity (name, description, sport type, gear, etc.) |
| `star_segment` | Star or unstar a segment |
## Documentation
- [Use cases](docs/use-cases.md) — example prompts and what you can do with the server
- [Architecture](docs/architecture.md) — file structure, data flow, and component responsibilities
- [Design decisions](docs/decisions.md) — rationale behind the read/write split, token refresh strategy, thin client approach, and other trade-offs
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues