suunto-mcp
This MCP server enables authoring, compiling, and managing SuuntoPlus™ Guides (structured workouts) for Suunto watches. Key capabilities include:
Preview workouts (
preview_workout): Compile and validate workouts without uploading. Detects unit conversions, truncated titles, and validation errors. Workouts can include step roles (warmup, work, rest, recovery, cooldown, other), durations (time, distance, manual lap), intensity targets (pace, speed, heart rate with % of max or LTHR, power with % FTP, cadence), nested repeats (up to 100 reps), per-step skip control (allowSkip), sports/activities, date, descriptions, and closing messages. Requires athlete profile data (max HR, threshold HR, FTP, rest HR, threshold pace) for percentage-based targets.List workouts (
list_workouts): Retrieve stored guides with optional pagination (limit,offset) and time filtering (sinceas epoch ms).Describe the backend (
describe_backend): Check whether the active backend isfile,cloud, orprivate, and see which operations it supports.Create, update, and delete workouts (require
--allow-writeand--allow-destructiveflags respectively; tools are hidden if not permitted).Multiple backends: File (default, generates local
guide.zip), cloud API (needs subscription key and OAuth), and private mobile API (reuses suuntool session).Safety tiers: Read-only by default; write and destructive actions are only available when explicitly enabled. Unauthorized tools are not listed.
Athlete profile support: Manage parameters needed for relative intensity targets (e.g., max HR, threshold HR, FTP, rest HR, threshold pace).
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@suunto-mcpcompile my interval workout and preview how it will look on my Suunto"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
suunto-mcp
An MCP server for authoring and managing structured workouts (SuuntoPlus™ Guides) on Suunto, designed so the transport can be swapped without touching the workout model.
Not affiliated with or endorsed by Suunto Oy.
Why it's built this way
There are three possible ways to get a structured workout onto a Suunto watch, and they differ enough that the transport has to be a replaceable part:
Path | Status | Notes |
Cloud API ( | Documented, needs a subscription key | The sanctioned path. Contract fully captured in docs/cloud-api.md. |
Private mobile API ( | Fully mapped and live-verified, reads and writes, unsanctioned | Confirmed by static analysis of the Suunto Android app, then exercised for real: create → duplicate-externalId 409 → update → delete, all against a live account. Full write-up in docs/private-guides-api.md. Genuinely undocumented; needs no new credential — reuses a |
Local zip | Works today | Emit a validated |
suuntool deliberately isn't one of these paths: it has no guide-creation
capability at all. What it does have is a good, read-only MCP server of its
own — suuntool mcp — for completed activity and wellness data, comments,
reactions, and profile info. This server doesn't wrap or re-expose any of
that: run the two side by side as separate MCP configs
(claude mcp add suunto-mcp -- ... and claude mcp add suuntool -- suuntool mcp)
rather than have this one duplicate a surface suuntool already covers better.
The private backend's use of suuntool's session file (below) is credential
reuse, not functionality overlap — it's the reason suuntool is a prerequisite
for the private backend either way, so adding its own MCP server alongside
this one costs nothing extra.
Its exit codes double as this server's own error taxonomy: codes 2–7
(USAGE/NETWORK/AUTH_EXPIRED/SERVER/NOT_FOUND/FORBIDDEN) are
numerically identical on both, so a session that has expired in suuntool's own
session.json surfaces through the same code as an expired Cloud API token.
Related MCP server: fitMCP
The interesting part
The guide format is a display format, not a training format. It has no step
roles, no percentages, no nesting, and a 13-character title budget. So the domain
model is what a coach would write, and src/compile lowers it:
roles (
warmup/work/rest/…) → titles, notifications and lap marksdurations → a per-step
triggerplus a matching countdown fieldevery duration/distance trigger grants lap-skip by default — a compound
{type:"or", triggers:[base, {type:"manualLap"}]}pluscreateManualLap:true, confirmed live against a real Runna guide after a user reported this compiler's own workouts couldn't be skipped early. Opt a step out withallowSkip: falseto lock it instead.pace ranges → m/s, with the bounds inverted (4:15–4:25 /km is 3.77–3.92 m/s)
cadence → Hertz (180 spm is 3.0)
%HRmax/%FTP→ absolutes, resolved from the athlete profilenested repeats → flattened, keeping the outer block so the step budget survives
every string truncated and charset-sanitised for the watch display
Correctness is anchored on Suunto's own published sample guide, which is stored
verbatim in test/fixtures/ and used two ways: to prove the format model accepts
real Suunto output, and as the compiler's target.
Layout
src/domain/ workout model, guide wire format, validator, limits, activity IDs
src/compile/ the lowering compiler, unit conversions, externalId hashing
src/package/ zip packing (manifest.json + guide.json + icon.png)
src/backends/ the GuideBackend port and its implementations
src/mcp/ MCP server
scripts/ APK acquisition and static analysis for the RE track
docs/ captured API contracts and RE findingsRunning it
Tool tiers follow suuntool's: read-only by default, --allow-write to create and
update, --allow-destructive on top of that to delete. Gating happens at
registration, so a tool you have not permitted is absent from the listing
entirely rather than present and always refusing.
claude mcp add --scope user suunto-mcp -e SUUNTO_MCP_BACKEND=private -- node /path/to/suunto-mcp/dist/mcp/main.js --allow-writeTier | Tools |
read |
|
|
|
|
|
preview_workout compiles and validates without uploading, and returns the
warnings — start there.
For completed-activity and recovery data, add suuntool's own MCP server as a
separate config rather than expecting this one to cover it:
claude mcp add --scope user suuntool -- suuntool mcpConfiguration
Variable | Purpose |
|
|
| Where the file backend writes; defaults under |
| Creator name. Must match the OAuth app name for the Cloud API |
|
|
| Static bearer token, for trying the API by hand |
| Enables refresh of the 24h token |
| Athlete profile, needed only for |
Configuration is validated at startup and a bad config is a hard exit — an MCP server that starts and then fails every call is much harder to diagnose.
Development
pnpm install
pnpm test
pnpm typecheckReverse-engineering track
scripts/pull-apk.sh # pull the APK off a connected Android device
scripts/analyze-apk.sh # stage 1: fast dex string scan
scripts/analyze-apk.sh 2 # stage 2: full jadx decompile, only if neededapk/ and capture/ are git-ignored and must stay that way — captures contain
session keys and account identifiers.
Available Tools
3 toolsdescribe_backendDescribe the active backendARead-only
Report which backend is active and which operations it supports. Check this before planning a sequence of changes — backends differ in what they can do.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations include readOnlyHint=true, and the description does not contradict this. The description adds the context that backends differ in capability, but does not disclose additional behavioral traits such as output format or performance characteristics. With read-only status already declared, the added value is modest, so a 3 is appropriate.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, front-loaded with the core function, and the second sentence provides actionable usage advice. No redundancy or fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter, read-only introspection tool, the description fully covers what the tool does and when to use it. It explains the output at a high level (active backend and supported operations) and does not need an output schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so the description carries no parameter burden. Per the rubric, the baseline is 4, and the description meets that baseline by not omitting anything required.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses the specific verb 'report' with the resource 'active backend' and 'which operations it supports', clearly distinguishing it from the workout-related sibling tools. The title reinforces the same.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly instructs to 'Check this before planning a sequence of changes', providing clear timing guidance. It also mentions that 'backends differ' which explains why checking is necessary. No alternatives are named, but none are needed given the tool's unique introspection role.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_workoutsList guidesBRead-only
List structured workouts stored on the configured backend.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| since | No | Epoch ms; only guides modified at or after | |
| offset | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The readOnlyHint annotation already signals a safe read operation, so the bar for additional transparency is lower. The description adds 'stored on the configured backend' but does not disclose pagination behavior, default limits, ordering, or whether the list is comprehensive.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single, well-structured sentence that directly states the tool's purpose. No wasted words or redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema and a paginated list operation (limit/offset/since), the description is too sparse. It does not mention return format, whether results are ordered, or how pagination works, leaving the agent guessing about the actual invocation behavior.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is only 33% (only 'since' is described). The tool description adds no explanation for limit, offset, or since. It fails to compensate for the low schema coverage, leaving agents uncertain about how parameters affect results.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states a specific action ('List') and resource ('structured workouts') plus context ('configured backend'). It distinguishes from siblings: preview_workout implies a focused view of a single workout, and describe_backend is about backend metadata, whereas this tool returns a list.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
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 its alternatives. There is no mention of exclusions, prerequisites, or situations where preview_workout or describe_backend would be more appropriate.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
preview_workoutPreview a workoutARead-only
Compile a structured workout into the SuuntoPlus guide format and return it WITHOUT uploading. Use this first: it surfaces unit conversions, truncated titles and validation errors, so mistakes are caught before anything reaches the watch.
| Name | Required | Description | Default |
|---|---|---|---|
| date | No | yyyy-MM-dd. Pins the guide to a date in the Suunto calendar | |
| steps | Yes | The session, in order | |
| title | Yes | Guide name, max 60 chars | |
| profile | No | Required only when the workout uses pctMax, pctLthr or pctFtp targets | |
| activities | No | Sports this guide offers itself for. The first drives pace-vs-speed display | |
| externalId | No | Stable id for idempotent re-pushes. Derived from the session when omitted | |
| description | Yes | Shown in the Suunto app listing, max 256 chars | |
| finalMessage | No | Closing screen shown when the session ends, max 13 chars | |
| shortDescription | No | Shown on the watch itself, max 23 chars. Defaults to title |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare readOnlyHint=true, and the description complements this by stating 'WITHOUT uploading' and detailing the types of errors it surfaces. This adds behavioral context beyond the annotation, such as the tool's side-effect-free nature and its focus on validation, without contradicting any annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, front-loaded with the core function, then a direct usage recommendation. No wasted words; ideal for a complex tool where the schema carries parameter details.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the high schema complexity (nested steps, many optional fields) and no output schema, the description provides useful context about behavior (compilation, no upload, error surfacing). It doesn't describe the return value format, but the purpose and usage guidance are sufficient for an agent to invoke correctly, especially with the schema covering parameter semantics.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, meeting the high-coverage threshold, so a baseline of 3 applies. The description text does not need to add parameter-level meaning; it only mentions outputs like 'unit conversions' and 'truncated titles', which are consequences of parameter processing rather than direct parameter semantics.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb-resource pair ('Compile a structured workout into the SuuntoPlus guide format') and explicitly states it returns without uploading, distinguishing it from sibling tools like list_workouts and describe_backend. The phrase 'Use this first' reinforces its unique role.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Clear guidance is provided: 'Use this first' followed by what it surfaces (unit conversions, truncated titles, validation errors) and why (catch mistakes before they reach the watch). This implies it's a pre-upload validation step, though it doesn't explicitly name alternative upload tools 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.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
3 tool updates
v0.0.1- First observed
describe_backend - First observed
list_workouts - First observed
preview_workout
TDQS
Scored across 3 tools
Each tool has a clearly distinct purpose: preview_workout compiles and validates without uploading, list_workouts retrieves stored workouts, and describe_backend reports backend capabilities. There is no ambiguity or overlap between them.
All tool names follow a consistent verb_noun pattern in snake_case: preview_workout, list_workouts, describe_backend. This is predictable and easy to navigate.
With only 3 tools, the server is at the lower end of the ideal range, but each tool earns its place. The count feels slightly thin for a full workout management workflow, yet is appropriate for a focused validation and overview server.
The server lacks core operations such as create, update, delete, or upload for workouts. It only supports previewing and listing, with describe_backend to check capabilities, leaving significant gaps for any actual modification or synchronization workflow.
Maintenance
Related MCP Connectors
Remote MCP server for training, nutrition, wellness, and performance data with OAuth 2.0.
MCP server for Zooza — class scheduling, attendance, and booking for activity businesses.
MCP server for Withings health data — sleep, activity, heart, and body metrics.
MCP server for the Inistate platform: module discovery, entry management, and activity submission.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceRunning-first MCP server for COROS that allows authoring, editing, and scheduling running workouts, plus accessing sleep, HRV, training load, and activity exports.MIT
- FlicenseNot gradedqualityBmaintenanceA multi-platform fitness MCP server that syncs data from Garmin, Strava, Google Fit, and Suunto into a local DuckDB database and provides analytics tools via MCP.1-
- AlicenseAqualityBmaintenanceMCP server for reading and querying Garmin Connect data, including activities, strength history, recovery, trends, and optionally creating workouts.12MIT
- AlicenseAqualityBmaintenanceAn MCP server that converts structured cycling workout specs into MyWhoosh .zwo and Garmin Connect workout files, with tools for validation, description, and rendering. It also includes skills for uploading workouts to both platforms.6MIT