Skip to main content
Glama
Mohith1612

Strava Planner MCP

by Mohith1612

Strava Planner MCP

A personal, read-only MCP server that exposes Strava training data to Claude. It supports:

  • Local stdio for MCP clients that can spawn a process.

  • Authenticated Streamable HTTP at /mcp for Claude Web and other remote MCP clients.

  • Docker deployment behind an HTTPS-terminating Nginx reverse proxy.

The server retrieves and reshapes data. Claude performs coaching, planning, and analysis.

Architecture

Claude Web
  -> HTTPS Streamable HTTP + OAuth 2.1
  -> Nginx
  -> 127.0.0.1:8000
  -> Docker: TypeScript MCP server
  -> Strava REST API

The remote transport is stateless Streamable HTTP. This keeps a single-instance personal deployment simple and avoids sticky-session requirements. Stdio remains available through npm start.

Related MCP server: claude-garmin

Requirements

  • Node.js 20 or newer for local development.

  • A Strava API application.

  • Docker and Docker Compose for deployment.

  • A DNS name and trusted HTTPS certificate for Claude Web.

  • Claude Pro, Max, Team, or Enterprise with custom connectors enabled.

Install And Test

npm ci
npm run typecheck
npm test
npm run build

Strava Configuration

Create an application at https://www.strava.com/settings/api. Local browser authorization uses these scopes:

  • read

  • activity:read_all

  • profile:read_all

Copy the environment example:

cp .env.example .env

Generate independent secrets:

openssl rand -base64 32
openssl rand -hex 16
openssl rand -base64 48
openssl rand -base64 48

Use them for STRAVA_TOKEN_ENCRYPTION_KEY, MCP_OAUTH_CLIENT_ID, MCP_OAUTH_CLIENT_SECRET, and MCP_OAUTH_TOKEN_SIGNING_KEY respectively.

There are two ways to seed Strava authorization:

  1. Set STRAVA_REFRESH_TOKEN from the Strava API application page. On first API use, the server exchanges it and writes the current token set to the encrypted token file.

  2. Run npm run auth on a machine with a browser, then preserve the generated encrypted token file and the same encryption key.

The encrypted token file takes precedence over STRAVA_REFRESH_TOKEN. When Strava rotates the refresh token, the new token is encrypted and persisted. In Docker it lives in the strava-mcp-data volume.

Token Persistence, Backup, And Recovery

The encrypted token file is written atomically (temp file, fsync, then rename) with 0600 permissions, so an interrupted write can never truncate the only good copy. Concurrent refreshes within the process are serialized.

Because Strava rotates refresh tokens, this file is the source of truth once seeded. If it becomes corrupt or is decrypted with the wrong key, the server fails loudly with a clear operational error and does not overwrite it — so recoverable state is never silently destroyed.

  • Back up the encrypted token file together with the exact STRAVA_TOKEN_ENCRYPTION_KEY (the file is useless without the key). In Docker, back up the strava-mcp-data volume.

  • Recover by restoring the file and key, or by re-seeding: set a fresh STRAVA_REFRESH_TOKEN (or run npm run auth) and delete the unreadable file so the seed path can run.

  • Losing both the file and a valid seed refresh token means re-authorizing Strava from scratch.

Environment Variables

Required for all modes (STRAVA_REFRESH_TOKEN may be omitted when a valid encrypted token file already exists):

Variable

Purpose

STRAVA_CLIENT_ID

Strava application client ID.

STRAVA_CLIENT_SECRET

Strava application client secret.

STRAVA_TOKEN_ENCRYPTION_KEY

Encrypts the persisted Strava token file.

STRAVA_REFRESH_TOKEN

Seeds Docker/remote authorization if no encrypted token file exists. One of these two sources is required.

Required for authenticated remote mode:

Variable

Purpose

MCP_PUBLIC_URL

Exact public endpoint, e.g. https://mcp-strava.example.com/mcp.

MCP_OAUTH_CLIENT_ID

Fixed OAuth client ID entered in Claude.

MCP_OAUTH_CLIENT_SECRET

Fixed OAuth client secret entered in Claude.

