Metadata-Version: 2.4
Name: strava-mcp-server-hnrx
Version: 0.1.0
Summary: A Model Context Protocol (MCP) server that exposes the Strava API v3 as tools, resources, and prompts for AI agents.
Project-URL: Homepage, https://git.hnrx.net/hnrx/strava-mcp-server
Project-URL: Repository, https://git.hnrx.net/hnrx/strava-mcp-server
Project-URL: Bug Tracker, https://git.hnrx.net/hnrx/strava-mcp-server/issues
License: MIT
Keywords: ai,fitness,llm,mcp,model-context-protocol,strava
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.10
Requires-Dist: fastapi
Requires-Dist: httpx
Requires-Dist: mcp[cli]
Requires-Dist: python-dotenv
Requires-Dist: uvicorn
Description-Content-Type: text/markdown

# 🚴 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 (uv)

We use `uv` for lightning-fast dependency management and task execution.

```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
```

### 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`:

```bash
# Set up your .env file in the current directory first!
uvx --from git+https://git.hnrx.net/hnrx/strava-mcp-server.git server
```

*(If you are already inside the cloned directory, you can also just run `uvx --from . server`)*

---

## 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
