The venue knew. The booking didn't.

A wheelchair space can be available even when the journey to it is not usable. A route can contain steps. A lift can go out of service. The adjacent companion seat or entrance-assistance capacity can change before the booking is confirmed.

That mismatch became the design question behind this prototype: what if the unit of booking were not the seat alone, but the accessible journey that makes the seat usable?

No Seat Without a Route links the route, lift status, wheelchair space, adjacent companion seat and entrance assistance in one plan. At confirmation, the server evaluates that plan again against the current venue state. It either commits the wheelchair space, companion seat and assistance together, or reserves none of them.

Federal ADA ticketing regulations require covered public entities and public accommodations that sell tickets for a single event or series of events to give individuals with disabilities an equal opportunity to purchase accessible seating, including during the same hours and through the same distribution methods used for other seating. Upon inquiry, entities that sell or distribute those tickets must identify and describe available accessible seating in enough detail for an individual with a disability to assess whether a particular seating location meets their accessibility needs (DOJ ticket-sales guidance, 28 CFR § 35.138 and 28 CFR § 36.302(f)). This prototype explores a product opportunity in that context. It does not claim that the ADA requires this route-and-booking model, or that the prototype establishes legal compliance.

The failure is the demo

A visitor asks their browser agent for a step-free route for a 72 cm wheelchair, under 80 metres, with one companion, a quieter entrance and somebody waiting at the door.

The page exposes structured WebMCP tools. The agent reads the current synthetic venue state and stages a complete plan through East Entrance and East Lift L2. The route and its named dependencies appear on the page for review. Planning reserves nothing.

The demo then introduces a race condition between review and confirmation. A visible control arms a deterministic fault on East Lift L2, but the lift remains operational until the next valid confirmation attempt.

When the visible confirmation control is pressed, the server applies the outage, advances the venue revision and refuses the now-stale plan with STALE_RESOURCE_VERSION. The result shows 0 partial reservations: no wheelchair space, companion seat or assistance slot was held on its own.

The agent calls explain_access_refusal. It receives the failed LIFT_OPERATIONAL rule, the old and current venue revisions, the zero-partial-reservation result and the Garden route that remains available. It replans through Garden Entrance and Garden Lift L4 without requiring the six inputs again.

The replacement is reviewed and confirmed through the visible page. The server checks the current venue state once more, then commits wheelchair space W12, companion seat W13 and entrance assistance in one transition.

A happy path would show only that a plan can become a booking. The demonstrated failure path shows the harder part: stale state books nothing, the refusal explains what changed, and the agent has a structured way to recover.

Why WebMCP fits

The booking page is both the human-facing interface and the agent surface. WebMCP gives the browser agent structured, site-authored tools. In the demonstrated stale-plan flow, the staged plan, refusal and replacement are rendered in that same page for review.

Tool availability follows the booking state. Search and staging are removed when they no longer apply. After the demonstrated stale-plan refusal, explanation and replanning become available. Once a booking exists, the visitor tool surface is read-only.

No registered WebMCP tool can prepare or commit a booking confirmation. In the intended page flow, confirmation is a visible page action. This is a tool-surface boundary, not an identity guarantee: the prototype has no user or operator identity authentication and does not prove who pressed a control.

The ordinary form and buttons complete the same visitor flow when WebMCP is unavailable.

This shared page state is why I built the project with WebMCP instead of placing an agent beside a separate booking API. The structured actions, map, failure explanation and confirmation control stay in one interface instead of becoming two experiences that need to be kept in sync.

How I built it

The client is plain HTML, CSS and JavaScript. A zero-dependency Node.js service holds synthetic venue state in memory and runs on Render.

The visitor code defines nine imperative tools and registers only the subset valid in the current state. A separate operator demo page defines three facility tools over the same synthetic venue state. It has no operator identity authentication and is not an authorization boundary. In the Chromium hosts I tested that exposed the declarative contract, the visible requirements form also appeared as a tool, deliberately without automatic submission.

Planning, explanation and final revalidation use the same route-evaluation implementation. Every plan records the venue revision against which it was built. Confirmation checks that revision again and moves every reservable resource in one synchronous in-memory state transition.

For expected browser-facing domain refusals, the first-party pages request a typed ok: false envelope. The result keeps its error code and original domain status, while the expected safety decision does not appear as a failed network request. Raw API clients still receive conventional 4xx responses, and unexpected failures remain 5xx.

What broke, and what I learned

During earlier testing in Chrome 151, I observed that unregistering a tool while its own call was returning could cancel the in-flight call. I fixed that by delaying the tool-surface update until the execution had settled.

Across the hosts I tested, I also observed different enforcement of required fields at the host boundary. The tool wrapper, visible form and server therefore each validate the input that reaches them.

While auditing the regression suite, I found a test that removed the very counterexample it was meant to detect before asserting that the list was empty. The test passed while its claim was false. Since then, for release-critical repairs, I have restored the defect deliberately, confirmed that the regression test fails, and only then put the fix back.

I manually exercised the deployed app in ChatGPT desktop's in-app browser on 2 September 2026. The automated browser run used Chrome 152.0.7977.66 and also exercised Edge 152.0.4191.53. Firefox 155 was checked only as a manual, non-WebMCP fallback. These are observations from those specific versions, not a claim of compatibility with every browser or agent.

The current release gate passed 740/740 Node tests and 389/389 browser checks.

Current scope

This is a focused synthetic prototype: two routes, one event, in-memory inventory, no payment, no real ticket and no user or operator identity authentication. The visitor and operator labels describe demo roles, not authenticated identities. The operator page is an open demo control surface, not a production authorization boundary.

Production would need durable storage, real authentication and authorization, live ticketing and facility feeds, outbound notifications, and testing with wheelchair users and venue access teams.

The same transaction pattern may be worth testing in other journeys where one missing dependency can make the rest of a reservation unusable, such as rail transfers, airports, hotels or hospitals.

A seat is only accessible when the whole route works.

Built With

Share this project:

Updates

Submission history