# ๐Ÿšด 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 - ๐Ÿ”„ **Automated OAuth** โ€” authentication flow via a standalone setup script with auto-rotation - ๐Ÿณ **Multi-Arch Docker** โ€” optimized builds for `linux/amd64` and `linux/arm64` powered by `uv` - ๐Ÿท๏ธ **Dynamic Versioning** โ€” versions are automatically derived from Git tags (powered by `hatch-vcs`) - ๐Ÿค– **Agent-First Design** โ€” includes specific instructions for LLMs on handling European date formats (DD.MM.YYYY) - ๐ŸŒ **Streamable HTTP transport** for broad client compatibility (SSE) - ๐Ÿ”’ **Read-only** โ€” no write operations, safe to use with AI agents - ๐Ÿ“ **Design Decisions** โ€” documented architectural choices in `docs/DESIGN_DECISIONS.md` --- ## ๐Ÿ“‹ 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 Clients](#connecting-with-mcp-clients) - [MCP Primitives](#mcp-primitives) - [Project Structure](#project-structure) - [Design Decisions](#design-decisions) - [CI/CD (Gitea Actions)](#cicd-gitea-actions) - [Development & Testing](#๏ธ-development--testing) - [Git Hooks](#git-hooks) - [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) --- ## Installation & Deployment ### Docker (Recommended) The project includes a multi-arch Docker build (amd64/arm64). ```bash # Clone the repository git clone https://git.hnrx.net/hnrx/strava-mcp-server.git cd strava-mcp-server # Build the image locally 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 (PyPI) The easiest way to get started is using our **interactive onboarding wizard**. You don't even need to clone the repository: ```bash # 1. Start the interactive setup wizard uvx --from strava-mcp-server-hnrx auth # 2. Run the MCP server uvx --from strava-mcp-server-hnrx server ``` The wizard will guide you through creating a Strava API application and automatically save your credentials to a `.env` file. ### Installation If you prefer a traditional installation: ```bash pip install strava-mcp-server-hnrx # or uv add strava-mcp-server-hnrx ``` ### Running from source If you want to contribute or run the latest dev version: ```bash git clone https://git.hnrx.net/hnrx/strava-mcp-server.git cd strava-mcp-server # Start the MCP server uv run server # Run the OAuth setup script uv run auth ``` --- ## 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 โœจ) The server is designed for zero-touch deployment. You can authorize it **after** it has started. 1. **Start the server** (it will log a warning that the refresh token is missing, but it will boot!). 2. **Run the Auth Script**: - Run `uv run auth` in your terminal on your local machine. 3. Your browser will open. Log in and authorize. 4. **Success:** The browser will show you the exact values for your `.env` (or Kubernetes Secret). The script will also automatically update your local `.env` file! --- ## Connecting with MCP Clients The server listens on **port 8000** and exposes an SSE endpoint: `http://localhost:8000/mcp` ### Claude Desktop Add to `claude_desktop_config.json`: ```json { "mcpServers": { "strava": { "url": "http://localhost:8000/mcp", "transport": "streamable-http" } } } ``` --- ## MCP Primitives ### Tools | Category | Tools | |----------|-------| | ๐Ÿƒ **Athlete** | `get_athlete_profile`, `get_athlete_stats`, `get_athlete_zones` | | ๐Ÿšด **Activities** | `list_activities`, `get_activity_details`, `get_activity_laps`, `get_activity_zones`, `get_activity_streams` | | ๐Ÿ˜๏ธ **Clubs** | `get_athlete_clubs`, `get_club_activities`, `get_club_members` | --- ## Design Decisions For a detailed list of architectural choices, unit standardizations, and LLM-specific optimizations, please refer to: ๐Ÿ‘‰ **[docs/DESIGN_DECISIONS.md](docs/DESIGN_DECISIONS.md)** --- ## CI/CD (Gitea Actions) Our pipeline (`.gitea/workflows/cicd.yml`) is fully automated: - **Linting:** Every push/PR is checked with `ruff`. - **Multi-Arch Builds:** Builds `amd64` and `arm64` images simultaneously using QEMU and DinD. - **Smart Tagging:** - Pushes to `main` are tagged as `:latest`. - Git Tags (e.g., `v1.2.0`) trigger a versioned build and **automatically update the Gitea Release description** with the correct `docker pull` command. --- ## Troubleshooting ### `[Errno 48] Address already in use` `lsof -ti :8000 | xargs kill -9` ### 401 Unauthorized Your token expired. Run `uv run auth` to refresh. --- ## ๐Ÿ› ๏ธ Development & Testing ### 1. Local Testing with MCP Inspector The [MCP Inspector](https://github.com/modelcontextprotocol/inspector) is the best way to test the server without a full LLM client. **Option A: Test via STDIO (Fastest)** This runs the server directly in your terminal (perfect for local debugging): ```bash npx @modelcontextprotocol/inspector uv run server ``` **Option B: Test via SSE (Remote/Docker)** If the server is already running (e.g., at `http://localhost:8000`): 1. Open [https://inspector.modelcontextprotocol.io/](https://inspector.modelcontextprotocol.io/) 2. Transport: **Streamable HTTP** 3. URL: `http://localhost:8000/mcp` ### 2. Manual SSE Health Check You can verify if the server is responding to SSE requests using `curl`: ```bash curl -v -X POST http://localhost:8000/mcp ``` *(It should return an SSE stream starting with `event: endpoint`)* ### 3. Linting & Formatting We use `ruff` for code quality: ```bash # Run the check uv run ruff check src # Run the formatter uv run ruff format src ``` ### 4. Build Multi-Arch Images To test if the multi-arch Docker build works locally (requires Docker Buildx): ```bash docker buildx build --platform linux/amd64,linux/arm64 -t strava-mcp-server:test . ``` --- ### 5. Git Hooks Two hooks are provided in `scripts/hooks/` โ€” install them both after cloning: ```bash cp scripts/hooks/pre-commit .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit cp scripts/hooks/pre-push .git/hooks/pre-push && chmod +x .git/hooks/pre-push ``` #### `pre-commit` โ€” Lint & Format check Runs on staged `.py` files before every commit. - ๐Ÿ” `ruff check` โ€” linting - ๐ŸŽจ `ruff format --check` โ€” formatting - ๐Ÿ”ง Fix: `uv run ruff check --fix` + `uv run ruff format` - โšก Bypass: `git commit --no-verify` #### `pre-push` โ€” Unit tests Runs the full unit test suite before every push. - ๐Ÿงช `pytest tests/unit/ -q` - โšก Bypass: `git push --no-verify` ### 6. Unit Tests Fast, offline unit tests for pure functions and MCP helpers (no Strava API required). ```bash # Run all unit tests uv run pytest tests/unit/ -v # Run with coverage (if pytest-cov is installed) uv run pytest tests/unit/ --cov=strava_mcp_server ``` **Test coverage:** | Module | Tests | |--------|-------| | `utils.py` โ€” `parse_iso_to_unix` | `None`, empty, invalid, UTC, offset, date-only | | `utils.py` โ€” `format_date_iso` | Normalization, `None`, datetime object, invalid | | `utils.py` โ€” `format_date_human` | German format, `None`, datetime object, regex pattern | | `tools/*` โ€” `_resource()` | Type, mimeType, URI, JSON validity, audience | | `tools/*` โ€” `_user_text()` | Type, text value, audience | --- ## License MIT