AI Race Engineer
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@AI Race EngineerHow much did Verstappen's tyres degrade at Austria 2024?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
AI Race Engineer
An AI race engineer for Formula 1: reads telemetry, models tyre degradation, evaluates pit strategy, and delivers calls the way a real engineer would — short, timely, and grounded in data.
Built on LangGraph for orchestration and MCP for the tool surface, over FastF1 telemetry.

Every point is a real, cleaned, fuel-corrected lap. Every line is the model fitted to it.
Three things are worth noticing:
The HARD line is shallower than the MEDIUM lines (+0.100 vs +0.135 s/lap). That ordering is nowhere in the code — it falls out of the fit, and it's the evidence the lap cleaning and fuel correction are working.
The gaps between stints are the pit stops, plus every lap thrown out as unrepresentative: safety car, in-laps, out-laps, timing glitches. 8 of VER's 71 laps didn't survive.
The dashed red line slopes downward, which is physically nonsense — tyres don't get faster as they age. That's a 5-lap end-of-race stint, and the system flags it as a weak fit (R²=0.31) rather than reporting it as a finding. Knowing when the data can't support an answer is the hard half of this problem.
The same result on an independent sample
Norris, same race, fitted separately — three stints, no weak fits:

MEDIUM (stint 1) | HARD | MEDIUM (stint 3) | |
Verstappen | +0.135 s/lap | +0.100 | +0.199 |
Norris | +0.117 s/lap | +0.097 | +0.238 |
Two different cars, two different drivers, fitted independently — and both put the HARD below the MEDIUM by a similar margin. One fit could be luck; two matching fits on independent data is the method working.
The third-stint figures are worth a look too: both drivers roughly double their opening MEDIUM degradation on the same compound, consistent with a hotter track and older tyres late on. Nothing in the code looks for that — it falls out of the fit.
Generate either for any race since 2018:
race-engineer plot --year 2024 --circuit Austria --driver VERSeparating fuel burn from tyre wear
Both make lap times change over a stint, and getting this wrong invalidates everything downstream. A car sheds ~100 kg of fuel across a race and gets quicker as it does; fit raw lap times and the model concludes tyres get faster with age, because fuel burn outruns tyre wear.
The obvious fix — subtract a fuel correction — just relocates the problem. How much per kg is circuit-specific: it scales with how much of the lap is spent accelerating, so a twisty circuit is far more mass-sensitive than a flat-out one. One global constant is the crudest possible assumption, and at a low-degradation circuit it can swamp the signal entirely.
Why you can't just regress it out
Within a single stint, fuel load and tyre age are both linear in lap number. They are perfectly collinear. No regression separates them.
What breaks the tie is the pit stop: tyre age resets to zero, fuel does not. So if a compound runs at two different points in the race, the pace difference at equal tyre age is attributable to fuel. That makes this identifiable:
lap_time = base[compound] + deg[compound] · tyre_age + k · fuel_kgOne intercept and one slope per compound, plus a single shared k. The compound intercepts
absorb pace differences, the slopes absorb degradation, and k is identified purely by the
resets. (Same structure as the state-space treatment in
arXiv:2512.00640, reduced to ordinary least squares.)
race-engineer analyse --year 2024 --circuit Monza --drivers NOR LEC --fit-fuelNOR: fuel effect 0.027 s/lap/kg (fitted, R²=0.75)
LEC: fuel effect 0.030 s/lap/kg (default — design is rank-deficient)Leclerc's fallback is the estimator working, not failing. He ran MEDIUM then HARD, one stint each — no compound repeats, so nothing separates fuel from wear. It refuses rather than returning a confident wrong number.
What this settled
Monza fits nearly flat — degradation of +0.000 to +0.015 s/lap, every stint flagged weak. That left an open question: is Monza genuinely low-degradation, or is the global constant wrong there? Fitting the coefficient answers it. At 0.027 s/kg the degradation is still ~zero, so it's the circuit, not the correction.
It's opt-in, for a stated reason
The estimator assumes one degradation slope per compound. Austria violates that: the two
MEDIUM stints genuinely differ (+0.135 vs +0.199 s/lap, from track evolution), which biases the
fit low. It's a real improvement in principle and not yet reliable enough to be the default, so
it sits behind --fit-fuel until the per-stint case is handled.
Related MCP server: fastf1-mcp-server
The core design constraint
An LLM agent loop is far too slow to be a race engineer. Real calls are sub-second and mostly reflexive. So the system is split by timescale:
┌── FAST LOOP (deterministic Python, no LLM) ────────────┐
│ telemetry tick → rule engine → alerts │
│ "box this lap", "blue flags", "P2 within DRS" │
│ Latency budget: <100 ms │
└────────────────────┬───────────────────────────────────┘
│ writes to shared RaceState
┌────────────────────▼───────────────────────────────────┐
│ SLOW LOOP (LangGraph + LLM) │
│ strategy reasoning, undercut math, debriefs, Q&A │
│ Latency budget: 2–30 s │
└────────────────────────────────────────────────────────┘There are zero LLM calls on the critical path. The model supplies judgment, not reflexes.
Graph shape
START → strategist ⇄ tools → radio → ENDstrategist loops against 7 MCP tools until it stops asking for them, then everything funnels
through radio — so the driver hears exactly one message per cycle, however much analysis
happened. radio is the only node that speaks to the driver, and its entire job is compression:
a race engineer says "Box, box, undercut Norris", not three paragraphs.
Routing between the two is a plain function, not a model call. It's decidable from state, so the round trip bought nothing. The original design had an LLM router picking between four specialists; dropping it removed a failure mode and a few hundred milliseconds.
It works on real races
$ race-engineer analyse --year 2024 --circuit Austria --drivers VER NOR
2024 Austrian Grand Prix — Race, 71 laps
VER: 63 clean laps (8 dropped as unrepresentative)
Stint 1 MEDIUM laps 1-23 deg +0.135 s/lap base 66.34s R²=0.98
Stint 2 HARD laps 24-51 deg +0.100 s/lap base 67.09s R²=0.89
Stint 3 MEDIUM laps 52-64 deg +0.199 s/lap base 66.82s R²=0.68
Stint 4 SOFT laps 65-71 deg -0.553 s/lap base 73.19s R²=0.31 ⚠ weak fit — indicative only
NOR: 58 clean laps (6 dropped as unrepresentative)
Stint 1 MEDIUM laps 1-23 deg +0.117 s/lap base 66.82s R²=0.87
Stint 2 HARD laps 24-51 deg +0.097 s/lap base 67.32s R²=0.93
Stint 3 MEDIUM laps 52-64 deg +0.238 s/lap base 67.32s R²=0.72The HARD compound degrading slower than the MEDIUM isn't hard-coded — it falls out of the fit. That ordering is the evidence the lap cleaning and fuel correction are working.
Stints flagged ⚠ have an R² too low to trust. A 5-lap end-of-race stint fits noise, and the system says so rather than reporting a confident wrong number.
Setup
uv sync
cp .env.example .env # add ANTHROPIC_API_KEY — only needed for the agentUsage
# Degradation analysis — no LLM, no API key
race-engineer analyse --year 2024 --circuit Austria --drivers VER NOR
# Ask the engineer — needs ANTHROPIC_API_KEY
race-engineer ask "Box now for the hard, or stay out?" \
--year 2024 --circuit Austria --drivers VER NOR
# Run either MCP server standalone (works in any MCP client)
race-engineer serve-data
race-engineer serve-strategyOn Kaggle: open notebooks/kaggle_demo.ipynb. Set
Internet → On, add ANTHROPIC_API_KEY under Add-ons → Secrets, and set the accelerator
to None — this is all CPU work.
Status
Phase | State |
1. Data layer — FastF1 loading, lap cleaning, fuel correction | ✅ Working on real races |
2. Strategy engine — degradation, crossover, undercut, pit window | ✅ 35 tests passing |
3. Agent layer — LangGraph + 7 MCP tools | ✅ End to end |
4. Evaluation harness — replay and score the calls | ⬜ Next, and the one that matters |
5–7. Live timing, voice, sim racing | ⬜ |
See ROADMAP.md for detail and docs/architecture.md for the design.
Development
uv run pytest # tests
uv run ruff check # lintData sources
Source | Cost | Used for |
Free, no key | Telemetry, laps, stints (2018→) | |
Free, no key | Results and standings (1950→) | |
Free historical / paid live | Real-time timing | |
Paid | The reasoning layer |
Full breakdown, including which are optional, in docs/apis.md.
License
MIT
Available Tools
4 toolscompare_driversB
Stint-by-stint degradation comparison across several drivers.
Args: drivers: Three-letter codes, e.g. ["VER", "NOR", "LEC"].
| Name | Required | Description | Default |
|---|---|---|---|
| year | Yes | ||
| circuit | Yes | ||
| drivers | Yes | ||
| session_type | No | R |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations to indicate read-only or side-effect behavior, the description carries the full burden of transparency. It fails to mention whether this is a safe read operation, what the output format is (e.g., table, chart), or any assumptions about data availability (e.g., requires race weekend). The description only states the comparison intent without disclosing behavioral traits.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is very concise, leading with a clear purpose sentence and then providing a parameter example. It is well-structured and front-loaded. However, it omits documentation for three of four parameters, making it efficient but incomplete; still, what is written earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool has 4 parameters, no output schema, and no annotations, so the description must provide substantial context. It only covers one parameter, ignores required inputs like year and circuit, and does not explain the expected output or how this tool relates to sibling functions. This is insufficient for an agent to confidently invoke the tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate. It only documents the 'drivers' parameter with an example format ('Three-letter codes'), but entirely omits 'year', 'circuit', and 'session_type' (which has a default). This leaves most parameters unexplained, providing minimal added meaning beyond the raw schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states a specific verb ('comparison') and resource ('stint-by-stint degradation across several drivers'), making it distinct from related tools like get_driver_stints (which likely returns raw stint data) and get_degradation (which may focus on a single driver). This purpose is immediately understandable and differentiates the tool well.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage when one needs to compare degradation across multiple drivers, but it does not explicitly state when to prefer this tool over siblings such as get_degradation or get_driver_stints. No exclusions or alternative recommendations are provided, leaving usage context to be inferred.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_degradationA
Fitted tyre degradation per stint for one driver.
Lap times are cleaned (safety car, VSC, in/out and inaccurate laps removed) and
fuel-corrected before fitting. Each stint reports an R² and a trusted flag —
a low-R² fit is describing noise, not degradation, and should not drive a decision.
| Name | Required | Description | Default |
|---|---|---|---|
| year | Yes | ||
| driver | Yes | ||
| circuit | Yes | ||
| session_type | No | R |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure. It transparently explains that lap times are cleaned (safety car, VSC, in/out, inaccurate laps removed) and fuel-corrected before fitting. It also warns about the R² and trusted flag, including the caveat that a low-R² fit is noise and should not drive decisions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise and well-structured: the opening sentence states the core purpose, the second details the data processing pipeline, and the third explains output interpretation and the trusted flag. Every sentence contributes meaningful information without filler or redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Even though there is no output schema, the description explains key output elements (R², trusted flag) and provides important context about data cleaning and fit interpretation. It is adequate for a moderately complex data retrieval tool, but it lacks parameter-level guidance and any mention of return format or pagination. Given the lack of output schema, the description still covers the essential behavioral context.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema has 0% description coverage, so the description needs to compensate by explaining parameters. It does not explicitly define any of the four parameters (year, driver, circuit, session_type), though the names are self-explanatory. The description only implicitly ties 'one driver' to the driver parameter, and it fails to clarify session_type's default meaning or values. Thus it adds little semantic value beyond the schema field names.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states the tool returns 'Fitted tyre degradation per stint for one driver,' clearly defining the resource and scope. It distinguishes itself from siblings like get_driver_stints and compare_drivers by focusing on fitted degradation rather than raw stint data or comparisons. It lacks an explicit verb like 'calculates,' but the noun phrase is specific and unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage for per-stint degradation analysis for a single driver, which helps separate it from comparison tools. However, it does not explicitly state when to use this tool versus siblings (e.g., get_driver_stints for raw stints or compare_drivers for multi-driver comparisons). No explicit exclusions or alternative guidance is given, only an implied scope.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_driver_stintsC
Per-stint compound, lap range and length for one driver.
Args: driver: Three-letter code, e.g. "VER", "NOR", "LEC".
| Name | Required | Description | Default |
|---|---|---|---|
| year | Yes | ||
| driver | Yes | ||
| circuit | Yes | ||
| session_type | No | R |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden of disclosing behavior. It mentions the output content but does not state that the operation is read-only, describe the return structure, or mention any defaults, errors, or side effects.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is extremely concise and front-loaded: one sentence states the tool's purpose, followed by a minimal but useful parameter note. There is no redundant information or wasted words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given no output schema, no annotations, and incomplete parameter documentation, the description is insufficient for an agent to fully understand the tool's behavior and return values. It covers the core concept but omits important usage context and parameter details.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema has 0% description coverage, and the description only adds meaning for the 'driver' parameter (three-letter code). The required 'year' and 'circuit' parameters and the optional 'session_type' remain undocumented, leaving significant ambiguities.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the resource (driver stints) and the specific data provided (compound, lap range, length). It scopes the tool to one driver, which helps distinguish it from compare_drivers, though it doesn't explicitly name alternatives.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No guidance is given about when to use this tool versus siblings like get_degradation or compare_drivers. The only context is 'for one driver,' which is a scope constraint but not a usage directive or exclusion.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_session_infoA
Load an F1 session and return its metadata and driver list.
Args: year: Season, 2018 or later (FastF1 coverage starts in 2018). circuit: Event or location name, e.g. "Austria", "Monza". session_type: "R" race, "Q" qualifying, "S" sprint, "FP1"/"FP2"/"FP3".
| Name | Required | Description | Default |
|---|---|---|---|
| year | Yes | ||
| circuit | Yes | ||
| session_type | No | R |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the burden of behavioral disclosure. It explains the operation (loads), the output (metadata and driver list), and a key data constraint (FastF1 coverage starts in 2018). It does not detail failure modes or exact metadata fields, but it is not misleading and provides useful behavioral context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is appropriately concise: a single purpose sentence followed by a compact bulleted list of parameter definitions. It is front-loaded with the main function and contains no redundant information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple session-information loader, the description covers purpose, all parameters, and output at a high level. Without an output schema, saying 'metadata and driver list' is slightly vague, but it is likely enough for an agent to select and invoke the tool successfully.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema provides only parameter names, types, and defaults with 0% description coverage. The description compensates fully by explaining each parameter: year with a minimum value, circuit with examples, and session_type with a list of valid values. This gives the agent everything needed to construct valid arguments.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's function: 'Load an F1 session and return its metadata and driver list.' This uses a specific verb and resource, and it distinguishes itself from sibling tools like get_driver_stints and get_degradation, which focus on other aspects.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear context for when to use the tool by explaining it loads session metadata/driver list and supplying parameter constraints (e.g., 2018 or later, valid session types). However, it does not explicitly mention alternative tools or when not to use this tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
4 tool updates
v0.1.0- First observed
compare_drivers - First observed
get_degradation - First observed
get_driver_stints - First observed
get_session_info
TDQS
The tools are largely distinct: session loading, stint data, degradation modeling, and multi-driver comparison. The only potential overlap is between get_degradation and compare_drivers, but the single-vs-multi-driver distinction is clear from descriptions.
Three tools follow the 'get_' verb-noun pattern, while compare_drivers uses a different verb without 'get'. This is a minor deviation, but the names remain predictable and readable overall.
With 4 tools, the server is well-scoped for its tyre-focused purpose. It covers the essential workflow without bloat, though it is on the smaller side, leaving room for additional specialized tools.
The server covers the full tyre degradation analysis lifecycle: session loading, stint retrieval, degradation fitting, and cross-driver comparison. Minor gaps like raw lap times or session context tools exist, but they are not critical for the stated purpose.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
MCP server exposing the Backtest360 engine API as tools for AI agents.
MCP server for building and testing AI agents with multi-model experimentation and insights.
MCP server providing access to the Scorecard API to evaluate and optimize LLM systems.
- UnifAPIOAuthcom.unifapi
Hosted MCP server for live public-data APIs and Skills for AI agents.
Related MCP Servers
- AlicenseAqualityDmaintenanceA Model Context Protocol server that provides comprehensive Formula One racing data, enabling access to event schedules, driver information, telemetry data, race results, and performance analytics through natural language queries.81MIT
- AlicenseAqualityCmaintenanceMCP server for Formula 1 data via the FastF1 library. Ask Claude (or any MCP-compatible client) about race results, lap times, telemetry, standings, pit stops, and qualifying — with historical data back to 1950 via the Ergast API.21MIT
- AlicenseNot gradedqualityCmaintenanceAn MCP server that provides 118+ Formula 1 analytics tools, enabling race analysis, driver comparisons, telemetry exploration, and strategy simulation through natural language.MIT
- AlicenseAqualityCmaintenanceA local MCP server that gives Claude (or any MCP-compatible AI client) access to Formula 1 race data. Load any session from 2018 onwards, ask questions in natural language, and get answers backed by real telemetry, timing, and strategy data.171MIT
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/fayazhussain2821/AI_Race_Engineer'
If you have feedback or need assistance with the MCP directory API, please join our Discord server