WebMCP surface: tools register and unregister dynamically as the seating plan changes - the tool list itself is state-dependent, not fixed at page load.
Inspiration
Seating a wedding is the classic task that's easy to state and miserable to do - "keep Tom away from Yusuf, the Menons sit together" takes four seconds to say and twenty minutes of dragging cards to satisfy. The constraints live in the host's head; the fiddly rearranging lives in the UI. We wanted both halves in the same room.
What it does
A host drags names onto tables by hand. An agent reads the same plan, adds "together"/"apart" rules, seats or moves guests, and repairs conflicts - through the identical functions the buttons call. resolve_conflicts is registered as a tool only while the plan actually has a conflict, and unregisters itself the moment it doesn't - so the agent's own capability list changes live, in front of the host, as the plan's state changes. A separate propose_seating tool can also draft a full re-seating from scratch without touching the board, and apply_seating - which exists only while that proposal is pending - commits it exactly as computed; any seating change in between, by either side, discards the pending proposal.
How we built it
One file, vanilla JavaScript, zero build step, zero runtime dependencies. Every WebMCP tool's execute routes through the same invoke() chokepoint the UI's own click handlers use, so a human drag and an agent tool call are indistinguishable to the rendering code. Tools register via document.modelContext.registerTool(tool, { signal }) and unregister by aborting that same AbortController - Chrome fires toolchange so the agent sees its own capability list update in real time.
Challenges we ran into
Live, real-Chrome testing (not just headless Playwright) surfaced a genuine race: resolve_conflicts' own disarm logic could call AbortController.abort() on the very controller its own execute() call was still running under, on the same call stack - Chrome would report the call as failed even though the underlying seating fix had actually succeeded. Fixed by deferring the abort one tick past the call's own promise settlement, verified against the real WebMCP bridge, not just a mock.
Accomplishments that we're proud of
A tool list that's never a static menu - it's a live reflection of whether there's currently something to repair. And a human and an agent who can both be mid-edit on the same board at once with no drift between what either one sees. he propose/apply pair adds a second shape of the same trust problem - a plan the agent can compute but not commit until asked - which is also Signoff's core mechanic, one level up.
What we learned
The real WebMCP bridge and a headless mock diverge in exactly the timing-sensitive cases that matter most - several bugs here only reproduced against real Chrome with real synchronous clicks, never against polling automation.
Part of a seven-app portfolio
Each entry proves a different WebMCP capability: Signoff (a tool that only exists once a human approves it), Paperwork (structurally human-only fields), Shortlist (a tool schema generated live from the catalog), Jhola (genuinely cancellable long-running tools), Almari (quarantined untrusted content), Dhwani (declarative, no-JavaScript tools).
Built With
- chrome
- css3
- html5
- javascript
- playwright
- webmcp
Log in or sign up for Devpost to join the conversation.