Zum Inhalt springen
A dashboard of bars, lines and a heatmap next to a small speech bubble, as a glowing low-poly illustration.
All posts
Series: training mcpPart 4 of 4
·AINuxtData VisualizationTraining

What doesn't belong in the chat

Weekly volume, zones, load and personal bests over a year: questions like these have fixed answers. training mcp calculates them on the server and shows them on the web, next to the conversation rather than inside it.

A language model can analyse a year of training. All it needs is 120 calls, and at the end there is still no chart you can open again next week.

The first three parts were about what the model needs in the conversation: the approach, the data and the knowledge. This part is about what I deliberately took out of the conversation.

Anyone who trains wants to see regularly how the last few months went. How much per sport? Was volume within the phase's corridor? How much of the plan got done, where was the time spent across the zones? These questions have fixed answers that follow from the data. You don't need a language model for them.


The attempt in the chat

On 14 October I tried it anyway. Claude was to build an analysis as an artifact from the data in my account, around 200 sessions from one year: weekly volume, zones, load, plan versus actual. Through the MCP tools, with no outside help.

The result looked good and showed two things. The first was unexpectedly useful: adding everything up exposed three bugs in the server. Older duplicate versions of a session had not been merged retroactively, time in zones was counted by percentage of maximum heart rate instead of by the user's own zones, and a planned session of type "other" occupied every sport on that day. My tests hadn't found any of the three. If you add up a year of data in one go, you hit the edges.

The second was the effort. For the zones over six months the artifact needed around 120 calls to activity-detail, one per session. That takes time, it costs context, and next time everything starts from scratch. An analysis you look at every week doesn't belong in a conversation that recalculates it every time.


One endpoint, everything calculated

So the web version calculates on the server, in a single call:

GET /api/analytics?weeks=52

The response contains everything the page needs, split into sections: calendar weeks per sport with duration, distance, load and zones, the phases with their targets in the version that applied in each week, months, totals, daily load with CTL, ATL, TSB and ACWR, trends per session, vitals as weekly averages and the races from the goals. The same sessions are counted as for load in the chat, meaning only the leading version of a session delivered several times. That way the numbers on the web and in the conversation match.

Load curves CTL, ATL and TSB over one year, with the ACWR and its shaded range below
Load tab: CTL, ATL and TSB from daily load, with the ACWR below. Demo account with made-up data.

The page itself has five tabs: overview, plan, load, intensity and personal bests, plus a period of 12, 26 or 52 weeks and a filter by sport. Among other things it shows weekly volume as stacked bars with the phases as bands behind them and races as markers, the phase corridors, plan versus actual, the load curves, zones per week and month, and a heatmap of when training happens: weekday against start hour.

Stacked weekly bars per sport over one year, with five phases as bands and three races as vertical lines
Weekly volume per sport with phases and races. Demo account with made-up data.

The charts are custom SVG components in Nuxt, without a charting library. That was more work than a ready-made package. In return they look like the rest of the site, and I can decide at every point what appears on mouse hover.

Heatmap of weekday against start hour, darkest on Saturday and Sunday mornings
When training happens: weekday against start hour. Demo account with made-up data.

Show, don't judge

The most important decision for this page is a single sentence in the concept:

The charts show, they don't judge. The language model writes any assessment in the coach note.

The analysis says that running volume was above the phase corridor in three out of four weeks. It doesn't say whether that is bad. Maybe there was a holiday, maybe it was intended, maybe a race is coming up. The model knows that because it knows the conversation, and so that is where the assessment belongs.

Running kilometres and hours per week against the target range of each phase, weeks outside it faded
Phase corridors. Weeks outside the range are faded, not judged. Demo account with made-up data.

The explanations on the charts stick to facts. They give the change against the four weeks before, whether a value is within the target range, or what a TSB value means on that day. Whatever doesn't exist in the data is left out instead of showing up as zero.

The same applies the other way round. The planning page on the web shows the week, the season and the structure of each session. You can't change anything there. Planning and replanning happen in the conversation, and the page shows what came out of it. Two places where you can edit the same plan drift apart sooner or later.

The two meet in one place: the individual session. The detail view shows heart rate and pace over time, elevation profile, zones and laps, and below that two notes. The user writes one, the model writes the other through the coach-note tool. If you ask in the chat how a run went, you will find the answer on the session afterwards too.


Personal bests, and why swimming is missing

The fifth tab shows personal bests: for running from 400 metres to 50 kilometres, for cycling the best power from five seconds to 60 minutes, each for the last 42 days, the season and all data. On top of that there is a pace curve with critical speed.

Pace curve from 400 metres to 15 kilometres with critical speed as a dashed line
Pace curve with critical speed. Below it are the compendium cards the estimate rests on. Demo account with made-up data.

Swimming is missing on purpose. For continuous front crawl the watches usually count lengths correctly. With a stroke change in the middle of a length, all tested models were off in one study. Best times from such recordings wouldn't be reliable, and a wrong best time is worse than none. The reasoning is a card in the compendium, with evidence strength "weak" and the note that it is a single study.


Conclusion

Everything that needs judgement stays in the conversation: planning, replanning, putting things in context, asking back. What has fixed answers and gets looked at again every week is on the web. Both sides calculate with the same numbers, and as long as neither takes over the other's job, they fit together well.

That wraps up the series for now: from the chat as an interface through the data and the training knowledge to the analytics next to it. Next up is the session's route as a map. The concept is written, but it isn't built yet.


Call to Action

Until the end of October training mcp runs as a beta with a small group of users, the public launch is in early November. Analytics and the detail view are part of the Plus tier. Everything else is at https://training-mcp.app.

If you have read the series and something is missing or unclear, let me know.