openGym
Self-hosted gym and body-weight tracker with workout planning and muscle recovery tracking
Alternative to: strong, hevy, fitnotes


v1.2.5
2026-08-04Changelog
v1.2.5 — 2026-08-04
Nothing in the app itself changed. This release adds an optional side door: a small server that lets an AI assistant you already run — Claude Desktop, Cursor, Cline — answer questions about your own training, off the same files your instance already writes. If you don’t use one, this release is invisible to you.
Ask an AI about your training, without the data leaving your box (#19)
- 🤖 An MCP server (
mcp/) — opt-in, read-only, and not part of the Docker build. Your LLM client spawns it as a local process, it reads./datadirectly, and it exits when the client disconnects. No new container, no extra auth on the api, no third-party service, nothing over the network. “What did I bench last week?”, “what’s my estimated 1RM on deadlift?”, “which muscles have I been neglecting?” - The answers match the Stats screen because they are the same numbers. The server calls the
very functions in
frontend/src/lib/the app already computes with, rather than reimplementing them. Eight tools: routines, the week plan, workouts, one session in detail, body weight, estimated 1RM and muscle balance. - Read-only on purpose. Logging a workout from an assistant needs a long-lived token the api does not have yet, plus a lock against the web UI’s read-modify-write. Until both exist, the server answers questions and does nothing else. It never reads passkey material, VAPID keys or session state — only the profile it was pointed at.
docker-compose.ymlis untouched and nothing new enters the image, so an instance that ignores this ships exactly what it shipped before. Setup is in mcp/README.md.
Under the hood
- The pure half of
i18n.js— the language state, the constants, the readers — moved intoi18n-core.js, so the helpers underfrontend/src/lib/can be loaded by a plain Node process and not only by Vite.i18n.jskeeps the Vite-only parts (the locale-pack loader, the React hook) and re-exports the rest, so nothing that imports it had to change. - The shared lib modules that only need
tnow take it from the core directly. A Vite-only import inside a shared module is invisible under vitest, which transforms it, and fatal to the MCP server, which does not — so the shared half stays clear of the bundler half by construction rather than by remembering to. npm run check:node-loadableinmcp/walks the server’s import graph under a barenode, which is the one thing the test suite cannot do from inside Vite. CI runs it, alongside the MCP tests — neither had ever run there before.
The MCP server was contributed by @Pengboi — the first feature in openGym written by someone other than me. Thank you.