MCP_OAUTH_TOKEN_SIGNING_KEY

Signs MCP access and refresh tokens.

Important optional variables:

Variable

Default

MCP_HOST

0.0.0.0 (production behind Nginx). Use 127.0.0.1 for authless local dev.

MCP_PORT

8000

MCP_AUTH_ENABLED

true

MCP_ALLOW_INSECURE_BINDING

false. When true, permits authless mode on a non-loopback host.

MCP_ALLOWED_HOSTS

Public hostname plus localhost names.

MCP_TRUST_PROXY

true

MCP_OAUTH_REDIRECT_URIS

https://claude.ai/api/mcp/auth_callback

STRAVA_TOKEN_PATH

~/.strava-planner-mcp/tokens.enc.json outside Docker; /data/tokens.enc.json in Compose.

ATHLETE_CONTEXT_PATH

Unset outside Docker; /config/athlete-context.json in Compose.

STRAVA_CACHE_TTL_SECONDS

900

STRAVA_CACHE_MAX_ENTRIES

500 (bounded LRU cache size).

STRAVA_MAX_RETRIES

4

STRAVA_REQUEST_TIMEOUT_MS

30000

HALF_MARATHON_TRAINING_START_DATE

Unset. Optional YYYY-MM-DD fallback for getHalfMarathonTrainingContext.

Production-critical secrets (STRAVA_CLIENT_SECRET, STRAVA_TOKEN_ENCRYPTION_KEY, MCP_OAUTH_CLIENT_SECRET, MCP_OAUTH_TOKEN_SIGNING_KEY) are validated at startup: the server refuses to boot if they still contain the .env.example placeholder text, and the signing/encryption/OAuth secrets must be at least 16 characters.

See .env.example for the complete list, grouped by local-dev / Docker / production.

Local Linux Development

For a quick authless loopback test, set this only in a development .env:

MCP_AUTH_ENABLED=false
MCP_HOST=127.0.0.1
MCP_ALLOWED_HOSTS=localhost,127.0.0.1

Authless mode refuses to start on a non-loopback host (MCP_HOST other than 127.0.0.1/::1/localhost) so it cannot accidentally expose unauthenticated tools to the LAN. Override deliberately with MCP_ALLOW_INSECURE_BINDING=true only if you know what you are doing.

Start Streamable HTTP:

npm run dev:http

Check liveness and readiness:

curl http://127.0.0.1:8000/health   # cheap liveness, always 200 when running
curl http://127.0.0.1:8000/ready    # 200 only when a usable Strava token source exists

Run the official MCP Inspector:

npm run inspect:http

Select Streamable HTTP and use http://127.0.0.1:8000/mcp.

Do not use MCP_AUTH_ENABLED=false on a public interface. To test OAuth locally, use MCP_PUBLIC_URL=http://localhost:8000/mcp and configure all MCP_OAUTH_* values.

For stdio debugging:

npm run dev
# or, after npm run build
npm start

Personal Athlete Context

Optional goals, heart-rate zones, constraints, and preferences live outside source code. Create the local file:

cp config/athlete-context.example.json config/athlete-context.json

Edit it as needed. getAthleteContext returns it to Claude. The actual file is gitignored and mounted read-only in Docker.

Docker Deployment

The image uses the multi-architecture node:22-bookworm-slim base and works on ARM64/aarch64.

Production deployment on the VM (GHCR image + central proxy) is documented step-by-step in DEPLOY.md. In short: GitHub Actions (.github/workflows/build.yml) builds and pushes ghcr.io/mohith1612/strava-planner-mcp (multi-arch, incl. linux/arm64); the VM runs compose.prod.yaml on the shared proxy network with no published ports, and proxy_nginx reaches it by container name.

For a local Docker smoke test (builds the image locally, publishes on loopback):

docker compose build
docker compose up -d
docker compose ps                       # "healthy" once /ready passes
curl http://127.0.0.1:8000/health
curl http://127.0.0.1:8000/ready
docker compose logs -f strava-mcp
docker compose down                     # add -v ONLY to delete the token volume

