For judges

Quickest test: open the simulated Alexa+ at https://mcp.racingaidataset.com.au/sim, choose Link account and sign in as [email protected]. The password, the MCP client secret and step-by-step tests are in trackside-testing.zip, the file uploaded with this entry. Full guide: https://github.com/zhangshen20/trackside/blob/main/docs/testing.md

Inspiration

Racing is one of Australia's biggest sports, and spring is its peak: the Caulfield Cup, the Cox Plate and the Melbourne Cup fall within three weeks. But almost every racing app is built to sell a bet. Fans who want to know who is running, how a horse has been going or why a race matters have to read around odds, promotions and bookmaker banners. We wanted the knowledgeable friend at the track instead: someone who knows the form, can explain a race in a sentence, and never mentions a price. Voice suits it well, because racing fans mostly ask short questions while doing something else.

What it does

Trackside is an Alexa+ add-on built as a self-hosted MCP server. A fan can ask:

  • "What racing is on at Caulfield on Sunday?" (meetings, track condition, first race)
  • "Who's in race 8?" (the full field with barriers, jockeys, trainers and weights)
  • "How has Jimmysstar been going?" (career, track, distance and going records, recent starts, last-600 m times)
  • "Tell me about this race." (what the race is, why it matters, the form contenders)
  • "Who won, and who ran home fastest?" (placings, margins, time and the fastest last 600 m from official sectional timing)
  • "How's Ethan Brown riding lately?" (jockey or trainer wins and places)
  • "Follow Extragalactic" and later "How's my stable?" (a personal watch list: where your horses are running next and how they went)
  • "What's on in the Spring Carnival?" (the 2026 feature races)
  • On a screen, Trackside draws its own answers with an MCP App: the race card with a colour-coded form strip, a results podium with the fastest last 600 m, and a horse's form. Tap a horse and the App calls Trackside for its form; tap Follow and it's in your stable.
  • "How's my stable?" a week later: "Since you last checked on Tuesday, Extragalactic won at Caulfield." Trackside remembers each listener across sessions (followed horses, home state, when they last asked) and opens with what's new. "I'm in Sydney" puts NSW meetings first; "forget me" deletes it all.
  • "How was it run?" Results say where the placegetters were at the 800 and the 400 and their last 600 m ("led throughout", "came from last"), form says where a horse usually settles, and explanations say which runners usually lead and which come from the back, never how this race will be run.
  • Names the way they're heard: "Cofield" finds Caulfield, "Demo Myla" finds Demo Miler ("Taking Demo Myla as Demo Miler"), and when two horses sound the same Trackside asks "Did you mean A or B?" instead of guessing.
  • "What's the next race?" and "I'm in Brisbane": start times are spoken in the listener's own state's clock and at the track ("12:30 pm Queensland time, 1:30 pm at the track"), with "due to jump in about 20 minutes" when it's close.
  • Group 1 status read out ("the Epsom, a Group 1 race over 1600 metres"), from a list of Australia's Group 1 races, since the source carries no grade. Wagering products are gone from race names too ("Edward Manifold Stakes", not "ONE POOL Edward Manifold Stakes").

Every answer is short enough to be spoken, names its source ("According to Racing Australia…"), and also returns structured content for screen devices. There is no betting anywhere: no odds, no tips, no bookmaker names. Price fluctuations are dropped when the data is read, and wagering brands are stripped from race and venue names.

How we built it

