strava-openapi-mcp

strava-openapi-mcp

Enables MCP clients to interact with the Strava REST API through tools generated from Strava's official Swagger spec, supporting OAuth, activity and athlete data retrieval, and configurable write/delete operations.

Category
Visit Server

README

strava-openapi-mcp

Local Python MCP server acting as a generic proxy between an MCP client—especially OpenCode—and the Strava REST API. Tools are not implemented endpoint by endpoint: they are generated at startup from Strava’s official Swagger 2.0 specification.

The repository contains a copy of the specification and its referenced schema documents. Startup therefore does not require Internet access to build the tool list. The update-spec command refreshes the user copy after validation.

Architecture

openapi.py loads and validates Swagger, resolves local references, and normalizes operations. tools.py transforms each operation into an MCP tool with a generated JSON Schema. client.py builds URLs, parameters, JSON bodies, and multipart forms without knowing Strava endpoints individually. auth.py handles the local OAuth flow and token refresh. server.py exposes everything over MCP stdio, while cli.py provides maintenance commands.

Strava’s currently published specification is Swagger 2.0, with info.version 3.0.0. The bundle is intentionally treated as replaceable data: if a new endpoint appears in the specification, it is discovered automatically.

Prerequisites and local installation

Python 3.12+ and uv are recommended.

git clone https://github.com/Arbodgad/strava-openapi-mcp.git
cd strava-openapi-mcp
uv sync
uv run strava-mcp list-tools

Start the MCP server with:

uv run strava-mcp

The server remains active on the MCP stdin/stdout transport. Application logs are sent to stderr. No diagnostic log must be written to stdout during stdio transport.

Create a Strava application

  1. Open https://www.strava.com/settings/api.
  2. Create an application and note its Client ID and Client Secret.
  3. Strava accepts localhost and 127.0.0.1 as callback domains. The default callback is http://127.0.0.1:8765/callback.

Credentials can be provided through the environment:

export STRAVA_CLIENT_ID="..."
export STRAVA_CLIENT_SECRET="..."

Or in ~/.config/strava-mcp/credentials.json with 0600 permissions:

{
  "client_id": "...",
  "client_secret": "..."
}

Environment variables take precedence. The secret is never displayed or written to logs.

OAuth

Run once:

strava-mcp auth

The browser opens the Strava authorization page. The local callback exchanges the authorization code for access_token, refresh_token, expires_at, and the granted scopes. Tokens are stored in ~/.config/strava-mcp/tokens.json with 0600 permissions. The server automatically refreshes expired access tokens and persists a rotating refresh token when Strava returns one.

By default, all scopes declared by the specification are requested. To request a subset:

export STRAVA_OAUTH_SCOPES="activity:read,activity:write"

Official descriptions are analyzed to infer explicit scopes. Read endpoints accepting either activity:read or activity:read_all are represented as alternatives. A conditional scope—such as activity:read_all for a private activity—is shown to the LLM, and the original Strava error remains visible.

Configuration

Supported variables:

Variable Default
STRAVA_CLIENT_ID none, or credentials.json
STRAVA_CLIENT_SECRET none, or credentials.json
STRAVA_API_BASE_URL https://www.strava.com/api/v3
STRAVA_OPENAPI_URL https://developers.strava.com/swagger/swagger.json
STRAVA_OPENAPI_PATH ~/.config/strava-mcp/openapi.json
STRAVA_ALLOW_WRITE true
STRAVA_ALLOW_DELETE false
STRAVA_LOG_LEVEL INFO
STRAVA_OAUTH_SCOPES all declared Strava scopes
STRAVA_CALLBACK_HOST / STRAVA_CALLBACK_PORT 127.0.0.1 / 8765

The aliases STRAVA_MCP_ALLOW_WRITE and STRAVA_MCP_ALLOW_DELETE are also accepted. strava-mcp show-config displays only a non-secret configuration view.

The recommended values are STRAVA_ALLOW_WRITE=true and STRAVA_ALLOW_DELETE=false. POST, PUT, and PATCH methods are not blocked by default. DELETE methods are generated when the specification contains them, but filtered from the MCP tool list while STRAVA_ALLOW_DELETE=false.

First startup

export STRAVA_CLIENT_ID="..."
export STRAVA_CLIENT_SECRET="..."
strava-mcp auth
strava-mcp list-tools
strava-mcp

The user specification copy takes precedence. If it does not exist, the bundled official specification is used without downloading anything at startup.

Update the specification

strava-mcp update-spec

The command downloads STRAVA_OPENAPI_URL, validates the Swagger document, and then downloads referenced JSON documents. The existing copy is replaced only after the entire download and validation process succeeds. The reported version and number of referenced schemas are displayed.

To force a different path:

STRAVA_OPENAPI_PATH="$HOME/.config/strava-mcp/openapi.json" strava-mcp update-spec

Direct installation with uvx from Git

The pyproject.toml declares the executable and all dependencies. No manual Python installation or clone is required:

uvx --from git+https://github.com/Arbodgad/strava-openapi-mcp strava-mcp auth
uvx --from git+https://github.com/Arbodgad/strava-openapi-mcp strava-mcp

To immediately use a new commit despite the uv cache:

uvx --refresh --from git+https://github.com/Arbodgad/strava-openapi-mcp strava-mcp

OpenCode configuration

Add the server to the OpenCode configuration:

{
  "mcp": {
    "strava": {
      "type": "local",
      "command": [
        "uvx",
        "--from",
        "git+https://github.com/Arbodgad/strava-openapi-mcp",
        "strava-mcp"
      ],
      "enabled": true
    }
  }
}

Export the variables in the environment that launches OpenCode, or use credentials.json, instead of committing secrets to this file. Run strava-mcp auth once for the same local account before starting OpenCode.

Generated tools and examples

Names are derived from operationId, normalized to snake case, with an HTTP method prefix added only when necessary to avoid ambiguity. For example, with the current specification:

Endpoint Current generated tool
GET /athlete get_logged_in_athlete
GET /athlete/activities get_logged_in_athlete_activities
GET /activities/{id} get_activity_by_id
PUT /activities/{id} put_update_activity_by_id
GET /activities/{id}/streams get_activity_streams
GET /athletes/{id}/stats get_stats

The UpdatableActivity body parameters are flattened into the PUT tool. The agent can therefore make conceptually equivalent calls:

put_update_activity_by_id(id=123456789, name="Long Z2 run")
put_update_activity_by_id(id=123456789, description="Easy aerobic endurance session, good sensations.")

Other examples of natural-language requests:

  • “List my latest running activities”: use get_logged_in_athlete_activities, then filter the returned results.
  • “Read the details of activity 123”: use get_activity_by_id(id=123).
  • “Get the distance and heartrate streams for 123”: use get_activity_streams(id=123, keys=["distance", "heartrate"], key_by_type=true).
  • “Get my statistics”: obtain the authenticated athlete, then use get_stats(id=...).

Pagination is fully controlled by the parameters in the specification (page, per_page, before, after, page_size, after_cursor, and so on). The server never automatically starts a long sequence of page requests.

Writes and dangerous operations

MCP descriptions include This operation modifies Strava data for POST/PUT/PATCH and WARNING for DELETE. If STRAVA_ALLOW_WRITE=false, write tools return an explicit error. If STRAVA_ALLOW_DELETE=false, DELETE tools are absent from list_tools and direct calls are rejected.

HTTP errors preserve the status, endpoint, Strava message, and available rate-limit headers, for example:

HTTP 401 Unauthorized
Endpoint: PUT /activities/{id}
Message: Invalid or expired token

A 204 response becomes the minimal object { "status": "success", "http_status": 204 }. JSON responses retain Strava field names.

CLI commands

strava-mcp                       # MCP stdio server
strava-mcp auth                  # Browser OAuth + localhost callback
strava-mcp update-spec           # Validated update of the local copy
strava-mcp show-config           # Non-secret configuration
strava-mcp list-tools            # Method, endpoint, tool, and summary
strava-mcp list-tools --schemas  # Also display each inputSchema JSON

list-tools --schemas is useful for diagnosing an MCP client that rejects a schema. JSON Schema keywords such as required are displayed at the relevant schema level; a Strava property named required remains under properties.

Tests and development

uv run pytest
uv run ruff check .

Tests use mocked HTTP transports and do not contact Strava. Integration tests against Strava are intentionally not run automatically.

Troubleshooting

  • No Strava authorization found: run strava-mcp auth with the correct credentials.
  • OAuth scope missing: run strava-mcp auth again with the scope requested in STRAVA_OAUTH_SCOPES.
  • Spec update aborted: the previous local copy remains intact; check the network or remove a custom STRAVA_OPENAPI_PATH.
  • No DELETE tools: this is the default behavior; set STRAVA_ALLOW_DELETE=true and restart.
  • MCP error related to stdout: do not add print calls to server code; logs must use logging configured for stderr.
  • OAuth port already in use: set STRAVA_CALLBACK_PORT to an available port and, if necessary, register the localhost domain in the Strava application.

Security

The client secret, access token, and refresh token are never included in logs, MCP descriptions, or error messages. Local credential and token files are ignored by Git and written with 0600 permissions. Never commit .env, credentials.json, or tokens.json.

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured