- Native
authno longer collapses Garmin SSO rate-limit or bot-protection responses intoUNKNOWN. HTTP 429, Cloudflare-like non-JSON challenges and JSON responses withoutresponseStatus.typenow return actionable guidance: stop retrying, back off, retry from a normal browser-like network, or use the legacy Python helper/trusted local token flow. - CLI auth failure output no longer double-prefixes nested messages as
Garmin login failed: Garmin login failed: ....
- Refresh README/docs into a shorter agent-first front door with a full
docs/tools.mdreference and safer auth troubleshooting guidance.
- Pin transitive
honoresolution to4.12.27via npm overrides, resolving production audit advisories while keeping the public MCP API unchanged.
examples/auth-quickstart.md— end-to-end walkthrough of the self-containedauthcommand with real captured terminal output:--help, interactiveauth, non-interactiveauth --json(success and missing-credentials error), and the post-authdoctorREADYreport. Linked from the README anddocs/quickstart.mdso the no-Python login flow is discoverable.
server.jsonGARMIN_TOKEN_PATHdescription now points at the self-containedauth(no Python) instead of the legacyauth --install-helper.
- Self-contained
authcommand (no Python helper required).garmin-mcp-server authnow runs a pure-Node Garmin Connect login — SSO sign-in, MFA, and OAuth1→OAuth2 ticket exchange are implemented insrc/cli/garmin-login.tsusingnode:cryptofor HMAC-SHA1 signing. Tokens are written to~/.garmin-mcp/garmin_tokens.json(0600) in the same shape the connector already consumes (di_token/di_refresh_token/di_client_id). auth --jsonnon-interactive login viaGARMIN_EMAIL/GARMIN_PASSWORD(+GARMIN_MFA_CODE).scripts/native-auth-test.mjs— verifies OAuth1 signature correctness against an independent recomputation, token-field mapping, full mocked login (happy path + MFA), error paths, and the CLI no-credentials failure.
- The legacy Python
garminconnectflow is now opt-in viaauth --use-python(orauth --install-helperto install the package). Defaultauthno longer requires Python. Docs, help text, doctor hints, and agent guidance updated accordingly.
- HTTP response cache middleware (
src/services/http-cache.ts) — in-memory cache layered OUTSIDE retry (fetchWithCache → fetchWithRetry → fetch), so cached responses skip both network and retry. Default 60s TTL for GET only; POST/PUT/DELETE and 4xx/5xx responses are never cached. GARMIN_NO_CACHE=trueenv var — global per-process cache bypass; advertised inserver.json.- Per-call
cache_ttl: 0request option — opts a single call out of cache without disabling globally. - Query-param-order-insensitive cache keys —
?startDate=…&endDate=…and?endDate=…&startDate=…share one cache entry. garmin_cache_statusnow reportshttp_cachestats alongside SQLite stats:size,hit_count,miss_count,hit_rate,default_ttl_seconds,bypass_env_var.scripts/http-cache-test.mjs— eight-case unit suite covering cache hit, POST never cached, TTL expiration, query-param normalization, 4xx not cached, env-var bypass, per-callcache_ttl: 0, andgetCacheStats()math.
- Dedicated HTTP retry middleware (
src/services/http-retry.ts) — extracted fromGarminClient.fetchWithRetryinto a reusable, testable function with exponential backoff (500ms / 1s / 2s), ±20% jitter, andRetry-Afterheader parsing (supports both seconds and HTTP-date formats). GARMIN_NO_RETRY=trueenv flag — disables retries entirely for tests or callers that want raw error propagation.- HTTP 408 added to retryable status set alongside 429, 500, 502, 503, 504 — request-timeout responses are now transparently retried.
- Network-error retries — fetch failures (ECONNRESET, ENOTFOUND, timeouts) are now retried with the same backoff schedule as HTTP errors instead of bubbling up on the first failure.
- Structured stderr logs — each retry now writes
[garmin-mcp] retry N/3 after Xms (status=Y or error=Z)so agents can correlate spike-and-recovery patterns in their logs. scripts/http-retry-test.mjs— six-case unit suite covering happy path, Retry-After header, env disable flag, 401 non-retry, exhaustion, and network-error retry.
GarminClient.fetchWithRetrynow delegates to the shared middleware so the auth-failure 401 re-auth flow benefits from the same backoff guarantees.- Backoff defers to
Retry-Afterfirst (HTTP standard) and only computes jittered exponential when the header is absent or unparseable.
- Profile-store regex no longer false-positives on common wellness words. Split
SECRET_PATTERNSintoSECRET_KEY_PATTERNS(broad, for field names likeoauth_token) andSECRET_VALUE_PATTERNS(high-specificity, only credential shapes: JWTs,Bearer <token>,sk_live_,sk-proj-,xoxb-,github_pat_, rawAuthorization:headers). Previously legitimate text like "5 training sessions per week", "limit cookies", "I need to refresh my approach", or "secret sauce: more sleep" was rejected. - Partial-profile reads no longer crash downstream.
readProfileFilenow structurally merges withDEFAULT_PROFILEwhen legacy Hermes/OpenClaw files lacked sub-objects (goals, devices, training, nutrition, preferences, safety). PreviouslybuildProfileSummaryandmissingCriticalFieldswould throw. - Onboarding
privacy_noteno longer hard-codes a single connector path. Lists multiple example paths so the message reads correctly from every connector.
- Add shared Delx wellness profile support, vendored from
delx-wellness/lib/profile-store.tsintosrc/services/profile-store.ts(no new npm deps; Node built-ins only). - Add
garmin_profile_gettool — read the shared profile (~/.delx-wellness/profile.json), returns summary, missing_critical fields and storage_path. Read-only. - Add
garmin_profile_updatetool — patch the shared profile; requiresexplicit_user_intent=true. Rejects any field that looks like a secret (oauth/token/secret/password/cookie/refresh/api_key/session). - Add
garmin_onboardingtool — return the 11-question onboarding flow (enorpt-BR) plus current profile state and missing critical fields. Read-only. - Add
garmin-mcp-server onboardingCLI command — print the onboarding flow JSON (and a TTY-friendly Markdown summary when stderr is a TTY). recommended_first_callsnow leads withgarmin_profile_getso agents check the shared profile state before walking quickstart.- Auto-migration from Hermes (
~/.hermes/profiles/delx-wellness/wellness-profile.json) and OpenClaw (~/.openclaw-delx-wellness/workspace/wellness-profile.json) legacy paths to the canonical~/.delx-wellness/profile.json. - Tool count: 38 → 41.
- Fixed agent-facing docs links to use
https://garminconnectmcp.vercel.app/. - Made new Hermes setup configs include
approvals.mcp_reload_confirm: falseby default. - Expanded HTTP smoke coverage to exercise the real MCP
/mcpprotocol, not only/health.
- Made
setupnon-interactive with respect to Garmin login by default; usesetup --authto start auth immediately. - Actually added automatic isolated Python venv fallback for Garmin auth helper when Homebrew Python blocks
pip --userinstalls. - Added CLI regression coverage so smoke tests do not depend on a real local Garmin token.
- Added automatic isolated Python venv fallback for Garmin auth helper when Homebrew Python blocks pip --user installs.
- Updated public docs URL to https://garminconnectmcp.vercel.app after Vercel alias assignment.
- Initial Garmin MCP Unofficial release.
- Added local Garmin Connect auth helper using
garminconnectwithout storing Garmin passwords. - Added 34 read-only tools for profile, devices, daily summaries, sleep, heart, HRV, stress, Body Battery, training readiness, activities, weight and hydration.
- Added daily and weekly agent summaries with data-quality confidence and non-medical action candidates.
- Added privacy modes, token redaction, optional SQLite cache, local doctor checks and Hermes integration guidance.
- Added static documentation and Vercel landing page.