runcoach
An MCP server that lets Claude read your Garmin training and recovery data, explain rule-based coaching decisions, and plan workouts you can approve onto your watch.
Get the coaching rulebook (
get_coaching_reference) — the full decision and planning rules, called once per conversation.Decide today's training (
get_training_readiness) — GO/EASY/REST verdict with the underlying HRV, sleep, Body Battery, resting HR, ACWR and hard-day signals; auto-syncs stale data.Review recovery (
get_recovery_summary,get_daily_metrics,get_trend) — averaged windows, one-day snapshots, and weekly metric trends.Assess training load and fitness (
get_training_load,get_vo2max_history) — ACWR with source, Garmin training status, VO2max steps, and 28-day comparisons.Inspect what was actually trained (
get_recent_activities,get_intensity_distribution,analyze_workout) — workout digests, 80/20 zone split, and deep dives into a single run's intervals, zones, weather and performance condition.Refresh data on demand (
sync_garmin) — pull the latest 1–7 days from Garmin Connect into the local database.Plan sessions (
propose_workout,propose_week) — build easy/long/threshold/VO2max/steady sessions or a full polarised week from your own zones, returned as previews only.Write to Garmin with explicit consent (
apply_workout,undo_workout) — upload, schedule, push to the watch and verify a proposal, or take an applied session/week back off; unavailable to the app's own card runs.
Integrates with Garmin Connect to sync and analyze running data, providing readiness scores, intensity bands, interval detection, and training insights.
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., "@runcoachshould I train today?"
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.
Runspector — AI running coach for Garmin
Formerly runcoach-ai: same project, renamed in v0.5.0. Old links redirect here. Coming from it: three steps to switch over, and your data moves by itself.
A free, open-source AI running coach for Garmin watches. Runspector syncs your Garmin Connect data - HRV, sleep, Body Battery, resting heart rate, training load and every run - into a file on your own computer, decides each day whether to go hard, keep it easy or rest, and lets Claude explain why and plan your week: in a small local web app, or as a Garmin MCP server in Claude Code and Claude Desktop. No cloud, no API key needed - your own Claude Code sign-in is enough.
I came to running late, and for a long time my VO2max would not move. I had always trained a lot, and it had never really shown up in that number — so I assumed more of the same would eventually work. It did not. I was training wrong, in ways the watch on my wrist had the data to show me and never did: Garmin collects everything and explains nothing.
That was the idea. The data is already there; let an AI read it and coach — with the training aimed squarely at raising VO2max rather than at collecting kilometres, and with the decision rules written down so the coach explains them instead of improvising. This repository started as that tool for one athlete. It is now installable for anyone with a Garmin watch and Claude Code (a Claude plan is enough).
No cloud backend, no API key needed, no Docker. One Python package that syncs your Garmin data into a SQLite file on your machine, shows it in a small web app, and lets Claude act as your coach — with a deterministic readiness engine underneath, so the AI explains decisions instead of inventing them.
If you came for the engineering rather than the running, the five decisions this repository is
actually about — each with the measurement behind it — are in
Design decisions. The short version: the verdict is
pure code and only the explanation is AI · the agent runs on
an allowlist, not a denylist (--tools "", one MCP server, empty temp cwd) ·
prompts are regression-tested by an LLM-as-judge suite whose last run is
in the repo · quantities that exist in two languages are pinned by
executing the shipped JavaScript against the Python · and the
release gate scans binaries too, with a false-positive counter-case. CI runs lint,
988 tests, 80 frontend tests and that gate on three operating systems and two Python versions, with every
action pinned to a commit SHA — and a second job that builds the wheel, installs it as a user would and
drives the installed executable through this quick start on all three systems,
because the suite proves the code and only an install proves the package.
https://github.com/user-attachments/assets/0b4d7d86-5ab7-439b-82cb-89a004b71405
The 90-second demo, with voice-over: the synthetic demo athlete, rebuilt from this repo by scripts/video.py (file). No player above? The GitHub mobile app shows no videos: ▶ play the demo in the browser.
The built-in synthetic athlete (runspector serve --demo). Every image is regenerated by
python scripts/screenshots.py, and the coach card is an unedited agent run against that same
demo database, kept in demo-card.json so the picture cannot
drift from the app — the decision it explains (EASY, a day after a hard session) is the one on the Today tab
beside it. docs/screenshots/ has each of the five tabs full-length, in both
themes — e.g. today.png and
today-light.png.
Not affiliated with Garmin. Uses the unofficial python-garminconnect library against your own account. This is a training tool, not medical advice.
Quick start
# 1. install (needs uv: https://docs.astral.sh/uv/)
uv tool install runspector
# without uv: pipx install runspector
# 2. look around without any account
runspector serve --demo
# 3. the real thing
runspector serve # first start: asks for the Garmin login (e-mail, password, MFA code -
# only session tokens are stored), checks Claude, then opens
# http://127.0.0.1:8765 and syncs the last 30 days in the backgroundClaude is not connected inside runspector. Coach cards run on the
Claude Code CLI on your machine, with your own sign-in - a Claude plan is
enough: install it and run claude auth login once. runspector serve checks that at start (and offers the login when
it is missing), the Coach tab says what is missing, and no API key is needed. A key or a cloud provider set up in
Claude Code itself is used by the cards too, and billed there; one only exported in the environment
is stripped unless you set RUNSPECTOR_ALLOW_API_KEY=1. Garmin can be
signed into from the page itself (Setup → Sign in to Garmin, or Sign in again when the session has
expired) - e-mail, password and Garmin's code if your account uses one, and the sync starts right after.
runspector login does the same from any terminal, and a running app picks it up on ↻ - no restart.
runspector doctor tells you what is missing — it checks the Garmin session by actually
using it, and reports the data age, because a token that quietly expired is how this kind of app dies.
Use runspector doctor --offline to skip the Garmin call. Exit 1 means something needs fixing, so
runspector doctor || notify-me works in a cron; a missing Claude CLI is reported but does not set it,
since everything except the coach cards works without it.
Garmin MCP server for Claude Code and Claude Desktop
The same data, and the same rules, as an MCP server with 16 tools - among them today's readiness verdict, recovery, training load, recent runs, intensity distribution, the VO2max history, what the coach has learned about you, a Garmin sync - and propose, apply and undo for a workout on your watch.
claude mcp add runspector -- runspector mcp
# then just ask: "should I train today?"
# or: "plan me an easy 40 minutes for tomorrow" - you see the session, and it goes
# on your watch only after you say yes ("take it back off" undoes it)In Claude Desktop, or any MCP client with a JSON configuration (sign in once first: runspector login):
{ "mcpServers": { "runspector": { "command": "uvx", "args": ["runspector", "mcp"] } } }Keep it fresh without the app (cron / Task Scheduler): runspector sync.
Later on:
uv tool upgrade runspector # the newest release; your data and Garmin session stay where they are
uv tool uninstall runspector # removes the program only - delete ~/.runspector yourself if you want the data goneuv tool install runspector takes the latest release from PyPI (runspector==0.5.0 pins one; pipx install runspector
and pipx upgrade runspector do the same without uv);
uv tool install git+https://github.com/edxleon/runspector takes whatever is on main.
Related MCP server: garmin-mcp
Questions people ask
How do I connect my Garmin data to Claude?
Install runspector, sign in to Garmin once (runspector login, or the button on the page), and either
open the app (runspector serve) or add it to Claude Code as an MCP server:
claude mcp add runspector -- runspector mcp. Then ask "should I train today?". See
the MCP server for Claude Desktop.
Do I need a Garmin developer account or an API key?
No. runspector signs in with your own Garmin account, through a community library - see Can Garmin block my account? below. On the Claude side no API key is needed either: a Claude plan and Claude Code are enough.
Does it work with Apple Watch, Coros, Polar or Strava?
No - Garmin only. Every number comes from Garmin Connect, including Garmin's own readings (HRV status, Body Battery, training load, VO2max), and the workouts it plans go back onto a Garmin watch.
Is my Garmin password stored?
No. It goes to Garmin once, to sign in. Garmin answers with a
session token, and only that is kept, on your computer, in a folder only your user account can read.
The page's sign-in takes the password only on the computer runspector runs on: opened from a phone,
the page is plain HTTP across your network, so there it points to runspector login instead.
Does it cost anything extra?
runspector is free (MIT). Coach cards use your Claude plan the way any Claude Code use does, with no API key and no bill per card. Without Claude, everything except the cards still works.
What does Claude see?
The numbers this app shows (readiness, recent runs, zones, weekly load), and only when you start a card or ask in Claude Code. It never sees your Garmin password, the session token or any raw file. It reaches Claude through your own account, under your plan's terms.
Can it mess up my watch or my calendar?
Reading is all it does on its own. Something goes to Garmin only when you press "Put on watch" or say yes in Claude Code, and then only the session shown to you. "Take off watch" removes it again. The coach's own card runs cannot write at all.
Can Garmin block my account?
runspector signs in through python-garminconnect, a community library that Garmin does not officially support. A daily sync makes about 25-40 requests and the first one (a month of data) a few hundred. It pauses between workout downloads, never fetches more than 90 days, and stops at the first rate-limit answer instead of retrying into it. That is no guarantee, but it is not scraping either.
Is this medical advice?
No. It is a training tool. Pain, dizziness or anything that feels wrong goes to a doctor, and the coach says so instead of planning around it.
How do I remove everything?
Delete ~/.runspector (the app's footer shows the folder) and run
uv tool uninstall runspector. To cut the token off at Garmin as well, change your Garmin password.
What you get
Tab | What it answers |
Today | GO / EASY / REST with the signals behind it, one decision sentence for today and, on a hard day, which hard session (VO2max intervals, threshold reps, or the steady block Garmin measures VO2max from), your VO2max with the days since it last really changed, this week planned vs. done, last night's sleep, your zone bounds with their origin |
Runs | Every run with its intensity band, real interval structure ( |
Trend | VO2max as honest steps (and whether a measurement is due), lactate threshold history, weekly hard-minute share vs. an 80/20 target, aerobic efficiency (pace at a fixed heart rate) with spread and trend, volume, polarisation, ACWR, and Garmin's own load focus (its 4-week load per band against its target range) as a second opinion |
Coach | Ask the coach anything in your own words (what am I doing wrong?) - a short card, the biggest lever first. One-tap analyses by Claude as short cards that remember the previous card of their kind, accept your 👍/👎, take follow-up questions and can be deleted. Two start here (Why is VO2max moving? · Review my week); the other two start where their subject is — Should I train today? on the Today tab, Analyze this run on a run |
You | Your week, the foundation the planner goes by: runs per week, your days, long run, days you can't run, another sport - read from your history and kept up to date, and yours to change: what you set wins until you hand it back. For your coach: a note in your own words ("gym Mon, Wed, Fri", "less time until December") the coach reads before every answer. And what the coach has learned about you, each with its evidence: how consistent you are, your intensity pattern, the VO2max trend, Garmin's load focus, resting-HR baseline and zones - and since when, where something changed. Every card and the MCP tool |
How it works
flowchart TD
G[Garmin Connect] -- "token resume, read" --> S[sync.py]
S --> DB[(SQLite<br/>~/.runspector)]
DB --> L[logic.py<br/>readiness · decision<br/>intervals · bands]
L --> SNAP[snapshot.py]
SNAP --> WEB[web app<br/>127.0.0.1:8765]
L --> MCP[MCP server<br/>16 tools<br/>13 read-only · sync · apply · undo]
WEB -- "spawn job" --> A["claude --print<br/>(your Claude sign-in)"]
A -- "only mcp__runspector__*" --> MCP
A -- "JSON card" --> WEB
MCP --- CC[Claude Code / Desktop]Design decisions
The verdict is code, the explanation is AI. GO/EASY/REST, today's decision, interval detection and
intensity bands are pure functions in logic.py — unit-tested, no network, no
LLM. The facts and the decision are computed once there, and the agent is handed the finished
decision through get_training_readiness rather than left to derive its own: a coach card is a reading
of that decision, not a second, independently produced verdict. Claude may explain it, add the context a
rule cannot — or disagree, in which case it has to say which number it disagrees with. Claude's job is
the part rules are bad at: weighing a conflict, phrasing a plan, answering a follow-up.
Your own Claude Code sign-in, not a key in the app. Coach jobs run claude --print as a subprocess,
signed in the way you signed Claude Code in. On a Claude plan there is no key to leak, no per-token bill,
nothing to configure. API-key variables exported for other work are stripped from the card runs by
default, so they never bill a card unasked; RUNSPECTOR_ALLOW_API_KEY=1 keeps them. A key or a cloud
provider configured in Claude Code itself is beyond any variable: the cards use it, and the setup check
says so - it asks Claude Code in the same environment and the same kind of empty directory the cards run in. If your quota is exhausted the job waits for the next
window instead of failing.
The agent gets an allowlist, not a denylist. Workout titles are attacker-controlled text that ends
up in a prompt. So the coach agent runs with --tools "" (no Bash, no file access, no web),
--strict-mcp-config with exactly one server — this one — and only mcp__runspector__* pre-approved, in an
empty temp directory. The card comes back as JSON and is validated and written by the app, not by the
agent — and an answer produced without a single tool round trip is discarded: a model that cannot reach
the data will otherwise invent it (this was measured, not assumed).
See web/agent.py.
What that does and does not buy, stated plainly: an injected workout title cannot reach anything but
this app's own tools — no shell, no filesystem, no network, no other MCP server, and no write path
from a card run: every tool is read-only except sync_garmin, which triggers authenticated requests to
your own Garmin account and so is worth a rate limit rather than nothing, and apply_workout, the one
tool that writes to Garmin — which the app's card runs cannot call at all (--disallowedTools, pinned
by a test), because applying a proposal is a human's click on the card or their word in a Claude Code
session, never a job's decision. What it can still do is influence
what a card says — and the card is the product. And cards are remembered: the previous card of the same
kind is fed back as context for the next one, so a bad card echoes forward until you delete it (the UI
has a delete button per card).
Aggregates for the model, series for the UI. MCP tools never return day-by-day rows for a period — daily series bloat a context window and get misread. The agent sees summaries and weekly buckets; only the frontend, which draws curves, gets points.
Honest numbers. A few examples of what that means in practice:
Garmin carries VO2max forward on days without a measurement, so a change is only reported when the value actually varied — never as a difference of window endpoints.
A self-computed ACWR needs ≥ 21 days and ≥ 8 workouts, is labelled
computed, and may dampen a verdict to EASY but never drive REST.A GO from fewer than two recovery signals is downgraded: thin data is not green.
If the sync is stale, today's decision is
unknownrather than yesterday's verdict in today's clothes.An incompletely fetched calendar month does not replace the local mirror ("nothing planned" would be a lie from a degraded source).
Garmin's lactate-threshold pace field is off by a factor of ten; it is normalised, range-checked, and loudly dropped if the unit ever changes.
A planned week lands on the athlete's own days: how many days, which weekdays and the long-run day are read from the last eight weeks with running (looking back half a year, and before a pause while coming back from one), and the quality sessions keep clear of a weekly other sport and the day after it - a gym Tuesday leaves room for one quality session, not two. A rotating every-other-day rhythm has no weekdays of its own and is planned in the default rhythm. Each value says where it came from: "long run on sat - your longest run fell on sat in 8 of 8 weeks".
A planned week is sized from the athlete's own running weeks - rolling weeks ending on the day of planning, never more than 10 % over the bigger of the last two - and then read against their history, in code: the load ratio can hold or unload it, every fourth week is a down week, ten days without a run make a return week of easy running only (half the weeks before after more than three weeks off), and twenty-eight days without a new VO2max value put a steady block in the threshold slot. A budget too small for the planned days plans a shorter week - one quality session at three days, never a lone hard one - instead of inflating the sessions. A week with one quality session takes the one that is due (VO2max until the zone-5 stimulus is set, then threshold, or the measurement). Back from a pause the athlete rebuilds: the step grows from the last seven days, not from a median that lags the climb, and there is no down week until the weeks are back to 80 % of the level before the pause. A step is never smaller than the week planned before it, and a week held or unloaded on the load ratio is never bigger: the median it holds rises with every week run above plan, which is what raised the ratio. Each rule is a named assumption on the proposal, none of them a prompt's discretion. On a hard day the decision also says which hard session, from what the last seven days did and did not contain; after a pause the day is easy instead.
Without a measured threshold pace, sessions are sized from Garmin's own 5 km and 10 km predictions rather than from a 5:00/km default that fitted one athlete.
Those rules are run, not only unit-tested:
evals/sim.pyputs eight synthetic athletes - one who follows the plan, one who skips 40 %, one who takes three weeks off, one who falls ill, one who always runs harder - through sixteen weeks of the app, day by day against the real tools, and checks what a runner would expect of a coach over the whole course. It found the two planner bugs above. It cannot tell whether the training works - its body is a toy - and it finds only what its data model knows, so the live test on a real account stays.
Prompts are tested like code. The coach's behaviour lives in two Markdown files
(skills/) with an LLM-as-judge regression suite (evals/):
32 cases such as "a run already happened today → no second hard session", "warning signs of low energy
availability are not explained away", "an instruction inside a workout name is not followed". They run
without Garmin data against the real CLI — though only over those two files, not over the other two
pieces of the shipped prompt (templates.json and the JSON-card frame in web/agent.py, which
tests/test_web.py covers deterministically instead), and under --safe-mode on whichever model the
suite is pointed at, neither of which production can use. The framing itself is imported from the app
rather than copied, so the suite cannot drift from what ships. --no-skills re-runs a case with the
skill files removed: a case that still passes is testing the model, not the prompt.
Boring technology. Python stdlib HTTP server, sqlite3, vanilla ES modules, inline SVG charts. No
framework, no build step, no ORM. ~40 SQL queries, all in store.py.
Runs on Windows, macOS and Linux (CI matrix).
More detail: docs/architecture.md.
Privacy
Everything stays in ~/.runspector/ (override with RUNSPECTOR_HOME): the SQLite database, Garmin session
tokens, coach cards. Your password is never stored. On Linux and macOS the directory is created 0700,
so on a shared machine other local accounts cannot read those files; Windows inherits the user profile's
ACL instead. While runspector serve runs, that no longer covers them: without a token any local account
or process can ask 127.0.0.1:8765 for your data, and since v0.2 apply or undo a proposal with your
Garmin session. On a machine you share, start it with RUNSPECTOR_TOKEN set - the server then wants the
token on loopback too, and prints the link to open once. The only outbound connections are to Garmin
(sync) and — when you trigger a coach card — to Claude through your own CLI session, carrying
aggregated training data. The server binds to 127.0.0.1; listening on the network requires
RUNSPECTOR_TOKEN and the server refuses to start without it. Note that --host 0.0.0.0 serves plain
HTTP with no TLS, so on that network the token and everything the app returns travel unencrypted and
readable by anyone who can see the traffic — it is meant for your own phone on your own LAN, not for a
network you do not control.
Configuration
Variable | Default | Purpose |
|
| data directory |
| system zone | IANA zone for "which day was this run" |
| – | required for |
| CLI default | model for coach cards |
|
| reuse an existing python-garminconnect token dir |
|
| in a Claude Code / Desktop chat, |
|
| how long a job waits for subscription quota |
|
| hard timeout for a single coach job |
| unset |
|
|
| minimum window of the workout list a sync fetches (your habits look back 26 weeks; zone detail is fetched for the last 35 days) |
|
| how far back the lactate-threshold history is fetched |
|
| log level — set |
Four more (RUNSPECTOR_DB, RUNSPECTOR_DEMO, RUNSPECTOR_CLAUDE_CMD, RUNSPECTOR_PII_EXTRA) exist for
internal plumbing and tests; they are not part of the supported surface.
Optional athlete profile: runspector profile --max-hr 182 --goal "sub-50 10k". Your week - days
per week, your days, long run, days off, another sport - is set on the You tab (or with
--days-per-week and --long-run-day); whatever you leave open the planner reads from your
history: the days you usually run, and the weekday your longest run falls on. A day off is never
planned on. The note to your coach is context for the coach, not a rule: the days are what the
planner itself keeps to.
Development
git clone https://github.com/edxleon/runspector && cd runspector
uv run pytest -q # no database server, no network, no Garmin account needed
node --test "web-tests/*.test.mjs" # frontend logic
uv run ruff check # lint — config is real and the backlog is zero
npx --yes @biomejs/biome@2.5.15 lint --error-on-warnings # frontend lint (biome.jsonc)
uv run python evals/run_evals.py # prompt regression (uses your Claude subscription)
uv run python scripts/pii_gate.py # release gateStatus & roadmap
v0.1 was read-only towards Garmin. v0.2 adds the write path: propose_workout builds a session
for your route from your own zones and files it, propose_week files a polarised week as one package,
a proposal can replace the hard session the calendar had on a red day, and apply_workout uploads,
schedules, pushes to the watch and reads back to verify. The app's card runs cannot apply anything;
in the app a proposal is applied by a click on the card ("Put on watch", two taps), in Claude Code by
your answer — and taken back off the same way, because a write you cannot undo is a write nobody makes
the first time.
What has been run against a real account, not only the fake Garmin client the suite uses: on
2026-10-28, one easy session through runspector mcp — proposed from the athlete's own zones, uploaded,
scheduled, pushed, seen on the watch, read back and verified, then undone — with every step
cross-checked by a second, independent Garmin client. The app's button calls the same plan.apply and
plan.undo. Not yet run for real: propose_week as a package, and the red-day swap that unschedules a
calendar entry. One athlete, one account, one watch.
Roadmap
docs/roadmap.md is the backlog, ordered by what it would do for the runner: an
athlete's own check-in (ill, sore, how it felt), sessions with your own structure, the proposed
session matched against the run that followed, a runspector today for the morning, deeper history,
body weight and Garmin's own readiness alongside the app's, and race preparation for those with a
date. v0.3 made the planner read the history for what comes next; the rest is there with the
reasoning and the order.
License
MIT
Available Tools
15 toolsanalyze_workoutA
Deep dive into ONE run: time in each HR zone, interval structure (rep count, rep length, work vs recovery HR), weather, performance condition, plus rule-based notes. Default: the latest run with detail. The interval structure is Garmin's auto-detection — if the athlete states a different structure, the athlete is right.
| Name | Required | Description | Default |
|---|---|---|---|
| day | No | ISO date YYYY-MM-DD. Omit for the latest. | |
| activity_id | No | Garmin activity id; wins over `day`. |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the behavioral burden and does meaningful work: it discloses reliance on Garmin's auto-detected interval structure and provides a fallback rule that the athlete's stated structure wins. It does not cover side effects, auth, or failure modes, but the tool is a read-only analysis and an output schema exists.
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?
Three sentences front-load the purpose, compactly list the outputs, and close with an important caveat. There is no filler 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?
An output schema exists, so return values do not need describing. The description covers the operation, default selection, and an interpretation rule; minor gaps remain around what 'rule-based notes' contain and when activity_id is necessary beyond the 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?
Schema description coverage is 100%, so the baseline is 3. The description reinforces the default latest-run behavior and the single-run focus, but it does not add semantics beyond what the schema already provides for 'day' and 'activity_id'.
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 states a specific task, 'Deep dive into ONE run', and enumerates concrete outputs: HR zones, interval structure, weather, performance condition, and rule-based notes. It is distinguishable from siblings like get_trend or get_intensity_distribution by its single-run scope, though it does not explicitly name alternatives.
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?
The 'Default: the latest run with detail' gives a clear default invocation, and the wording implies this is for granular single-run analysis. However, it does not state when to prefer this tool over siblings such as get_recent_activities or get_intensity_distribution, nor any exclusion conditions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
apply_workoutA
WRITE a proposed session to the athlete's Garmin account: upload the workout, schedule it on the proposal's day, push it to the watch, then read it back and verify structure and targets. Call ONLY after the athlete has seen the preview and explicitly agreed - a request ("plan me 10 km") is not agreement, "yes, put it on the watch" is. Not available to the app's own card runs; there the athlete clicks. Returns what is now on Garmin, with any warning (not pushed because the watch is offline; a mismatch found on read-back). A proposal can be applied once; an unknown or used id says so and lists the open ones.
| Name | Required | Description | Default |
|---|---|---|---|
| proposal_id | Yes | The id propose_workout returned. |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries full burden and delivers: it discloses that this is a write operation with side effects, that it pushes to the watch and then verifies, possible warnings (offline watch, mismatch), single-use semantics, and the response content. No contradictions or unsupported claims.
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?
Every sentence earns its place: purpose, consent gate, card-run exception, return value, and id behavior. The purpose is front-loaded with 'WRITE', and the structure is dense without being bloated.
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?
The description covers when to call, what it does, what can go wrong, error outputs, and return value, and an output schema covers the structured result. Nothing required for correct invocation is missing, even without annotations.
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 sole parameter proposal_id is already well documented in the schema (pattern + 'returned by propose_workout'), so the baseline is 3. The description adds valuable lifecycle semantics: the proposal can be applied once, and unknown/used ids produce an explanatory response listing open proposals.
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 opens with 'WRITE a proposed session to the athlete's Garmin account' and specifies the full sequence: upload, schedule, push, read back, verify. This clearly distinguishes it as the mutation counterpart to read-only siblings like get_* and propose_workout, while naming the artifact it operates on.
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?
It states an explicit gate: call only after the athlete has seen the preview and explicitly agreed, and gives concrete examples of what counts and what does not. It also declares a when-not case ('Not available to the app's own card runs; there the athlete clicks').
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_coaching_referenceA
The coach's full rule set: how to read the data, today's decision, the planning rules, zones and intensity distribution, the energy boundary and the sources behind each threshold - the same text the app's own coach works from. Call it ONCE per conversation, before the first training advice, and follow it. No arguments, no Garmin call; it reads nothing of the athlete.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden and does disclose key traits: no arguments, no Garmin call, no athlete data read, and a once-per-conversation cadence. It could say more about caching or whether the text changes over time, but the safety and side-effect profile is clear.
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?
Purpose is front-loaded and every sentence carries information, though the itemized content list is long and slightly leans on breadth over brevity. No filler or restatement of the tool name.
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?
An output schema exists, so return-format detail is unnecessary; the description still characterizes payload content well. For a zero-param fetch tool, the invocation rules and side-effect statement make the definition complete.
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?
Zero parameters gives a baseline of 4, and the description reinforces this with 'No arguments, no Garmin call'. There is no additional semantic depth to add beyond that.
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?
States a specific verb and resource (coaching reference / full rule set) and enumerates its contents, so the agent knows exactly what comes back. It is clearly distinguished from every sibling, which fetch athlete data, by saying it 'reads nothing of the athlete'.
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?
Gives an explicit trigger and an explicit limit: 'Call it ONCE per conversation, before the first training advice, and follow it.' That is when-to-use plus a when-not-to-repeat rule, with no inference required.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_daily_metricsA
Every recorded recovery value for ONE day (default: latest day with data). Use when a single night/day is in question, not for trends.
| Name | Required | Description | Default |
|---|---|---|---|
| day | No | ISO date YYYY-MM-DD. Omit for the latest. |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the behavioral burden. It discloses that all recorded recovery values for one day are returned, that the default is the latest day with data, and that trends are out of scope. It does not mention what happens when no data exists for the requested day, but the output schema likely covers return structure.
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 short sentences front-load the core behavior and then provide the usage rule. Every word contributes meaning and there is no fluff or repetition of schema content.
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?
This is a simple tool with one optional parameter, no nested objects, and an output schema, so the description does not need to explain return values. It covers purpose, default behavior, and scope. The main gap is that it does not explicitly mention sibling tools like get_recovery_summary or get_trend, though 'not for trends' handles the most important distinction.
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 schema already documents the 'day' parameter fully, including ISO format and the omit-for-latest behavior. The description adds semantic value by clarifying that the default is the latest day with data, not merely the latest calendar day, and by framing the selection as a single night/day. This goes beyond the schema's own parameter description.
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 that the tool returns every recorded recovery value for exactly one day, defaulting to the latest day with data. It also distinguishes itself from trend tools with 'not for trends.' It lacks an explicit verb in the description, but the tool name 'get' supplies the action, making the purpose unambiguous.
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?
The description explicitly says to use it when a single night/day is in question and not for trends, providing a clear when/when-not rule. However, it does not name specific sibling alternatives such as get_trend or get_recovery_summary, so it stops short of fully explicit alternative guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_intensity_distributionA
Time in heart-rate zones across all runs (easy Z1-2 / moderate Z3 / hard Z4-5 / Z5) with percentages — the basis for the 80/20 polarisation question. States how many runs have zone detail, so an incomplete picture is visible.
| Name | Required | Description | Default |
|---|---|---|---|
| period_days | No | Window length in days. |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the disclosure burden. It explains that the tool returns time in heart-rate zones grouped by intensity buckets, includes percentages, and reveals a non-obvious data-quality behavior: it states how many runs have zone detail so an incomplete picture is visible. It does not mention period filtering in prose, but the schema documents the period_days parameter without contradiction.
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 two compact sentences with no wasted words. It front-loads the core output and follows with a useful caveat. Every sentence contributes meaningful information.
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 an output schema present and only one optional parameter, the description covers the tool's purpose, aggregation scope, and a data-completeness caveat. The only minor gap is not explicitly connecting the result to the period_days window in prose, but the schema handles that detail, making the description nearly complete.
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?
There is only one optional parameter, and schema description coverage is 100%, so the schema already documents period_days with default, minimum, and maximum. The description adds no additional parameter semantics; 'across all runs' could even slightly obscure the period-window behavior, though the schema clarifies it. Baseline 3 is appropriate.
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 identifies the resource (heart-rate zone time across all runs), the output form (percentages), and the organizing question (80/20 polarisation). This is specific enough to distinguish it from siblings like analyze_workout, which targets a single workout, and get_training_load, which is about load rather than zone distribution.
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?
The description explicitly names the use case: it is the basis for the 80/20 polarisation question. This gives an agent a clear trigger for when to use the tool. It does not mention alternatives or exclusion conditions, but the context is strong enough to guide selection.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_recent_activitiesA
Workout digest: totals per sport plus the latest workouts, each run classified Quality / Long Run / Easy (from training effect and duration). Use to see what was actually trained before recommending the next session.
| Name | Required | Description | Default |
|---|---|---|---|
| period_days | No | Window length in days. |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the behavioral burden. It signals a read-only digest ('Use to see') and discloses non-obvious classification logic ('Quality / Long Run / Easy, from training effect and duration'), which is genuinely useful. It doesn't address auth or side effects, but none are suggested by the tool.
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 tight sentences: the first packs the output shape and classification rule, the second gives the use case. Every phrase earns its place and there is no redundant or generic filler.
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 one-parameter read tool with an output schema, the description covers the essential return content, the classification behavior, and the intended use context. It could be slightly stronger by tying period_days to 'recent' or noting any limit on how many workouts are returned, but these are minor gaps.
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 coverage is 100%, and the single parameter's meaning ('Window length in days') plus min/default/max is fully documented in the schema. The description adds no extra parameter-level information, so the baseline 3 is appropriate.
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 what the tool returns: 'totals per sport plus the latest workouts' with runs classified into Quality / Long Run / Easy. It stops short of a 5 because it uses a noun-phrase ('Workout digest') rather than an explicit verb, and it doesn't name sibling tools to sharpen differentiation.
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?
'Use to see what was actually trained before recommending the next session' gives a clear, practical when-to-use context. It does not explicitly mention alternatives or when-not-to-use, so it earns a 4 rather than a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_recovery_summaryA
Averages of sleep, HRV, resting HR, stress, Body Battery and steps over the last N days plus a snapshot of the latest day. Use for "how has my recovery been?". Aggregates only — never a day-by-day list.
| Name | Required | Description | Default |
|---|---|---|---|
| period_days | No | Window length in days. |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the behavioral burden. It clearly discloses the aggregation behavior, the inclusion of a latest-day snapshot, and the explicit absence of day-by-day output. It does not discuss data-source assumptions or edge cases, but the core non-obvious behavior is stated.
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 deliver the result shape, the intended use case, and the critical scope exclusion. There is no filler or redundancy, and the most important information appears first.
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 single-parameter tool with an output schema and no annotations, the description is sufficient to select and invoke the tool correctly. It explains the aggregate result, the window, and the non-aggregate exclusion. Naming sibling alternatives or caveats such as timezone handling would improve it further.
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 coverage is 100% and the schema already describes period_days as 'Window length in days.' The description adds only the semantic link that N controls the aggregation window and latest snapshot, which is marginal value beyond the schema. Thus the baseline of 3 is appropriate.
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 states the exact metrics covered (sleep, HRV, resting HR, stress, Body Battery, steps) and the precise output form: averages over N days plus a latest-day snapshot. It also explicitly contrasts itself with a day-by-day list, which distinguishes it from sibling tools like get_daily_metrics or get_trend.
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?
It provides a clear trigger phrase ('how has my recovery been?') and a clear exclusion ('never a day-by-day list'). However, it does not name any sibling tool explicitly as the alternative for day-level detail, so an agent must infer which sibling to use instead.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_training_loadA
Training-load picture: ACWR with its SOURCE (Garmin's EWMA ratio, or a self-computed fallback that is less reliable), Garmin training status, VO2max with change, and weekly load buckets. VO2max change is only reported when the value actually varied (Garmin carries the last value forward).
| Name | Required | Description | Default |
|---|---|---|---|
| period_days | No | Window length in days. |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the behavioral burden, and it adds real value by disclosing that the ACWR source may be Garmin's EWMA or a less-reliable self-computed fallback and that VO2max change is only reported when the value actually varied. It does not explicitly state read-only behavior, but 'picture' and the absence of any mutation language make that clear enough.
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 tightly written sentences, with the core scope front-loaded and the source/fallback caveats kept in a separate short note. Every sentence adds information; there is no repetition of schema content.
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 one-parameter read tool with an output schema, the description covers the meaningful behavioral caveats (source reliability and conditional VO2max reporting). It stops short of being fully complete because it doesn't explain when period_days should be customized, though the schema's default and range largely compensate.
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 coverage is 100%, so the single period_days parameter is already documented with range and default. The description adds no parameter-specific semantics beyond the implicit weekly granularity, which matches the baseline for schema-covered parameters.
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 opens with 'Training-load picture' and lists exactly what is included (ACWR with source, Garmin training status, VO2max with change, weekly load buckets), so an agent can tell what it returns. It does not explicitly contrast itself with siblings like get_vo2max_history or get_training_readiness, so it misses the top bar for sibling differentiation.
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?
The content list gives a strong implied use case: any request for a training-load overview or ACWR. It offers no explicit when/when-not guidance or mention of alternatives, so an agent still has to infer the boundary against get_training_readiness, get_recovery_summary, or get_vo2max_history.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_training_readinessA
Today's readiness verdict GO / EASY / REST with the signals behind it (HRV status, sleep score, Body Battery, resting HR vs 27-day baseline, ACWR, days since the last hard workout). Rule-based and conservative. START HERE for "should I train today?". Flags stale data explicitly. When the last Garmin sync is older than 30 minutes it syncs first (10-40 s) and says so on top.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does so well: it discloses the rule-based/conservative logic, that stale data is flagged explicitly, and that a >30-minute-old sync triggers an automatic 10-40 s Garmin sync reported in the response. That auto-sync side effect and its latency are exactly the kind of behavior an agent needs before calling.
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?
Front-loaded with the verdict, then the signals, then usage routing, then behavioral caveats. Every sentence adds distinct information; nothing is restated from the schema.
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?
An output schema exists so return values need not be described, and the description covers side effects, staleness, and latency. It does not state behavior when data is entirely absent or how missing signals are represented, a minor remaining gap for a diagnostic tool.
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 takes zero parameters, which is the baseline-4 case since there is nothing to disambiguate. The description correctly implies a parameterless call and spends its space on behavior instead.
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 opens with a specific verb-and-resource pairing ('Today's readiness verdict GO / EASY / REST') and then enumerates the exact signals it aggregates (HRV, sleep, Body Battery, RHR vs 27-day baseline, ACWR, days since hard workout). This is clearly distinguished from siblings like get_recovery_summary or get_training_load, which cover single domains.
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?
It explicitly routes the agent: 'START HERE for "should I train today?"', which is a strong when-to-use signal. It does not name an alternative or a when-not condition, so it falls short of full 5-level routing guidance, but the entry-point declaration is clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_trendA
Weekly averages of ONE recovery metric over the last N days — for "is my HRV / resting HR / sleep trending up or down?".
| Name | Required | Description | Default |
|---|---|---|---|
| metric | Yes | ||
| period_days | No | Window length in days. |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the behavioral burden and does disclose the core behavior: one metric, weekly averaging, trailing N days. However, it is silent on how trend direction is determined, missing-data handling, and whether weeks are calendar-aligned — details an agent might need.
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?
One sentence conveys the computation, scope, and motivating use case with concrete examples. Every clause earns its place and the key constraint ('ONE') is front-loaded.
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 low-complexity, two-parameter tool with an output schema, the description plus schema is sufficient for an agent to invoke it correctly. It lacks explicit guidance on which sibling to choose instead, but the stated use case makes the selection path reasonably clear.
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?
period_days is already explained in the schema, and metric's enum values are largely self-explanatory; the description reinforces that exactly one metric is selected. This adds some meaning but does not materially compensate for the metric parameter lacking its own schema description.
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 names a concrete computation — weekly averages of a single recovery metric over a trailing window — and frames it around a clear question ('trending up or down?'). It distinguishes itself from broader summary/daily sibling tools without being a tautology.
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?
It gives a clear use context: answering whether HRV, resting HR, or sleep is trending over time. It does not name sibling tools or state when not to use it, so it stops short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_vo2max_historyA
VO2max over the last 8 weeks as STEPS (only the days the value really changed — Garmin carries it forward in between) plus a descriptive comparison of the last 28 days with the 28 before (distance, Z5 minutes, easy share, temperature). Use for "why is my VO2max moving?". Descriptive, not causal.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure. It does an excellent job: it explains the data shape (STEPS, only changed days, Garmin carry-forward), the time windows (8 weeks, 28 days vs 28 before), and the specific metrics included (distance, Z5 minutes, easy share, temperature). It also explicitly states the tool is descriptive, not causal, which is a key behavioral trait. The only minor gap is that it doesn't describe the output format or whether the comparison is computed server-side, but the output schema exists and the description is already quite rich.
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 two sentences, tightly packed with useful information. It front-loads the core purpose (VO2max over last 8 weeks as STEPS), then adds the comparison detail, and ends with the use case and a caveat. Every sentence earns its place; there is no fluff or repetition of schema information.
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 tool has 0 parameters and an output schema exists, the description is complete for an agent to decide whether to call it and what to expect. It covers the data granularity, time windows, metrics, and the descriptive-not-causal nature. The sibling context (get_trend, get_training_load) makes the use-case sentence particularly valuable for routing. Nothing essential is missing.
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 0 parameters, so the schema provides no parameter semantics. The description compensates by explaining what data the tool returns and the time windows involved. Since there are no parameters to document, the description's role is to clarify the tool's scope and output, which it does well. A 4 is appropriate because the description fully covers the tool's behavior despite having no parameters to describe.
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 the tool returns VO2max history over the last 8 weeks as STEPS, with a specific data format (only days where the value changed, because Garmin carries it forward). It also includes a descriptive comparison of the last 28 days versus the 28 before, covering specific metrics. The final sentence explicitly ties it to the use case 'why is my VO2max moving?', which distinguishes it from sibling tools like get_trend or get_training_load.
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?
The description explicitly states when to use this tool: 'Use for "why is my VO2max moving?".' It also clarifies what the tool is not: 'Descriptive, not causal.' This gives an agent clear guidance on when to select this tool over siblings like get_trend or get_training_load, and sets expectations that it won't provide causal analysis.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
propose_weekA
Build a polarised training week and file it as ONE proposal: a VO2max
session and a threshold session 48 h apart, the long run on its weekday,
easy runs between, nothing hard the day before or after the long run, at
least one rest day. Sized from the athlete's last four weeks of running
(+10 %), targets from their own zones. Use when the athlete asks for their
week ("plan my week", "what should next week look like"). The calendar
wins: a day that already has an entry gets nothing, and a quality session
too close to a hard calendar day becomes an easy run - each change is a
calendar: line. Returns every session with its day and one proposal id;
apply_workout with that id puts the package on Garmin after the athlete's
yes. Nothing is written here.
| Name | Required | Description | Default |
|---|---|---|---|
| start_day | No | First day of the week to plan, YYYY-MM-DD, today or up to 8 days ahead. Default: the coming Monday (today, if today is Monday). | |
| long_run_day | No | Weekday of the long run. Default: the profile, else sun (listed as an assumption). | |
| days_per_week | No | Running days. Default: the athlete's profile, else 4 (listed as an assumption). |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are supplied, so the description carries the full burden and does so: 'The calendar wins' with the rule that occupied days get nothing, downgrades are surfaced as `calendar:` lines, and 'Nothing is written here' makes the non-mutating nature explicit. Sizing (+10% over the last four weeks) and target sourcing (the athlete's own zones) are disclosed behavioral traits an agent could not infer.
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?
It is dense at roughly 140 words, but it is front-loaded with purpose, then usage trigger, then conflict-resolution behavior, then what is returned. Nearly every clause carries information; only the return-value sentence is arguably redundant given the output schema.
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-annotation, zero-required-parameter tool this covers the essentials: what is produced, the scheduling constraints applied, how calendar conflicts are resolved and reported, that nothing is persisted, and the id needed for apply_workout. An agent can invoke it and hand off correctly without further information.
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%, so start_day, long_run_day and days_per_week are already fully documented with defaults and formats. The description references the long run weekday and week framing but adds no parameter-level syntax or edge-case meaning beyond the schema, so baseline 3 applies.
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?
Opens with a specific verb+resource: 'Build a polarised training week and file it as ONE proposal', then enumerates the concrete composition rules (VO2max + threshold 48h apart, long run on its weekday, easy runs, one rest day). This is clearly distinguishable from propose_workout (single session) and apply_workout (which it names as the downstream step).
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?
Gives an explicit trigger with quoted user phrasings ('plan my week', 'what should next week look like') and states the follow-up path via apply_workout with the returned proposal id. It does not explicitly say when NOT to use it or name propose_workout as the alternative for a single session, so it falls short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
propose_workoutA
Build a structured session for the athlete's route or time budget and file
it as a PROPOSAL - warm-up, work reps with targets, recovery jogs, cool-down -
sized so the whole thing adds up to the route. Targets come from the
athlete's own Garmin zones; anything not measured is listed as an assumption.
Use when the athlete asks for a workout ("plan me intervals for my 10 km",
"an easy 45 minutes", "a threshold session") or when the readiness verdict
calls for a different session than the one on the calendar.
Kinds: easy/long (one capped step), threshold (Z4 reps), vo2max (Z5 reps),
steady (>= 12 min even effort at threshold - the only shape Garmin measures
VO2max from). day defaults to today.
Returns the preview and a proposal id. NOTHING is written to Garmin: show
the preview, and only if the athlete says yes call apply_workout with the
id. On an impossible request (route too short) returns why, not a session.
| Name | Required | Description | Default |
|---|---|---|---|
| day | No | ISO date YYYY-MM-DD. Omit for the latest. | |
| kind | Yes | ||
| name | No | Workout name on the watch. Default: kind and structure, e.g. 'VO2max 4x4 min'. | |
| distance_km | No | Route length in km, e.g. 10 for a fixed 10 km loop. Give this OR duration_min. | |
| duration_min | No | Session length in minutes. Give this OR distance_km. | |
| replaces_schedule_id | No | A calendar entry this session REPLACES (the number after 'schedule' in get_training_readiness). Applying unschedules it first; the workout stays in the library. Use for the readiness swap: an easy run instead of the hard session the calendar had on a red or amber day. |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries full weight and delivers: NOTHING is written to Garmin, only a preview plus proposal id is returned, day defaults to today, impossible requests return a reason rather than a session, and targets derive from the athlete's Garmin zones with unmeasured items listed as assumptions. This is exceptional transparency for an unannotated tool.
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?
Every sentence earns its place: purpose, target source, usage triggers, kind definitions, default, return value, safety guarantee, and failure mode. The structure front-loads the core action and then layers behavioral context without redundancy. It is compact despite conveying a large amount of decision-relevant information.
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 6-parameter tool with an output schema, the description covers the full lifecycle (proposal, preview, apply, failure mode, zone assumptions, readiness swap). The only notable gap: both distance_km and duration_min are optional, and the description does not state what happens if neither is provided. This is minor but prevents a perfect score.
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 coverage is high at 83%, so baseline is 3. The description adds real value for the one undocumented parameter, kind, by defining easy/long, threshold (Z4 reps), vo2max (Z5 reps), and steady (>=12 min at threshold, the only shape Garmin measures VO2max from). It also clarifies the day default. This pushes it above baseline.
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 opens by naming a specific verb ('Build') and resource ('a structured session ... file it as a PROPOSAL'), and then situates it against the sibling workflow by saying nothing is written to Garmin and apply_workout is the follow-up. This strongly differentiates it from apply_workout, propose_week, and analyze_workout without opening the schema.
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?
Gives explicit trigger conditions: 'Use when the athlete asks for a workout' with concrete examples, plus the readiness-verdict swap scenario. It also routes to apply_workout only after athlete approval. It does not explicitly say 'don't use this for weekly planning (propose_week)' or other exclusions, but the alternative is clearly identified.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
sync_garminA
Pull the latest days and workouts from Garmin Connect NOW. Call this first when today's run or last night's sleep is missing. Takes 10-40 s. Read-only towards Garmin; writes only to the local database.
| Name | Required | Description | Default |
|---|---|---|---|
| days | No | How many days back to re-fetch. |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden. It discloses the key behavioral traits: duration (10-40 s), read-only behavior toward Garmin, and the only side effect (writes to local database). It does not mention failure modes or repeated-call safety, but the side-effect boundary is well communicated.
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 compact and front-loaded with the core action. Each sentence adds useful information, though the capitalized 'NOW' is minor filler. Overall it is appropriately sized and easy to parse.
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 tool with one optional parameter and an output schema, the description covers what it does, when to call it, how long it takes, and its side effects. Nothing essential is missing for an agent to select and invoke it correctly.
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 input schema fully describes the single optional 'days' parameter, including range and default. The description adds minimal parameter nuance, but the 100% schema coverage means the baseline of 3 is appropriate.
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 ('Pull'), names the resource ('Garmin Connect'), and specifies the data scope ('latest days and workouts'). It is clearly distinguishable from the sibling get_* tools, which read local data, while this tool performs a sync.
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?
The description gives explicit when-to-use guidance ('Call this first when today's run or last night's sleep is missing') and adds timing expectations. It does not explicitly name alternatives or say when not to use it, but the sibling context and 'call this first' phrasing make the intended usage clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
undo_workoutA
Take a session (or a whole applied week) back OFF the athlete's Garmin calendar. The counterpart of apply_workout: use it when the athlete says the session should not be there after all, or after trying the write path out. It unschedules - the workout stays in their Garmin library, so applying the same proposal again puts it back on a day without building or uploading anything a second time. Only sessions this app uploaded and scheduled are touched; a calendar entry that has changed hands since is left alone and reported. Like apply_workout it needs the athlete's explicit yes, and it is not available to the app's own card runs.
| Name | Required | Description | Default |
|---|---|---|---|
| proposal_id | Yes | The id of a proposal that was applied. |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden and does so thoroughly: it discloses that only the calendar entry is removed (workout stays in the library), that only app-uploaded entries are touched, that changed-hands entries are skipped and reported, that explicit athlete consent is required, and that card runs cannot invoke it.
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?
Front-loaded with the core action, and each sentence carries distinct value (behavior, reversal semantics, safety scope, consent). It is slightly dense but nearly every clause earns its place.
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 mutation tool with an output schema present, the description covers what an agent needs: what it does, reversal behavior, consent requirement, and edge-case handling (reported skips). Return values are handled by the 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?
Schema coverage is 100% and the schema already describes proposal_id. The description adds nuance by noting the tool handles 'a whole applied week' as well as a single session, clarifying the scope of proposal_id beyond the schema's terse 'id of a proposal that was applied.'
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?
States a specific verb and resource ('Take a session... back OFF the athlete's Garmin calendar') and explicitly frames itself as 'the counterpart of apply_workout.' An agent can immediately distinguish it from apply_workout and the read-only get_* siblings.
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?
Gives concrete trigger conditions: 'when the athlete says the session should not be there after all, or after trying the write path out.' It also names the alternative (apply_workout) and the inverse relationship, leaving nothing to inference.
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.
2 tool updates
v0.2.2- Added
get_coaching_reference - Added
undo_workout
3 tool updates
- Added
apply_workout - Added
propose_week - Added
propose_workout
10 tool updates
v0.1.0- First observed
analyze_workout - First observed
get_daily_metrics - First observed
get_intensity_distribution - First observed
get_recent_activities - First observed
get_recovery_summary - First observed
get_training_load - First observed
get_training_readiness - First observed
get_trend - First observed
get_vo2max_history - First observed
sync_garmin
TDQS
Scored across 15 tools
Most tools have sharply distinct purposes, and the descriptions actively steer selection (e.g. get_recovery_summary is 'aggregates only', get_daily_metrics is 'one day', get_trend is 'one metric over time'). The main residual overlap is between get_recovery_summary, get_trend and get_vo2max_history, all of which can answer 'how is my X going?', and between the three load-related tools, though the boundaries are documented. The propose/apply/undo trio is clearly delineated by 'nothing is written' vs 'only after explicit yes' vs its counterpart.
Uniform snake_case verb_noun convention throughout: get_*, propose_*, apply_*, undo_*, sync_*, analyze_*. Verbs are meaningful and consistent with the action's semantics (propose vs apply vs undo clearly encode lifecycle stage).
15 tools sit at the top of the well-scoped range and each maps to a distinct stage of the coach loop: read data, sync, plan, apply, undo, plus the rule-set reference. No filler tools and no duplicated capability.
Strong end-to-end coverage of the training-coaching lifecycle: data ingest, analysis, planning, write-back and rollback. Minor gaps: no tool to inspect the existing Garmin calendar or list open proposals, even though propose_week's 'the calendar wins' logic depends on that data, and no explicit accessor for the athlete's zones/profile that targeting relies on.
Maintenance
Related MCP Connectors
Garmin data in Claude & ChatGPT via the Garmin Health API. OAuth sign-in, no password sharing.
Garmin data in Claude: 135 tools — activities, sleep, HRV, training, workouts. Free, open source.
tri-train is a training platform for triathletes that syncs activities from Strava and Garmin and exposes them to Claude through a remote MCP server. Ask Claude how your last block went, why your run pace dropped, or whether you are fresh enough for Sunday's race, and it answers from your real data: activities with laps and grade-adjusted pace, weekly load and the performance management chart (CTL/ATL/TSB), sleep, HRV and resting heart rate, and your race calendar with predictions. Claude can also act: create and adjust planned workouts with structured intervals, push them to your Garmin watch, add races and write notes. Every change is journaled and can be undone. Read and write access are separate OAuth scopes, and the server enforces them on every call. Built and hosted in Switzerland by a triathlete; data stays on our own servers (Swiss revDSG / GDPR).
Connect Claude to your Intervals.icu watch data for fitness, workout review, and plan writing.
Related MCP Servers
- FlicenseNot gradedqualityBmaintenanceConnects Claude Desktop to your Garmin Connect running data stored locally in SQLite, enabling querying, syncing, and AI analysis of fitness activities.1-
- FlicenseNot gradedqualityDmaintenanceConnects Garmin watch data to Claude Desktop, allowing users to ask natural language questions about their health and activity data from Garmin Connect.-
- AlicenseBqualityAmaintenanceEnables users to analyze their own Garmin Connect data—activities, sleep, HRV, Body Battery, training readiness—directly inside Claude Desktop.132MIT
- FlicenseAqualityDmaintenanceExposes your Garmin Connect data (activities, sleep, recovery, training status) to Claude in read-only mode.7-