Garmin MCP — the localhost-only bridge to Garmin Connect¶
Container garmin-mcp-server, built from the checkout ~/garmin-mcp (upstream Taxuspt/garmin_mcp) with the untracked compose.server.yaml (bind-mounts tokens ~/.local/share/garmin-mcp/tokens and ~/garmin-data). Serves http://127.0.0.1:8000/mcp with /healthz. Localhost only — nothing off-box can reach it, which is why the training scheduler runs as local cron and not as a claude.ai cloud routine.
Why this shape¶
Garmin credentials and tokens stay on the box. Consumers are all local: Claude Code's garmin MCP server, training-sync, and claude-hub.
Shape¶
- Health:
curl -fsS http://127.0.0.1:8000/healthz(training-sync waits up to 5 min for it,~/.local/bin/training-sync:39-45). - Consumers: Claude Code MCP
garmin(150+ tools) ·training-sync(three cron passes) ·claude-hub/app.py(GARMIN_URL; live :8090 runs the sandbox-tested code since 2026-10-17, deploy = copy from~/claude-hub-sandbox, rollback dir~/claude-hub.pre-golive-20261017-2157; also takes Apple Health uploads at/apple→POST /api/apple-export). garmin-tunnel.service(user unit) is an OpenAI/ChatGPT tunnel via~/.local/bin/tunnel-client; it is not reusable for Claude cloud routines.- Adaptive-plan
workout_uuids change overnight for the same workout — match by name + sport + duration, never by uuid alone.
Updating¶
cd ~/garmin-mcp && git pull --ff-only origin main && docker compose -f compose.server.yaml build && docker compose -f compose.server.yaml up -d, then curl -fsS http://127.0.0.1:8000/healthz and one real tool call. Last done 2026-10-17 (87 commits, to 202913f; garminconnect 0.3.2). Rollback image: garmin-mcp-garmin-mcp:pre-update. The MCP get_race_predictions takes no dates; the hub reads Garmin's daily history through docker exec -i garmin-mcp-server python - (see claude-hub import_prediction_history).
Connected to¶
- owned-by: host/claude-tooling
- joins: processes/training-sync
If you change this¶
- Hits: training-sync (hard dependency; aborts
garmin-down) · claude-hub summaries · the Claude CodegarminMCP config. - Does not hit: anything in the torrentvpn stack; the array.
Surfaces¶
| Surface | Role |
|---|---|
docker ps → garmin-mcp-server (healthy) |
liveness |
hub.lan/api/summary |
claude-hub's view of it |
See¶
- Source:
~/garmin-mcp/docker-compose.yml·~/garmin-mcp/README.md
Live race/training release (2026-10-22)¶
claude-hub.service on :8090 now runs committed race/training code from ~/claude-hub-sandbox, branch sandbox/race-lab-20261022, runtime commit 3d1606f. Owner authorized promotion via “Continue with recommendations.” Source doc checkpoint 7f24444. Includes prediction comparisons, actual race setup and local run GPX, explicit result confirmation/scoring, four race strategies and Training direction. Existing 15-minute sync handles evidence/forecasts; Coach cache is four hours/new local day. Page reads use local cache, refreshing every five minutes/foreground. No new timer or Garmin mutations.
claude-hub-sandbox.service stays on :8091 with automatic sync/refresh disabled. Live SQLite was retained; only new feature data was merged. Original activity/health/Apple/history tables passed before/after fingerprints and integrity checks. Release pointer: ~/claude-hub/.race-lab-release.json. Scoped deploy/code-only rollback: ~/claude-hub-sandbox/deploy_race_lab.py; latest backup ~/claude-hub-releases/20261023T034805Z-3d1606f4, prior full-feature backup …/20261023T034219Z-417d0209. Never restore an old DB over newer health data for a code rollback. Optional Open-Meteo calls send location/date only. Current implementation, tests, remaining data inputs and rollback chain: projects/claude-hub and repo RACE-LAB.md.