MCP server providing read-only access to Garmin Connect health and fitness data through Cloudflare Workers, with OAuth-based account linking
Strong foundation with well-structured tool definitions, clear descriptions, and comprehensive input/output schemas. All 9 tools have verb-noun naming (get_*, list_) and descriptions (avg 120 chars, within 10-1024 baseline). Input schemas use Zod with proper type constraints, enums, and ranges. Output schemas are documented via Zod contracts. Error handling is sophisticated with 9 categorized error codes and actionable recovery guidance. Tool annotations (readOnlyHint, idempotentHint) are present. Main gaps: parameter descriptions are minimal (1-2 sentences, could be richer per pattern:tool-description baseline of 72 chars avg); no batch variants for common multi-call patterns; date-based tools lack timezone guidance despite SERVER_INSTRUCTIONS mentioning it.
Get one Garmin activity summary with bounded basic splits and no GPS or chart samples
Get available steps, heart-rate, stress, body-battery, intensity, and respiration totals for one Garmin local date
Get Garmin overnight HRV summary, status, and personal baseline without raw samples for one local date
Get the linked owner's concise Garmin profile
Get a bounded sleep summary, stage durations, scores, SpO2, stress, and respiration for one Garmin local date
Get capped Garmin training-readiness snapshots with recovery, sleep, load, HRV, and stress factors for one local date
Date parameter descriptions lack timezone context. All 6 date-based tools accept 'YYYY-MM-DD local calendar date' but descriptions do not explain how to determine the owner's timezone or handle DST transitions. SERVER_INSTRUCTIONS mention 'owner's Garmin timezone' but individual tool descriptions should reinforce this.
No batch variants for multi-call patterns. Agents calling get_activity in a loop (e.g. for 10 recent activities) must make 10 sequential calls. A batch_get_activities(activity_ids: string[]) tool would reduce latency and token waste.
Parameter descriptions are terse (1-2 sentences). Baseline for A+ tools is 72 chars avg per param; most date params here are ~50 chars. Expand with context: 'Garmin local calendar date in YYYY-MM-DD format. Must match the owner's timezone (e.g., 2024-01-15 for US/Eastern). Dates outside the owner's activity history return empty results.'
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 66 | 2025-06-18+ | v2 |
Get Garmin training status, acute/chronic load, workload ratio, and load focus for one local date
Get available generic and cycling Garmin VO2 max and fitness-age estimates for one local date
List a bounded page of the owner's most recent Garmin activity summaries, newest first
Error handling does not distinguish retryable vs. fatal errors in the response structure. errorText() returns a string, but CallToolResult.isError is boolean. LLMs cannot programmatically determine if a rate_limited error should trigger exponential backoff vs. a not_linked error requiring user intervention.
No dry-run or confirmation pattern for potential future write operations. While all current tools are read-only, the server architecture does not reserve a pattern for future destructive tools (e.g., delete_activity, update_profile). Recommend adding a 'confirm' parameter or separate 'preview_*' tools if write operations are planned.