garmin-mcp
This MCP server lets you query and analyze your Garmin training history in plain language, with optional confirmed writes for managing Garmin workouts.
List activities, most recent first, filtered by date range and sport (multisport legs are revealed when filtering by sport).
Get full activity detail: summary, laps, and multisport legs.
Get activity streams — heart rate, pace, altitude, power, cadence, etc. — as bucketed series plus true peaks.
Get weekly training totals per sport, with multisport counted once.
Compare two activities side by side, including computed deltas and a plain-language pace verdict.
Check database status to see what is stored and whether Garmin is reachable.
Trigger an immediate sync from Garmin and import locally dropped FIT files, if the ingest worker is running.
Create or delete structured Garmin workouts — off by default and requiring a two-step confirmation before anything is sent.
Allows querying Garmin training history, including activities, laps, cumulative streams, weekly summaries, and comparisons between runs, as well as creating and deleting structured workouts on a Garmin account.
Click on "Install 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., "@garmin-mcpcompare my last two runs"
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.
garmin-mcp
Ask questions about your Garmin training history in plain language, from Claude — and send structured sessions back to your watch. Raw FIT files in, DuckDB out, sixteen typed MCP tools on top.
> compare my last two runs
Trail du Corbier Treadmill
Distance 16.35 km 8.52 km
Duration 2:41:15 35:02
Pace 9:52/km 4:07/km
Avg HR 176 bpm 165 bpm
Ascent 1264 m —No exporting files by hand, no spreadsheets, no third-party service holding your data. Everything runs on your machine.
Why this exists
Garmin Connect holds years of training data and offers no practical way to ask it a question. The web UI answers what its designers anticipated; anything else means exporting CSVs and opening a spreadsheet.
This project pulls the original FIT files, parses them properly, stores them in a local analytical database, and exposes a small set of typed tools over MCP so a model can answer questions against real data instead of guessing.
Related MCP server: fitMCP
Architecture
Garmin Connect data/inbox/
│ (drop FIT files)
▼ │
┌──────────────────────┐ │
│ ActivitySource │ one narrow │
├──────────┬───────────┤ interface │
│ cffi │ playwright│ │
└──────────┴───────────┘ │
│ │
└────────────┬────────────────────┘
▼
┌─────────────────────────┐
│ ingest pipeline │
│ dedupe → parse → store │
│ → write │
└────────────┬────────────┘
│ single writer, short transactions
▼
┌─────────────────────────┐
│ DuckDB │
│ files · activities │
│ laps · records │
└────────────┬────────────┘
│ read-only, one connection per query
▼
┌─────────────────────────┐
│ MCP server │
│ 16 typed tools, no │
│ generic SQL │
└────────────┬────────────┘
│ stdio (or streamable HTTP)
▼
ClaudeRaw FIT files are kept forever under data/raw/. About 150 kB per activity —
a decade of triathlon fits in well under a gigabyte — which means the entire
history can be re-parsed whenever the parser learns a new field, with no
network involved. garmin-mcp reparse does exactly that, and it works on an
account that no longer authenticates.
Quick start
Requires Python 3.12+ and uv.
uv run garmin-mcp setupThat asks two questions, writes an owner-readable .env, creates the database,
logs in to Garmin, pulls your recent activities and prints the command to
connect it to Claude. Choose manual if you would rather not hand it your
Garmin password — the import path needs no account at all.
Then register the server:
claude mcp add garmin -- uv run --directory "$(pwd)" garmin-mcp serveRestart Claude Code and ask it something.
cp .env.example .env # fill in GARMIN_EMAIL and GARMIN_PASSWORD
uv sync
uv run garmin-mcp init-db
uv run garmin-mcp auth # interactive, handles MFA
uv run garmin-mcp sync --limit 50Commands
Command | Purpose |
| Interactive first run — credentials, database, login, verification |
| Create the database and apply the schema (setup does this for you) |
| Log in to Garmin and save the session (the only command that sees a password) |
| Download and ingest new activities, incrementally |
| Ingest FIT files from |
| Run the MCP server |
| Background sync loop — the only process that writes |
| Re-read every stored FIT file after a parser change — no network |
| Report whether Garmin is currently reachable |
| Show what the database holds |
Tools exposed to Claude
Tool | Returns | Bound |
| Compact record per activity | 20 by default, 200 max |
| Summary, laps, multisport legs | 50 laps |
| Columnar time series + true peaks | 200 points, 2000 max |
| Per-sport totals for one week | one week |
| Two activities with deltas computed | fixed |
| A session against the workout it was run from | fixed |
| A week's plan against what was actually done | one week |
| What is stored, worker health, reachability | fixed |
| Pull new activities without leaving the chat | needs the worker |
| Saved sessions in your Garmin library, with their ids | 20 by default, 100 max |
| Put a structured session on your Garmin account | off by default, two-step |
| Undo the above | off by default, two-step |
| What is planned this month, with both ids | one month |
| Put a saved session on a date in your calendar | off by default, two-step |
| Drop a planned session, keeping the workout | off by default, two-step |
| Write an analysis into an activity's Notes field | off by default, two-step |
Design decisions
The parts that were not obvious, and why they went the way they did.
No generic SQL tool
Giving a language model arbitrary query access to a personal training database is a liability rather than a feature. It can be talked into reading anything the file holds, and it will occasionally write a query that scans a million rows to answer a question about last Tuesday.
Every statement lives in db/queries.py, fully
parameterised. Stream field names go through an allow-list — they arrive from a
model, and that list is what keeps them out of the SQL text. One module to
audit, rather than a promise to trust.
Manual import is a pillar, not a fallback
In March 2026 Garmin deployed Cloudflare TLS fingerprinting, which blocks
clients by the shape of their TLS handshake before authentication even begins.
garth — the library this project was originally specified to use — was
deprecated within days, and every plain HTTP client stopped working. It will
happen again.
So data/inbox/ is a first-class ingestion path, tested as such. Drop FIT files
exported from Garmin Connect into it and run garmin-mcp import. No network, no
credentials, nothing that can be revoked. It is the only route that can honestly
be promised to still work in a year.
Two backends behind one interface
ActivitySource is deliberately narrow —
list what exists, fetch one file, report health. Two implementations sit behind
it: a lightweight HTTP client impersonating Chrome's TLS handshake, and a real
headless Chromium for when that stops being enough. An official-API backend
drops in the day Garmin reopens its developer programme.
auto falls back to the browser only for failures a browser can actually fix.
An expired token is not one of them: no backend can invent a login you have not
performed, and falling back there would replace a clear run garmin-mcp auth
with a slow, confusing browser failure.
Authentication cannot happen in the server
The backends are constructed without credentials, so they are structurally incapable of starting a fresh login — they can only resume a saved token. That is what lets the unattended ingest path fail loudly instead of hanging on an MFA prompt nobody will answer, and it is why a dead Garmin session degrades into "the history stops at last Tuesday" rather than a server that will not start.
Passwords are never written to disk by this project, never logged, and never
stored in the database. Only the OAuth token is persisted, chmod 600. Once
you have run auth, you can delete GARMIN_PASSWORD from .env entirely.
A triathlon is one activity and six
A FIT file is a message stream, not "an activity". A normal run holds one
session message; a multisport recording holds several — swim, T1, bike, T2,
run — with transitions being real sessions of their own.
Stored as a parent row plus one leg per discipline. Lists show the parent, so a triathlon reads as one line. Filtering by sport reveals the legs, so my running volume this month correctly includes the 10 km inside a triathlon. Weekly totals count top-level rows only, so 51.5 km is counted once rather than once per leg.
More than one session does not imply multisport, incidentally: a file can
chain independent recordings, or repeat one twice. That is decided from the
activity message, with temporal contiguity and transition legs as fallback.
Output is a budget
Every byte a tool returns is spent from a context window. Nulls are dropped;
units are resolved ("4:42/km" costs less than avg_speed_mps: 3.5432 plus the
arithmetic to read it); runners get pace and cyclists get km/h but never both;
and series come back columnar rather than as objects, for roughly a third of the
tokens.
Streams are averaged into buckets — a three-hour ride holds ~11 000 samples per
channel. Because averaging flattens extremes, every stream response also carries
true_range: minimum, maximum and mean computed over every raw sample. Without
it, a coarse 10-point overview of a real ride reports a maximum heart rate of
160 against an actual 174, and states it with complete confidence.
Writing is held to a different standard than reading
Reading someone's training history and modifying their account are different
acts, and the second one arrives on a wrist. Three tools can write —
create_workout, delete_workout and the listing that makes them usable —
and they are built accordingly.
Off by default. GARMIN_ENABLE_WRITES=false. Cloning a repository must not
hand a language model the ability to change someone's Garmin account.
Never on a single call. The first call to create_workout validates the
session locally, sends nothing, and returns it written out in full. Only a
second call carrying confirm=true creates it. Nothing leaves the machine
during the preview — no credential is even loaded — which is what makes the
confirmation real rather than ceremonial.
Reachable undo. delete_workout takes an id, and list_workouts is what
makes ids discoverable. Without it the undo existed only for whoever still had
the conversation that created the workout, which is not an undo.
Not scheduled unless asked. A created workout lands in the library and syncs to the watch from there. Scheduling it on a date is a separate tool with its own confirmation, because a workout in the library is a suggestion and one on tomorrow's calendar is a plan.
Still no credentials in the server. Every call that needs an authenticated session — including reading the workout library — goes through the ingest worker. The MCP server cannot authenticate, which is what keeps a dead Garmin session degrading into stale history rather than a broken server.
Device compatibility
The parser targets the FIT protocol, not one watch. It is validated against a corpus of 42 real recordings spanning 19 devices from 8 manufacturers — Garmin (fr70 through fēnix 5, Edge 200/500/800/810/820, fr920xt, vívoactive), Wahoo ELEMNT and BOLT, Coros Pace 2, Stryd, Zwift, SigmaSport and the Strava mobile app.
30 of the 42 parse. The other 12 are correct rejections: 11 are not activity files (settings, workouts, weight scales, daily monitoring) and one is truncated before its first session survived.
Quirks that only real hardware reveals, all handled:
devices that log for 45 minutes before you press start (a fēnix 2 does), which would otherwise produce negative elapsed times;
writers that record heart rate
0instead of the "missing" sentinel, dragging every average down — while0cadence and0power are real readings from a coasting cyclist and are left alone;firmware writing
start_timeas an unresolvable integer, reconstructed from the next best anchor and flagged as such;files with no
activitymessage at all, where the timezone would silently become UTC and file a Sunday evening run under Monday;cadence, which FIT stores in three incompatible units depending on sport.
Reconstructed values carry a provenance marker, so an inferred number is never mistaken for a measured one.
make test-all # fetches the corpus, then runs the deep suiteData model
Table | Contents |
| One row per ingested FIT, keyed by content hash |
| One row per session, plus a parent row for multisport |
| Intervals — what makes a structured session legible |
| The prescription, when a session came from a structured workout |
| One sample per second: HR, pace, altitude, power, running dynamics |
Wide tables rather than key/value: DuckDB is columnar, so unused columns cost
almost nothing and SELECT heart_rate reads exactly one column. An extra
JSON column absorbs rare fields, so a new device never silently loses data.
Ingestion is idempotent. Identity is the content hash, so the same ride pulled from Garmin and later dropped into the inbox by hand is recognised as one file whatever it is named. Re-ingesting replaces rather than merges, inside a single transaction.
Testing
make test # 214 tests, hermetic — no data, no network
make test-all # 258 tests, adds validation against real recordingsThe committed suite is entirely synthetic. fitdecode only reads FIT files, so
testing the parser would normally mean committing real recordings — but a GPS
trace starts at someone's front door, and that has no place in a public
repository. tests/fit_builder.py is a minimal FIT
encoder written for the purpose: the suite runs anywhere after a clone, and it
can fabricate a multisport triathlon that the author never actually records.
The real-device corpus is third-party licensed and gitignored. Every quirk it revealed is reproduced synthetically, so regressions are caught without it.
Docker
docker compose up -d ingest # background sync, the only writer
docker compose run --rm auth # log in once (interactive)
docker compose logs -f ingestOne writer, enforced by the compose file: DuckDB grants exclusive access to a
single writer and blocks readers while it is held, so only ingest may write.
It stops on SIGTERM with a 30-second grace period rather than being killed
mid-transaction.
The image runs as a non-root user. /data is the only mutable path and the
only one worth persisting; the build context excludes it entirely, so no
database, FIT file or token can end up in a layer.
For the stdio transport the MCP client owns the process lifecycle, so register the command with the client rather than starting it with compose:
claude mcp add garmin -- docker compose -f /abs/path/docker-compose.yml \
run --rm -T mcp-stdio-T matters: without it compose allocates a TTY and corrupts the JSON-RPC
stream on stdout.
Profile | What it adds |
(default) |
|
|
|
|
|
|
|
Continuous integration
GitHub Actions runs, on every push and pull request: ruff (lint and format), mypy in strict mode, pytest on Python 3.12 and 3.13, a Docker build with a smoke test that the image actually starts, the corpus suite as a job of its own, and a scan of every commit in history for credentials, databases and FIT files.
That last job exists because this repository is built around personal data: a secret committed by accident stays recoverable long after it is deleted from the working tree, so checking the current state is not enough.
Configuration
Two values matter, and only if you want automatic sync:
GARMIN_EMAIL=
GARMIN_PASSWORD=One more is worth knowing about:
GARMIN_ENABLE_WRITES=falseOff by default. It gates the three tools that touch your Garmin account rather
than just reading it. Everything else in .env.example already
has a working default.
Privacy
This repository is built on the assumption that training data is personal. GPS traces start where you live.
.gitignorewas in the first commit, before any data existed:.env, tokens,data/,*.duckdb,*.fit.Nothing is sent anywhere. The database, the raw files and the tokens all stay on your machine.
The MCP server has no authentication of its own. Under stdio that is fine — only the client that launched it can talk to it. If you switch to streamable HTTP, keep it bound to localhost.
The server holds no Garmin credentials at all, not even for reading your workout library. Every call that needs a session goes through the ingest worker.
Limitations
The
cffibackend is an arms race. It works today. Garmin can change its fingerprinting at any time, and that is what the manual inbox is for.The Playwright backend is unverified against a live account. Its structure, error mapping and interface conformance are tested; its network calls are not. Expect to adjust the endpoint paths on first run.
Activities only. HRV, sleep, Body Battery and training status are not ingested. The schema leaves room for them.
One user per database. Multi-tenancy would be one database file per user rather than a
user_idcolumn.Writing covers workouts, scheduling and activity notes. Pushing a workout directly to a device, and editing an existing session, are not exposed, though the library supports both.
License
MIT. See LICENSE.
Not affiliated with or endorsed by Garmin. "Garmin" and "Garmin Connect" are trademarks of Garmin Ltd.
Available Tools
9 toolscompare_activitiesA
Compare two activities side by side, with the differences computed.
Args: id_a: First activity id. id_b: Second activity id.
Deltas are calculated here rather than left to the reader, including a plain-language verdict on pace — where a positive number means slower, which is easy to misread.
| Name | Required | Description | Default |
|---|---|---|---|
| id_a | Yes | ||
| id_b | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full transparency burden. It goes beyond a basic statement by disclosing that deltas are calculated within the tool, that a plain-language pace verdict is included, and that positive numbers mean slower, which is a common point of confusion. This is genuinely helpful behavioral context.
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 purpose in the first sentence, followed by parameter definitions and then a key behavioral caveat. Every sentence earns its place; there is no fluff 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?
The tool is simple (two integer ids) and an output schema exists, so the description need not explain return values. It covers purpose, parameters, and behavior (including the sign convention), which fully equips an agent to select and invoke the tool 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 has no property descriptions (0% coverage), so the description's Args section is the only source of meaning for id_a and id_b. It provides clear, sufficient semantics: 'First activity id' and 'Second activity id.' While minimal, it is adequate for a simple two-id comparison and adds value beyond the schema's bare titles.
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 first sentence, 'Compare two activities side by side, with the differences computed,' clearly names the verb, the resource, and the tool's distinctive output. It also distinguishes this from sibling tools like get_activity_detail or list_activities by emphasizing comparison and computed deltas.
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 implies the tool should be used when a side-by-side comparison with computed differences is needed, but it does not explicitly state when to use it over alternatives or exclude other scenarios. There is no mention of alternatives or conditions like 'use get_activity_detail for a single activity.'
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_workoutA
Create a structured workout in the athlete's Garmin library.
Args: name: What the session is called, e.g. "Threshold 4x2km". blocks: The session, in order. A block is either a step or a repeat. Step: {"kind": "warmup"|"interval"|"recovery"|"cooldown"|"rest", "duration_s": 1200} or {"distance_m": 2000}, optionally "target_pace": "3:55" and "pace_tolerance_s": 5. Exactly one of duration_s or distance_m per step. Repeat: {"times": 4, "steps": [ ...steps... ]} sport: running, cycling or swimming. description: Optional note attached to the workout. confirm: Must be true to actually create it.
Call this without confirm first. That returns the session written out
in full and creates nothing. Show it to the athlete, and only call again
with confirm=true once they have agreed — this puts a real workout on their
Garmin account and onto their watch, and they should see it before that
happens rather than after.
Example blocks for "20 min easy, then 12 x 1 min at 3:45 with 1 min float": [{"kind": "warmup", "duration_s": 1200}, {"times": 12, "steps": [ {"kind": "interval", "duration_s": 60, "target_pace": "3:45"}, {"kind": "recovery", "duration_s": 60}]}, {"kind": "cooldown", "duration_s": 600}]
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | ||
| sport | No | running | |
| blocks | Yes | ||
| confirm | No | ||
| description | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries full responsibility for disclosing side effects. It does so excellently: 'creates nothing' without confirm, and with confirm=true it 'puts a real workout on their Garmin account and onto their watch.' It also explains the preview behavior, which is far beyond what annotations would typically provide.
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?
Though lengthy, the description is well-structured: purpose statement, Args list, a prominent usage note, and a concrete example. Every section serves a clear purpose and the length is justified given the complexity of the blocks parameter. No fluff 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?
Given the tool's complexity (nested blocks structure), the presence of an output schema (though not shown), and the lack of annotations, the description covers the essential aspects: what the tool does, the full input structure, the two-phase invocation pattern, and side effects. It is sufficiently complete for an agent to use it correctly without external guidance.
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 provides only names and types (0% description coverage), so the description must fully elaborate each parameter. It does: name, blocks with step/repeat structure, constraints (exactly one of duration_s or distance_m), sport options, description, and the critical confirm flag. The extensive example further clarifies the blocks format, adding substantial semantic value beyond the schema.
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: 'Create a structured workout in the athlete's Garmin library.' This clearly distinguishes it from sibling tools (e.g., delete_workout, get_activity_detail) and accurately conveys the tool's core function.
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 provides explicit, step-by-step usage guidance: call without confirm first to preview, then call again with confirm=true only after athlete agreement. It clearly states the side effect of the final call. However, it does not explicitly mention alternatives or when-not-to-use scenarios, so it falls slightly 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.
database_statusA
What the database currently holds, and whether it is reachable.
Worth calling before concluding that an activity is missing: an empty result may mean nothing has been ingested yet rather than that the session never happened.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden of behavioral disclosure. It reveals that the tool returns database contents and reachability, and crucially warns that an empty result may mean nothing has been ingested yet. While it doesn't explicitly state side-effect-freeness, the read-only nature is clearly implied.
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 brief sentences, front-loading the core purpose ('What the database currently holds, and whether it is reachable') followed by a valuable usage tip. Every sentence earns its place with no redundancy or 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 zero-parameter diagnostic tool with an output schema, the description is complete. It explains what the tool does, gives a usage scenario, and clarifies a potential misinterpretation of results. The output schema covers return structure, so no further detail is needed.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so there is nothing to document. According to the rules, with 0 params the baseline is 4. The description correctly focuses on the tool's output and usage rather than inventing parameter details.
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's function: reporting the database's current contents and reachability. It distinguishes itself from sibling tools that focus on specific activity operations by addressing the underlying data store state. The phrasing, while not using an explicit verb like 'reports' or 'checks,' unambiguously communicates the tool's purpose.
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 states when this tool is worth calling: 'before concluding that an activity is missing.' It explains the rationale that an empty result could indicate no ingestion rather than a missing session, providing actionable guidance for the agent. It doesn't name alternatives, but gives a clear trigger for use.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_workoutA
Remove a workout from the athlete's Garmin library.
Args: workout_id: The id returned by create_workout. confirm: Must be true to actually delete.
The undo for create_workout. Same two-step rule: without confirm it reports what would be removed and does nothing.
| Name | Required | Description | Default |
|---|---|---|---|
| confirm | No | ||
| workout_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description discloses key behavioral traits: deletion only occurs when confirm=true, and without confirm it performs a dry-run ('reports what would be removed and does nothing'). This goes beyond the schema and is valuable safety-critical information. It does not mention reversibility or permissions, but the dry-run behavior is the most important aspect.
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?
Efficiently structured with a clear action statement followed by an Args block. Every sentence contributes value: the one-line purpose, the arg explanations, and the important two-step rule. No redundancy or 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 destructive tool with no annotations, the description covers the core behavioral contract: the confirm guard, the dry-run behavior, and the relationship to create_workout. An output schema exists, so return format need not be described. It omits potential edge cases (e.g., invalid workout_id) but is otherwise complete for typical usage.
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 0%, so the description carries full responsibility for parameter meaning. It explains workout_id as 'The id returned by create_workout' and confirm as 'Must be true to actually delete,' adding essential semantics that the schema (type/default) does not convey.
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+resource: 'Remove a workout from the athlete's Garmin library.' It clearly identifies the action and object, and positions itself as the inverse of create_workout, distinguishing it from sibling read-oriented tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly characterizes this as 'The undo for create_workout,' providing clear contextual linkage. Also explains the two-step confirm rule, which is essential for safe invocation. Does not list alternative tools, but the sibling context makes the appropriate usage clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_activity_detailA
Full detail for one activity: summary, laps, and multisport legs.
Args: activity_id: The activity's id, as returned by list_activities.
Laps are what make an interval session legible — eight quarter-mile repeats look identical to a steady run in the summary numbers alone.
| Name | Required | Description | Default |
|---|---|---|---|
| activity_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden of disclosure. It adds useful context about the content of the response (laps, multisport legs), but doesn't explicitly state read-only behavior, error conditions, or any side effects. The information provided is helpful but not exhaustive.
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 concise and well-structured: a clear opening statement, a single-line argument explanation, and a memorable rationale for laps. Every sentence 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?
The output schema exists, so return values need no further explanation. The description covers the main use case and even provides a motivational example for laps. It lacks explicit limitations or comparison to get_activity_streams, but is sufficient for a low-complexity, single-parameter 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 schema has 0% parameter description coverage, but the description fully compensates by explaining 'activity_id' is 'The activity's id, as returned by list_activities', providing both meaning and source.
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 'Full detail for one activity: summary, laps, and multisport legs', which specifies the verb (get), resource (activity detail), and distinguishes it from siblings like list_activities or get_activity_streams.
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 explains when laps matter ('eight quarter-mile repeats look identical to a steady run in the summary numbers alone'), implying this tool is for interval-level detail. However, it doesn't explicitly name alternatives or state when not to use it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_activity_streamsA
Time series for one activity — heart rate, pace, altitude and so on.
Args: activity_id: The activity's id. fields: Which series to return. Defaults to heart_rate, speed and altitude. Available: heart_rate, speed, altitude, cadence, power, distance, temperature, grade, lat, lon, vertical_oscillation, stance_time, step_length, respiration_rate. max_points: How many points per series (default 200, capped at 2000).
Series are averaged into buckets rather than returned raw — a three-hour ride holds around 11 000 samples per channel. Output is columnar: {"heart_rate": [...], "elapsed_s": [...]}.
Because the series is smoothed, its own highest and lowest values
understate the real ones. Read peaks from true_range, which is
computed over every sample.
| Name | Required | Description | Default |
|---|---|---|---|
| fields | No | ||
| max_points | No | ||
| activity_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description fully carries the burden of behavioral disclosure. It explains that series are averaged into buckets, output is columnar, smoothed values understate real peaks, and true_range provides the real extremes. This is thorough and actionable.
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 well-structured with a header, parameter list, and behavioral notes. Every sentence adds value—the example about samples per channel helps justify max_points. No fluff 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?
Despite having an output schema, the description also details the return format and data processing, making it complete for a tool of this complexity. It covers both parameter semantics and edge-case behavior (smoothing, true_range) without requiring additional external knowledge.
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 has zero description coverage, so the description must explain all parameters. It does so comprehensively: activity_id, fields with available options, and max_points with defaults and caps, adding meaning beyond the raw schema.
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 time series data for a single activity, listing the data types (heart rate, pace, altitude). It distinguishes from siblings by specifying 'one activity' and the columnar output format.
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 clearly scopes the tool to retrieving time series for a single activity, which implies when to use it. It does not explicitly name alternative tools for other use cases, but the context is clear enough for a user to decide.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_activitiesA
List recorded activities, most recent first.
Args: since: Only activities on or after this ISO date (e.g. "2026-03-01"). activity_type: Filter by sport — running, cycling, swimming, transition, multisport. Filtering by sport also reveals the legs inside multisport events, so asking for running includes the run of a triathlon. limit: Maximum activities to return (default 20, capped at 200). until: Only activities before this ISO date.
Returns a compact record per activity. Multisport events appear as a single entry unless a sport filter is given.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| since | No | ||
| until | No | ||
| activity_type | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden and meets it well. It discloses ordering ('most recent first'), filtering semantics (including the nuanced behavior of sport filters revealing multisport legs), response format ('compact record'), and default/capped limit. This goes beyond basic expectations and fully informs the agent.
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 well-structured: a clear one-sentence summary followed by an 'Args' section. Every sentence provides valuable information, including the multisport edge case. It is detailed yet efficient, with 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?
Despite moderate complexity (4 optional params, no annotations), the description covers ordering, filtering, formatting, and a subtle multisport behavior. An output schema exists, so not detailing every return field is acceptable. The description is self-contained and sufficient for correct invocation.
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 has 0% description coverage (no property descriptions), but the description compensates by explaining every parameter: ISO date format with example, sport filter options, default and max limit, and the 'until' boundary. It adds significant meaning beyond the raw schema.
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+resource statement: 'List recorded activities, most recent first.' This clearly differentiates it from siblings like get_activity_detail or weekly_summary, and the mention of 'compact record' further highlights its listing focus.
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 useful context about filters and output format but does not explicitly state when to use it over alternatives (e.g., 'for a single activity's details, use get_activity_detail'). Usage is implied through the described behavior, but no exclusions or alternative guidance is provided.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
sync_nowA
Pull new activities from Garmin right now, without leaving the conversation.
Args: limit: Maximum activities to download (default: the configured batch size, 25).
Requires the ingest worker to be running — it is the only process allowed to write, since DuckDB grants exclusive access to a single writer. If no worker is listening this fails immediately with instructions, rather than appearing to hang.
Also imports anything waiting in data/inbox/, which needs neither network nor credentials.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description fully reveals the tool's behavior: it writes to DuckDB, requires the ingest worker, fails fast on worker absence, and also handles inbox imports. This discloses failure modes and a concurrency constraint, which is exemplary transparency.
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 well-structured: a one-line summary, an Args section, then two paragraphs of behavioral notes. Every sentence provides distinct information with no fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the single parameter and the presence of an output schema, the description covers purpose, usage, prerequisites, and failure behavior comprehensively. No critical aspect is left undocumented.
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 only defines 'limit' with a default null, while the description explains it as 'Maximum activities to download' and clarifies the default is the configured batch size (25). This adds critical semantic meaning that the schema lacks.
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 'Pull new activities from Garmin right now', which is a specific verb+resource combination that clearly distinguishes this sync tool from siblings like list_activities or get_activity_detail. The added mention of importing from data/inbox further expands the scope, but the core action is 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 implies usage for on-demand synchronization ('right now') and states a prerequisite (ingest worker running), but it does not explicitly name alternatives or when-not-to-use scenarios. This clear context places it above mere implication but below explicit exclusion guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
weekly_summaryA
Training totals for one week, broken down by sport.
Args: week_start: Any ISO date within the week of interest. Snapped back to the Monday, so "2026-03-18" and "2026-03-16" describe the same week. Defaults to the current week.
Multisport events count once, at their combined distance, rather than once per leg.
| Name | Required | Description | Default |
|---|---|---|---|
| week_start | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
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. It discloses important behavioral details such as week-start snapping, defaulting to the current week, and special handling of multisport events. It does not explicitly mention read-only nature, but the summary context makes it obvious.
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 concise and well-structured, with a clear breakdown of the parameter. It provides essential information without any fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool is simple with one optional parameter and an output schema. The description covers core functionality, parameter semantics, and edge cases like multisport counting, making it complete for an agent to invoke 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 description thoroughly explains the week_start parameter, including the snapping behavior and default value, fully compensating for the lack of schema descriptions. This adds significant meaning beyond the input schema.
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 provides training totals for one week, broken down by sport. This distinguishes it from sibling tools like get_activity_detail (individual activities) and compare_activities (comparisons).
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 implies the tool is for weekly summary use, providing clear context. However, it does not explicitly state when to prefer this over alternatives or when not to use it, so it lacks explicit exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
TDQS
Each tool has a clearly distinct purpose: activity retrieval, streams, weekly aggregation, comparison, workout creation/deletion, sync, and database status. No two tools overlap in functionality, so an agent can reliably select the correct one.
Most tools follow a verb_noun pattern (list_activities, create_workout, get_activity_detail), but weekly_summary and database_status are noun phrases, and sync_now is verb+adverb. The mixed style is still readable and predictable, but not perfectly uniform.
Nine tools is well within the ideal 3-15 range. Each tool covers a distinct operation needed for a Garmin data server, and none feel redundant or unnecessary.
Activity data is comprehensively covered: list, detail, streams, weekly summary, and comparison. Workout creation and deletion are present, but there is no way to list or update existing workouts, which is a minor gap for managing a workout library. Sync and status tools round out the surface well.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Garmin data in Claude: 135 tools — activities, sleep, HRV, training, workouts. Free, open source.
Private Apple Health metrics and workout detail for ChatGPT, Claude, and any MCP client.
Garmin data in Claude & ChatGPT via the Garmin Health API. OAuth sign-in, no password sharing.
- mcpOAuthcom.gibsonai
GibsonAI MCP server: manage your databases with natural language
Related MCP Servers
- AlicenseAqualityBmaintenanceA Model Context Protocol (MCP) server for Garmin Connect integration. Access your activities, health data, training metrics, and more through Claude and other LLMs.2261MIT
- FlicenseNot gradedqualityBmaintenanceA multi-platform fitness MCP server that syncs data from Garmin, Strava, Google Fit, and Suunto into a local DuckDB database and provides analytics tools via MCP.1
- AlicenseBqualityAmaintenanceMCP server for local fitness-data extraction and analysis from Garmin Connect, Intervals.icu, and Strava. Provides read-only analytical tools over DuckDB and targeted Strava enrichment.235778AGPL 3.0
- AlicenseNot gradedqualityCmaintenanceA local MCP server that exposes your Garmin Connect data—sleep, HRV, training readiness, workouts, and more—to any MCP-compatible AI assistant. Runs entirely on your machine and keeps your Garmin credentials private.MIT
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/NoaMatout/garmin-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server