How to implement WebMCP on a website
Add WebMCP to an existing website using Chrome's origin trial, document.modelContext, validated tool handlers, and six working examples.

To set up WebMCP on your website, you need:
- A token from the Chrome origin trial for WebMCP for your domain
- A script that registers tool objects on
document.modelContext - Handlers that validate their own inputs
This post walks through the six tools surfaced through WebMCP on this site, how they were implemented, and what I found while building them.
What is WebMCP?
WebMCP is an experimental browser API being developed in the W3C Web Machine Learning Community Group, with editors from Google and Microsoft. It lets a website register JavaScript functions as tools. An agent running in the visitor’s browser can list and call them.
Despite the name, a WebMCP tool does not require a separate MCP server or remote tool endpoint. Tools run in the page and can reuse the website’s existing JavaScript, UI state, authentication, and backend APIs.
In late August 2026, OpenAI shipped WebMCP functionality to the ChatGPT app via Site Tools in the built-in browser, bringing more attention to the experimental proposal.
What do you need to get set up with WebMCP?
WebMCP is still experimental, so setup takes a few extra steps. None of them is expensive.
First, register your domain in the Chrome origin trial and add your token to your HTML <head> as a <meta> tag.
<meta http-equiv="origin-trial" content="YOUR_TOKEN">
These tokens are public by design and bound to a domain. This site’s token, when decoded to JSON, shows:
{"origin":"https://www.mattpyle.com:443","feature":"WebMCP","expiry":1794873600}
Note: All tokens are set to expire at 2026-11-17 00:00 UTC as it’s the end date of the Chrome origin trial.
Register your first WebMCP tool
To register a tool, call registerTool on document.modelContext and pass it an object with four fields: name, description, inputSchema, and a handler in the execute field.
name= what the agent callsdescription= what the model reads to decide whether to call itinputSchema= JSON Schema for the argumentsexecute= the handler
Here is an example that returns the page’s title:
<script type="module">
if (document.modelContext?.registerTool) {
await document.modelContext.registerTool({
name: 'get_page_title',
description: 'Return the title of the current page.',
inputSchema: { type: 'object', properties: {}, additionalProperties: false },
execute: async () => ({ title: document.title }),
});
}
</script>
Some things to notice about the above code sample:
- Not every browser supports WebMCP so
document.modelContextmay be undefined. Wrap it in anifto ensure compatibility. inputSchemadeclares no arguments:propertiesis empty, andadditionalProperties: falsetells the agent that no other arguments exist. A tool with inputs declares them underproperties, and the next section shows six that do.- The handler returns a plain object. The browser serialises it to JSON for the agent, and it parses the agent’s arguments before calling the handler, so the handler never touches JSON.
The six tools on this site
This site is my personal website for experimentation, learning, and writing. I have implemented WebMCP to learn for myself and share those learnings with others.
This website has four read tools and two write tools, all listed on /webmcp, where they can be run. The tools are:
| Tool | Kind | Inputs | Returns |
|---|---|---|---|
describe_site |
read | none | An object with person, site, and sections |
get_recent_writing |
read | limit, tag |
Posts with title, url, date, tags, description |
search_content |
read | query |
Results with type, title, url, snippet |
list_related_sites |
read | none | The web ring: name, url, description, status per site |
set_appearance |
write | mode |
The mode actually applied |
sign_guestbook |
write | name, message |
The written entry and its number |
How a tool on this site works
One of the tools on this site is get_recent_writing. The snippet below shows how the tool returns recent posts from this website with two optional inputs:
{
name: 'get_recent_writing',
description:
'List the most recent published articles, newest first, optionally filtered to a single tag.',
inputSchema: {
type: 'object',
properties: {
limit: {
type: 'integer', minimum: 1, maximum: 20, default: 5,
description: 'How many articles to return (1-20).',
},
tag: {
type: 'string',
description: 'Only return articles carrying this tag (case-insensitive).',
},
},
additionalProperties: false,
},
execute: async (args = {}) => {
const limit = Math.min(Math.max(args.limit ?? 5, 1), 20);
const tag = args.tag?.toLowerCase();
// posts is the site's article index
return {
posts: posts
.filter((post) => !tag || post.tags.includes(tag))
.slice(0, limit),
};
},
}
Two things this implementation does, and why:
- Every property, like the tool itself, carries its own
description. The agent decides when and how to call a tool from its description, so the property descriptions are how the agent interprets the argument. inputSchemais read by the agent, not the browser. It tells the agent what arguments exist and what values they take, and the browser passes the agent’s arguments toexecutewithout checking them. So the handler here clampslimitto 1 to 20 and falls back to 5 when it is missing, because the schema describes the inputs and only the handler enforces them.
How does an agent call a WebMCP tool?
To use a WebMCP tool, an agent makes two calls to the browser. getTools() lists the registered tools on the site, and executeTool() runs one. These calls go through five steps:
- The agent asks for the tool list.
getTools()returns every registered tool with itsname,description, andinputSchema. The agent reads the descriptions against what the user asked for and picks one. - The agent calls
executeTool()with the tool object and the arguments as a JSON string. - The browser parses the string into an object and calls the tool’s
executewith it. - The browser serialises whatever
executereturns back into a JSON string. - The agent reads the result and decides what to do next: answer the user, call another tool, or stop.
Here is the sequence on this site. A Claude session, driving the browser through an extension, was asked to sign the guest book:

The agent called getTools() and saw six tools, chose sign_guestbook, and called executeTool() with:
{"name":"Claude","message":"Signed by a Claude agent over WebMCP during a recorded demo, 2026-08-08."}
The handler wrote entry #009, badged it as signed by an agent, and returned a message saying so and where to find it.
See WebMCP work
When I first implemented WebMCP tools, they were easiest to try from the browser console. Open DevTools on a page that registers tools, list them, and run one.
Try it on this website! You’ll need Chrome 149 or later for the browser to use the registered token on this domain.
In the DevTools console:
- Check that
typeof document.modelContextreturns'object'so DevTools can reach it. - Run
await document.modelContext.getTools()to return the same list of tools that an agent reads.

- Execute a tool via these two lines of JavaScript:
const tool = (await document.modelContext.getTools()).find(t => t.name === 'set_appearance');
await document.modelContext.executeTool(tool, '{"mode":"retro"}');
- Watch the site respond to the tool call and change the website’s CSS. Enjoy the retro web!
Test WebMCP locally
To try the same on your own site before it has a token, Chrome needs a flag instead.
Navigate to chrome://flags/#enable-webmcp-testing, enable it, and relaunch.
For further testing, I would recommend the Model Context Tool Inspector Chrome extension which allows you to inspect, monitor, and execute tools manually or with Gemini directly in the browser (download and use at your own risk).
What ChatGPT’s Site Tools changed
You could use Chrome DevTools, you could use an extension with a Gemini token to try WebMCP functionality, or now you could take the easier path that OpenAI added to the ChatGPT desktop app in late August 2026.
The video above shows the ChatGPT desktop app, running the built-in browser, and a set of prompts to open this website (mattpyle.com), check for WebMCP tools, and execute them. Site Tools available on a valid website are displayed with an arrow next to the URL which turns blue when tools are being executed. The demo includes getting the most recent writing posts, turning the website CSS to retro mode, and signing the guest book.
The Site Tools feature launch has put significant visibility and momentum behind WebMCP at a time I was questioning whether it’d take off. To support this feature launch, OpenAI announced a WebMCP Challenge, in collaboration with Netlify, to award creative implementations with prizes worth up to $36k. The competition closed on September 3, 2026.
WebMCP gotchas
WebMCP origin trial tokens expire
Every token for this trial expires on 2026-11-17. That is the token’s deadline, not the feature’s. Chrome may ship WebMCP to everyone, extend the trial with a renewal, or end it. If it is extended, you get an email and must deploy a new token, because Chrome ignores expired tokens and the tools stop registering without an error. Put the date in your calendar.
WebMCP tools only reach agents inside a browser
The tools live in the page, so an agent has to load the page in a browser that supports them. Today that is Chrome with a token and ChatGPT’s built-in browser. Crawlers, MCP clients, and agents calling your site over HTTPS never see them, which is why this site also serves llms.txt and an MCP endpoint.
Site Tools are not available on ChatGPT Enterprise
ChatGPT’s Site Tools are off for Enterprise and Edu workspaces. I found this testing this site’s tools with a colleague on our IT-approved ChatGPT tooling. If your audience is at work, most of them cannot use Site Tools yet. You also need to use 5.6 Sol or 5.6 Terra.
Make WebMCP write tools safe to repeat
An agent can call a write tool twice: a retry after a slow response, or a user asking again. sign_guestbook on this site treats an identical name and message as a replay and returns duplicate: true without writing again. A write tool needs an idempotency check, the same as any API, and the same as a Temporal Activity, where I spend my working day.
Make your WebMCP tool responses descriptive
Your tool’s response is the copy the user reads. set_appearance returns {"mode":"retro","message":"Retro mode is now on for this browser."} rather than {"ok":true}, and in the recording above ChatGPT relayed it almost word for word. Say what happened and where.
Play with WebMCP (including on this site)
I had a lot of fun experimenting with WebMCP and learning about what is possible. Recent developments over the past few weeks have increased the momentum around the API and I suspect adoption will continue to ramp up.
The six tools are live on /webmcp, which is my living reference for what this site registers. Each tool is documented and includes a Run button, so you can call them with no setup.