Garmin Local Archive
This server exposes a local Garmin archive to MCP clients for read-only querying and analysis of health, context, and archive-state data.
query_health— fetch one health field (daily or intraday_series) over an inclusive date range.query_context— fetch weather, pollen, or air-quality fields over a date range.query_raw— pull raw, unprocessed health archive fields (weigh-ins, nutrition, floors, body-battery events, etc.) with no value extraction.baselines— get a field's rolling 90-day mean and ±3 SD reference range for comparison.anomalies— detect single-day outliers and 5+ day above/below-baseline streaks.gaps— list days with no usable data for the health or context domain.correlate— measure Pearson/Spearman correlation between any two health/context fields, with optional −7 to +7 day lag.get_archive_metadata— read archive coverage/quality stats, device table, quality/sync/token logs, and capability config.list_available_fields— discover queryable field names (health/context) plus their units and domains.refresh_cache— manually resync the SQLite query cache after archive changes.query_fit_activities— placeholder that cleanly reports FIT activities as not yet available (planned v1.8).
Provides tools for querying and analyzing a local archive of Garmin health data, including daily summaries and intraday metrics such as heart rate, stress, body battery, SpO2, and respiration, originally synced from Garmin Connect.

Archive and analyze your Garmin Health data local.
Privacy first — inspired by European principles.
A look at the app
Home · Files · Settings · Chat · MCP Server — click any tab to view full size.
Related MCP server: Garmin MCP
Download
Version | Description | Requires |
Recommended — no setup needed | Nothing | |
Standard version | Python 3.10+ |
No install, no terminal. Download, unzip, run.
Standard version: install dependencies first — pip install -r requirements.txt.
The app checks GitHub for a new version on startup. Updates can be downloaded and applied automatically; on the Standalone version this includes a restart, on the standard version you start it again yourself.
Try it first
No Garmin account yet, or just want to see it in action? A synthetic 180-day demo archive (fictional data, clearly marked as such) lets you explore dashboards, Chat and the MCP server without connecting a real account. See QUICKSTART.txt, "Try it first".
Project status & disclaimer
GNU General Public License v3.0 — provided as-is, without warranty of any kind, express or implied.
Not an official Garmin product: This tool is not affiliated with, endorsed, or supported by Garmin.
Not medical advice: All health metrics, reference ranges, and dashboard data are for personal informational use only — not a substitute for medical advice.
AI and health data — handle with care: If you use an external AI service (ChatGPT, Claude, Gemini) to interpret your data: never upload documents containing your name, date of birth, or other identifying information. Cloud AI services store what you send — linked to your account. Use a local model (Ollama) or at minimum a session without login. This includes the app's own in-app Chat tab — its optional Cloud backend (Anthropic/OpenAI) sends your question and any archive data the model requests to that provider's API, same as pasting into their website; with the Ollama backend, local models stay fully local, while an Ollama cloud model (name ending in
-cloud) is processed on Ollama's servers. AI responses on health topics are statistically generated — not medically validated. Treat them as a first orientation, not a conclusion.Context data: Weather data is provided by Open-Meteo and Brightsky (DWD), pollen data and air quality data by Open-Meteo — accuracy and availability are not guaranteed. Air quality data (CAMS dataset) is available from approximately 2020 onwards.
Early stage: Core functionality is stable. APIs and internal structure may still change.
No guaranteed support: Development happens when time and interest allow.
Use at your own risk: I am not responsible for data loss or Garmin account issues.
Feedback welcome: If something feels off — logic, structure, results — open an issue.
Scope & limitations: Local-first, personal use, no enterprise ambitions.
Relies on python-garminconnect. Since Garmin does not offer a public API for personal use, moste non-commercial tools share this dependency. To mitigate unannounced upstream changes, GLA automatically detects and logs structural API shifts.
Local test suites cover the full pipeline plus a separate build-output validation suite. The full suite also runs automatically via GitHub Actions on every push to main (Windows runner), alongside dead-code and CVE-relevance checks; CodeQL security scanning runs on every push/PR to main
HTML dashboards require a one-time internet connection to download Plotly (~3 MB) — cached locally after that
Per-day checkpointing: an interrupted sync resumes from the last completed day, no full re-sync required
Historical data quality depends on Garmin servers
This project is built for my own use. If it happens to be useful to others, feel free to use it — but evaluate it like any other unverified open-source tool.
What this is not: Garmin Connect is still required — the app pulls data from there via API.
A note on cloud folders: the archive itself is stored as plaintext on disk — if garmin_data/ lives inside a cloud-synced folder, that data gets uploaded automatically. See SECURITY.md for details and the encrypted Mirror alternative. This tool does not replace Connect, the Garmin app, or your device sync. It has no cloud component, no remote access, and no sharing features. The GUI and EXE are Windows-only.
Why this exists
I wanted to ask an AI questions about my health data without sending that data to another cloud service. So I built a local alternative instead.
What "intraday resolution" actually means in practice:
Metric | API resolution | Data points / day |
Heart Rate | ~1 minute | up to 1,440 |
Stress | ~3 minutes | up to 480 |
Body Battery | ~15 minutes | up to 96 |
SpO2 | ~1 hour | up to 24 |
Respiration | variable | variable |
→ For the full story, see MINDSET.md.
What makes this different
This project is as much a statement as it is a tool.
This is not a data export script — it maintains a complete, consistent local copy of your Garmin data over time. Your data stays in open formats, readable and analyzable with any tool you choose. Local AI, cloud AI, or no AI at all. Your data, your call.
Feature | Garmin Connect | Cloud-AI Bridges | Garmin Local Archive |
Data storage | Garmin servers (USA) | US AI servers | Your machine |
Privacy risk | Medium | High (training data risk) | Minimal |
Access | Online only | Requires subscription | 100% offline |
History | Erodes over time | Depends on source | Permanent local copy |
AI-assisted development
I can't write Python. The architecture, module boundaries, and decisions are mine. Every line of code is Claude's.
→ How this collaboration actually worked — who had which idea, where Claude was wrong — is documented in MINDSET.md.
How it works
The app works in two modes: live sync pulls recent data directly from Garmin Connect via API; Bulk Import loads your complete history from a Garmin GDPR export ZIP — this is the primary path for recovering years of data that the API no longer serves.
Everything is stored locally in structured formats (JSON, Excel, HTML dashboards). Once downloaded, nothing is transmitted anywhere.
Bulk Import
The GDPR export from Garmin contains your complete daily history.
The Bulk Import feature fills in the rest: request your full data export from Garmin (typically ready in 20–30 minutes), point the app at the ZIP, and your complete daily history lands in the local archive — in the same format as live API data. Days already present with good quality are skipped automatically.
Dashboards
The built-in dashboards cover roughly 90% of what most users are looking for — without any AI at all. For deeper analysis, your data is prepared in a format any local AI can work with directly.
Dashboard | What it shows | Output |
Health Analysis | HRV, Resting HR, SpO2, Sleep, Body Battery, Stress — daily values vs 90-day personal baseline vs age/fitness-adjusted reference ranges. Flags days outside range. | HTML, Mobile HTML, JSON + AI prompt |
Timeseries | Intraday heart rate, stress, SpO2, body battery and respiration as zoomable charts across any date range. | HTML, Excel |
Heatmap | Six intraday metrics (Heart Rate, Steps, Stress, Body Battery, SpO2, Respiration) as time-of-day × date grids — spot daily rhythms and irregularities at a glance. | HTML |
Daily Overview | All summary fields in one flat table, one row per day. | Excel |
Health + Context | Garmin health metrics alongside local weather and pollen data. | HTML, Excel |
Sleep Dashboard | One row per night — segmented phase bar (Deep / Light / REM / Awake), sleep duration, score, quality badge, feedback label, HRV, Body Battery, and 7-day HRV moving average (computed from archive, no extra API call). Color-coded numbers via continuous gradient against personal reference ranges. Inspired by Garmin's own HRV pattern guide. | HTML, Excel |
Sleep & Recovery | HRV, Body Battery, Sleep duration and phase breakdown (Deep / Light / REM / Awake) alongside weather and pollen context. Intraday detail per day. | HTML |
Explorer | Free metric exploration — choose up to 4 metrics from all Garmin daily fields plus weather, pollen, and air quality on a shared time axis. Sleep phase breakdown and sleep quality log included. Built-in field descriptions and air quality interpretation guide. | HTML |
Custom Dashboard | Pick any combination of Garmin daily fields and Context fields, set a date range, and build a one-off dashboard — no fixed field list, no specialist file written to disk. Field selections can be saved as named presets for reuse. Optional AES-256 encryption for the HTML output. | HTML, Excel |
Live Tracking — a separate, always-current view: today's progression (Body Battery, Heart Rate, Steps, Stress) plus last night's sleep summary, refreshed automatically after every sync and on demand via an "Update Live" button in the Home tab. Not part of the Create Dashboards selection above — it has its own trigger, by design.
Export
Export Data writes your archive to external formats for other tools to consume — a separate layer from the dashboards above, reading the same broker data. Available from the Dashboards section's "Export Data" button, per-adapter grid (toggle Health/Context/Metadata independently per format):
Adapter | Format | Notes |
JSON |
| Full archive, 1:1 broker output |
CSV |
| One row per day, |
InfluxDB Line Protocol |
| Full resolution incl. intraday/series data — for feeding garmin-grafana or similar tools without a Garmin API call of their own. |
"Save & Add to Daily Sync" persists your selection and runs it automatically as part of every daily sync — no need to export by hand after each sync.
Chat
A native chat panel is built into the app (Chat tab, renamed from "Ollama-Chat") — no separate setup beyond having Ollama itself installed and a model pulled, or a Cloud API key configured. Two independent choices, made before Start:
Backend — a local Ollama model (fully local, no setup beyond Ollama itself), or a Cloud LLM (Anthropic or OpenAI — requires an API key, configured on the MCP Server tab; see the AI disclaimer above for what that means for your data).
Source — the archived daily-summary snapshot (fast, no live query, the original behavior), or live MCP tool-calling — the model queries your archive on demand via the same MCP server external tools use, for questions the daily snapshot alone can't answer.
Replies stream in as they're generated for every combination except
Ollama + MCP tool-calling (Ollama's own streaming support for tool-calling
is not yet reliable upstream, so that one path waits for the complete
answer). Chat sessions auto-save and can be reloaded later via the
Chat History button — a session using live MCP data can always be
resumed; a session using the daily snapshot can only be resumed if that
snapshot hasn't changed since, otherwise it opens read-only. For connecting
external tools (Open WebUI, AnythingLLM, Claude Desktop) for more advanced
document/RAG workflows instead, see docs/README_APP.md and the MCP
Server section below.
MCP Server
Garmin Local Archive exposes your archive to local LLMs via the Model Context Protocol — ask an LLM about your health and context data directly, without exporting files or copy-pasting into a chat window. Runs as its own standalone process, independent of the main app.
Two ways to run it: a ▶️ Start MCP Server button on the app's own
MCP Server tab (uses the same archive path and settings as the rest of
the app), or the standalone mcp_server.exe — a self-contained tool with
its own small window, usable even without the main app installed.
Running Open WebUI (or any other MCP client) inside Docker? An opt-in
"Extra allowed hosts" field on the MCP Server tab lets you add
host.docker.internal — pre-filled by default — to the server's allowed-
host list, since the underlying MCP SDK otherwise rejects connections
that don't arrive as 127.0.0.1/localhost.
Works with Ollama (local models, default) or an optional cloud LLM backend — your choice, no default push toward either. Point your MCP-compatible client (Claude Desktop, Open WebUI, or similar) at the server and start asking questions.
Connect: Streamable HTTP at http://127.0.0.1:<port>/mcp (default
port 8756, configurable on the MCP Server tab) — bound to localhost
only, not reachable from other machines by default. See "Extra allowed
hosts" above for Docker clients.
Tools exposed (see docs/REFERENCE_MCP.md for full signatures):
Tool | What it does |
| Daily health metrics (e.g. sleep, stress, heart rate, body battery) for a date or date range |
| Weather, pollen and air-quality data for a date or date range |
| FIT activity data — planned, not yet available (see |
| Archived data as stored in |
| Archive coverage and quality info (date ranges, missing days, data quality) |
| Which fields the archive actually contains, per domain |
| Rebuilds the server's local query cache on demand, e.g. right after a sync |
| A health field's rolling 90-day average and normal range, day by day |
| Days (or multi-day streaks) where a health field broke from its own recent pattern |
| Days with no usable data in a given domain and date range |
| How closely two fields move together over time — any combination of health and context, with optional lag (e.g. "does X today predict Y a few days later") |
Example: "How did my sleep and stress compare over the last two weeks?" — the model calls the tools above itself and answers from your real archive, no manual export needed. No archive yet? See Try it first above.
Architecture
Token security & Login
Garmin login works via SSO — logging in with email and password on every run triggers Captcha or MFA. The solution: log in once manually, and Garmin returns an OAuth token that handles all subsequent runs for approximately one year. This token is equivalent to a logged-in session and must not sit unprotected on disk.
The token is encrypted at rest. Details on the encryption design and threat model: SECURITY.md
Pipeline
Live sync and Bulk Import both flow through the same validation and quality pipeline before anything lands in the local archive — the diagram below shows the full picture.
Pipeline Architecture: For a detailed view of the v1.3.4 data flow including the validation layer and self-healing loop, open screenshots/flowchart_v134.html in your browser.
System Architecture
The diagram below shows how all components relate to each other as of v1.7.x — from API ingestion and context collection through the broker layer to dashboard export. The broker layer also includes gateway_map, a cross-domain routing layer used by the local MCP server among other consumers — existing dashboards are unaffected and continue to query health_map/context_map directly.

What is included
The project is structured into six focused layers — Garmin pipeline, Context pipeline, Data brokers, Dashboard layer, Export/MCP layer, Desktop app. Each layer has a single responsibility — collect, validate, assess, broker, or render. No crossover between layers.
The diagram above shows how the layers connect. Each module is self-contained and designed to be extended — for a script-by-script reference (what each module does, owns, and how to add new ones), see docs/MAINTENANCE_GLOBAL.md.
The desktop app includes a Background Timer — once started, it automatically repairs failed/incomplete days, upgrades bulk-imported days within its 180-day retry window, fills missing days, keeps a raw API-response backup current, retroactively adds newly supported data fields (like step count) to already-archived days, and re-fetches bulk-imported days for the handful of data fields the official Garmin export never contains at all (HRV, SpO2, Body Battery, respiration, training status, race predictions, max metrics) — regardless of how old the day is, since that gap has nothing to do with intraday resolution — with no further manual steps in between. The timer must be started manually and only runs while the app is open — it does not resume automatically after a restart.
Data is stored in two root folders:
garmin_data/
├── raw/ – complete API dumps (~500 KB/day) — permanent archive / basis for dashboards and analysis
├── source/ – unmodified API responses (~250 KB/day) — replay-safe intraday backup
├── summary/ – compact daily JSONs (~2 KB/day) — basis for dashboards and analysis
└── log/ – session logs, quality register, encrypted token
context_data/
├── weather/summary/ – daily weather archive (Open-Meteo)
├── pollen/summary|raw/ – daily / hourly pollen archive (Open-Meteo Air Quality)
├── brightsky/summary|raw/ – daily / hourly weather archive (Brightsky DWD)
└── airquality/summary|raw/ – daily / hourly air quality archive (Open-Meteo Air Quality)See docs/MAINTENANCE_GLOBAL.md for full technical documentation, how to add new fields, troubleshooting, and developer notes.
Testing
The test suites (one list in run_tests.ps1, counts in docs/METRICS.md) cover the full pipeline — no network, no API required
(the Chat tab's Cloud LLM/MCP-tool-calling tests mock every SDK/HTTP call,
same as everything else here):
python tests/test_local.py # Garmin pipeline — cross-module checks (determinism, invariants, robustness, end-to-end)
python tests/test_<module>.py # Garmin pipeline — one file per module of src/garmin/ (19 files, e.g. test_writer.py, test_quality.py, test_collector.py; shared setup in tests/gla_testenv.py)
python tests/test_local_context.py # Context pipeline (external APIs mocked)
python tests/test_dashboard.py # Dashboard pipeline
python tests/test_broker.py # Broker layer (health_map / gateway_map routing, metadata_map)
python tests/test_mcp.py # MCP layer (mcp_map protocol translation)
python tests/test_export.py # Export Layer
python tests/test_app_logic.py # App layer (entry points, path resolution)
pytest tests/test_qt_app.py # PyQt6 App layer
python tests/test_static.py # ruff + bandit + regression guards
python tests/test_build_output.py # Build output validation (run after build)
pytest tests/test_cloud_llm.py # Chat tab — Cloud LLM connector
pytest tests/test_mcp_tool_chat.py # Chat tab — Ollama + MCP tool-calling
pytest tests/test_cloud_tool_chat.py # Chat tab — Cloud + MCP tool-calling, streaming
pytest tests/test_chat_session_store.py # Chat tab — session save/load/resume
pytest tests/test_cloud_credential_store.py # Chat tab — API key storage in Windows Credential Manager
pytest tests/test_mcp_process.py # MCP Server Start/Stop process control
python tests/test_updater.py # T2 + T3 self-updaterbuild_all.py runs run_tests.ps1 — all suites, one list, the pytest ones included — as the pre-build gate; a failing suite aborts the build before either target is built. test_build_output.py and test_app_logic.py run automatically after both builds complete, as post-build gates. test_qt_app.py and the Chat-tab/MCP-process suites are part of that list and can still be run on their own via pytest.
GUI changes are verified manually before release. Automated builds and release packaging run via a manually-triggered GitHub Actions workflow (build-release.yml) — not on every push, kept as a deliberate one-click step so not every commit cuts a release.
⚠️ API Usage Notice: This project uses an unofficial interface. Large-scale data retrieval (e.g., syncing long time ranges in a single run) may trigger rate limiting or temporary IP blocks by Garmin (HTTP 429).
It is recommended to:
fetch data in smaller increments
include delays between requests
allow cool-down periods between sync sessions
Built with Claude · ☕ buy me a coffee
Available Tools
11 toolsanomaliesA
Find days where one health field broke from its own recent pattern. Returns two kinds of findings: "outliers" (single days far outside the normal range) and "streaks" (five or more consecutive days consistently above or below the baseline average, even if none of them individually qualifies as an outlier -- useful for catching a slow drift, e.g. a gradually rising resting heart rate).
| Name | Required | Description | Default |
|---|---|---|---|
| field | Yes | ||
| date_to | Yes | ||
| date_from | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden, and it does so for the important part: it defines the detection semantics (single-day outliers vs. streaks of 5+ consecutive days relative to a baseline average) and notes streaks can fire even when no single day is an outlier. It omits operational traits such as whether results are computed live or cached, and any sensitivity/threshold configurability, which keeps it short of a 5.
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 purpose, then output taxonomy in a single follow-on sentence with no filler. The parenthetical streak definition is dense but earns its place by teaching the agent a non-obvious detection rule.
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?
There is no output schema and no annotations, so the description's explanation of the two finding kinds is genuinely necessary and good. However, the three required parameters remain completely unspecified (field naming and date format), which an agent needs in order to call 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?
Schema description coverage is 0% and all three parameters (field, date_from, date_to) are undocumented. The description only obliquely implies field is 'one health field' and says nothing about date_from/date_to format or range semantics, so it only partially compensates for the coverage gap.
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 object ('Find days where one health field broke from its own recent pattern') and immediately distinguishes itself from detection-adjacent siblings like baselines, gaps, and correlate by naming the two finding types it produces. An agent can tell what this tool returns 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?
Usage is implied rather than stated: the outlier/streak examples show when the tool is useful ('catching a slow drift, e.g. a gradually rising resting heart rate'), but there is no explicit when-to-use-this-vs-baselines-or-gaps routing and no excluded case. Minimum viable guidance for a tool with several overlapping siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
baselinesA
Query the rolling 90-day baseline and statistical reference range (mean +/- 3 standard deviations) for one health field over a date range. Use this to see what "normal" has looked like for a field recently, or alongside query_health() to compare today's value against the person's own history rather than a fixed threshold.
| Name | Required | Description | Default |
|---|---|---|---|
| field | Yes | ||
| date_to | Yes | ||
| date_from | 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. It does disclose the analytical model (rolling 90-day window, mean ±3 standard deviations) and that it is a read/query operation, but it says nothing about permissions, cost, handling of sparse data, or whether results are precomputed vs. computed on demand.
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 tight sentences with the core definition front-loaded and no filler. The parenthetical '(mean +/- 3 standard deviations)' earns its place by defining the reference range; the second sentence is slightly long but carries the routing guidance.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema and no annotations, the description does well to explain what the result conceptually contains (baseline plus reference range), but omits return shape details (sample counts, SD, per-metric structure) and the operational facts an agent needs for a 3-required-parameter query 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?
Schema description coverage is 0%, so the description must compensate for all three undocumented parameters. It clarifies that 'field' is a health field and that the date pair defines a window, but gives no field naming convention (despite the list_available_fields sibling), no date format, and no constraints such as whether the range is capped by the 90-day 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?
States a specific verb (Query) plus a precisely defined resource: the rolling 90-day baseline and mean ±3 SD reference range for one health field. It also names the sibling it contrasts with (query_health) and the scoping constraint (one field, one date range), so an agent can distinguish it 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 when-to-use guidance (to see what 'normal' looks like recently, or paired with query_health to compare against the person's own history rather than a fixed threshold) and names the alternative tool. It stops short of stating when not to use it or when other siblings like anomalies or correlate are the better fit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
correlateA
Measure how closely two fields (health or context, any
combination) move together over a date range -- e.g. does stress
correlate with pollen count, or resting heart rate with sleep
duration. lag_days (-7 to +7) shifts field_b relative to field_a:
a positive lag compares field_a on a given day against field_b
lag_days days LATER (useful for "does X today predict Y a few
days from now" questions); 0 (the default) compares same-day
values. Returns both Pearson (linear relationship) and Spearman
(monotonic relationship, more robust to outliers) correlation
coefficients, each in [-1, 1], plus how many overlapping data
points they were computed from.
| Name | Required | Description | Default |
|---|---|---|---|
| date_to | Yes | ||
| field_a | Yes | ||
| field_b | Yes | ||
| lag_days | No | ||
| date_from | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations and no output schema, the description carries the full behavioral burden and does so well: it discloses the two returned coefficients, their [-1, 1] range, the meaning of Pearson vs Spearman (linear vs monotonic, outlier robustness), and that an overlap count is returned. It omits any note on permissions, rate limits, or behavior when sample sizes are too small.
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-loads the core operation, then the lag semantics, then the return values. Every clause adds information, though the parenthetical explanations in the lag sentence are slightly dense. No filler or restatement of the 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?
For a 5-parameter, no-output-schema statistical tool, the description covers operation, parameter semantics, and return values adequately. The remaining gap is field-name/date-format discovery and minimum-sample behavior, which an agent may need before invoking successfully.
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 must carry parameter meaning. It fully specifies lag_days (bound -7 to +7, default 0, direction of shift), and covers field_a/field_b ('two fields, health or context, any combination') and the date range implicitly. It does not state the accepted field-name or date-string formats.
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 statistical verb+resource ('Measure how closely two fields move together over a date range') and gives concrete examples (stress vs pollen, resting HR vs sleep duration). This is clearly distinguishable from siblings like baselines, anomalies, and query_health, which return values rather than relationships.
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?
Usage context is clear via worked examples and the lag_days use case ('does X today predict Y a few days from now'), giving the agent a good sense of when this tool applies. It does not, however, name which sibling to use instead when the agent merely wants raw values (query_health/query_context) or how to discover valid field names (list_available_fields).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
gapsB
List days with no usable data over a date range. domain is "health", "context", or "fit" ("fit" always degrades to "domain not yet available" -- no FIT pipeline exists yet). A gap is a day that was never synced at all, or was synced but came back empty/failed.
| Name | Required | Description | Default |
|---|---|---|---|
| domain | Yes | ||
| date_to | Yes | ||
| date_from | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden. It does disclose the key behavioral trait that domain='fit' is non-functional, and defines gap semantics (never synced vs synced-but-empty/failed), which is genuinely useful. However, it says nothing about return shape, permissions, or reliability limits, leaving real gaps for a read-style tool with zero annotation coverage.
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-loaded with the core purpose, followed by domain caveats and the gap definition. Minimal waste; the parenthetical is slightly awkward but earns its place by preventing a doomed 'fit' call.
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?
Covers purpose, domain semantics, and gap definition, but with no output schema and 0% parameter coverage, the description omits date format and any hint of what the returned list looks like. Adequate but with clear omissions for a 3-required-param 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?
Schema description coverage is 0%, so the description must compensate. It does document the domain enum values and the fit degradation, which is real added value the schema lacks. But the two date parameters (date_from/date_to) get no format or syntax guidance despite being bare 'string' types, leaving 2 of 3 params under-specified.
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 precise verb+resource: 'List days with no usable data over a date range,' and goes on to define what a gap is. This clearly differentiates the tool's function from sibling query tools, though it never names an alternative sibling the way a 5 would.
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?
Usage is implied (find uncovered days), and the note that domain 'fit' always degrades to 'domain not yet available' is a useful when-not caveat. But there is no explicit statement of when to choose this over anomalies, baselines, or query_health, nor any stated prerequisites.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_archive_metadataA
Read archive-state metadata: how complete and healthy the archive is, plus sync and quality logs. kind selects the artefact:
"stats": small coverage/quality overview (total, high/standard/failed counts, date range, coverage_pct, last sync, integrity warnings). Use this for "how big/complete/healthy is my archive" questions.
"device_table": devices seen in the archive, with date range and number of days each.
"quality_log": per-day quality register.
"source_api_log": one record per data fetch (time, source, validator status, endpoints fetched).
"token_log": token events (created/invalidated/blocked/valid), never token contents.
"capability_config": which optional Garmin endpoints are enabled (may be empty).
"daily_logs", "fail_logs", "recent_logs": sanitized sync log lines, one entry per log file.
date_from/date_to (ISO "YYYY-MM-DD", inclusive) are needed for "quality_log", "source_api_log", "daily_logs", "fail_logs" and "recent_logs": without both dates these return an empty result (no error, no default range). They are ignored for the other kinds. The log kinds are large (a single week of daily_logs is several hundred KB), so request only the days you need and prefer "stats" for overview questions.
Result: {"data": ..., "error": None}. An unknown kind raises an error.
| Name | Required | Description | Default |
|---|---|---|---|
| kind | Yes | ||
| date_to | No | ||
| date_from | No |
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: silent empty results when date_from/date_to are missing (no error, no default range), dates ignored for other kinds, log payloads of several hundred KB, token_log never exposing token contents, and an error on unknown kind. These are exactly the non-obvious behaviors an agent needs.
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 purpose followed by a clean bulleted kind list, so it is easy to scan. It is somewhat long and mildly repetitive — the log kinds are re-listed once in the bullet list, again in the date-requirement sentence, and again in the size warning — but each repetition carries a distinct constraint, so the cost is small.
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?
No output schema exists, yet the description documents the response envelope ({"data": ..., "error": None}) and the error behavior for unknown kinds. Combined with complete parameter semantics and the size/date caveats, an agent has everything required to call this 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?
Schema coverage is 0%, so the description must compensate — and it does. It defines the full domain of 'kind', gives ISO 'YYYY-MM-DD' inclusive semantics for date_from/date_to, and states precisely which kinds require both dates and which ignore them. Nothing about the three parameters is left ambiguous.
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+resource ('Read archive-state metadata') and then enumerates every accepted 'kind' with a concrete description of what each returns (stats, device_table, quality_log, source_api_log, token_log, capability_config, log kinds). This is far more than a name restatement and clearly separates it from siblings like query_health or gaps.
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?
Explicit routing guidance: 'Use this for "how big/complete/healthy is my archive" questions', and 'prefer "stats" for overview questions'. It also gives the when-not/no-op condition — the log kinds silently return empty without both dates — and warns to request only needed days because logs are large.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_available_fieldsA
List the queryable fields, grouped by domain and source. Call this first when the exact field name for query_health or query_context is unknown (the fields of query_raw are not listed here; they are named in that tool's description). Omit domain for the full overview, or pass "health" or "context" to narrow it — the narrowed result is much smaller. "fit" returns an empty list (not available yet); an unknown domain returns no fields rather than an error.
Result: "domains" (the domain names), "metadata_kinds" (the archive- state kinds get_archive_metadata works with), "fields" ({domain: {source: [field, ...]}}; an empty list for "fit") and "units" ({field: unit} covering every field returned; values such as "index" or "text" mark fields without a physical unit). A field name ending in "_series" holds an intraday timeseries; any other name holds one value per day.
| Name | Required | Description | Default |
|---|---|---|---|
| domain | No |
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 that 'fit' returns an empty list, that an unknown domain silently returns no fields rather than erroring, and that narrowing by domain produces a much smaller result. These are non-obvious edge-case behaviors an agent would otherwise have to discover by trial.
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 purpose and trigger, then parameter behavior, then return shape — a sensible order. It is dense and slightly long, but every clause carries operational information (edge cases, units, _series naming convention), so little is wasted.
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?
There is no output schema and no annotations, yet the description fully documents the return payload (domains, metadata_kinds, fields, units) and the _series naming convention. Nothing needed to call or interpret this tool 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?
Schema coverage is 0% — the domain parameter has no description or enum in the schema — so the description must compensate, and it does: omit domain for the full overview, pass 'health' or 'context' to narrow, and the special-case 'fit' returns empty. This is richer than the schema by a wide margin.
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 ('List the queryable fields') plus the grouping dimension ('by domain and source'). It explicitly differentiates from siblings by noting that query_raw's fields are excluded and named elsewhere, and it names query_health and query_context as the consumers of the output.
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 ('Call this first when the exact field name for query_health or query_context is unknown') and names the sibling it does not cover (query_raw). No ambiguity about when to reach for this tool versus the query tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
query_contextA
Query external context data (weather, pollen, air quality) for one field over a date range. Use query_health for Garmin body data (heart rate, sleep, stress, ...) instead; call list_available_fields first if the exact field name is unknown.
field: a context field name (e.g. "temperature_max", "pollen_grass_series"), or a category name — "weather", "pollen" or "air" — to get the main fields of that category at once. A "_series" suffix means intraday timeseries data; a plain name means one value per day. date_from/date_to: ISO "YYYY-MM-DD", inclusive. resolution is accepted but ignored — the field name alone decides the shape.
Unknown or ambiguous field names return an "error", often with a "did_you_mean" list of candidates. A clear typo or alias is resolved automatically; _meta.field_resolved_from / field_used then show what was substituted. A field with no data in the range returns empty values, not an error.
| Name | Required | Description | Default |
|---|---|---|---|
| field | Yes | ||
| date_to | Yes | ||
| date_from | Yes | ||
| resolution | No | daily |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations present, the description carries the full burden and does so well: it discloses that resolution is accepted but ignored, that unknown/ambiguous fields return an error with a did_you_mean list, that typos are auto-resolved with _meta.field_resolved_from/field_used, and that an empty range yields empty values rather than an error.
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 core action is front-loaded in the first sentence, routing guidance follows immediately, and the second paragraph is a dense but purposeful set of edge-case behaviors. No sentence is 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?
With no output schema and no annotations, the description still covers error behavior, resolution metadata, and empty-result semantics, which is exactly what an agent needs to call and interpret this 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?
Schema coverage is 0%, so the description must compensate, and it does: it defines the field parameter's naming conventions (category names, _series suffix meaning intraday timeseries vs one value per day), the ISO YYYY-MM-DD inclusive format for date_from/date_to, and the no-op nature of resolution.
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 specific verb and resource ('Query external context data (weather, pollen, air quality) for one field over a date range'), and explicitly contrasts itself with query_health and list_available_fields. An agent can distinguish it from every sibling without opening a 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?
It gives explicit routing rules: use query_health for Garmin body data, and call list_available_fields first when the field name is unknown. Both the alternative tool and the condition that selects it are stated, 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.
query_fit_activitiesA
Query FIT activity data for a field over a date range. Not yet available (FIT pipeline is v1.8) — returns a clean "not available" result until then, never an error.
| Name | Required | Description | Default |
|---|---|---|---|
| field | Yes | ||
| date_to | Yes | ||
| date_from | Yes | ||
| resolution | No | daily |
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 add genuinely non-obvious behavior: the call succeeds with a clean 'not available' result and never errors. That prevents an agent from misreading a placeholder as real data. It still omits auth requirements, what an invalid field returns, and how resolution affects output.
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, zero filler. The core purpose leads and the availability caveat, which is the single most decision-relevant fact, follows immediately.
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, 4-parameter query tool with no output schema, the description adequately covers the behavioural quirk but leaves parameter formats (field naming, resolution values, date format) undocumented in both schema and text. An agent could call it successfully but not interpret the arguments well.
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 0%, so the schema documents nothing and the description must compensate. It covers the 'field' and date-range parameters implicitly but says nothing about 'resolution' (default 'daily'), its allowed values, or the expected date format, leaving the coverage gap half-filled.
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 verb and resource ('Query FIT activity data for a field over a date range'), which is concrete and distinct from sibling names like query_health or query_raw. It does not explicitly contrast itself with those siblings, so it loses the top tier, but the purpose 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?
It discloses an important negative condition — the tool is not yet functional (FIT pipeline v1.8) and returns a placeholder rather than an error. However, it never names an alternative (e.g., query_raw) for an agent that actually needs this data now, so routing guidance is implied rather than explicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
query_healthA
Query Garmin health data (e.g. heart rate, sleep, stress, body battery, steps) for one field over a date range. Use query_context instead for weather, pollen and air quality; call list_available_fields first if the exact field name is unknown.
field: a health field name, e.g. "resting_heart_rate", "hrv_last_night", "body_battery_max", "steps_series". A "_series" suffix means intraday timeseries data; a plain name means one value per day. Pass the field that matches what you want — "sleep_score" returns three related fields together (sleep_score, sleep_score_feedback, sleep_score_qualifier). date_from/date_to: ISO "YYYY-MM-DD", inclusive. resolution is accepted but ignored — the field name alone decides the shape.
Unknown or ambiguous field names return an "error", often with a "did_you_mean" list of candidates (e.g. "steps" or "stress" without suffix: choose the daily or the _series variant). A clear typo or alias is resolved automatically; _meta.field_resolved_from / field_used then show what was substituted. A field with no data in the range returns empty values, not an error. Every field carries a "unit".
| Name | Required | Description | Default |
|---|---|---|---|
| field | Yes | ||
| date_to | Yes | ||
| date_from | Yes | ||
| resolution | No | daily |
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 that resolution is accepted but ignored, that unknown/ambiguous fields error with a did_you_mean list, that typos/aliases are auto-resolved via _meta.field_resolved_from/field_used, and that empty ranges return empty values rather than errors. It stops short of explicit read-only/permission statements, but the behavioral disclosure is unusually 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?
Front-loaded with purpose and routing before the denser parameter semantics. Every sentence earns its place, though the parameter block is dense enough that it reads as one long paragraph rather than being cleanly separated.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description still explains the return shape: per-field 'unit', empty values for no-data ranges, and _meta resolution fields. Combined with the error semantics, an agent has all it needs to call and interpret results.
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 must compensate for all 4 parameters — and it does: field naming conventions ('_series' = intraday timeseries vs plain = one value per day), the multi-field sleep_score behavior, ISO YYYY-MM-DD inclusive dates, and the ignored resolution param. This meaning exists nowhere else.
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 (Query) and resource (Garmin health data: heart rate, sleep, stress, body battery, steps) over a date range, and distinguishes itself from siblings query_context (weather/pollen/air quality) and list_available_fields. An agent can tell exactly which tool to pick without opening any 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?
Explicitly routes alternatives: 'Use query_context instead for weather, pollen and air quality; call list_available_fields first if the exact field name is unknown.' When-to-use and when-to-use-elsewhere are both stated concretely.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
query_rawA
Query archived Garmin data as stored in raw/, without value extraction, for fields the processed tools cannot reduce to a daily value (lists, event logs). Interpreting the content is up to the caller, and no units are provided. Use query_health instead whenever the data you need exists there: three of these fields have a processed counterpart there for everyday questions (floors: floors_climbed, daily_weigh_ins: body_weight, intensity_minutes_data: intensity_min_moderate and intensity_min_vigorous).
field: one of the raw field names (health domain only; these are NOT listed by list_available_fields): daily_weigh_ins, menstrual_calendar_data, pregnancy_summary, lifestyle_logging_data, nutrition_daily_food_log, nutrition_daily_meals, nutrition_daily_settings, floors, intensity_minutes_data, body_battery_events, lactate_threshold, running_tolerance. date_from/date_to: ISO "YYYY-MM-DD", inclusive. domain: only "health" holds raw data.
Only the requested field is returned. A field name that is almost right is corrected automatically (_meta.field_resolved_from / field_used show the substitution); an unknown name returns an error, with did_you_mean when similar names exist. A domain other than "health" returns an error.
Result: {"health": {field: {"values": [{"date": ..., "raw": ...}], "source_resolution": "raw"}}}. A day that is not in the archive has no entry; an archived day with nothing stored for the field has "raw": null. Read-only; does not change the archive.
| Name | Required | Description | Default |
|---|---|---|---|
| field | Yes | ||
| domain | No | ||
| date_to | Yes | ||
| date_from | 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 discharges it: read-only and non-mutating ('does not change the archive'), no units provided and interpretation left to the caller, auto-correction of near-miss field names surfaced via _meta.field_resolved_from/field_used, error semantics with did_you_mean, and domain rejection. It also discloses result-shape edge cases (missing day = no entry, stored-but-empty = raw null), which is more than the schema or any annotation would reveal.
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 and the query_health routing are front-loaded in the first two sentences, and the remaining blocks (field list, date semantics, resolution behavior, result shape) are each load-bearing rather than filler. It runs long and the parenthetical field-to-counterpart mapping is dense, but nothing is redundant with structured data, so the size is justified.
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 4-param, no-output-schema tool, the description covers everything an agent needs: valid fields, date format and inclusivity, the single valid domain, resolution and error behavior, and the exact response envelope with null-vs-absent semantics. Nothing material is left to inference.
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 0%, so the description must compensate, and it does for all four parameters: field is fully enumerated with an explicit caveat that these names are absent from list_available_fields, date_from/date_to are specified as ISO YYYY-MM-DD and inclusive, and domain is constrained to 'health' with the error behavior spelled out. This adds substantial meaning the bare 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?
States a specific verb and resource ('Query archived Garmin data as stored in raw/') plus the scope constraint (no value extraction, only fields the processed tools cannot reduce to a daily value). It names the sibling it is not (query_health) and explains why the raw path exists, so an agent can distinguish it from query_health and list_available_fields without opening either 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?
Explicitly routes the agent: 'Use query_health instead whenever the data you need exists there', then names the three fields with processed counterparts and the exact target fields. It also states when the tool is inappropriate (fields that reduce to a daily value) and warns that its field names are NOT listed by list_available_fields, which prevents a common misuse.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
refresh_cacheA
Manually trigger a SQLite cache sync against the archive — use this if recent archive changes (a sync just run, a backfill/recheck just completed) might not yet be reflected in query results. Runs the same sync the server already performs automatically at startup. May take a while on a large pending delta (long idle period since the last sync) — this call blocks until the sync finishes.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden and does well: it discloses that the call blocks until the sync finishes, that it may take a long time on a large pending delta, and that it reuses the startup sync path. It omits any concurrency concern (e.g. what happens if a sync is already running) and any indication of what the call returns.
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 action and its trigger are front-loaded in the first sentence, with blocking behavior last. It is slightly longer than needed — the em-dash aside about backfill/recheck could be tightened — but every sentence carries real 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 parameterless, annotation-free maintenance call with no output schema, the description covers the essential agent-facing facts: what it does, when to invoke it, and that it blocks. It does not say what happens on failure or whether the call is idempotent, which are minor gaps for a zero-arg 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; there is nothing in the schema for the description to clarify or compensate for.
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 gives a specific verb and resource — "Manually trigger a SQLite cache sync against the archive" — and frames it as the manual counterpart to the sync the server runs at startup. This is the only maintenance action among the query_* siblings, so an agent can distinguish it without opening a 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?
It states the triggering conditions precisely: use when a sync, backfill, or recheck has just run and results may be stale. It does not explicitly say when to avoid calling it (e.g. during normal operation, or that query tools are the alternative), so it stops short of full when/when-not/alternatives guidance.
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.
4 tool updates
v1.7.5- Added
anomalies - Added
baselines - Added
correlate - Added
gaps
7 tool updates
v0.1.0- First observed
get_archive_metadata - First observed
list_available_fields - First observed
query_context - First observed
query_fit_activities - First observed
query_health - First observed
query_raw - First observed
refresh_cache
TDQS
Scored across 11 tools
Each tool has a clearly distinct role: query_health vs query_context split cleanly by body vs external data, baselines vs anomalies vs gaps vs correlate each target a different analytical question, and query_raw is explicitly scoped against query_health with guidance on when to prefer each. The descriptions actively cross-reference and steer the agent away from the most likely confusions.
All names are lowercase snake_case, but the pattern mixes verb_noun forms (query_health, refresh_cache, get_archive_metadata, list_available_fields) with bare-noun analytical tools (baselines, anomalies, gaps, correlate). Readable and predictable enough, but not a single consistent convention.
Eleven tools is well within the ideal range and each one earns its place: discovery, health/context querying, raw access, three analytics tools, gap listing, cache refresh, and archive metadata. No redundant or filler tools.
For a read-only local archive the query/analytics surface is thorough (field discovery, daily and series queries, raw access, baselines, anomalies, gaps, correlation, metadata, cache refresh). The main gap is that FIT support is entirely non-functional — query_fit_activities is a stub and gaps 'fit' always degrades — which leaves a whole promised domain uncovered.
Maintenance
Related MCP Connectors
Garmin data in Claude & ChatGPT via the Garmin Health API. OAuth sign-in, no password sharing.
Unofficial Garmin Connect access: sleep, HRV, training, activities, workouts and weight.
Garmin data in Claude: 135 tools — activities, sleep, HRV, training, workouts. Free, open source.
MCP server for Withings health data — sleep, activity, heart, and body metrics.
Related MCP Servers
- AlicenseBqualityAmaintenanceConnects to Garmin Connect and exposes your fitness and health data (activities, sleep, heart rate, steps, body composition) to Claude and other MCP-compatible clients.1481,348MIT
- AlicenseBqualityAmaintenanceLocal-first MCP server that connects AI agents to your Garmin sleep, HRV, Body Battery, stress, training readiness and activities, keeping tokens on your machine.242125 npm13MIT
- AlicenseBqualityAmaintenanceDownloads all your Garmin health and fitness data into a local SQLite database and exposes 45 MCP tools for AI analysis, enabling assistants to query sleep, training load, HRV, and more.48183 PyPI160AGPL 3.0
- AlicenseAqualityAmaintenanceLocal-first Garmin data warehouse with an analysis-grade MCP server. Sync once, analyze forever, even when the API is down.13142 PyPI9MIT