Skip to main content
Glama

health_mcp

Personal health data for Claude: Garmin Connect and Technogym mywellness, served as one MCP endpoint that runs on a Windows machine, starts with it, and is reachable from claude.ai anywhere through a Cloudflare Tunnel.

claude.ai ─HTTPS─▶ Cloudflare Tunnel ─▶ cloudflared (service)
                                           │  http://127.0.0.1:18080/s/<secret>/mcp
                                           ▼
                                     health-caddy  (Caddy, loopback :18080)
                                           │  strips /s/<secret>/, 404s everything else
                                           ▼
                                     health-mcp    (aggregator, loopback :18001)
                                       ├─ garmin_*     ─▶ garmin-mcp     (loopback :18000)
                                       └─ technogym_*  ─▶ technogym-mcp  (loopback :<port>/<secret>/mcp)

Everything is a Windows service running as LOCAL SYSTEM. No inbound ports are opened; cloudflared dials out. The only thing between the internet and your data is the secret path, so the URL is treated like a password.

Repository layout

Path

What

src/health_mcp/

The aggregator. FastMCP 4 ProxyProviders expose upstream tools as garmin_* and technogym_*. GET /healthz checks both upstreams.

technogym/

technogym-mcp: MCP server for Technogym mywellness (own pyproject.toml, .env, tests). Merged from the former technogym_mcp repo with history.

deploy/install.ps1

One idempotent installer for all four services.

deploy/install-cloudflared.ps1

Installs the Cloudflare Tunnel connector as a service (token based).

deploy/Caddyfile.template

The secret-path gate.

deploy/enabled_tools.txt

Garmin tool allowlist (read-only tools only).

deploy/list_garmin_tools.py

Lists every tool the installed garmin-mcp offers.

scripts/list_tools.py

Lists the tools health-mcp exposes.

Garmin's MCP server itself is upstream Taxuspt/garmin_mcp, installed as a uv tool; nothing of it is vendored here.

Related MCP server: garmin-mcp

Machine layout

Path

Purpose

C:\health-mcp\secret.txt

URL secret. Treat like a password.

C:\health-mcp\Caddyfile

Rendered gate config (contains the secret).

C:\health-mcp\enabled_tools.txt

Active Garmin allowlist.

C:\health-mcp\logs\*.log

One rotated log per service.

%USERPROFILE%\.garminconnect

Garmin OAuth tokens.

%USERPROFILE%\.local\bin\garmin-mcp.exe

Garmin server binary.

technogym\.env

Technogym credentials, port and secret path (never committed).

.env

Upstream URLs for the aggregator (never committed).

Services: garmin-mcp, technogym-mcp, health-mcp (depends on both), health-caddy (depends on health-mcp), cloudflared.

Setup from scratch

1. Tools

winget install --id Git.Git
winget install --id astral-sh.uv
winget install --id CaddyServer.Caddy
winget install --id Cloudflare.cloudflared
winget install --id NSSM.NSSM

Reopen PowerShell, then:

uv tool install --python 3.12 git+https://github.com/Taxuspt/garmin_mcp
garmin-mcp-auth            # interactive Garmin login (email, password, MFA). Once.
garmin-mcp-auth --verify

2. Configure

git clone https://github.com/ErikAnkerKilbergSkallevold/health_mcp
cd health_mcp
copy technogym\.env.example technogym\.env   # mywellness email+password, a port (e.g. 38472),
                                             # and TECHNOGYM_MCP_PATH=/<32 hex>/mcp
copy .env.example .env                       # TECHNOGYM_MCP_URL = http://127.0.0.1:<port><that path>

3. Install the services

Elevated PowerShell:

.\deploy\install.ps1

Generates the secret on first run, renders the Caddyfile, syncs both Python environments, (re)creates the four services and prints probe results plus the connector path. Re-run it after changing deploy\enabled_tools.txt or either .env.

Expected:

garmin    http://127.0.0.1:18000/healthz            -> 200 ok
technogym http://127.0.0.1:38472/healthz            -> 200 ok
health    http://127.0.0.1:18001/healthz            -> 200 {"status":"ok","upstreams":{"garmin":"ok","technogym":"ok"}}
gated     http://127.0.0.1:18080/s/<secret>/healthz -> 200
nosecret  http://127.0.0.1:18080/healthz            -> 404

4. Cloudflare Tunnel

Needs a Cloudflare account (Free) and a domain whose nameservers point at Cloudflare. For a .no domain from Domeneshop: buy it with the free DNS option, add the domain in Cloudflare, then set the two Cloudflare nameservers under "Egne navnetjenere". Norid publishes the change within about an hour.

  1. Zero Trust → Networks → Tunnels → Create a tunnel → Cloudflared. Name it after the machine.

  2. Pick Windows and copy the token from the shown service install command.

  3. Published application route: subdomain health, your domain, HTTP, localhost:18080, Path empty.

  4. On the machine, elevated: .\deploy\install-cloudflared.ps1 -Token <token>

One tunnel per machine. More apps later are just more routes on the same tunnel.

In the zone's AI Crawl Control, set Agent to Allow, or claude.ai's requests may be blocked.

5. claude.ai

Settings → Connectors → Yours → Add custom connector. URL:

https://health.<domain>/s/<contents of C:\health-mcp\secret.txt>/mcp

No OAuth fields. Custom connectors are per account: on Team/Enterprise plans only Owners can add them, a Free account gets one, Pro/Max unlimited.

Tools

deploy\enabled_tools.txt limits Garmin to read-only tools (every get_*/search_* plus count_activities, 106 of 150). Write tools such as uploading workouts, editing activities or logging food are excluded because the URL alone guards the account. Trim it further to save context per chat and re-run the installer. Technogym is read-only by design.

uv run python scripts\list_tools.py                                  # what the connector exposes
& "$env:APPDATA\uv\tools\garmin-mcp\Scripts\python.exe" deploy\list_garmin_tools.py "$env:USERPROFILE\.local\bin\garmin-mcp.exe"

Operations

Task

Command

Status

Get-Service garmin-mcp, technogym-mcp, health-mcp, health-caddy, cloudflared

Logs

Get-Content C:\health-mcp\logs\health-mcp.log -Tail 50 -Wait

Restart everything

Restart-Service garmin-mcp -Force (dependents restart with it)

Upgrade garmin-mcp

uv tool upgrade garmin-mcp; Restart-Service garmin-mcp -Force

Update this repo

git pull; .\deploy\install.ps1

Re-auth Garmin

garmin-mcp-auth --force-reauth; Restart-Service garmin-mcp -Force

Rotate the secret

delete C:\health-mcp\secret.txt, re-run installer, update the connector URL

Uninstall

foreach ($s in 'health-caddy','health-mcp','technogym-mcp','garmin-mcp') { nssm remove $s confirm }; cloudflared service uninstall

Troubleshooting

Symptom

Cause / fix

421 Misdirected Request from /mcp

An upstream rejects non-local Host headers. Caddy sets header_up Host {upstream_hostport}; check the rendered Caddyfile.

/healthz says "technogym": "down"

Get-Service technogym-mcp, then C:\health-mcp\logs\technogym-mcp.log. Usually a wrong port or path in .env.

Cloudflare shows "Invalid nameservers" on a new domain

Registry publishing lag. Wait, then "Check nameservers".

Connector gets 403 from Cloudflare

AI Crawl Control → Agent → Allow; Security → Settings → "Block AI bots" off.

Service stuck in StopPending

The installer kills the nssm host after 15 s; manually: sc queryex <name> then taskkill /F /T /PID <pid>.

Tokens invalid after a while

Garmin tokens expire eventually: garmin-mcp-auth --force-reauth.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

No tool schema history has been recorded yet.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers