4 min read

WebMCP's Second API Skips JavaScript Entirely. It's Two HTML Attributes.

Add toolname and tooldescription to an existing form and Chrome derives a JSON Schema tool from your labels — no navigator.modelContext, no hand-written schema.

WebMCPBrowser StandardsHTML

WebMCP’s origin trial sign-up opened in Chrome 149 on June 9, and the API I like best in it isn’t the one I expected to. Alongside the imperative navigator.modelContext.registerTool path, Chrome shipped a Declarative API: annotate an existing form with two HTML attributes and the browser turns it into a callable agent tool. No JS rewrite, no schema you write by hand.

The mechanism: add toolname and tooldescription to a formelement. Chrome walks the form’s fields, reads each field’s associated label (or aria-descriptionif there’s no label), and builds a JSON Schema from what’s already there. A select with three optionvalues becomes an enum, with each option’s text captured as a title. Add toolparamdescriptionon a field if the label text alone doesn’t say enough. The schema comes out of markup you already shipped.

Chrome’s own example is a support form at example.com/get-customer-support:

<form toolname="createSupportRequest" tooldescription="Submits a request for customer support.">

Remove either attribute and the tool unregisters itself. That’s the entire opt-out mechanism.

Submission has two modes. By default, the agent fills the fields and a human still has to click Submit — the form stays visible and nothing happens without that click. Add toolautosubmit and Chrome submits automatically when the tool is invoked. Either way, SubmitEvent gets a new agentInvoked boolean, so an existing submit handler can branch on whether a human or an agent triggered it. Call preventDefault() and pass a promise to the new respondWith()method, and whatever that promise resolves to gets serialized back to the model as the tool’s output — validation errors included.

There’s a second signal layer for UI feedback. When a tool fires, the window gets a toolactivatedevent carrying the tool’s name; if the user cancels or the agent calls reset(), it’s toolcancel instead. Two CSS pseudo-classes ride along: :tool-form-active on the form, :tool-submit-activeon the submit button, both active only while the agent-driven fill is in progress. Chrome’s default styling for both is a dashed outline — fine for testing, and the first thing a designer will override.

None of this replaces the imperative API. If a tool does something that isn’t “fill and submit a form” — a diagnostics tool that runs code, a search that hits an endpoint the form doesn’t actually submit to — you’re still writing registerTool and an executecallback. Declarative only covers the actuation-on-a-form case. On most sites, that’s still most of what an agent needs to do.

The gating is the same as everywhere else in WebMCP. Origin isolation is required — enable document.domain or send Origin-Agent-Cluster: ?0 and both APIs turn off. The tools Permissions-Policy defaults to self, so a cross-origin iframe needs allow="tools" explicitly before it can register anything. Test it locally at chrome://flags/#enable-webmcp-testing, or sign up for the origin trial to run it without a flag.

What you get today is small but real: two attributes on a form you’ve probably already built, and Chrome does the schema work for you. It’s still an origin trial, still Chrome-only, and the explainer is still open for changes on GitHub. But if a support form or a booking flow is the entire surface an agent needs, this is less code than a single registerTool call.

Tap the mic to load the voice agent