The architecture diagram is in docs/architecture.mmd and renders on GitHub.

  • MCP server in Rust (axum + rmcp), MCP spec 2025-11-25 over Streamable HTTP, thirteen tools with fan-language descriptions so a model picks the right one, each with a title and MCP behaviour hints (read-only, destructive) and a plain input schema that passes MCP Inspector's portability check.
  • On AWS: an arm64 Lambda (provided.al2023) behind an API Gateway HTTP API with throttling, a private S3 bucket for the data snapshot, a custom domain with an ACM certificate, all in one CloudFormation stack deployed by a single script.
  • OAuth 2.1 the way Alexa+ expects it: Amazon Cognito issues a client-credentials token (mcp:service) for discovery and an authorization-code + PKCE (S256) token (mcp:tools) for tool calls. Unauthenticated requests get a 401 pointing at RFC 9728 protected-resource metadata, and the server publishes RFC 8414 authorization-server metadata. Each signed-in user has their own follow list. That memory lives in a DynamoDB table keyed by the Cognito subject, so it survives across sessions and Lambda instances.
  • Real data: a Rust ingester reads Racing Australia fields and form, official results and state sectional timing from an existing read-only archive, and builds one compact snapshot. It never reads prices.
  • Kept fresh and watched: an EventBridge schedule rebuilds the snapshot twice a day (after the evening's results and with the morning's final fields), and an hourly probe checks the public endpoint, the Cognito sign-in metadata, the simulator and the snapshot's age, with CloudWatch alarms on any failure.
  • Testing: a protocol smoke test (36 checks, including "no answer mentions betting, odds or a bookmaker" and "no holes in spoken text"), MCP Inspector runs with a real Cognito sign-in, and natural-language checks through a Claude custom connector as a stand-in for Alexa+.
  • Amazon Bedrock rewords explain_race's facts (Claude Haiku 4.5 through the au. inference profile, so requests stay in Australia) into a few spoken sentences a newcomer can follow. The model sees only the retrieved facts, never prices, and an answer with a betting word in it falls back to the tool's own template.
  • A web simulator (crates/trackside-sim) plays the Alexa+ conversation for the demo, because the Alexa+ toolkit is US-only: Bedrock chooses Trackside's MCP tools, Amazon Polly's Australian voice speaks the answers, and the page hosts the MCP App like an Echo Show would.

Challenges we ran into

  • Alexa+ testing is US-only. The MCP toolkit requires distributionCountries: ["US"], and we're in Australia, so we couldn't test on a real Alexa+ device. We tested the exact protocol and auth Alexa+ uses with MCP Inspector and a real model through a custom connector, and built a simulator for the demo.
  • Cognito and the MCP auth metadata don't line up. Alexa+ wants /.well-known/oauth-authorization-server with code_challenge_methods_supported: ["S256"]; Cognito only publishes OpenID configuration without that field. The server publishes its own metadata document pointing at Cognito. Cognito also prefixes custom scopes (trackside/mcp:tools), so the server accepts both forms.
  • Streamable HTTP on Lambda. API Gateway HTTP APIs buffer responses, so server-sent-event streams don't work. We run the server stateless with plain JSON responses, which suits Alexa+'s request-response tool calls.
  • Making data speakable. Real racing data has empty fields, legalese prize strings, barrier trials mixed with race starts and venue codes like "CAUL". Each one produced a sentence that sounded wrong aloud; the smoke test now fails on holes in spoken text.
  • Keeping gambling out. Racing data is soaked in prices. Keeping them out took design at the boundary (the wire types don't declare price fields at all) rather than filtering at the end.

Accomplishments that we're proud of

  • A real, public, OAuth-protected MCP server that follows the Alexa+ auth model end to end, verified with a real user sign-in.
  • Live Australian racing data including sectional timing, which lets Trackside answer "who ran home fastest", a question fans ask and most apps don't answer.
  • A racing product with no betting in it at all.

What we learned

  • The tool description is the interface. Which tool a model picks, and with what arguments, depends far more on a description written in a fan's words than on the schema.
  • Voice answers have to be tested by ear. "0.8 L", "VIC" and "2:02.41" pass every schema check and sound wrong, so the smoke test now fails on unspeakable text.
  • Memory changes the product. Once Trackside remembers your horses and your state, every answer can open with what matters to you, and a single-question bot becomes a companion.
  • Standard OAuth pays off. Because Alexa+ uses plain OAuth 2.1 metadata, MCP Inspector and a Claude connector could test the exact auth path, and the Cognito gap became a reusable open-source crate.

What's next for Trackside

  • Push alerts when a followed horse is entered or has run (memory across sessions is built; Alexa+ proactive notifications would deliver it).
  • More jurisdictions (New Zealand, Hong Kong, the UK) as new ingesters, and Alexa+ certification once the toolkit opens outside the US.

Built With

Share this project:

Updates

Submission history