{
  "markdown": "# MissingMCP\n\n*The MCP servers exist. Connecting them shouldn't be complicated.*\n\n[![smithery badge](https://smithery.ai/badge/vaclav/garmin)](https://smithery.ai/servers/vaclav/garmin)\n\nA multi-user, OAuth 2.1–protected gateway that hosts the connectors Claude is\nmissing, so a small trusted circle can connect their own accounts from any\nClaude client (iOS, Android, Web, Desktop). The flagship connector is\n[Garmin Connect](https://connect.garmin.com): the gateway wraps the\n**unmodified** [`garmin_mcp`](https://github.com/Taxuspt/garmin_mcp) worker and\nadds OAuth, per-user token isolation, and a reverse proxy. [WHOOP](https://www.whoop.com)\nis served the same way but in-process, on WHOOP's own official OAuth v2 API.\nThe core is adapter-based and supports three forward strategies — **worker**\n(garmin), **local** (whoop, in-process, no subprocess), and **remote** (MCP +\nheader injection, for services with a hosted MCP that lacks its own OAuth) —\nsee [Connectors](#connectors). The reference deployment runs at\n[missingmcp.com](https://missingmcp.com).\n\n```\nClaude → POST /garmin/mcp (Bearer) → Gateway → 127.0.0.1:<port>/mcp (per-user garmin_mcp) → connect.garmin.com\n```\n\n## Why\n\n`garmin_mcp` is a great MCP server, but it's single-user and stdio-only: each\nperson has to run it locally with their own Garmin tokens. This gateway makes it a\n**remote MCP server** any Claude client can connect to over HTTP, with a proper\nOAuth sign-in flow — so non-technical users just click \"connect\" and log in with\ntheir Garmin credentials, and never touch a terminal or a token file.\n\n## Features\n\n- **OAuth 2.1** — Authorization Code + PKCE (S256) with Dynamic Client\n  Registration. Connect from any Claude client; no manual token wrangling.\n- **Garmin password is never stored** — used once to sign in (MFA supported);\n  only the resulting session tokens are persisted.\n- **Encrypted at rest** — tokens sealed with AES-256-GCM; the DB is useless without\n  `GATEWAY_SECRET`. Bearer tokens are stored only as SHA-256 hashes.\n- **Per-user isolation** — each account gets its own `garmin_mcp` worker bound to\n  `127.0.0.1`, started on demand and reaped when idle.\n- **Hardened** — one-time 10-min auth codes, CSRF on forms, per-IP/-token rate\n  limits, the `garmin_mcp` worker pinned to a reviewed commit.\n- **Off-box backups** — periodic encrypted SQLite snapshots to an S3-compatible\n  bucket (see [Backups](#backups)).\n- **Instructional landing page** served on `/` and as a friendly fallback for\n  unknown paths.\n\n## Deploy\n\n**Railway (how missingmcp.com runs):** one service built from the `Dockerfile`,\na volume mounted at `/data`, a single replica (the gateway keeps process-local\nstate by design), and TLS terminated at the Railway edge. Set the env vars from\n[Configuration](#configuration) on the service; pushes to `main` deploy\nautomatically once the service is connected to the GitHub repo. An optional\nRailway bucket enables [Backups](#backups) (`railway bucket create backups`,\nthen wire its credentials as the `BACKUP_S3_*` variables).\n\n**Self-hosted (plain Docker):**\n\n```bash\ncp .env.example .env          # set GATEWAY_SECRET, PUBLIC_URL, pin GARMIN_MCP_REF\ndocker build -t missingmcp .\ndocker run -d --name missingmcp --restart unless-stopped --env-file .env \\\n  -p 127.0.0.1:8080:8080 -v missingmcp-data:/data missingmcp\n```\n\nPut any TLS-terminating proxy in front (Caddy, nginx, Traefik). One non-obvious\nrequirement: `/​<adapter>/mcp` streams SSE, so disable response buffering and\nraise the read timeout (nginx: `proxy_buffering off; proxy_read_timeout 3600s;`).\nThen add `https://<your-domain>/garmin/mcp` as a remote MCP server in Claude.\n\n## Local development\n\n```bash\nuv pip install -e \".[dev]\"\nuv run --extra dev pytest -q                 # run the test suite\n\n# Run the gateway locally (no Garmin account needed to exercise the OAuth surface).\n# garmin-mcp isn't on PATH locally, so point GARMIN_MCP_CMD at uvx.\nGATEWAY_SECRET=\"$(openssl rand -base64 48)\" \\\nPUBLIC_URL=http://localhost:8088 PORT=8088 DATA_DIR=./.localdata \\\nGARMIN_MCP_CMD=\"uvx --python 3.12 --from git+https://github.com/Taxuspt/garmin_mcp garmin-mcp\" \\\n  uv run missingmcp\n```\n\nA `.env` file in the working directory is loaded automatically (real environment\nvariables take precedence), so you can drop the same values there instead.\n\n## Connectors\n\nEach connector is mounted under its own path prefix with its own OAuth flow and\nits own Bearer tokens (there is no bare `/mcp`). The landing page on `/` lists\nthe available connectors.\n\n### Garmin — `/garmin/mcp`\n\nSign in with your Garmin Connect email + password (MFA supported). The password\nis used once for the Garmin login and discarded; only the resulting Garmin\nsession tokens are stored (AES-256-GCM encrypted). Each account gets its own\n`garmin_mcp` worker process, bound to `127.0.0.1`, started on demand and reaped\nwhen idle.\n\n### WHOOP — `/whoop/mcp`\n\nSign in on WHOOP's own OAuth page — the gateway never sees your WHOOP password,\nonly the resulting (encrypted, read-only) tokens. Covers recovery, sleep,\nstrain & daily cycles, workouts, and body measurements. Served in-process on\nWHOOP's official v2 API (no worker subprocess, no shared upstream). Requires\nthe operator to register a WHOOP developer app — see [WHOOP connector\nsetup](#whoop-connector-setup) in Configuration.\n\n### Rohlík — no longer missing\n\nThe gateway briefly served a Rohlík connector (remote strategy: credential\nheaders injected into the hosted Rohlík MCP). It was retired in 2026-07 when\nRohlík shipped **its own OAuth-protected MCP** — add\n`https://mcp.rohlik.cz/mcp` directly as a custom connector in Claude and sign\nin in the browser ([Rohlík's guide](https://www.rohlik.cz/stranka/mcp-server)).\nThe remote-forward strategy remains a first-class, tested part of the core\n(`tests/test_remote_forward.py`) for the next service that needs it.\n\n## Connecting from Claude\n\n1. In any Claude client: **Settings → Connectors → Add custom connector**, or in\n   the CLI: `claude mcp add --transport http garmin https://<your-domain>/garmin/mcp`\n   (whoop: `claude mcp add --transport http whoop https://<your-domain>/whoop/mcp`).\n2. Claude opens the gateway's sign-in page — enter the service's email +\n   password (Garmin also prompts for an MFA code when needed), or for WHOOP,\n   sign in on WHOOP's own OAuth page.\n3. Done — the service's tools are now available in Claude.\n\n## Configuration\n\nSet via environment (or `.env`). See [`.env.example`](.env.example).\n\n| Variable | Required | Default | Description |\n|---|---|---|---|\n| `GATEWAY_SECRET` | **yes** | — | ≥32-char key for token encryption. Refuses to start with the placeholder. Generate with `openssl rand -base64 48`. |\n| `PUBLIC_URL` | yes | `http://localhost:8080` | Public URL used in OAuth metadata + redirects. |\n| `PORT` | no | `8080` | Listen port. |\n| `DATA_DIR` | no | `/data` | Where the SQLite DB and per-user token dirs live. |\n| `DB_PATH` | no | `$DATA_DIR/gateway.db` | Override the DB path. |\n| `GARMIN_MCP_CMD` | no | `garmin-mcp` | Command to spawn the worker. Use a `uvx …` invocation when `garmin-mcp` isn't on PATH. |\n| `GARMIN_MCP_REF` | no | pinned SHA in `Dockerfile` | Docker build arg: commit of `garmin_mcp` to install. Bumping it is a deliberate, reviewed action — afterwards run `python scripts/gen_garmin_tools.py` to refresh the tool listing on the `/garmin` page. |\n| `WORKER_PORT_START` / `WORKER_PORT_END` | no | `9000` / `9099` | Port range for per-user workers. |\n| `WORKER_IDLE_TTL` | no | `900` | Seconds before an idle worker is reaped. |\n| `WORKER_STARTUP_TIMEOUT` | no | `20` | Seconds to wait for a worker to become healthy. |\n| `MAX_WORKERS` | no | `10` | Max concurrent per-user workers. |\n| `ACCESS_TOKEN_TTL_DAYS` | no | `90` | Bearer token lifetime; user re-authenticates after it. `0` disables expiry. |\n| `OPERATOR_NAME` / `OPERATOR_EMAIL` | no | — | Shown on the landing page. |\n| `OPERATOR_URL` | no | — | Homepage the operator name links to (footer, trust notes). Unset → plain text. |\n| `WHOOP_CLIENT_ID` / `WHOOP_CLIENT_SECRET` | no | — | Credentials of your WHOOP developer app (see WHOOP connector setup); both unset ⇒ the whoop connector is disabled. |\n| `WHOOP_API_BASE` | no | `https://api.prod.whoop.com` | WHOOP API origin; override only for testing. |\n| `BACKUP_S3_ENDPOINT` / `BACKUP_S3_BUCKET` / `BACKUP_S3_ACCESS_KEY` / `BACKUP_S3_SECRET_KEY` | no | — | S3-compatible bucket for off-box DB backups (see Backups). Backups are disabled unless all four are set. |\n| `BACKUP_S3_REGION` | no | `auto` | SigV4 region of the bucket. |\n| `BACKUP_S3_URL_STYLE` | no | `virtual-host` | `virtual-host` (Railway buckets) or `path`. |\n| `BACKUP_INTERVAL_HOURS` | no | `6` | Hours between backup uploads. First backup runs right after startup. |\n| `POSTHOG_API_KEY` | no | — | Public `phc_` project key. Unset ⇒ **all** PostHog telemetry (events, log tee, web analytics) is off. |\n| `POSTHOG_HOST` | no | `https://eu.i.posthog.com` | Server-side ingestion host (SDK events + OTLP log tee). |\n| `POSTHOG_WEB_HOST` | no | `$POSTHOG_HOST` | posthog-js `api_host` — set it to the managed reverse proxy (e.g. `https://j.missingmcp.com`) for ad-blocker resilience. |\n| `POSTHOG_UI_HOST` | no | `https://eu.posthog.com` | PostHog app host; posthog-js needs it when `POSTHOG_WEB_HOST` is a proxy. |\n| `GATEWAY_LOG_FILE` | no | — | If set, tees structured + stdlib logs to this file. |\n| `GATEWAY_LOG_LEVEL` | no | `info` | `debug`\\|`info`\\|`warning`\\|`error`\\|`critical`. `debug` is verbose (logs garminconnect/urllib3 internals) — avoid in production. |\n\n### WHOOP connector setup\n\n1. Create an app at <https://developer-dashboard.whoop.com> (instant self-service).\n2. Redirect URI: `https://<your-domain>/whoop/oauth/callback` — exact match required.\n3. Scopes: `read:recovery read:cycles read:workout read:sleep read:profile read:body_measurement offline`\n   (`offline` is required — without it WHOOP issues no refresh token and sessions die after an hour).\n4. Put the app's Client ID/Secret into `WHOOP_CLIENT_ID` / `WHOOP_CLIENT_SECRET`.\n\nNote: an unapproved WHOOP app is limited to **10 WHOOP members**. To lift the\nlimit, submit the app for approval: <https://developer.whoop.com/docs/developing/app-approval/>\n(requirements: [API Terms of Use](https://developer.whoop.com/api-terms-of-use/)\ncompliance, tested with ≥1 member, accurate app name / contact email / privacy-policy\nURL in the dashboard, [brand-guidelines](https://developer.whoop.com/docs/developing/design-guidelines)\ncompliance, and their Typeform submission).\n\nOperating obligations under the WHOOP API Terms of Use (the gateway's design\nalready covers the technical ones — no WHOOP data stored, encrypted tokens,\nauto-purge of revoked accounts):\n\n- Report any security incident involving WHOOP member data to\n  <apisupport@whoop.com> without undue delay, and to affected users.\n- No press release or public announcement that references WHOOP without\n  WHOOP's prior written approval.\n- Keep the app's Client ID/Secret out of the repo (env vars only) and never\n  reuse them for another application.\n- When a member revokes the app at WHOOP, the gateway detects it on the next\n  refresh (`invalid_grant`), deletes the stored tokens, and revokes the\n  member's gateway access tokens (log event `whoop-account-revoked`).\n\n## Backups\n\nWhen the `BACKUP_S3_*` variables are set, the gateway uploads a consistent\nSQLite snapshot (via the SQLite backup API, WAL-safe) to the bucket right\nafter startup and then every `BACKUP_INTERVAL_HOURS`. Keys rotate by weekday\n(`db/gateway-mon.db` … `db/gateway-sun.db`), giving seven days of retention\nwith no cleanup logic. Watch the `backup-ok` / `backup-failed` log events.\n\nOnly the DB is backed up — per-user token dirs under `DATA_DIR/users/` are\nre-materialized from the DB on demand.\n\n> **The backup is useless without `GATEWAY_SECRET`** (account blobs stay\n> AES-256-GCM encrypted inside it) — and that is the point. Keep a copy of\n> `GATEWAY_SECRET` somewhere that is *not* the bucket and *not* Railway\n> (e.g. your password manager). Losing the secret = losing every account.\n\n**Restore:** download the newest object, put it at `$DATA_DIR/gateway.db`\n(delete any stale `gateway.db-wal`/`-shm` next to it), set the same\n`GATEWAY_SECRET`, start the gateway. Bearer tokens and logins survive;\nworkers respawn lazily.\n\n## Monitoring\n\nThree helper scripts work directly on the gateway's DB (safe to run while the gateway is live):\n\n```bash\npython scripts/status.py          # summary counts only (safe to paste/share)\npython scripts/status.py --detail # + per-account devices (token prefixes),\n                                  #   usage summary, OAuth clients, running workers\npython scripts/revoke.py --list                       # accounts + token counts\npython scripts/revoke.py --account [<adapter>:]<key>  # kill-switch: revoke ALL the\n                                                      #   account's tokens (bare key = garmin)\npython scripts/revoke.py --account <key> --purge      # + delete stored account & usage\npython scripts/revoke.py --device <hash-prefix>       # revoke ONE device (prefix from status.py)\npython scripts/usage.py                               # per-account tool usage + leaderboard\npython scripts/usage.py --account [<adapter>:]<key>   # one account's per-tool breakdown\npython scripts/subscribers.py                         # newsletter signups + suggestions\npython scripts/subscribers.py --emails                # subscriber emails, one per line\npython scripts/daily_report.py                        # yesterday's new/active/total users (print)\npython scripts/daily_report.py --post                 # + POST it to Slack ($SLACK_WEBHOOK_URL)\npython scripts/add_beer.py --email <supporter>        # record a \"buy me a beer\" donation (1 beer, 5 EUR)\npython scripts/add_beer.py --email <supporter> --beers 3 --at 2026-07-20  # 3 beers, backdated\n```\n\nThe gateway also posts this daily user-stats report to Slack on its own each\nmorning (`DAILY_REPORT_HOUR`, default 08:00 `DAILY_REPORT_TZ`) when\n`SLACK_WEBHOOK_URL` is set; `scripts/daily_report.py` runs the same report on\ndemand for testing.\n\n**Daily triage** — a GitHub Actions workflow\n(`.github/workflows/daily-triage.yml`) runs `scripts/daily_triage.py` every\nmorning (~07:45 Prague): it pulls the last 24 h of error/warn rows from the\ngateway's Railway logs, classifies the known error signatures\ndeterministically, and — only when something actionable remains — has Claude\nwrite the operator's triage (what happened → what it means → proposed action,\nper class) and posts it to Slack. Healthy days post a one-line \"all quiet\"\n(the daily heartbeat); if the analysis fails, the deterministic aggregate\ntable posts instead — never silence. The analysis runs on the operator's\nClaude subscription via headless Claude Code (repo secret\n`CLAUDE_CODE_OAUTH_TOKEN`, minted with `claude setup-token`); an\n`ANTHROPIC_API_KEY` secret works as the fallback backend. Also needs\n`RAILWAY_API_TOKEN` + `SLACK_WEBHOOK_URL` (already present for the hourly\npager). Run by hand with `python scripts/daily_triage.py --dry-run` (needs the\n`RAILWAY_*` env; the analysis step degrades gracefully without a token).\n\n**Hourly hard-signal pager** — a GitHub Actions workflow\n(`.github/workflows/hourly-digest.yml`) runs `scripts/hourly_digest.py` every\nhour: it reads the last 60 min of the gateway's Railway logs via the Railway\nAPI, does a liveness probe, and pages (`<!here>`) **only** on a hard signal —\na failed probe (site down), `worker-start-failed` across ≥2 distinct accounts\nin the hour (the broken-image signature), any 5xx, or any `critical`. It is\notherwise fully silent — plain error volume belongs to the daily triage.\nRequires repo secrets `RAILWAY_API_TOKEN` + `SLACK_WEBHOOK_URL`\n(service/environment ids are set as workflow env). `RAILWAY_API_TOKEN` may be a\nRailway **project** token (narrowest scope — reaches only this project; sent via\nthe `Project-Access-Token` header) or an account/workspace token (sent as\n`Authorization: Bearer`); the script tries both. Run it by hand with\n`python scripts/hourly_digest.py --dry-run` (needs `RAILWAY_API_TOKEN`,\n`RAILWAY_SERVICE_ID`, `RAILWAY_ENVIRONMENT_ID` in the environment).\n\n**PostHog telemetry** — with `POSTHOG_API_KEY` set (see the env table), the\ngateway also ships analytics to PostHog (EU cloud; design:\n`docs/superpowers/specs/2026-07-20-posthog-telemetry-design.md`):\nper-request `$mcp_*` events (PostHog's built-in MCP analytics), a lean\nconnect-funnel/conversion event set, an OTLP tee of the structured log stream\n(PostHog Logs, beta — Railway stays the durable archive), and posthog-js on the\nsite (UTM campaign attribution; autocapture disabled on OAuth pages). Egress\nrule: identity + metadata only — never MCP bodies, credentials, or form\ncontents; the account email travels only as `distinct_id`. Everything is\nfire-and-forget: a PostHog outage never blocks a request. The Slack reports\nabove keep running unchanged — PostHog complements them. When off-boarding a\nuser (`revoke.py --purge`), also delete the person in PostHog (People → delete)\nto complete the GDPR path.\n\n**Beer supporters** — `scripts/add_beer.py` records a \"buy me a beer\" donation\n(`buymeacoffee.com/venik`) into a local `beers` audit table and emits a\n`beer_purchased` PostHog event, best-effort attributed to the supporter's\ngateway account (`matched` when the email is a known login). The event is meant to\nfeed the operator's connect→paying funnel and \"beers this month\" metric on the\nGrowth dashboard — those PostHog insights are **set up manually** and don't exist\nuntil built. Ingestion is manual for now (BMC automation deferred; design:\n`docs/superpowers/specs/2026-07-24-beer-supporters.md`). Same egress rule as\nabove — the email travels only as `distinct_id`, never the supporter name or\nnote.\n\n**With Docker** the scripts are baked into the image at `/app/scripts`; run them\ninside the container. `status.py` finds the DB under `/data` automatically:\n\n```bash\ndocker exec missingmcp python /app/scripts/status.py\ndocker logs -f missingmcp                   # live structured-JSON events\n```\n\n**On Railway** run them over `railway ssh`; logs live in the Railway dashboard\n(`railway logs --service gateway` for a live tail):\n\n```bash\nrailway ssh --service gateway \"python3 /app/scripts/status.py\"\nrailway ssh --service gateway \"python3 /app/scripts/revoke.py --account <email>\"\n```\n\nAll logging is structured JSON on stdout (one event per line) with a proper\n`level` attribute — including uvicorn/stdlib records (event `stdlib-log`) and\neach worker's own output (event `worker-log`, with an `account` attribute;\nlines matching ERROR/Traceback are elevated to error severity). On Railway\nthat makes everything searchable in the log explorer — filter e.g.\n`@event:worker-log @account:<email>` to trace one user's worker, or\n`@level:error` for problems. Request latency is recorded per call in the\n`mcp-response` event (`ttfb_ms`, `total_ms`, `bytes`, `tool`, `account`);\nlogin/verify and worker-spawn events carry `ms` durations. The gateway also\nlogs a `stats` event (accounts / tokens / people-with-token / clients /\nactive-workers) on startup and whenever those counts change, and `status.py`\nlists the running workers.\n\n### Automatic data hygiene\n\nThe gateway keeps its own DB tidy — no manual grooming needed. Alongside expiring\nold auth codes and access tokens, the background loop (roughly once a minute):\n\n- **sweeps abandoned OAuth clients** — Claude registers a fresh client (DCR) on\n  every connection attempt, and one that never completes the flow leaves a\n  token-less client behind. Any client with **zero access tokens** older than one\n  hour (comfortably above the 10-min code lifetime, so an in-progress sign-in is\n  never touched) is removed. This is why `status.py`'s OAuth-client count tracks\n  the number of connected devices rather than growing with every failed attempt.\n  Revoking a device also leaves its client token-less, so it's swept the same way.\n- **purges retired-adapter data** — if a connector is ever dropped, all its rows\n  (accounts, tokens, clients, codes, usage) are deleted across the board. The\n  retired set is an **explicit** list in the code, never inferred from\n  configuration, so a missing env var can't accidentally wipe a live connector.\n\nBoth are logged only when they actually delete something — `cleanup-orphan-clients`\n(`count`) and `cleanup-dead-adapter` (`adapter` + per-table counts) — so a quiet\nlog means there was nothing to clean. The one-hour threshold is a fixed constant,\nnot an env var, by design.\n\n## How it works\n\n1. Claude registers a client (DCR) and starts OAuth 2.1 (Authorization Code + PKCE).\n2. On the authorize page the user signs in with Garmin (email + password, + MFA if\n   prompted). The gateway logs in via `garminconnect`, stores **only the resulting\n   tokens** (encrypted), and discards the password.\n3. Claude exchanges the code for a Bearer token.\n4. On each `/garmin/mcp` call the gateway ensures the user's `garmin_mcp` worker is running\n   (its own tokens, bound to `127.0.0.1`) and reverse-proxies to it.\n\nAdapters using the remote strategy replace steps 2 and 4 with a probe-verify\nagainst the upstream MCP and a direct header-injected forward — no worker.\n\nAdapters using an upstream-OAuth login (whoop) replace step 2 with a redirect\nto the provider's own OAuth page; the provider calls back with tokens, which\nthe gateway verifies and persists the same way (verify-then-persist is\nunchanged). Adapters using the local strategy (whoop) replace step 4: the\nrequest is handled in-process — no worker, no shared upstream — and the\ngateway refreshes the account's rotating WHOOP tokens itself as needed.\n\n## Security\n\n- Garmin password is never persisted; WHOOP passwords are never even seen\n  (sign-in happens on WHOOP's own OAuth page, read-only scopes).\n- Tokens encrypted at rest (AES-256-GCM); the DB is useless without `GATEWAY_SECRET`.\n- A member revoking the app at WHOOP is detected on the next refresh and their\n  stored tokens are purged automatically.\n- Bearer tokens stored only as SHA-256 hashes.\n- OAuth 2.1 PKCE (S256), one-time 10-min codes, CSRF on forms, per-IP/-token rate limits.\n- Workers bind `127.0.0.1` only; `garmin_mcp` is pinned to a reviewed commit.\n\n> Deploy only on infrastructure you control and trust. Back up `DATA_DIR`; keep\n> `GATEWAY_SECRET` separately.\n\n## Before you deploy\n\n- **Set a real random `GATEWAY_SECRET`** (`openssl rand -base64 48`) — the app\n  refuses to start with the placeholder from `.env.example`.\n- **Pin `GARMIN_MCP_REF` to a reviewed commit SHA** — `main` is a floating ref that\n  can change without notice (supply-chain).\n- **Revoking access** — access tokens expire after `ACCESS_TOKEN_TTL_DAYS` (default\n  90; the user just re-authenticates in Claude). To revoke sooner — a leaked token\n  or a removed user — run `python scripts/revoke.py --account [<adapter>:]<email>` (kill-switch\n  for all of that account's tokens). A single device can be revoked with `--device <hash-prefix>` (prefixes are shown by `status.py`).\n- **Run a manual end-to-end smoke test** with a real Garmin account (including the\n  MFA path) before connecting real users — the upstream is mocked in the\n  automated tests.\n\n## Support\n\nIf this gateway is useful to you, you can [buy me a beer 🍺](https://buymeacoffee.com/venik).\n\n## License\n\n[MIT](LICENSE) © 2026 Vaclav Slajs\n\n## Acknowledgements\n\nWraps the excellent [`garmin_mcp`](https://github.com/Taxuspt/garmin_mcp) by Taxuspt,\nunmodified. Garmin and Garmin Connect are trademarks of Garmin Ltd.; this project is\nnot affiliated with or endorsed by Garmin.\n",
  "bytes": 23706,
  "sha": "2b340e951f4b602d74a3f8005155824b8fa04945422f048c149d987533143d0c",
  "repo_slug": "velkyvenik/missingmcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_com_missingmcp_whoop_9924159a/readme"
}