suunto-mcp

suunto-mcp

An MCP server for authoring and managing structured workouts (SuuntoPlus™ Guides) on Suunto watches, with swappable transports (file, Cloud API, or private mobile API) and a coach-oriented domain model that lowers to the display-oriented guide format.

Category
访问服务器

README

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 (cloudapi.suunto.com/v2/guides) Documented, needs a subscription key The sanctioned path. Contract fully captured in docs/cloud-api.md.
Private mobile API (suuntoplus/guides/*) 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 suuntool session.
Local zip Works today Emit a validated guide.zip; no auth involved.

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.

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 marks
  • durations → a per-step trigger plus a matching countdown field
  • every duration/distance trigger grants lap-skip by default — a compound {type:"or", triggers:[base, {type:"manualLap"}]} plus createManualLap: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 with allowSkip: false to 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 profile
  • nested 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 findings

Running 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-write
Tier Tools
read preview_workout, list_workouts, describe_backend
--allow-write create_workout, update_workout
--allow-destructive delete_workout

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 mcp

Configuration

Variable Purpose
SUUNTO_MCP_BACKEND file (default), cloud, or private (reuses a suuntool session; see the warning above)
SUUNTO_MCP_OUTPUT_DIR Where the file backend writes; defaults under ~/.local/share
SUUNTO_OWNER Creator name. Must match the OAuth app name for the Cloud API
SUUNTO_SUBSCRIPTION_KEY Ocp-Apim-Subscription-Key, required for cloud
SUUNTO_ACCESS_TOKEN Static bearer token, for trying the API by hand
SUUNTO_CLIENT_ID / SUUNTO_CLIENT_SECRET Enables refresh of the 24h token
SUUNTO_MAX_HR, SUUNTO_THRESHOLD_HR, SUUNTO_FTP, SUUNTO_REST_HR Athlete profile, needed only for %HRmax / %FTP targets

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 typecheck

Reverse-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 needed

apk/ and capture/ are git-ignored and must stay that way — captures contain session keys and account identifiers.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选