Athlete MCP
by chamilqs
README.md
# Athlete MCP
An open-source, Strava-first remote MCP server for ChatGPT, Claude, and Gemini clients.
Athlete MCP lets each user authorize with Strava and then ask an AI about their own training. It is a hosted, multi-user MCP service—not a local script and not a shared Strava token. The initial release is read-only by design.
## Why this exists
FitMCP demonstrates a strong product shape: hosted onboarding, per-user sports connections, and one remote MCP URL. Athlete MCP keeps that low-friction shape but starts much narrower and deeper:
- One-click delegated Strava authorization; no separate Athlete MCP password.
- Streamable HTTP at `/mcp`, OAuth 2.1-style authorization code + PKCE, protected-resource metadata, refresh-token rotation, and dynamic client registration.
- Encrypted Strava access and refresh tokens in PostgreSQL.
- Deterministic analysis instead of asking the model to calculate from thousands of samples.
- Rate-limit-aware caching and Strava webhook invalidation.
- No raw GPS coordinates in tool requests or responses.
- Read-only tools and least-privilege MCP annotations.
## Tools
| Tool | Best use |
| --- | --- |
| `strava_get_profile` | Athlete identity, recent totals, lifetime totals |
| `strava_list_activities` | Find workouts and answer calendar/history questions |
| `strava_get_activity` | Exact laps, splits, best efforts, and activity facts |
| `strava_analyze_activity` | Power, HR zones, splits, aerobic decoupling, pacing |
| `strava_training_summary` | Weekly volume, sport mix, consistency, Relative Effort |
| `strava_compare_activities` | Compare repeated routes, races, or workouts |
The analysis layer labels estimates and missing sensor data. Strava's activity API does **not** provide the sleep, HRV, Body Battery, or readiness data that a Garmin-native integration can expose.
## Architecture
```mermaid
flowchart LR
C["ChatGPT / Claude / Gemini client"] -->|"OAuth + Streamable HTTP"| M["Athlete MCP"]
M -->|"Authorization code"| S["Strava OAuth"]
M -->|"Encrypted token storage + cache"| P[("PostgreSQL")]
M -->|"V3 API"| A["Strava API"]
A -->|"Activity webhooks"| M
```
There are two separate trust boundaries:
1. The MCP client authenticates to Athlete MCP. Athlete MCP issues short-lived access tokens and rotating refresh tokens.
2. Athlete MCP authenticates to Strava for that athlete. Strava tokens are encrypted with AES-256-GCM before storage.
The user's Strava login is the Athlete MCP account. The authorization endpoint delegates directly to Strava, then returns an MCP authorization code to the requesting client.
## Local setup
Requirements: Node.js 22+, Docker, and a [registered Strava API application](https://www.strava.com/settings/api).
New Strava applications begin with an athlete capacity of **1** (Single Player Mode). The API settings dashboard can raise this to 10 once the app is configured; connecting more than 10 athletes requires Strava review. A technically public MCP server does not bypass this platform limit.
1. Copy `.env.example` to `.env` and fill in the values.
2. Set the Strava application's authorization callback domain to the hostname in `PUBLIC_BASE_URL`.
3. Start PostgreSQL:
```bash
docker compose up -d postgres
```
4. Install and run:
```bash
npm install
npm run dev
```
Strava must redirect to:
```text
https://YOUR_HOST/oauth/strava/callback
```
For local development, use an HTTPS tunnel and set `PUBLIC_BASE_URL` to the public tunnel URL. Remote AI clients cannot call `localhost` directly.
## Webhooks
Register one Strava webhook subscription for the application. One subscription receives events for all athletes who authorized that Strava app.
```bash
curl -X POST https://www.strava.com/api/v3/push_subscriptions \
-F client_id="$STRAVA_CLIENT_ID" \
-F client_secret="$STRAVA_CLIENT_SECRET" \
-F callback_url="https://YOUR_HOST/webhooks/strava" \
-F verify_token="$STRAVA_WEBHOOK_VERIFY_TOKEN"
```
Activity events invalidate cached activity lists. Athlete deauthorization deletes the athlete connection and cascades deletion of MCP tokens and cached data.
## Connect clients
Use the same endpoint for every client:
```text
https://YOUR_HOST/mcp
```
### Claude
In Claude, go to **Customize → Connectors → Add custom connector**, enter the MCP URL, and complete the OAuth flow. Claude remote connectors support Streamable HTTP, OAuth, refresh tokens, and dynamic client registration. See [Anthropic's current custom connector guide](https://support.claude.com/en/articles/11175166-get-started-with-custom-connectors-using-remote-mcp).
### Gemini CLI
```bash
gemini mcp add --transport http athlete-mcp https://YOUR_HOST/mcp
```
Then run `/mcp auth athlete-mcp` if authentication does not start automatically. Gemini CLI supports OAuth discovery and dynamic client registration for remote HTTP MCP servers. See the [Gemini CLI MCP documentation](https://github.com/google-gemini/gemini-cli/blob/main/docs/tools/mcp-server.md).
### ChatGPT
Create a custom app in **Settings → Apps** (or the workspace Apps settings), supply the MCP URL, scan tools, and complete OAuth. Availability is currently plan/workspace dependent; consult the [current ChatGPT developer-mode documentation](https://help.openai.com/en/articles/12584461-developer-mode-apps-and-full-mcp-connectors-in-chatgpt-beta).
## Production checklist
- Terminate TLS at a trusted public hostname; never deploy OAuth over plain HTTP.
- Run the SQL migrations before starting the app.
- Store `TOKEN_ENCRYPTION_KEY` and the Strava client secret in a secret manager.
- Back up PostgreSQL and test token-key rotation before production.
- Add request-level rate limiting at the edge for registration, authorization, and token endpoints.
- Add OpenTelemetry/audit events without logging bearer tokens, refresh tokens, activity descriptions, or tool payloads.
- Configure Strava webhooks; default Strava limits are small enough that polling is not a viable multi-user design.
- Upgrade the Strava application's athlete capacity and complete Strava review before promising open signup.
- Complete a security review before enabling any future `activity:write` tool.
- Publish a privacy policy and data deletion path before inviting external users.
## Current limitations
- No user-facing dashboard or token-revocation UI yet.
- No weather correlation, route matching, or FIT-file ingestion yet.
- No write tools. Strava cannot schedule structured workouts through the V3 activity API.
- PostgreSQL migrations are plain SQL; there is no migration runner yet.
- Tool output is English-only.
## Development
```bash
npm run typecheck
npm test
npm run build
```
The project follows the [MCP Streamable HTTP recommendation](https://ts.sdk.modelcontextprotocol.io/server), the [MCP authorization specification](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization), and Strava's official [OAuth](https://developers.strava.com/docs/authentication/), [API reference](https://developers.strava.com/docs/reference/), [webhook](https://developers.strava.com/docs/webhooks/), and [rate-limit](https://developers.strava.com/docs/rate-limits/) documentation.
## License
MIT. See `LICENSE`.
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues