markodenic.tech as your preferred Google source Creem.io
Merchant of record payments for SaaS and digital products, handling checkout, subscriptions, taxes, invoices, and global payment operations.
Sponsor this newsletter · reach 9,500+ active developers
Watch an AI agent use a website and it looks like someone working through a mouse with oven mitts on. It reads the screen, guesses which input is the search box, types, guesses which button submits, then reads the screen again to see if it worked. Every guess is a chance to get it wrong.
The agent is scraping your UI because your UI is all you gave it. WebMCP fixes that: it lets a page expose its features to agents as tools, with a name, a description, and typed inputs. The agent stops guessing and calls a function.
The fix #
If the feature is already a form, add three attributes to it, plus an optional description on each input:
<form action="/newsletter/" method="get" role="search"
toolname="search_issues"
tooldescription="Search the newsletter's issues on web development, CSS, HTML and JavaScript. Opens the results page."
toolautosubmit>
<input type="search" name="q" toolparamdescription="Search keywords" aria-label="Search issues">
</form>
The browser turns the form into a tool and each named input into a parameter. Browsers without WebMCP ignore attributes they do not know, so nothing changes for anyone else.
This is the search form on the newsletter page. Results load on the same URL with ?q=, and the page’s canonical tag still points to /newsletter/, so search engines fold every results page back into the real one.
The attributes #
toolname: what the agent calls. Use a verb, snake_case, unique on the page.tooldescription: what the tool does and what happens after. The model reads every word, so keep it short and include limits it needs to know.toolparamdescription: on each input, what the value means.toolautosubmit: lets the agent submit without the user clicking. Use it for searches and filters. Leave it off for anything that sends, buys, or deletes, so the user confirms it.
Features that are not forms #
A calculator, a generator, a configurator: register the tool from JavaScript instead. This is a simplified version of the one running on my border radius generator:
if (document.modelContext) {
const controller = new AbortController();
document.modelContext.registerTool({
name: 'generate_border_radius',
description: 'Set each corner of the preview and return the CSS. Max is 150px.',
inputSchema: {
type: 'object',
properties: {
topLeft: { type: 'number', minimum: 0, maximum: 150 },
topRight: { type: 'number', minimum: 0, maximum: 150 },
},
required: ['topLeft'],
},
execute: ({ topLeft, topRight = topLeft }) => {
setCorners(topLeft, topRight);
return `border-radius: ${topLeft}px ${topRight}px;`;
},
}, { signal: controller.signal });
}
Three details make this work well:
executecalls your existing code:setCornersis the same function the sliders call, so when an agent uses the tool, the user watches the preview change. One code path for humans and agents.- Removing a tool is
controller.abort(): call it when the component unmounts or the feature is no longer on screen. - Return a string: Chrome currently converts whatever
executereturns into a string. Return the text you want the agent to read, orJSON.stringifyan object yourself.
Turning it on for real visitors #
WebMCP is still an experiment in Chrome. Shipping the attributes is not enough: without an origin trial token, document.modelContext only exists for people who flipped a flag.
- Register your domain for the WebMCP trial at developer.chrome.com/origintrials .
- Add the token to your
<head>:
<meta http-equiv="origin-trial" content="YOUR_TOKEN">
The current trial runs on Chrome 149 to 162 and ends on March 30, 2027. For localhost, enable chrome://flags/#enable-webmcp-testing instead.
Test it in the console #
On a page with the tool above registered, run:
const tools = await document.modelContext.getTools();
const tool = tools.find((t) => t.name === 'generate_border_radius');
await document.modelContext.executeTool(tool, { topLeft: 40, topRight: 0 });
// 'border-radius: 40px 0px;'
If you get “Failed to parse input arguments”, your Chrome version wants the input as a string. Chrome 152 did. Pass JSON.stringify({ topLeft: 40, topRight: 0 }) instead.
Where this comes up #
- Site search: the easiest win, and usually a form already.
- Filters and sorting: “show me red shoes under $100” becomes one call instead of six clicks.
- Calculators and generators: pricing estimators, shipping calculators,CSS tools .
- Booking and contact forms: with
toolautosubmitleft off, so the human stays in charge.
Why this matters #
- It costs almost nothing: three attributes on a form, invisible to every visitor who does not need it.
- Agents stop breaking on your redesigns: a tool has a contract, a screen layout does not.
- It is early, so feature detect: the API can still change. Never let your page depend on
document.modelContextexisting.
Want to see more? Several generators on markodenic.com/tools expose WebMCP tools, like the border radius generator. I wrote up how I added all seven, including the Lighthouse checks and the rough edges I hit: I Added WebMCP to My Site. Here’s What It Took.
If you like thinking about where all this is heading, I’ve been enjoying The Next Commit, a podcast for developers about the tools, workflows, and ideas shaping the next generation of software. I’m subscribed, and it’s a good one for a commute.
Pick the search form on your site and add the three attributes. It takes five minutes.
Happy coding!
Marko