← Back to list

Pyrox MCP Server — access to and analysis of Hyrox Results directly via LLMs

Introduction

Vlad Matei · 2026-06-19 23:25 · 4 claps · 8.9 min read
#data-science #sports #large-language-models #ai-agent
Open on Medium ↗
Wiki topics: LLM · Large Language Models AGT · AI Agents ML · Machine Learning 🔬 · Science · General 🏆 · Sports · General

Hyrox MCP Server: query and analyse race data from Claude / Codex

Introduction

In my previous articles, I’ve done data modelling using scraped Hyrox results — looking at what the stylistic differences are between athletes running similar times, what seem to be the parts of the race that most influence the final result, and if we could apply [optimisation](https://medium.com/codex/data-driven-path-towards-sub-60-in-hyrox-a-case-study-18ee8444e47e) techniques to guide our training.

Then, I shared pyrox-client— a Python library that came out of that work, built with the idea of exposing race data as DataFrames via simple functions, allowing others to also pull back race info without having to rewrite the scraping pipeline. I was very pleased to find this article later on, where someone extracted cool insights usingPyroxClient !

This article presents the next step of this project — the pyrox MCP server to which your agents can connect. Below, I am sharing a few pieces of info on the technical setup, then doing a ‘demo’ of the kind of analysis that can be done (integrating with Strava’s MCP as well). Finally, I write some of my thoughts on where this sort of analysis might fall short, and what I see as techniques to guard against that.

The Idea

The HYROX data space is definitely growing, with quite a few cool solutions being built by people in the community — which I believe shows that there are technically minded people with a passion for sport in this area.

However, as LLMs become more and more integrated into everyone’s daily life (not just developers/engineers) — I thought it would be interesting to get HYROX data directly into these models, see what insights can be extracted by these models, and leverage some of the functionality to automate what could be a ‘post-race’ report.

An issue with LLMs is that they can be ‘confidently wrong’, returning an answer that sounds plausible and that someone without prior race experience might trust, not knowing any better.

I see a Hyrox MCP tool as something that athletes and coaches can leverage alongside their experience. These people have the most context about their race and might have a very good idea of what directions to focus on, but getting the data to back it up — or alternatively, raise new points of concern that might have slipped- can be a real differentiator. Even a simple discussion regarding a previous race and the overall trends seen in the division and age group could, in my opinion, prompt people with the right skills to ideas they might not have had otherwise — and lead to faster progress.

Why would we need an MCP for this?

The frontier models are incredibly powerful out of the box. Ask your favourite model about Hyrox — and it will very quickly come back up with a clear, detailed report of what the sport is.

However, when it comes to more personal questions — a model won’t have knowledge about your latest result, your split profile or how your race compares to the rest of the division at that specific venue. It might be able to write a scraper, but that would not scale to race-level analysis, let alone season-wide analysis.

This is where the MCP servers comes in, exposing this data to the powerful models. As we’ll see below, a question as: “Where do my splits stand in the context of the field averages?” — becomes a tool call to the Pyrox server — and the LLM is the interface that explains the result and turns numbers into natural language.

Technical Approach

The server exposes a set of tools that the LLM can access once connected:

  • find_athlete — resolve a given name to a candidate race results
  • get_athlete_profile — career profile with station-by-station PBs and run+roxzone PBs
  • get_race_report — full split by split report for one race
  • get_deepdive — cross-location cohort (age/gender pairing) comparison for one result
  • get_distribution — histogram + summary stats for a cohort (age/gender pairing)
  • get_rankings — leaderboard for a season/division/gender cohort
  • get_race_summary — summary stats (count, mean, median, p10/p90) for every segment in one race
  • get_cohort_segment_averages — per segment averages for a rank-based slice of a race field (e.g. fastest 20)
  • list_races — discover which races are available
  • list_filters — valid filter values for the LLM to know how to structure its tool calls

For transport, I used HTTP. This way, the server is a remote web service at a URL — and the client connects over the network. Otherwise, with stdio — the server would be running as a local process on the user’s own machine — which would have also required users to download and store the database locally.

The server is hosted on Fly.io

Demo #1 — ‘What are my results?’ + reasoning about data quality

Now, time to have a look at what analysis we can actually do with this data integrated into our LLM.

Below, I’ll be using the claude-[cli](https://code.claude.com/docs/en/quickstart) and for setting the Pyrox-MCP up, we need to run:

claude mcp add — transport http pyrox https://pyrox-api.fly.dev/mcp/

We can see below how the agent has called Pyrox directly to get the data. Interestingly enough, the points above about the importance of knowing the domain still stand. The London Excel race that is flagged as a “data oddity” — is actually valid. The course had a different layout to most other venues, however, the model does not know that.

Simple example of pulling back race data

Simple example of pulling back race data

Now, let’s see if we can get the model to reason around the Excel data by itself, without us directly specifying the run / roxzone split:

Model’s own thinking in what might cause odd run / roxzone split (when prompted in that direction)

Model’s own thinking in what might cause odd run / roxzone split (when prompted in that direction)

Nice! Making use of the get_race_summary tool — the model has correctly identified that it wasn’t just me with an odd run / roxzone split, and this was across the whole field. An interesting point is still the model’s tendency to be ‘overly helpful’ and ‘confidently wrong’. The London Excel is a huge hall (true) and the roxzone physically takes longer to traverse statement is wrong. Although the venue is indeed large, it was just Season 8’s race where the run / roxzone split was quite different. The sum of run+roxzone should always still be 8.7KM.

Demo #2 — Gap Analysis

Okay — now that we can see the data is there and the model is able to pull it back, I was curious to see if we can reproduce, via natural language — some of the analysis in my first article — where I did ‘Gap Analysis’, on what separates the Top athletes in the field from the average finishers.

We will now switch to looking at doubles pairs, both Men and Women, Open group (but could be Pro with just a prompt update). I did have to prompt towards using get_cohort_segment_averages directly, as initially the model preferred using get_race_summary (which takes in a top_percentile parameter). Interestingly, it has chosen London Excel — Season 7 after searching for race sample size. It argued against using the Season 8 race due to the unusual run / roxzone split.

Model’s takeaways of Top vs Average — Runs

Model’s takeaways of Top vs Average — Runs

Model’s takeways of Top vs Average — Stations

Model’s takeways of Top vs Average — Stations

Guided by the numbers — the model’s natural language conclusions are very similar to what I found in the original article — the stations with the largest variation are considered the biggest differentiators. Interestingly, the model is able to pick up on the running consistency of the top pairs against the drop-off seen in the average of the field.

Demo #3 — Race Report and Athlete Recommendations

Finally, let’s go back to the individual athlete analysis and ask the model to provide an athlete ‘label’ and suggest focus training areas.

The model then picked my PB-race and performed the same segment-by-segment analysis there.

Arguably, most of my races tend to look better in the running department, and wall balls have been an area where I’ve struggled before — and it wouldn’t take a frontier LLM to figure this out — but still, quite interesting to see a breakdown of my racing style and focus areas that I would 100% agree with! The recommendations were the stations where I generally tend to lose time across all my races — leveraging the tools, the model was able to ground its recommendations in a consistent trend of station performances. Each recommendation is backed by a number returned from the server — thus taking away the risk of wrong arithmetic on the LLM side — and letting it only do reasoning.

Pretty accurate description :(

Pretty accurate description :(

LLM Training Recommendations

LLM Training Recommendations

Finally, passing down one of the graphs in the first article — I asked the model to replicate it using only data it can extract from the MCP.

I was quite pleased with the output — if anything — the title annotation from the model was better than my original plot!

Initially, the massive spike on Run 2 of the LLM plot made me think there might be a hallucination, but checking against the results website — there was indeed one entry with a Run 2 time that would pull the times up. Now, that might be a data quality issue on Hyrox’s side — which my scraping script could (should) guard against in the future.

Deterministic code across all Season 6 London 2023 athletes (Left) — LLM generated graph of London S7 data (Right)

Deterministic code across all Season 6 London 2023 athletes (Left) — LLM generated graph of London S7 data (Right)

Next Steps

/race-report skill

The next step to build is a race-report skill.

  • Once someone finishes a race, the analysis usually follows a fairly repeatable path. Find the result, check the time, compare against the field, look at the run/station split — identify weakest splits, compare against previous times etc. The steps can be standardised into one workflow.
  • This simply forces the model to follow the same checks every time, therefore moving the output towards a more ‘deterministic’ style. The MCP makes the data available in the same way an API would have, and with a skill we can still replicate what would have previously been a script that was pulling in that data. Rather than writing code — this now becomes a .md file, but the overall principles I believe are still the same.
  • Ultimately, this could become a standard post-race pack — which gives athletes a one-page summary of their strongest / weakest segments, and a comparison to the field, along with training focus areas — all grounded in real data and personal insights.

Expand ML-capabilities via MCP

  • The MCP server could perform further analysis given someone’s race data — and potentially return expected finish times at future races within time-bands — a form of ‘potential’ recommendation
  • As a local tool — users could also integrate with their own Strava account — and similarly, leverage the LLM to build out models that can account for their training load when trying to either explain a previous race, or predict / model a future race
  • Building an ‘Agentic Recommender System?’ — recently came across an interesting paper that looks at ‘agentic frameworks’ — I believe something similar could be applied to Hyrox — especially if there are other integrations with Strava / Google that can further personalise the experience to the user. An interesting point to research here would be whether or not the ‘multi-agentic’ approach would be necessary — or if a single agent could achieve similar performance.

Final Notes

I’ve tried to structure this project in a way in which the server handles the calculations — and the model is only fed the structured results — then works on top of them.

This enables users to ask loose questions, such as ‘What separates top pairs from the field?’ / ‘ Where have I lost most of my time against the field?’, and allows the LLM to provide an answer that is genuinely grounded in a backend function, removing the ‘concern’ of a ‘guess’.

The Excel roxzone example at the start shows how domain knowledge is still incredibly important. To someone without this information, the question over the Excel running performance there might have sounded valid. However, once we prompted the model towards the right direction (and with the right tools), it was able to correct its mistake — and reconsider its response to a genuinely helpful analysis.

To mitigate against these risks, I believe that a simple ‘hyrox-coach’ skill (or even an updated CLAUDE.md file) — that incorporates the specific domain knowledge of this race format could limit these mistakes.

Pyrox Setup: https://vmatei2.github.io/pyrox-client/mcp/#how-to-read-the-answers


메타데이터
post_id
4e8ebf486525
slug
pyrox-mcp-server-access-to-and-analysis-of-hyrox-results-directly-via-llms-4e8ebf486525
url
https://medium.com/@vladmatei432/pyrox-mcp-server-access-to-and-analysis-of-hyrox-results-directly-via-llms-4e8ebf486525
canonical_url
https://medium.com/@vladmatei432/pyrox-mcp-server-access-to-and-analysis-of-hyrox-results-directly-via-llms-4e8ebf486525
author_url
https://medium.com/@vladmatei432
status
ok
fetched_at
2026-06-21 07:44:09