The container HEALTHCHECK polls /ready (which verifies a usable token source without calling Strava). The container runs as the non-root node user, drops Linux capabilities, enables no-new-privileges, and handles SIGTERM.

Nginx And HTTPS

On the VM, Nginx is the shared central proxy at /opt/proxy/. Drop nginx/strava-mcp.conf.example in as /opt/proxy/nginx/conf.d/strava.conf (it already targets strava.mohith16.comstrava_mcp:8000) and reload:

docker compose -f /opt/proxy/docker-compose.yml exec nginx nginx -t
docker compose -f /opt/proxy/docker-compose.yml exec nginx nginx -s reload

The config proxies all paths (including /.well-known/oauth-* and /mcp) to the container over the proxy network using the resolver 127.0.0.11 + set $upstream pattern, disables buffering, and uses long streaming timeouts. The shared *.mohith16.com wildcard cert already covers the subdomain, so no new certificate is required. Do not expose container port 8000 publicly.

After DNS + TLS are live, verify:

curl https://strava.mohith16.com/health
curl https://strava.mohith16.com/ready
curl -i https://strava.mohith16.com/mcp
curl https://strava.mohith16.com/.well-known/oauth-protected-resource/mcp
curl https://strava.mohith16.com/.well-known/oauth-authorization-server

An unauthenticated /mcp request should return 401 with a WWW-Authenticate resource metadata link.

Connect Claude Web

  1. Open Claude → Settings → Connectors.

  2. Click Add custom connector.

  3. Name it Strava Planner.

  4. Set the remote MCP server URL to https://strava.mohith16.com/mcp.

  5. Open Advanced settings.

  6. Enter the exact MCP_OAUTH_CLIENT_ID and MCP_OAUTH_CLIENT_SECRET from the server .env.

  7. Click Add, then Connect.

  8. Enable the desired Strava tools in the Search and tools menu.

The URL entered in Claude must match MCP_PUBLIC_URL exactly, including the /mcp path and with no trailing slash. Access tokens are bound to that exact resource (RFC 8707 audience), so a mismatch is rejected. The redirect URI Claude uses is https://claude.ai/api/mcp/auth_callback, which is the default in MCP_OAUTH_REDIRECT_URIS.

The OAuth flow uses Claude's callback URL, PKCE (S256), a fixed confidential client authenticated with client_secret_post, one-hour signed access tokens, rotating 30-day refresh tokens, and MCP protected-resource + authorization-server metadata discovery. Claude supports this static-client model natively — the OAuth Client ID/Secret fields in Advanced settings exist precisely so a server can skip Dynamic Client Registration.

Tools

Existing tools remain available:

  • getAthleteProfile

  • getActivities

  • getActivity

  • getActivityStreams

  • getRecentActivities

  • getActivitiesByType — paginated: accepts after, before, page, and limit (max 200), and returns a nextPage hint.

  • getTrainingHistory — paginated: accepts after, before, page, and limit (max 200), returns newest-first with a nextPage hint.

  • getAthleteOverview — bounded to a look-back window (sinceDays, default 365) instead of the full history.

  • getHalfMarathonTrainingContext

Additional detailed-analysis tools:

  • getActivityLaps

  • getActivityZones

  • getRecentRuns

  • getAthleteContext

Pagination change (backward-compatible defaults): getActivitiesByType and getTrainingHistory previously returned the athlete's entire history in one response. They now return one bounded page and expose nextPage; call again with that value to page through older activities. This keeps responses small and predictable.

Tool results are returned as pretty-printed JSON text content. Structured output schemas were intentionally not adopted to keep Claude Web operation maximally reliable; response size is controlled by the pagination above.

getHalfMarathonTrainingContext window

The training-cycle start date is resolved in this order: trainingStartDate in the athlete context file, then HALF_MARATHON_TRAINING_START_DATE, then a trailing 26-week window. Optional targetRaceDate, targetDistance, and targetTime in the athlete context are surfaced in the report. Weekly buckets use athlete-local time (Strava start_date_local) with Monday week starts, and every calendar week in the range is represented so streak and consistency metrics never bridge an inactive week.

