Files
strava-mcp-server/README.md
T
matthias 6db9e87f96
CI/CD Pipeline / Lint & Check (push) Successful in 10s
Deploy Website to S3 / deploy (push) Successful in 6s
CI/CD Pipeline / Publish to PyPI (push) Has been skipped
CI/CD Pipeline / Build & Push Docker Image (push) Successful in 1m19s
feat: add server_info tool for diagnostics and implement interactive CLI onboarding wizard for easier authentication
2026-05-14 21:12:48 +02:00

291 lines
8.7 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 🚴 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