# 🚴 Strava MCP Server A production-ready **Model Context Protocol (MCP) server** that exposes the Strava API v3 as MCP-compatible tools, resources, and prompts — enabling AI agents and LLM-based applications to query your Strava training data through a standardized interface. Built with [FastMCP](https://github.com/jlowin/fastmcp) and the [MCP Python SDK](https://github.com/modelcontextprotocol/python-sdk), compatible with MCP Inspector, Claude Desktop, and any MCP-compliant client. --- ## ✨ Features - 🛠️ **25+ MCP Tools** covering all major Strava API read endpoints (including athlete profile & stats) - 💬 **2 MCP Prompts** for structured AI-driven training analysis - 🔄 **Fully Automated OAuth** — authentication flow integrated directly as an MCP tool with auto-rotation - 🐳 **Docker-Ready** — highly optimized multi-stage Docker build utilizing `uv` - 🌐 **Streamable HTTP transport** for broad client compatibility (SSE) - 🔒 **Read-only** — no write operations, safe to use with AI agents --- ## 📋 Table of Contents - [Requirements](#requirements) - [Installation & Deployment](#installation--deployment) - [Docker (Recommended)](#docker-recommended) - [Local Python (uv)](#local-python-uv) - [Strava API Setup](#strava-api-setup) - [Connecting with MCP Inspector](#connecting-with-mcp-inspector) - [MCP Primitives](#mcp-primitives) - [Project Structure](#project-structure) - [CI/CD (Gitea Actions)](#cicd-gitea-actions) - [Known Strava API Limitations](#known-strava-api-limitations) - [Troubleshooting](#troubleshooting) --- ## Requirements - A [Strava account](https://www.strava.com) with API access - A [Strava API Application](https://www.strava.com/settings/api) - **Docker** (for containerized deployment) OR **Python 3.10+** & [uv](https://github.com/astral-sh/uv) (for local execution) --- ## Installation & Deployment ### Docker (Recommended) The project includes a highly optimized, deterministic Dockerfile powered by `uv`. ```bash # Clone the repository git clone https://git.hnrx.net/hnrx/strava-mcp-server.git cd strava-mcp-server # Build the image docker build -t strava-mcp-server:latest . # Run the container (injecting your .env file) docker run --rm -p 8000:8000 --env-file .env strava-mcp-server:latest ``` ### Local Python (uv) ```bash git clone https://git.hnrx.net/hnrx/strava-mcp-server.git cd strava-mcp-server # Install dependencies and start the server uv run strava-mcp ``` ### Run on the fly with `uvx` (No git clone required) You can run the server directly from the repository without cloning it manually by using `uvx`. `uv` will download it into a temporary isolated environment and execute it: ```bash # Set up your .env file in the current directory first! uvx --from git+https://git.hnrx.net/hnrx/strava-mcp-server.git strava-mcp ``` *(If you are already inside the cloned directory, you can also just run `uvx --from . strava-mcp`)* --- ## Strava API 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 3. Set **Authorization Callback Domain** to `localhost` 4. Note your **Client ID** and **Client Secret** ### 2. Configure Environment Copy the example environment file: ```bash cp .env.example .env ``` Edit `.env` and fill in your Client ID and Secret: ```env STRAVA_CLIENT_ID=your_client_id_here STRAVA_CLIENT_SECRET=your_client_secret_here ``` ### 3. Authenticate (The Magic Way ✨) You **do not** need to manually fiddle with OAuth tokens. The server includes an interactive MCP tool to handle authentication! 1. Start the server (`docker run ...` or `uv run strava-mcp`). 2. Connect to the server via an MCP Client (like Claude Desktop or MCP Inspector). 3. Call the `get_new_oauth_token` MCP tool. 4. Your browser will open for you to authorize the app. The server will intercept the callback locally, generate your tokens, and automatically save the `STRAVA_REFRESH_TOKEN` to your `.env` file! > **Required OAuth Scopes:** > `activity:read_all,profile:read_all,read` --- ## Connecting with MCP Clients The server listens on **port 8000** by default and exposes an SSE endpoint: `http://localhost:8000/mcp` ### Claude Desktop Add to your `claude_desktop_config.json`: ```json { "mcpServers": { "strava": { "url": "http://localhost:8000/mcp", "transport": "streamable-http" } } } ``` ### MCP Inspector 1. Open [MCP Inspector](https://inspector.modelcontextprotocol.io/) 2. Select transport: **Streamable HTTP** 3. Enter URL: `http://localhost:8000/mcp` 4. Click **Connect** --- ## MCP Primitives ### Tools #### 🔐 Authentication | Tool | Description | |------|-------------| | `get_new_oauth_token` | Starts the interactive browser OAuth2 flow to generate and save your initial Refresh Token | #### 🏃 Athlete | Tool | Description | |------|-------------| | `get_athlete_profile` | Full athlete profile: name, city, country, follower count, gear list | | `get_athlete_stats` | Training totals: all-time, year-to-date, and last 4 weeks for runs, rides, and swims | | `get_athlete_zones` | Heart rate and power zones | #### 🚴 Activities | Tool | Description | |------|-------------| | `list_activities` | Paginated activity list with optional time range filters | | `get_activity_details` | Full activity details incl. segment efforts | | `get_activity_laps` | Lap splits | | `get_activity_zones` | Heart rate and power zones for a specific activity | | `get_activity_comments` | Comments on an activity | | `get_activity_kudoers` | Athletes who gave kudos | | `get_activity_streams` | Raw GPS/sensor data streams | *(Note: Additional tools exist for Clubs, Routes, Segments, Segment Efforts, and Gear. See MCP Inspector for full details.)* ### Prompts Prompts pre-structure AI conversations with the right tool-calling instructions. - **`analyze_activity`**: Triggers a structured analysis of a specific activity including summary, performance metrics, and key takeaways. - **`training_summary`**: Generates a training load report for the last N weeks (volume, trends, recommendations). --- ## Project Structure ``` strava-mcp-server/ ├── Dockerfile # Multi-stage optimized uv build ├── src/ │ └── strava_mcp_server/ # Installable Python package │ ├── __init__.py │ ├── main.py # Server entrypoint → strava-mcp │ ├── strava_client.py # Strava API client with auto token rotation │ └── tools/ # Modularized MCP tools directory │ ├── __init__.py # Tool registry │ ├── activities.py │ ├── athlete.py │ ├── auth.py # OAuth automation flow │ └── ... ├── .gitea/ │ └── workflows/ # Gitea Actions CI/CD Pipeline ├── tests/ ├── pyproject.toml └── .env ``` --- ## CI/CD (Gitea Actions) This repository includes a pre-configured Gitea Action (`.gitea/workflows/cicd.yml`) that automatically: 1. **Lints** the codebase using `ruff` on every push/PR. 2. **Builds & Pushes** the Docker container to the local Container Registry (`git.hnrx.net`) upon a successful merge to `main`. --- ## Known Strava API Limitations | Endpoint | Status | Reason | |----------|--------|--------| | `GET /segments/{id}/leaderboard` | `403 Forbidden` | Requires Strava API partnership | | `GET /segment_efforts/{id}` | `403 Forbidden` | Requires Strava API partnership | | `GET /athlete/zones` | `401 Unauthorized` | Requires `profile:read_all` OAuth scope | > **Workaround for segment efforts:** Use `get_activity_details` to access segment efforts embedded in activity data. The `segment_efforts[]` array contains effort IDs, times, heart rate, power, and PR/KOM ranks. --- ## Troubleshooting ### `[Errno 48] Address already in use` Port 8000 is occupied by a previous server process: ```bash lsof -ti :8000 | xargs kill -9 ``` ### ModuleNotFoundError / iCloud Sync Issues (macOS) If you are developing locally on macOS and your `strava-mcp-server` directory is located inside `Documents/` or `Desktop/`, **iCloud Drive** will constantly sync and delete files inside your virtual environment (`.venv`), leading to missing packages. **Solution:** Move the project out of iCloud or rename the folder to end in `.nosync` (e.g. `strava-mcp-server.nosync`). ### 401 Unauthorized Your refresh token has expired or been revoked. Simply run the `get_new_oauth_token` MCP tool again to re-authenticate! --- ## License MIT