Garmin Local Archive
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": false
} |
| prompts | {
"listChanged": false
} |
| resources | {
"subscribe": false,
"listChanged": false
} |
| experimental | {} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| 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". |
| 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. |
| 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. |
| anomaliesA | 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). |
| 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. |
| 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
|
| 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. |
| 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. |
| get_archive_metadataA | Read archive-state metadata: how complete and healthy the archive is, plus sync and quality logs. kind selects the artefact:
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. |
| 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. |
| 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. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
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.