Companion to the talk. Each lab is one static page you open in Chrome. No build step, no agent SDK in the page, no server. You feature-detect navigator.modelContext, register a tool, and watch the same button a human clicks become a tool an agent calls. Same DOM, two callers. We start with the fifteen-line hello, land the cart-counter demo, wire three of your own tools, then gate a destructive action behind consent.
What you build tonight: a page whose add-to-cart, search, and save actions are all agent-callable, with the destructive one behind a consent gate.Feature-detect navigator.modelContext, register one say_hello tool, and prove it lives. Fifteen lines of vanilla JS, no imports. The page still greets humans when WebMCP is absent.
A real <button id="add-to-cart"> and a live counter. Ten lines register add_to_cart. Invoke it from the Tool Inspector, outside the page, and watch the counter tick. The most legible "agent did that" moment in modern web.
A mock product page, pre-wired for humans. You register three tools against it: add_to_cart, search_products, save_item. TODO comments mark every blank you fill. Your page becomes agent-callable.
A delete_account tool that refuses to act until a human clicks confirm in the page. The execute callback returns a pending state, not a deletion. Destructive actions are never one-shot. This is the pattern the room will get wrong.
> 'modelContext' in navigatorFeature-detect first, always. Register real tools when navigator.modelContext is present. Degrade to a working human page when it is absent, with a banner that tells the attendee how to turn WebMCP on. The button a human clicks and the tool an agent calls run the exact same function. That is the whole idea.
Twenty minutes: do 01 then 02. You leave with a page an agent can drive. Full hour: all four in order, each builds the model the next one needs. Lab 04 is the one most people skip and the one that matters most, so we make it the smallest stub that still proves the consent gate.
All four labs are single-file, no build step, view-source friendly. Each one registers real tools through navigator.modelContext.registerTool and each one still renders and works for a human when WebMCP is not detected. Lift any pattern you want.