MCP server for Suunto sports tracking API access, providing tools for workouts, guides, wellness data, and user profiles with tiered access control (read/write/destructive)
Strong foundation with 35 well-named tools covering a cohesive Suunto API domain. All tools have descriptions (10-200 chars, well within baseline 34-392 range). Input schemas are present and typed for all tools. Naming follows verb_noun convention (workouts_list, workouts_get, workouts_delete). However, output schemas are not documented in the visible code, responses are inferred from endpoint calls rather than explicitly declared. Error handling exists but lacks recovery guidance (mapErrorToCallToolResult returns generic errors). Tool descriptions are functional but brief (avg ~80 chars); they state WHAT but rarely WHEN or WHY. Parameter descriptions are present and specific (e.g., 'unix-millisecond timestamp', 'sharing-flag bitmask'). No tool annotations (readOnlyHint, destructiveHint) visible in registration. Risk classification (READ_ONLY, WRITE, DESTRUCTIVE) is tracked but not exposed to the MCP protocol.
Map a numeric activity ID to its human-readable name (e.g. 1 → RUNNING). Local lookup; no API call.
Suunto server health probe (GET /v1/servertime). Unauthenticated.
Permanently delete a guide (DELETE /v1/suuntoplus/guides/files/{id}). Cannot be undone. Requires both --allow-write AND --allow-destructive.
Download a guide zip archive (GET /v1/suuntoplus/guides/files/{id}). Returns base64-encoded zip.
List guides (GET /v1/suuntoplus/guides/items).
Pin a guide (PATCH /v1/suuntoplus/guides/items/{id}). Requires --allow-write.
Output schemas not documented. Tool descriptions state what the API endpoint returns, but MCP response structures are not explicitly declared. LLMs cannot plan downstream tool calls without knowing what fields to expect.
Tool annotations missing. Risk classification (READ_ONLY, WRITE, DESTRUCTIVE) is tracked internally but not exposed via MCP readOnlyHint, destructiveHint, or idempotentHint. Agents cannot infer safety properties from the protocol.
Error handling lacks recovery guidance. mapErrorToCallToolResult returns generic errors without actionable next steps. E.g., 'User not found' should suggest 'Try search_users() with a partial name.'
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 64 | 2025-06-18+ | v2 |
Fetch guide priority/ordering metadata (GET /v1/suuntoplus/guides/priority).
Unpin a guide (PATCH /v1/suuntoplus/guides/items/{id}). Requires --allow-write.
Update a guide with new content from base64-encoded zip (PUT /v1/suuntoplus/guides/files/{id}). Requires --allow-write.
Upload a new guide from base64-encoded zip archive (POST /v1/suuntoplus/guides/files). Requires --allow-write.
Return social follow/block counts for the authenticated user (GET /v1/user/follow).
Return the authenticated user's settings (GET /v1/user/settings).
Look up a user profile by username (GET /v1/user/name/{username}).
Fetch activity data from the 24/7 wellness timeline (GET https://247.sports-tracker.com/v1/activity/export). Paginated by since/limit/order.
Fetch recovery data from the 24/7 wellness timeline (GET https://247.sports-tracker.com/v1/recovery/export). Paginated by since/limit/order.
Fetch sleep data from the 24/7 wellness timeline (GET https://247.sports-tracker.com/v1/sleep/export). Paginated by since/limit/order.
Fetch sleep stage data from the 24/7 wellness timeline (GET https://247.sports-tracker.com/v1/sleepstages/export). Paginated by since/limit/order.
Return the authenticated user's profile (GET /v1/user).
Apply multiple partial workout updates (POST /v1/workouts/batchUpdate). Requires --allow-write.
Post a comment on a workout (POST /v1/workouts/comment/{key}). Requires --allow-write.
Fetch comments on a workout (GET /v1/workouts/comments/{key}).
Return workout counts (GET /v1/workouts/count). Both until and sharingFlags are required by the server.
Permanently delete a workout (DELETE /v1/workouts/{key}/delete). Requires both --allow-write AND --allow-destructive.
Patch workout attributes (PUT /v1/workouts/{key}/attributes). Requires --allow-write.
Fetch per-workout extension payloads (POST /v1/workout/extensions/{key}). Despite POST, this is read-shaped — gated as write because cmd parity treats it under the write tier.
Fetch FIT file export for a workout (GET /v1/workout/exportFit/{key}). Returns base64-encoded binary.
Fetch a single workout summary by key (GET /v1/workouts/{key}). Response includes activityName alongside the numeric activityId.
List workouts (GET /v1/workouts). Paginated by since/limit/offset. Each item is enriched with activityName alongside the numeric activityId.
Add a 'like' reaction to a workout (POST /v1/workouts/reaction/{key}). Requires --allow-write.
Request a signed GPX share URL for a workout (PUT /v1/workouts/{user}/{key}/share/{format}). Requires --allow-write.
Fetch raw SML (Suunto Markup Language) for a workout (GET /v1/workouts/{key}/sml). When Streams or Downsample is set, returns filtered structured JSON instead of base64 blob.
Per-activity totals for a user (GET /v1/workouts/{username}/stats). Empty username defaults to the authenticated user.
Delete a comment by its comment key (DELETE /v1/workouts/comment/{commentKey}). Requires both --allow-write AND --allow-destructive.
Remove the caller's reaction from a workout (DELETE /v1/workouts/reaction/{key}). Requires both --allow-write AND --allow-destructive.
Upload a new workout from base64-encoded SML (and optional extensions JSON) (POST /v1/workout). Requires --allow-write.
Descriptions are functional but brief (avg ~80 chars). They state WHAT the tool does but rarely WHEN to use it or dependencies. E.g., 'workouts_sml' mentions streams/downsample filtering but doesn't explain why (1 MB response cap).
Pagination parameters present but no total_count or next_cursor returned. workouts_list accepts limit/offset but response structure is not documented, agents cannot determine if more results exist.