A workout app that Claude or ChatGPT can read and log to. One FastAPI service powers the REST API, an OAuth-protected MCP server and the Next.js web app, so the chat and the browser always show the same training history.
tempo.clupai.com. The web app is one of two front doors to the same data.
70
API tests at checkpoint
Aug 2026
live since
The problem
For months I planned my workouts and tracked my progress with Claude, and it was genuinely useful. But every new session lost the context, even inside one project, so my history kept disappearing. I didn't want another gym app to type sets into. I wanted Claude to have somewhere to keep them.
The fix
Tempo is that place. I ask Claude to plan a workout, and Tempo stores it, organises it and tracks it: sessions, sets, personal records and an illustrated exercise library. The same data has two front doors. In the browser it is tempo.clupai.com; inside Claude or ChatGPT it is an MCP connector that signs in over OAuth. Both stay in sync because both call the same Python service.
System shape
apps/webNext.js 16 on tempo.clupai.com: library, logging, dashboard
apps/apiFastAPI on api.tempo.clupai.com: REST, /mcp and /oauth
The rule that holds it together: every capability lives once, in services/. Logging a set from the web app and logging it from Claude run the same function, including personal-record detection.
Engineering details
one service layer over separate REST and MCP logic
Every capability lives once in a shared service layer. The REST routers and MCP tools are thin adapters over the same functions, so the web app and Claude never drift apart.
an OAuth-protected MCP server over a connector anyone can call
Claude and ChatGPT connect to the MCP server over OAuth, and people sign in with Google.
one Turborepo over separate repos per app
The Next.js web app and the FastAPI service live in one Turborepo and share one Neon Postgres database.