Skip to main content
Glama
README.md
# Strava MCP Server

A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that provides full access to the Strava API v3. Connect Claude (or any MCP client) to your Strava fitness data — activities, athletes, clubs, segments, routes, streams, gear, and more.

## Features

- **30+ tools** covering all major Strava API v3 endpoints
- **Auto token refresh** — OAuth2 tokens are refreshed and cached automatically
- **Formatted responses** — distances in km, times in minutes, speeds in km/h
- **Convenience tools** — weekly/monthly summaries, filter by sport type

### Available Tools

| Category | Tools |
|----------|-------|
| **Activities** | Create, get details, comments, kudos, laps, list, zones, update |
| **Athletes** | Profile, zones, stats, update weight |
| **Clubs** | List, details, members, admins, activities |
| **Gear** | Get gear details (bikes & shoes) |
| **Routes** | List, details, GPX export, TCX export |
| **Segments** | Explore, starred, details, star/unstar |
| **Segment Efforts** | List efforts, effort details |
| **Streams** | Activity, route, effort, segment time-series data |
| **Uploads** | Upload status |
| **Summaries** | Weekly summary, monthly summary, filter by sport type, filter by date |

## Prerequisites

- Python 3.12+
- [uv](https://docs.astral.sh/uv/) (Python package manager)
- A Strava account
- A Strava API application (see setup below)

## Setup

### 1. Create a Strava API Application

1. Go to [https://www.strava.com/settings/api](https://www.strava.com/settings/api)
2. Create a new application:
   - **Application Name**: anything (e.g. "MCP Server")
   - **Category**: choose any
   - **Website**: `http://localhost`
   - **Authorization Callback Domain**: `localhost`
3. Note your **Client ID** and **Client Secret**

### 2. Clone and Install

```bash
git clone https://github.com/manojanasuri16/strava-mcp-server.git
cd strava-mcp-server
uv sync
```

### 3. Configure Environment

Create a `.env` file in the project root:

```env
STRAVA_CLIENT_ID=<your_client_id>
STRAVA_CLIENT_SECRET=<your_client_secret>
STRAVA_REFRESH_TOKEN=<your_refresh_token>
```

To get your refresh token, follow the **OAuth Authorization** steps below.

### 4. OAuth Authorization (Getting Your Refresh Token)

Strava uses OAuth2. You need to authorize the app once to get a refresh token.

**Step 1: Authorize in browser**

Open this URL in your browser (replace `YOUR_CLIENT_ID`):

```
https://www.strava.com/oauth/authorize?client_id=YOUR_CLIENT_ID&response_type=code&redirect_uri=http://localhost&scope=read_all,activity:read_all,activity:write,profile:read_all,profile:write
```

**Step 2: Grab the authorization code**

After you click "Authorize", Strava redirects to:

```
http://localhost?code=AUTHORIZATION_CODE&scope=...
```

Copy the `code` value from the URL. This code is **single-use** and **expires in minutes**, so proceed quickly.

**Step 3: Exchange code for tokens**

**On Linux/macOS (bash):**

```bash
curl -X POST https://www.strava.com/api/v3/oauth/token \
  -d client_id=YOUR_CLIENT_ID \
  -d client_secret=YOUR_CLIENT_SECRET \
  -d code=AUTHORIZATION_CODE \
  -d grant_type=authorization_code
```

**On Windows (PowerShell):**

```powershell
Invoke-RestMethod -Method Post -Uri "https://www.strava.com/api/v3/oauth/token" -Body @{
  client_id = "YOUR_CLIENT_ID"
  client_secret = "YOUR_CLIENT_SECRET"
  code = "AUTHORIZATION_CODE"
  grant_type = "authorization_code"
}
```

**Step 4: Save the refresh token**

The response will include `access_token`, `refresh_token`, and `expires_at`. Copy the `refresh_token` value into your `.env` file.

The server will automatically refresh the access token when it expires using this refresh token.

## Usage

### Test with MCP Inspector

The MCP Inspector provides a web UI to test your tools:

```bash
uv run mcp dev strava_server.py
```

1. Open the Inspector URL shown in the terminal
2. Click **Connect**
3. Go to the **Tools** tab
4. Try running a tool like `get_athlete_profile` or `get_recent_activities`

### Use with Claude Code (CLI)

From your project directory, register the server:

```bash
claude mcp add strava -- uv run strava_server.py
```

Then start a new Claude Code session and ask things like:

- "How was my running this week?"
- "Show me my last 10 activities"
- "What are my all-time cycling stats?"
- "Find popular running segments near Bangalore"
- "Create a manual yoga activity for today"

### Use with Claude Desktop

```bash
uv run mcp install strava_server.py
```

Or manually add to your config file:

- **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
- **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`

```json
{
  "mcpServers": {
    "strava": {
      "command": "uv",
      "args": ["run", "strava_server.py"],
      "cwd": "/path/to/strava-mcp-server"
    }
  }
}
```

Restart Claude Desktop after saving.

### Use with ChatGPT Desktop

1. Open ChatGPT Desktop app
2. Go to **Settings** > **MCP**
3. Add a new server with:
   - **Name:** `strava`
   - **Command:** `uv run strava_server.py`
   - **Working directory:** `/path/to/strava-mcp-server`

Restart ChatGPT Desktop after adding.

### Use with Cursor / VS Code

Add to `.cursor/mcp.json` or `.vscode/mcp.json` in your workspace:

```json
{
  "servers": {
    "strava": {
      "command": "uv",
      "args": ["run", "strava_server.py"],
      "cwd": "/path/to/strava-mcp-server"
    }
  }
}
```

### Run Standalone

```bash
uv run strava_server.py
```

## Troubleshooting

### 401 Unauthorized Error

This is the most common issue. It means your access token is invalid.

**Cause 1: Stale cached tokens**

The server caches tokens at `~/.strava-mcp-tokens.json`. If this file contains expired/invalid tokens, the server will keep using them.

**Fix:**

```bash
# Linux/macOS
rm ~/.strava-mcp-tokens.json

# Windows (PowerShell)
Remove-Item "$HOME\.strava-mcp-tokens.json" -ErrorAction SilentlyContinue
```

Then restart the server.

**Cause 2: Invalid refresh token**

Your refresh token in `.env` may be expired or revoked. This happens if:
- You re-authorized the app and got a new refresh token (old one is invalidated)
- You revoked the app's access in Strava settings
- The token was never obtained properly

**Fix:** Redo the [OAuth Authorization](#4-oauth-authorization-getting-your-refresh-token) steps to get a fresh refresh token.

**Cause 3: Wrong client credentials**

Double-check your `STRAVA_CLIENT_ID` and `STRAVA_CLIENT_SECRET` in `.env` match what's shown at [https://www.strava.com/settings/api](https://www.strava.com/settings/api).

### 429 Rate Limit

Strava limits API requests to:
- 100 requests per 15 minutes
- 1,000 requests per day

The server returns a helpful error message when rate-limited. Wait a few minutes and try again.

### Token Refresh Fails on Startup

If the server crashes immediately with a token error, make sure:
1. Your `.env` file exists in the project root
2. All three variables are set (`STRAVA_CLIENT_ID`, `STRAVA_CLIENT_SECRET`, `STRAVA_REFRESH_TOKEN`)
3. There are no extra spaces or quotes around the values

### Missing Scopes

Some tools require specific OAuth scopes:
- `activity:read_all` — needed for activity details, streams
- `activity:write` — needed for creating/updating activities
- `profile:read_all` — needed for athlete zones
- `profile:write` — needed for updating weight

If a tool returns a 403 Forbidden error, you may need to re-authorize with the correct scopes. Use the authorization URL in Step 1 above (it includes all scopes).

## Project Structure

```
strava-mcp-server/
├── strava_server.py              # Entry point (thin wrapper)
├── src/
│   └── strava_mcp/
│       ├── __init__.py           # Package exports
│       ├── server.py             # FastMCP instance & logging
│       ├── auth.py               # OAuth2 token management
│       ├── client.py             # HTTP helpers (GET, POST, PUT)
│       ├── formatters.py         # Response formatting utilities
│       └── tools/
│           ├── __init__.py       # Auto-imports all tool modules
│           ├── activities.py     # Activity CRUD, laps, zones, comments, kudos
│           ├── athletes.py       # Profile, stats, zones, weight
│           ├── clubs.py          # Clubs, members, admins, activities
│           ├── gear.py           # Bike & shoe details
│           ├── routes.py         # Routes, GPX/TCX export
│           ├── segments.py       # Segments, efforts, explore, star
│           ├── streams.py        # Time-series data streams
│           └── extras.py         # Summaries, filters, upload status
├── pyproject.toml                # Project config and dependencies
├── uv.lock                       # Locked dependencies
├── .env                           # Your Strava credentials (not committed)
└── README.md                      # This file
```

## License

MIT

TDQS

B3.3/5.0

Scored across 37 tools

Disambiguation5/5

Each tool targets a distinct resource and action, with clear separation between activities, segments, routes, clubs, athlete profiles, and summaries. Even similar tools like get_recent_activities and get_activities_by_date have unambiguous differences in filtering.

Naming Consistency4/5

Most tools follow a consistent verb_noun pattern (e.g., create_activity, get_activity, update_activity), but a few like monthly_summary and weekly_summary deviate from this pattern, using noun_adjective instead.

Tool Count2/5

With 37 tools, the server is on the heavy side. While it covers a broad domain, this number exceeds the recommended range and may overwhelm agents, making selection and navigation more difficult.

Completeness4/5

The tool set covers most CRUD operations for activities, segments, routes, clubs, and athlete data, plus exports and summaries. A notable gap is the absence of a delete activity tool, but overall the surface is comprehensive.

Maintenance

ActivityInactive
ResponsivenessNo issues