technogym-mcp
# technogym-mcp
An [MCP](https://modelcontextprotocol.io) server that gives AI assistants read access to your
Technogym **mywellness** training history: workout sessions, per-exercise data with per-set
reps / weight / rest, per-second exercise analytics (machine channels + heart rate) and
whole-session heart-rate traces.
It runs as a persistent local HTTP server that starts with Windows, so every MCP client on the
machine (Claude Desktop, Claude Code, Cursor, ...) can connect to the same endpoint.
> **How it works / caveat.** Technogym's official API is partner-only (B2B). This server
> speaks the same JSON API the official end-user web app (endusernext.mywellness.com) uses,
> logging in with your normal email + password and identifying itself with that app's
> client headers. No HTML scraping is involved, but it will still break if Technogym changes
> the login flow or the client-trust rules. The old `mywellness.com/cloud` portal that
> earlier tools (e.g. [lcanis/gymexport](https://github.com/lcanis/gymexport)) scraped was
> retired in 2026 and now redirects to technogym.com.
## Tools
| Tool | What it returns |
|---|---|
| `get_account_info` | Logs in and returns user id, name, culture, facilities and current Movergy score. Use to verify credentials. |
| `list_workout_sessions(from_date?, to_date?, only_workouts=true)` | Sessions in a range (default: last 30 days), newest first: `session_id`, `date`, time, name, duration, MOVEs, calories, exercise counts. |
| `get_workout_session(session_id, date?)` | Every exercise with machine, status, duration, calories, MOVEs, muscles, performed and prescribed sets (reps, kg, rest), plus session efficacy and muscle scores. |
| `get_recent_workouts(days=14)` | Lists and fully loads all sessions from the last N days. |
| `get_exercise_analytics(analytics_id, sample_every_seconds=1)` | Per-second machine channels, HR samples, HR zones and summary metrics (total weight, est. 1RM, METs...) for one exercise. |
| `get_session_heart_rate(session_id, date?, sample_every_seconds=10)` | Timestamped HR trace, HR zones and avg/min/max HR for a whole session. |
A session is addressed by `session_id` **and** its `date` (the API partitions by day). Pass the
date whenever you have it; if omitted, the last year of history is scanned to find it.
`GET /health` on the same port reports liveness and whether a session is authenticated.
## Setup
Requires [uv](https://docs.astral.sh/uv/) (it manages Python for you).
```powershell
git clone https://github.com/ErikAnkerKilbergSkallevold/technogym_mcp
cd technogym_mcp
copy .env.example .env # then fill in MYWELLNESS_EMAIL / MYWELLNESS_PASSWORD
uv sync
```
### Run once (foreground)
```powershell
uv run technogym-mcp # HTTP on http://127.0.0.1:8765/mcp
uv run technogym-mcp --transport stdio # for clients that spawn the server themselves
```
### Start with Windows (always available)
```powershell
powershell -ExecutionPolicy Bypass -File scripts\install-startup.ps1
```
This registers a Scheduled Task named **Technogym MCP Server** that starts silently at logon
(via `uvw.exe`, no console window), restarts on failure, and never times out. Logs go to
`logs\technogym-mcp.log`. Remove it with `scripts\uninstall-startup.ps1`. After editing
`.env`, restart it: `Restart-ScheduledTask "Technogym MCP Server"`.
### Expose it publicly (Cloudflare Tunnel)
To reach the server from other machines or from a hosted Claude, tunnel it out through Cloudflare
instead of opening a port. One-time prerequisites:
```powershell
winget install Cloudflare.cloudflared
cloudflared tunnel login # browser: pick your zone
```
Set `TECHNOGYM_MCP_PUBLIC_HOST=technogym.example.com` in `.env`, restart the server task, then:
```powershell
powershell -ExecutionPolicy Bypass -File scripts\install-tunnel.ps1 -Hostname technogym.example.com
```
This creates a named tunnel, routes the DNS record, and installs `cloudflared` as a Windows
service (it will ask for elevation once). The MCP endpoint is then `https://technogym.example.com/mcp`.
Alternatively, create the tunnel in the Zero Trust dashboard (Networks → Tunnels → Create,
choose Windows, run the provided `cloudflared service install <token>` command) and add a public
hostname there pointing at `http://localhost:<port>`. That also installs an auto-starting Windows
service; skip the script in that case, since it would create a second tunnel.
**The MCP endpoint has no authentication of its own.** Pick one of two ways to protect it:
* **Cloudflare Access** (best when your clients can complete a browser login, e.g. Claude Code
with `claude mcp add --transport http`): add an Access application for the hostname
(Zero Trust → Access → Applications → Self-hosted) with a policy that allows only your email.
* **Unguessable path** (needed for clients that cannot send auth headers or do a Cloudflare
login, e.g. **claude.ai custom connectors**): set `TECHNOGYM_MCP_PATH=/<32 random hex>/mcp`
in `.env`, restart the server task, and treat the full URL as a secret. Requests to any other
path get 404. Optionally still put Access on `/health` and `/`.
### Adding to claude.ai
1. Make sure the tunnel is up and `https://<host>/health` answers.
2. In claude.ai go to **Settings → Connectors → Add custom connector**.
3. Name it `Technogym`, paste the full endpoint URL (`https://<host><TECHNOGYM_MCP_PATH>`),
leave OAuth fields empty, and click **Add**.
4. In a chat, open the tools menu (the sliders icon) and enable the Technogym connector.
## Connecting a client
**Claude Code**
```powershell
claude mcp add -s user --transport http technogym http://127.0.0.1:8765/mcp
```
**Claude Desktop** (`claude_desktop_config.json`), either pointing at the running server:
```json
{
"mcpServers": {
"technogym": { "type": "http", "url": "http://127.0.0.1:8765/mcp" }
}
}
```
or spawning it over stdio:
```json
{
"mcpServers": {
"technogym": {
"command": "uv",
"args": ["run", "--directory", "C:\\path\\to\\technogym_mcp", "technogym-mcp", "--transport", "stdio"]
}
}
}
```
## Configuration (`.env`)
| Variable | Default | Purpose |
|---|---|---|
| `MYWELLNESS_EMAIL` | | Your mywellness login |
| `MYWELLNESS_PASSWORD` | | Your mywellness password |
| `TECHNOGYM_MCP_HOST` | `127.0.0.1` | Bind address. Keep on loopback; there is no auth on the MCP endpoint. |
| `TECHNOGYM_MCP_PORT` | `8765` | Port |
| `TECHNOGYM_MCP_PUBLIC_HOST` | | Public hostname(s) accepted in the Host header, e.g. `technogym.example.com`, when exposed through a tunnel. The SDK's DNS-rebinding protection rejects unknown hosts with HTTP 421. |
| `TECHNOGYM_MCP_PATH` | `/mcp` | URL path of the MCP endpoint. Use an unguessable path when the endpoint must be reachable without auth headers (see claude.ai below). |
| `TECHNOGYM_MCP_LOG_LEVEL` | `INFO` | Logging level |
## API notes (for maintainers)
* Login: `POST https://core.mywellness.com/v2/enduser/authentication/login` with
`{username, password, keepMeLoggedIn}` and headers `X-MWAPPS-APPID: EC1D38D7-D359-48D0-A60C-D8C0B8FB9DF9`,
`X-MWAPPS-CLIENT: enduserweb`. Returns `token`, `userContext.id`, `facilities[].url`.
* Data: `POST https://services.mywellness.com/{facilityUrl}/Training/User/{userId}/<Action>`
with `Authorization: Bearer <token>` and the same X-MWAPPS headers. Actions used:
`ActivityHistory`, `GetPerformedWorkoutSessionByIdCr`, `GetHrSession`, `MyMovergy`;
plus `Training/CardioLog/{analyticsId}/Details`. Dates are `yyyyMMdd` partition keys.
* Localised strings (exercise names, units) come back in the account's culture.
## Development
```powershell
uv run pytest
uv run ruff check .
```
## License
MIT
TDQS
Scored across 6 tools
Each tool targets a distinct data view: account, session list, session detail, recent workouts, per-exercise analytics, and heart-rate trace. get_recent_workouts overlaps with list_workout_sessions plus get_workout_session, but it is clearly framed as a convenience wrapper, so the ambiguity is minimal.
All tool names follow a consistent verb_noun pattern using get_ or list_ prefixes: get_account_info, list_workout_sessions, get_workout_session, get_recent_workouts, get_exercise_analytics, get_session_heart_rate. There is no mixing of styles or vague verbs.
Six tools is well-scoped for a read-only fitness/wellness data server. Each tool serves a clear purpose and the count is neither bloated nor too thin.
The tool surface covers the full read-only workflow: account verification, session listing, session detail, exercise-level analytics, heart-rate traces, and a convenience bulk loader. The data flow from list_workout_sessions to get_workout_session to get_exercise_analytics is complete with no obvious dead ends.