trainheroic-mcp
# trainheroic-mcp
An unofficial [Model Context Protocol](https://modelcontextprotocol.io) server for
[TrainHeroic](https://www.trainheroic.com). It lets Claude (or any MCP client) read
your training history and **plan complete workouts on your personal calendar**:
exercises from the TrainHeroic library (each with its video and technique notes),
sets, reps, per-set weights, supersets, session notes and block instructions.
> **Unofficial.** TrainHeroic has no public API. This server talks to the same
> endpoints the TrainHeroic apps use, so it can break whenever they change, and
> automated access may not be allowed by TrainHeroic's terms. Use it for your own
> account, at your own risk.
## What you can ask
- *"Build next week's two sessions: trap bar deadlift 3×8 at 30/35/40 kg, split squats, incline DB press, lat pulldown, calves."*
- *"Show Tuesday's workout with links to the exercise videos."*
- *"Bump all deadlift sets in Thursday's session by 2.5 kg."*
- *"Find a landmine press variation that has a video."*
- *"Copy this week's two sessions to the next three weeks, +2.5 kg on the main lifts."*
## Tools
| Area | Tool | What it does |
|---|---|---|
| Account | `get_profile` | Athlete id, name, coach teams |
| History | `get_workout_history` | Sessions in a date range (past or planned) |
| | `get_workout` | One session: blocks, exercises, prescription, results, video links |
| | `get_set_exercise` | Per-set values for one exercise |
| Library | `search_exercises`, `get_exercise` | Find exercises (all words must match), with video links |
| | `search_circuits`, `get_recent_exercises`, `get_recent_circuits` | |
| Performance | `get_exercise_stats`, `get_personal_records`, `get_working_max` | |
| Feedback | `get_workout_surveys`, `submit_survey`, `get_workout_comments`, `get_workout_leaderboard`, `save_workout_feedback` | |
| Planning | `create_workout` | Whole session in one call, supersets included; rolls back if any step fails |
| | `create_session`, `add_exercises`, `add_to_block`, `set_prescription` | Build or edit step by step; `add_to_block` turns a block into a superset |
| | `update_session`, `update_block` | Titles, notes, block instructions |
| | `remove_exercise`, `remove_block`, `delete_session` | Remove an exercise (an emptied block goes too), a block or a session |
| | `copy_workout`, `move_workout` | Repeat a session's plan on another date; reschedule a session |
Weights are always passed **in kilograms**; the server converts to the pounds the
API stores and the app shows them in your preferred units. Targets can be one value
for every set or a list with a value per set (`weight_kg: [30, 35, 40, 40]`). Only
targets the exercise actually tracks are accepted, so a typo like weight on a
bodyweight drill fails loudly instead of being dropped.
### Supersets
TrainHeroic has no separate superset object: a block that holds several exercises
is a superset. In `create_workout`, give exercises the same `superset` label to put
them in one block (the label becomes the block title unless `block_title` is set):
```json
[
{"exercise_id": 68077, "reps": 8, "weight_kg": [30, 35, 40], "superset": "A"},
{"exercise_id": 1971, "reps": 10, "weight_kg": 30, "superset": "A",
"block_instruction": "Alternate, rest 90 s after each pair"}
]
```
## Setup
Requires Python 3.12+ and [uv](https://docs.astral.sh/uv/).
```bash
git clone https://github.com/IBazylchuk/trainheroic-mcp
cd trainheroic-mcp
uv sync
```
### Credentials
Either of:
| Variable | Notes |
|---|---|
| `TRAINHEROIC_EMAIL` + `TRAINHEROIC_PASSWORD` | Logs in and caches the session token; re-logs in when it expires |
| `TRAINHEROIC_SESSION_TOKEN` | The `session-token` header from a logged-in TrainHeroic web session |
Optional: `TRAINHEROIC_CONFIG_DIR` (default `~/.config/trainheroic-mcp`) holds the cached
token, the exercise library cache and the ids it learned about your calendar.
`TRAINHEROIC_LOG_LEVEL=DEBUG` logs every API call to stderr.
### Claude Code
```bash
claude mcp add trainheroic \
-e TRAINHEROIC_EMAIL=you@example.com -e TRAINHEROIC_PASSWORD=... \
-- uv run --directory /path/to/trainheroic-mcp trainheroic-mcp
```
To keep the password out of config files, start the server through a secrets
manager, e.g. a wrapper script with the 1Password CLI:
```bash
#!/bin/sh
# op.env contains: TRAINHEROIC_EMAIL=op://Private/TrainHeroic/username
# TRAINHEROIC_PASSWORD=op://Private/TrainHeroic/password
exec op run --no-masking --env-file "$HOME/.config/trainheroic-mcp/op.env" -- \
uv run --directory /path/to/trainheroic-mcp trainheroic-mcp
```
and register the script instead: `claude mcp add trainheroic -- /path/to/script.sh`.
### Claude Desktop / other clients
```json
{
"mcpServers": {
"trainheroic": {
"command": "uv",
"args": ["run", "--directory", "/path/to/trainheroic-mcp", "trainheroic-mcp"],
"env": { "TRAINHEROIC_EMAIL": "you@example.com", "TRAINHEROIC_PASSWORD": "..." }
}
}
}
```
## Limitations
- Only **personal calendar** sessions can be created or edited. Coach programs are
read-only, and once a coach license expires TrainHeroic hides those sessions from
the API entirely (use the app's data export for that history).
- TrainHeroic has no reschedule call for personal calendars, so `move_workout`
re-creates the session on the new date and deletes the old one (ids change).
Sessions with logged results are never moved, only copied.
- For personal sessions the TrainHeroic app writes the reps and weights you type while
logging back into the plan; the original targets are not kept anywhere. Copying a
logged personal session therefore repeats what you entered, and `copy_workout` says
so in its result.
- Logging set results and marking a session complete happen in the TrainHeroic app;
`save_workout_feedback` only stores notes, RPE and rating.
- Reps must be whole numbers. Put "per side", tempo or rest cues in the exercise or
block instruction.
## Development
```bash
uv sync --all-groups
uv run pytest # no network, all HTTP is mocked
uv run ruff check . && uv run ruff format --check .
```
Code map:
- `api.py` — HTTP client: auth and token cache, errors, team resolution, library cache.
- `workouts.py` — pure functions that parse workout payloads and build prescriptions.
- `units.py` — parameter types and kg/lb conversion.
- `server.py` — MCP tools.
## License
[MIT](LICENSE)
TDQS
Scored across 29 tools
Most tools target distinct resource/action pairs, and descriptions clarify granular session editing such as create_session vs create_workout, add_exercises vs add_to_block, and copy_workout vs move_workout. Some exercise-stat tools (get_exercise_stats, get_personal_records, get_working_max) and session variants still require careful selection, so the set is not perfectly unambiguous.
Almost all tools use predictable snake_case verb_noun names, e.g. create_session, update_block, get_workout_comments. add_to_block is a minor prepositional variant but remains readable and consistent with the overall convention.
At 29 tools, the set is heavy and exceeds the typical well-scoped range; several granular session-editing and exercise-stat retrieval tools feel like they could be consolidated. The domain is broad enough to justify many operations, but the count is still above what the surface needs.
The core lifecycle is covered: sessions, blocks, exercises, prescriptions, copying/moving, library search, history, stats, surveys, comments, and feedback. Minor gaps remain, such as no posting comments and no per-set result logging, though the latter is explicitly app-only.