Strava MCP Server
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| STRAVA_CLIENT_ID | Yes | The Client ID of your Strava API application | |
| STRAVA_ACCESS_TOKEN | Yes | Strava API access token with required scopes (e.g., read, activity:read_all) | |
| STRAVA_CLIENT_SECRET | Yes | The Client Secret of your Strava API application | |
| STRAVA_REFRESH_TOKEN | Yes | Strava API refresh token used to automatically refresh expired access tokens |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server is scoped to one authenticated athlete's Strava mirror. Start with strava_sync_status and strava_schema (or strava://mirror/status and strava://mirror/schema), then use bounded strava_query aggregates: local reads cost zero API calls. Coverage is partial until backfill finishes; an empty result does not prove no activity. SQL uses one engine-enforced read-only transaction, at most 5000 rows and 30 seconds; external file/network access is disabled. Other read tools/resources are cache-first and can refresh from Strava. Prompt retrieval uses only stored data and never starts sync. Preserve source, fetched_at, stale and truncation notices in analyses. Text is capped at 25000 characters; use smaller windows/pages or SQL aggregates. Stream tools downsample to max_points, not full-resolution analysis. Check strava://mirror/budget before expensive remote reads. Overall and GET/read quotas have separate 15-minute/daily pools, header-driven allowances and background reserves. Missing headers mean unknown accounting, NOT free calls; local estimates cannot see other clients. Single-object refreshes normally cost one request; complete-list refreshes may traverse every upstream page even when requesting one local page. Each Strava API attempt, including a 401 retry, reserves known pools. OAuth token refresh is separate; its allowance is unknown. 429 pauses all priorities until the applicable window reset or later Retry-After; interactive waits are capped at 20 seconds and may serve stale data. Do not tight-loop retries or upload-status polling. Create/update/upload/star tools write to Strava and require the athlete's intent and scope. Hints are descriptive, not approval gates. Only explicit 401 responses are retried once after token refresh; lost responses/5xx/429 are not automatically replayed. Reconcile an ambiguous write with a read before retrying; duplicate creation is possible.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": true
} |
| prompts | {
"listChanged": true
} |
| resources | {
"listChanged": true
} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| strava_list_activitiesA | List the authenticated athlete's activities, newest first. Supports filtering by date range and pagination. Served from stored history, which can be incomplete during backfill. A stale listing refresh traverses upstream pages; local page size does not cap API requests. Check strava_sync_status or strava://mirror/budget before broad refreshes; narrow dates/use local SQL first. |
| strava_get_activityA | Get detailed information about a specific activity including description, calories, splits, laps, segment efforts, best efforts, and gear. Served from the local mirror; recent activities are refreshed from Strava. |
| strava_create_activityA | Create a manual activity entry (one API request). Requires activity:write scope and intent. Do not blindly retry a lost response; duplicate creation is possible. |
| strava_update_activityA | Update an activity's mutable properties. Requires activity:write scope. Only provided fields are updated (one API request); replacing values can destroy prior content. Reconcile lost responses before retrying. |
| strava_list_activity_commentsA | Page through stored comments. A cache refresh fetches the entire upstream list, potentially many API pages regardless of per_page. |
| strava_list_activity_kudoersA | Page through stored kudoers. A cache refresh fetches the entire upstream list, potentially many API pages regardless of per_page. |
| strava_list_activity_lapsA | List laps of an activity. Each lap includes distance, time, speed, heart rate, watts, and elevation. |
| strava_list_activity_zonesA | Get heart rate and power zone distribution for an activity. Shows time spent in each zone. |
| strava_get_authenticated_athleteA | Get the profile of the currently authenticated athlete, including name, location, stats summary, bikes, and shoes. |
| strava_get_athlete_zonesB | Get the authenticated athlete's heart rate and power zones. |
| strava_get_athlete_statsA | Get activity statistics for the authenticated athlete: recent, year-to-date, and all-time totals for runs, rides, and swims. Includes biggest ride distance and biggest climb. |
| strava_list_starred_segmentsA | Page through stored starred segments. A stale refresh fetches the complete upstream list (multiple API pages); local per_page does not cap request cost. |
| strava_list_athlete_clubsA | Page through stored clubs the athlete belongs to. Refresh fetches all upstream pages; local per_page does not cap API request cost. |
| strava_get_clubA | Get detailed information about a club including description, member count, and sport type. |
| strava_get_gearA | Get equipment details by ID (name, brand, model, total distance). Gear IDs look like "b12345" for bikes or "g12345" for shoes. |
| strava_list_athlete_routesB | Page through stored routes. Refresh fetches all upstream pages; local per_page does not cap API request cost. |
| strava_get_routeB | Get detailed route information including distance, elevation gain, and estimated moving time. |
| strava_export_route_gpxA | Export a route as a GPX file. Returns raw GPX XML content. |
| strava_export_route_tcxA | Export a route as a TCX file. Returns raw TCX XML content. |
| strava_get_segmentB | Get detailed information about a segment including distance, elevation, grade, effort count, and athlete count. |
| strava_list_segment_effortsA | List the authenticated athlete's efforts on a segment, newest first, optionally filtered by date range. Limited to per_page results (no next-page cursor). Mirror history can be incomplete; fallback fetches all upstream pages before slicing, so per_page does not cap API calls. Check strava_sync_status or strava://mirror/budget; prefer stored SQL when budget is tight. |
| strava_get_segment_effortB | Get a specific segment effort by ID, including elapsed time, heart rate, watts, and achievements. |
| strava_star_segmentA | Star or unstar a segment for the authenticated athlete (one write API request). Unstarring removes an existing preference. Reconcile a lost response before retrying. |
| strava_export_segment_gpxA | Export a Strava segment as a GPX file for navigation. Returns GPX XML that can be imported into Garmin Connect, Coros, or other GPS devices. Note: The Strava API does not support uploading routes, so the GPX must be imported manually into your device/app. |
| strava_get_activity_streamsA | Get high-resolution time-series data for an activity (heart rate, power, cadence, altitude, GPS coordinates, etc.). Data is downsampled if too large. Cache miss normally costs one API request. Missing stream rate headers mean unknown accounting, not free calls; check strava_sync_status or strava://mirror/budget first. |
| strava_get_segment_effort_streamsA | Get bounded/downsampled time-series data for a segment effort. Cache miss normally costs one API request; check strava_sync_status or strava://mirror/budget. Missing headers do not exempt requests from known quotas. |
| strava_get_segment_streamsA | Get bounded/downsampled GPS, distance and altitude data for a segment. Cache miss normally costs one API request; check strava_sync_status or strava://mirror/budget first. |
| strava_get_route_streamsA | Get bounded/downsampled GPS, distance and altitude data for a route. A cache refresh normally costs one API request; check strava_sync_status or strava://mirror/budget first. |
| strava_get_uploadA | Read upload processing status directly from Strava (one API request per call, never mirrored). Returns status, error and the resulting activity ID. Poll sparingly; check strava_sync_status or strava://mirror/budget and respect cooldown. |
| strava_create_uploadA | Upload an activity file (FIT, TCX, GPX) to create a new activity (one API request plus later status polls). Requires activity:write. Maximum decoded file size 25 MiB; canonical base64 only. Do not replay a lost response; reconcile first. Returns an upload ID for strava_get_upload. |
| strava_queryA | Run a read-only DuckDB SQL query against the athlete's stored Strava data (possibly incomplete; check sync status). Enforced read-only transaction, 30 seconds, 5000 rows maximum; no file/network access. Use strava_schema first to see the tables. Main tables: activity, activity_stream_point (per-point streams: time, distance, lat, lng, altitude, heartrate, cadence, watts, temp, velocity_smooth, grade_smooth), activity_lap, activity_split, activity_best_effort, activity_segment_effort, segment, activity_zone_bucket, gear, route, athlete_stats. Units: meters, seconds, m/s. raw_current holds every payload as JSON (column body) for fields not in a typed table. Costs no Strava API calls. |
| strava_schemaA | List the mirror's tables and views with their descriptions and row counts, or, given a table name, its columns with types, units and descriptions. Use before strava_query. |
| strava_sync_statusA | How complete and fresh the local mirror is: activity coverage (details, streams, zones), the sync queue by kind, last run of each periodic task, recent errors, and unmapped fields. Zero API calls. Rate status distinguishes header observations from local estimates and unknown pools; missing headers never mean free calls. Check this or strava://mirror/budget before broad remote reads; 429 cooldown/reset times apply to all priorities. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
| weekly-summary | Summarize a UTC week from at most 200 stored activities, with coverage/provenance. Local only, zero API calls; refresh via tools separately if needed. |
| activity-analysis | Analyze a stored activity and stream summaries with provenance. Local only, zero API calls; missing streams are disclosed. Refresh via tools separately. |
| training-plan-review | Review 1-365 days of stored training (at most 200 activities) with coverage/provenance. Local only, zero API calls; refresh with tools separately. |
| segment-comparison | Compare up to 200 stored efforts on a segment with coverage/provenance, not necessarily all efforts. Local only, zero API calls; refresh via tools separately. |
| race-readiness | Assess readiness using stored profile/stats and up to 200 activities over 90 days. Local only, zero API calls; preserve coverage limitations and refresh via tools separately. |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
| athlete-profile | Authenticated athlete profile, bikes and shoes, with provenance. Cache-first; refresh costs one API request. |
| athlete-stats | Year-to-date/all-time run, ride and swim totals with provenance. Cache-first; refresh costs one API request. |
| sport-types | Valid Strava sport_type values for create/update activity and for interpreting activity data. |
| stream-types | Available stream types for the stream tools. Pass one or more keys as the keys parameter. |
| usage-and-limits | Start here: zero-call guide listing every URI template and workflow prompt with bounded examples, API costs, budgets, safe retries and coverage guidance. |
| mirror-schema | Tables/views, units and approximate counts; local only, zero API calls. Read /schema/{table} for columns. |
| mirror-status | Local coverage, queue, recent errors and cached rate/webhook status. Does not sync or contact Strava. |
| mirror-budget | Header observations, conservative estimates, unknown pools, reserve shares and reset/cooldown times; zero API calls. |
TDQS
Scored across 33 tools
Most tools map cleanly to a distinct Strava resource plus action, and the stream/list variants are scoped by entity. The main conceptual overlap is between local mirror listing tools and the generic SQL query/schema tools, but the descriptions make the distinction fairly clear.
Nearly all tools use a consistent strava_ snake_case prefix and verb_noun pattern, such as strava_list_activities and strava_create_upload. Minor deviations like strava_schema, strava_query, and strava_sync_status are still readable and predictable.
With 33 tools, the surface is large enough to feel heavy and increases selection burden. The broad Strava domain justifies many endpoints, but several stream, export, and local-mirror operations could potentially be consolidated.
The server covers core Strava workflows well: activities, segments, efforts, routes, streams, clubs, gear, uploads, athlete stats, and SQL mirror access. Some gaps remain, such as club activities/members and a few mutation endpoints, though some may reflect Strava API limitations.