getActivityStreams accepts streamTypes, allowing requests such as:

{
  "activityId": 123456789,
  "streamTypes": ["time", "distance", "heartrate", "velocity_smooth", "cadence"]
}

Supported streams are time, distance, latlng, altitude, velocity_smooth, heartrate, cadence, watts, temp, moving, and grade_smooth. Strava returns only streams available for that activity.

Caching And Rate Limits

  • Activity responses use a bounded in-memory TTL + LRU cache per process (STRAVA_CACHE_MAX_ENTRIES, default 500). Expired entries are pruned lazily and on write, and the least-recently-used entry is evicted past the cap, so memory cannot grow unbounded.

  • Historical data is not persisted in a database.

  • Multi-page fetches are capped (getAllActivities never exceeds 25 pages) and the history tools page explicitly.

  • Transient failures and 429/5xx responses use exponential backoff with jitter; Retry-After is honored in both integer-seconds and HTTP-date forms.

  • The Strava OAuth token exchange and refresh now also have a request timeout and transient-failure retry; a permanent 4xx is returned immediately.

  • A single 401 triggers one refresh-and-retry; permanent 4xx responses are returned immediately rather than retried.

  • Strava token refreshes are serialized (single-flight) to prevent concurrent rotation races.

SQLite was deliberately not added. For a single-user instance, the current cache avoids repeated calls within a session while keeping the ARM64 image and operational model small.

Security Status

Production protections implemented:

  • OAuth-compatible Claude Web authentication with PKCE (S256) and refresh-token rotation.

  • MCP access and refresh tokens signed with HS256; access tokens are audience-bound to the exact MCP URL.

  • Encrypted Strava tokens at rest with AES-256-GCM, written atomically with 0600 permissions.

  • Startup validation rejects placeholder/short production secrets.

  • Authless mode fails closed on any non-loopback bind unless explicitly overridden.

  • Read-only MCP tools with bounded, paginated responses.

  • HTTPS enforced at Nginx with HSTS, X-Frame-Options, X-Content-Type-Options, and modern TLS ciphers.

  • Host-header allowlist and DNS-rebinding protection from the MCP SDK.

  • OAuth endpoint rate limiting supplied by the MCP SDK.

  • No secrets or Strava payloads in normal logs.

  • Non-root container, dropped capabilities, no-new-privileges, and localhost-only published application port.

Single-user limitations:

  • Dynamic Client Registration is intentionally disabled; enter the fixed client credentials in Claude.

  • A valid fixed client authorization request is approved immediately; there is no separate human consent page. Security therefore depends on keeping the OAuth client secret private and serving only over HTTPS.

  • Token revocations are remembered in memory. A restart clears the revocation set, although signed tokens still expire normally. Rotate MCP_OAUTH_TOKEN_SIGNING_KEY to invalidate every outstanding MCP token immediately.

  • The in-memory cache is not shared across replicas. Run one container unless you add shared state.

Never publish the endpoint with MCP_AUTH_ENABLED=false. GPS routes and heart-rate data are private and may be returned by tools.

Available Tools

13 tools
getActivitiesGet Strava ActivitiesA
Read-only

Use this to browse activity summaries in manageable pages. Supports Unix-second date bounds and client-side activity type filtering; call getActivity for splits and full detail.

ParametersJSON Schema
NameRequiredDescriptionDefault
pageNo
afterNoUnix timestamp in seconds.
beforeNoUnix timestamp in seconds.
perPageNo
activityTypeNoStrava activity type such as Run, Walk, Ride, Swim, or Hike.

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations provide readOnlyHint; description adds pagination and filtering context, no contradictions.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, no fluff, front-loaded with purpose and key capabilities.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Adequately covers pagination, date filtering, type filtering, and points to detail tool; lacks output format info but acceptable for browsing tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Adds meaning beyond schema by explaining Unix-second date bounds and client-side activity type filtering, covering 60% schema description.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description clearly states it browses activity summaries in manageable pages, distinguishes from getActivity for full details.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly recommends getActivity for detailed data, but doesn't address when to use siblings like getRecentActivities.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

getActivitiesByTypeGet Strava Activities By TypeA
Read-only

Returns one bounded page of activities of a supported type (newest first). Each page scans up to limit activities; when nextPage is non-null, call again with that page value to retrieve older activities. Use after/before (Unix seconds) to constrain the range.

ParametersJSON Schema
NameRequiredDescriptionDefault
pageNo1-based page; page 1 is most recent.
typeYes
afterNoUnix timestamp in seconds (inclusive lower bound).
limitNoActivities scanned per page (max 200).
beforeNoUnix timestamp in seconds (exclusive upper bound).

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare readOnlyHint=true, and the description adds value by explaining pagination (nextPage, scanning up to limit), ordering (newest first), and date range constraints (after/before as Unix seconds). It does not contradict annotations. Minor omission: no mention of rate limits or authentication, but acceptable for a read-only paginated tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is only two sentences, front-loaded with the core purpose, and immediately provides key details. Every word earns its place; no redundancy or filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with 5 parameters and no output schema, the description covers the essential aspects: type filter, pagination flow, and optional date range. Sibling tools exist but the description is self-contained. It could mention that the output includes activity summaries, but given the tool's simplicity, it is largely complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 80% (4 of 5 parameters have descriptions). The description adds meaning beyond the schema: clarifies that 'limit' is a scan limit, explains pagination with 'nextPage', and reiterates the date range parameters' unit (Unix seconds). The 'page' parameter is described as 1-based. This adds useful context.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool returns a bounded page of activities of a supported type, newest first. It specifies the verb ('returns'), resource ('activities'), and key constraints (type filter, pagination, ordering). This distinguishes it from sibling tools like getActivities (likely all types) and getRecentRuns (specific type implied).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explains when to use the tool: to retrieve activities of a specific type with pagination and optional date range. However, it does not explicitly state when not to use it or mention alternatives among sibling tools. The context is clear but lacks exclusion guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

getActivityGet Strava ActivityA
Read-only

Use after finding an activity ID. Returns full activity detail including metric splits and embedded laps when Strava provides them.

ParametersJSON Schema
NameRequiredDescriptionDefault
activityIdYes

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare readOnlyHint=true, which the description does not contradict. Description adds behavioral detail that metric splits and embedded laps are returned 'when Strava provides them', indicating optional content.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Single sentence, front-loaded with usage context, no wasted words. Achieves maximum conciseness.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple 1-parameter, read-only tool, the description covers main purpose and return content hints. Could mention error handling, but is largely complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0% and the description only indirectly mentions the parameter ('after finding an activity ID'). It does not add explicit details about the activityId parameter beyond the schema constraints.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it returns full activity detail including metric splits and embedded laps. The verb 'Returns' and resource 'activity' match the tool name, and it distinguishes from sibling tools like getActivityLaps.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The phrase 'Use after finding an activity ID' provides clear prerequisite context. It implies this is the primary detail endpoint, but does not explicitly state when not to use it vs. siblings like getActivityStreams.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

getActivityLapsGet Strava Activity LapsA
Read-only

Use for lap-by-lap pacing, heart-rate, cadence, and elevation analysis when an activity has recorded laps.

ParametersJSON Schema
NameRequiredDescriptionDefault
activityIdYes

TDQS

A3.9/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations indicate readOnlyHint=true; description adds data types returned but does not cover edge cases (e.g., no laps) or error behavior. Adequate but not enhanced.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Single, front-loaded sentence with zero wasted words. Highly concise and to the point.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple read-only tool with no output schema, the description adequately covers the returned data types and usage context, though could mention empty/error cases.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage 0%; description does not explain the activityId parameter or its format, relying solely on the schema constraint. Adds minimal value beyond the structure.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description clearly states the tool retrieves lap-by-lap analysis for pacing, HR, cadence, and elevation, distinct from sibling tools like getActivityStreams or getActivity.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states the condition 'when an activity has recorded laps', providing clear use context, though no direct alternatives or negations are mentioned.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

getActivityStreamsGet Strava Activity StreamsA
Read-only

Use for point-by-point analysis. Request only needed stream types to control response size; omitted streamTypes selects a useful set based on activity metadata. Strava returns only available streams.

ParametersJSON Schema
NameRequiredDescriptionDefault
activityIdYes
streamTypesNoRequested streams, e.g. time, distance, heartrate, velocity_smooth, cadence, altitude, grade_smooth, temp.

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Adds behavioral context beyond annotations: 'Strava returns only available streams' and describes default selection behavior when streamTypes is omitted. Annotations already indicate readOnlyHint=true.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, front-loaded with purpose, no wasted words. Efficient and clear.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given 2 parameters, no output schema, and annotations present, the description fully explains purpose, parameter usage, and behavioral notes. Complete for agent use.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 50% schema coverage, description adds meaning: explains default behavior for omitted streamTypes and advises on controlling response size. ActivityId is implicitly understood.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clearly states 'Use for point-by-point analysis' and implies retrieval of activity streams. Distinguishes from siblings like getActivity which likely returns summary data.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Advises to request only needed stream types to control response size and notes that omitting streamTypes selects a useful default set. Provides clear guidance on parameter usage.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

getActivityZonesGet Strava Activity ZonesA
Read-only

Use for time-in-zone analysis. Returns heart-rate or power zones when Strava exposes them for the athlete and activity.

ParametersJSON Schema
NameRequiredDescriptionDefault
activityIdYes

TDQS

A3.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Adds beyond readOnlyHint: clarifies zones only when Strava exposes them. Does not detail error conditions or empty returns, but adequate for a read-only tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two efficient sentences, front-loaded, no redundant information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Adequate for a simple tool with one parameter and no output schema, but could benefit from a note on return structure or availability conditions.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 0% schema coverage, description adds no meaning to the activityId parameter. Should specify that it refers to a Strava activity ID.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clearly states it returns heart-rate or power zones for time-in-zone analysis, distinguishing it from other activity tools. Could be more explicit about sibling differentiation.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides the context 'time-in-zone analysis' but lacks guidance on when not to use it or alternatives like getActivityStreams.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

getAthleteContextGet Personal Athlete ContextA
Read-only

Use alongside Strava data for user-configured goals, heart-rate zones, race targets, constraints, and training preferences. This data is optional and separate from Strava.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description adds that the data is optional and separate from Strava, providing useful context beyond the readOnlyHint annotation. It does not contradict any annotations and discloses that the data may not always be present.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise with two sentences, front-loading the purpose and adding necessary context. Every sentence provides value.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with no parameters and no output schema, the description adequately explains the data returned. However, it does not specify the structure or format of the results, which could be helpful.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

There are no parameters, so baseline is 4. The description does not need to add parameter details, and schema coverage is complete.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool retrieves user-configured goals, heart-rate zones, race targets, constraints, and training preferences, distinct from Strava data. This differentiates it from siblings like getAthleteOverview or getAthleteProfile, which likely return Strava-derived data.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explains the tool is for use alongside Strava data and that the data is optional and separate, implying it should be called when needing personal context. However, it does not explicitly list alternatives or when not to use it.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

getAthleteOverviewGet Strava Athlete OverviewA
Read-only

Returns a compact training overview and recent trends over a bounded look-back window (default last 365 days). Pass sinceDays or an explicit after for a different window.

ParametersJSON Schema
NameRequiredDescriptionDefault
afterNoUnix seconds; overrides sinceDays when set.
sinceDaysNoLook-back window in days (max 5 years).

TDQS

A4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true; the description adds that it returns a compact overview and recent trends, but does not detail other behavioral aspects like data recency or computation cost.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, no filler, front-loaded with the main purpose and essential usage guidance.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple read tool with no required parameters and no output schema, the description adequately covers the tool's purpose and parameter usage, though the exact contents of the overview are unspecified.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% with descriptions for both parameters. The description adds meaningful context by explaining the relationship between `sinceDays` and `after` and the default window, going beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states 'Returns a compact training overview and recent trends over a bounded look-back window' with specific verb and resource, and distinguishes from sibling tools like getTrainingHistory and getRecentActivities.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides guidance on using parameters to adjust the time window but does not explicitly state when to use this tool versus alternatives, nor when not to use it.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

getAthleteProfileGet Strava Athlete ProfileA
Read-only

Use this for identity and baseline athlete metadata. Returns the authenticated Strava athlete profile; use activity tools for training data.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.2/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already indicate readOnlyHint=true. The description adds that it returns the authenticated athlete profile, which is useful context but not extensive behavioral detail beyond that.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two concise sentences that front-load purpose and immediately provide usage guidance. No wasted words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description covers purpose and usage boundaries. It is adequate for a zero-parameter read tool, though it could explicitly distinguish from similar sibling tools like getAthleteContext.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

There are zero parameters, so baseline is 4. The description does not need to add parameter info.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it returns the authenticated Strava athlete profile for identity and baseline metadata, and explicitly distinguishes from activity tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It tells when to use (identity/baseline metadata) and when not to (use activity tools for training data). However, it does not differentiate from siblings like getAthleteContext or getAthleteOverview.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

getHalfMarathonTrainingContextGet Half Marathon Training ContextA
Read-only

Returns compact, information-dense run/walk training context for the current training cycle. The start date comes from the athlete context (trainingStartDate), else HALF_MARATHON_TRAINING_START_DATE, else a trailing 26-week window. Weeks are athlete-local and inactive weeks are included so streaks reflect real gaps.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations include readOnlyHint=true, and the description adds behavioral details: return is compact/information-dense, date derivation logic, and inclusion of inactive weeks for real gap streaks. No contradictions with annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise at two sentences and adequately explains functionality. However, it could be slightly more structured, e.g., separating purpose from date logic.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool has no parameters and a readOnlyHint, the description covers all necessary context: what it returns, date derivation, and week behavior. No output schema exists, but the description sufficiently explains the return value.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

There are zero parameters, so the description is not required to add parameter semantics. The baseline for 0 parameters is 4.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it returns 'compact, information-dense run/walk training context for the current training cycle', which is a specific verb+resource. It distinguishes from sibling tools like getActivities or getAthleteContext by focusing on half marathon training context.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage context (for current training cycle) and explains date logic, but does not explicitly state when to use or when not to use compared to alternatives. No exclusions or alternative references are provided.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

getRecentActivitiesGet Recent Strava ActivitiesB
Read-only

Returns the most recent activities.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo

TDQS

B3/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The readOnlyHint annotation already signals no mutations. The description adds no further behavioral details (e.g., ordering, pagination). Beyond annotations, transparency is minimal.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single concise sentence with no redundancy. However, it could be slightly more informative without losing brevity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple read-only list tool, the description is adequate but incomplete. It does not explain the output format or the effect of the limit parameter. Given no output schema, more context would be helpful.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema has 0% description coverage for the 'limit' parameter, and the tool description does not explain what the parameter does or how to use it. This leaves agents guessing about its purpose.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb 'returns' and the resource 'most recent activities'. However, it does not differentiate from siblings like getRecentRuns or getActivities which also return activity lists.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance is provided on when to use this tool versus alternatives such as getActivities or getRecentRuns. The description lacks both when-to-use and when-not-to-use instructions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

getRecentRunsGet Recent RunsA
Read-only

Use for current training analysis without fetching unrelated sports. Returns the newest Run activities up to the requested limit.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description discloses the tool is read-only (consistent with readOnlyHint=true) and returns newest runs. It adds that results are limited by the limit parameter. No contradictions. Could mention any additional behavioral traits like sorting or absence of filters, but for a simple read tool it's adequate.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description consists of two concise sentences. The first sentence provides usage guidance, the second states the behavior. No unnecessary text; every sentence adds value.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's simplicity (1 parameter, no output schema, readOnlyHint annotation), the description is fairly complete. It explains the resource, ordering, and limit. It does not describe the return format or whether it returns full activity details, but this is acceptable for a list endpoint with low complexity.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The description mentions 'up to the requested limit,' clarifying that the limit parameter controls the maximum number of results. The schema has 0% description coverage, so the description partially compensates by giving meaning to the parameter, but it does not elaborate on the default (20) or min/max constraints (already in schema).

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool returns the newest Run activities up to a limit, specifying verb (returns), resource (Run activities), and scope (newest, limit). It distinguishes from siblings like getRecentActivities (likely all types) and getActivitiesByType (with type filter).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly says 'Use for current training analysis without fetching unrelated sports,' providing clear context for when to use and what to avoid. It does not name specific alternatives but implies them through the exclusion of unrelated sports.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

getTrainingHistoryGet Strava Training HistoryA
Read-only

Returns one bounded page of activities (newest first) for long-term training analysis. When nextPage is non-null, call again with that page value for older activities. Use after/before (Unix seconds) to constrain the range.

ParametersJSON Schema
NameRequiredDescriptionDefault
pageNo1-based page; page 1 is most recent.
afterNoUnix timestamp in seconds (inclusive lower bound).
limitNoActivities per page (max 200).
beforeNoUnix timestamp in seconds (exclusive upper bound).

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already signal readOnlyHint=true for safety. The description adds behavioral details: pagination with bounded pages, newest-first ordering, and timestamp constraints. No contradiction; context beyond annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two efficient sentences: first states core purpose, second explains pagination and time range. Front-loaded, no extraneous words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Description covers pagination and time bounds but omits return format (e.g., full activity objects). No output schema. Still, it is complete enough for a read-only paginated tool with clear usage instructions.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% with descriptions for all 4 parameters. The description mentions 'nextPage' (not a parameter) and reinforces the use of after/before as Unix seconds, but adds little new semantic meaning beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states verb 'Returns', resource 'activities', scope 'one bounded page', sorting 'newest first', and purpose 'long-term training analysis'. This distinguishes it from siblings like getRecentActivities or getActivities.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explains pagination logic ('When nextPage is non-null, call again') and temporal constraints ('Use after/before'). It positions the tool for 'long-term training analysis', but does not explicitly exclude alternatives or state when not to use it.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 13 tool updatesv1.1.0
    • First observedgetActivities
    • First observedgetActivitiesByType
    • First observedgetActivity
    • First observedgetActivityLaps
    • First observedgetActivityStreams
    • First observedgetActivityZones
    • First observedgetAthleteContext
    • First observedgetAthleteOverview
    • First observedgetAthleteProfile
    • First observedgetHalfMarathonTrainingContext
    • First observedgetRecentActivities
    • First observedgetRecentRuns
    • First observedgetTrainingHistory

TDQS

A4/5.0

Scored across 13 tools

Disambiguation5/5

Each tool targets a distinct aspect of Strava data retrieval: activities by various criteria, activity details, laps, streams, zones, athlete profile, overview, context, training history, and a specific half marathon context. Descriptions clearly differentiate overlapping tools like getActivities, getActivitiesByType, getRecentActivities, and getTrainingHistory by specifying paging, filtering, and purpose.

Naming Consistency5/5

All tool names use consistent camelCase verb+noun pattern (get followed by resource name). This predictable naming helps agents quickly understand the action and target resource without confusion.

Tool Count5/5

With 13 tools, the server is well-scoped for a Strava planner. Each tool serves a specific purpose in retrieving training data and athlete context, without unnecessary duplication or missing essential operations for a read-only planner.

Completeness4/5

The tool surface covers core data retrieval: activity listing with filters, detailed activity data, laps, streams, zones, athlete info, training history, and half marathon context. Missing tools for updating athlete context or Strava settings, and for retrieving routes or segments, but these are minor gaps for a planner focused on training analysis.

Maintenance

ActivityMaintained
ResponsivenessSyncing

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    MCP server that connects Garmin Connect data to Claude, enabling training analysis, recovery checks, and personalized plans based on real metrics like HRV, training load, and activities.
    15
    MIT
  • F
    license
    Not graded
    quality
    D
    maintenance
    Provides MCP servers for Claude to access fitness data from Strava and intervals.icu, enabling natural language queries for activity analysis, advanced training metrics, and wellness tracking.
    1
    -