WebMCP document.modelContext Tutorial: Build an Agent-Ready Site
Learn how to use the current WebMCP document.modelContext API to expose website actions as structured tools for browser AI agents, with testing and security patterns.
On this page
A website can now expose its own actions as structured tools instead of making an AI agent guess which button, field, or JavaScript handler to use. Microsoft says its Edge implementation of WebMCP is ready for testing, while the current WebMCP draft defines document.modelContext as the browser API for registering tools that agents can invoke. :contentReference[oaicite:0]{index=0}
This WebMCP document.modelContext tutorial builds a small agent-ready website with a real JavaScript tool, shows how the browser discovers it, explains the current API shape, and covers the security decisions you need before exposing actions that change user data.
Why WebMCP uses document.modelContext instead of page scraping
A traditional browser agent has to infer what a website can do from its visible interface. It may inspect headings, buttons, forms, and page structure before deciding which interaction to perform. WebMCP changes the contract by allowing the site to publish structured tools with a name, description, JSON Schema input definition, and execution function. The agent can then work with an explicit capability instead of reconstructing the site's internal logic from its interface.
The current specification describes WebMCP as a JavaScript interface for exposing web application functionality to AI agents, including browser-based agents and in-page agents. It is still a Draft Community Group Report rather than a W3C Standard, so the API can change and should be isolated behind a small integration layer in production code.Β
The API location also matters because older examples can be misleading. The current specification uses document.modelContext, and recent Edge documentation lists WebMCP as an experimental feature available for testing. If a tutorial starts with an older API surface, check its publication date before copying the code.Β
Set up a page that can expose a WebMCP tool
You do not need to rebuild the website around WebMCP. The useful pattern is to take an action your application already performs and expose a thin tool around it. For this tutorial, imagine a small task application with an existing addTask() function. The human interface can continue using its normal form and button while WebMCP provides an additional structured path for an agent.
Use a secure context when testing the API, such as a local development server or a normal HTTPS deployment. WebMCP's current specification requires a secure context and also integrates with the browser's permissions policy through the tools feature. That means the API is not simply an unrestricted global function that any page can invoke.Β
const mc = document.modelContext;
if (!mc) {
console.log("WebMCP is not available in this browser.");
}Keeping the feature detection separate from the rest of your application is worthwhile because WebMCP remains experimental. Your normal website should still work when the API is unavailable.
Register your first tool with document.modelContext
The imperative WebMCP API uses registerTool(). The current draft requires a tool name, description, and execution callback, while the input schema describes the parameters the tool accepts. The description is especially important because it tells an agent what the tool does and when it should be used. ([Web Machine Learning][1])
Here is a complete example that exposes a task-creation operation. The tool calls an existing application function rather than duplicating the application's business logic.
const mc = document.modelContext;
if (mc) {
await mc.registerTool({
name: "add-task",
title: "Add task",
description: "Add a new task to the user's task list.",
inputSchema: {
type: "object",
properties: {
title: {
type: "string",
description: "The title of the task to add."
}
},
required: ["title"],
additionalProperties: false
},
async execute({ title }) {
await addTask(title);
```
return {
content: [
{
type: "text",
text: `Added task: ${title}`
}
]
};
}
```
});
}There are four pieces worth understanding. name gives the tool a stable identifier, description explains its purpose to an agent, inputSchema restricts the expected arguments, and execute performs the actual operation. The specification allows the execution callback to be asynchronous, so it can wait for existing application code or network requests before returning its result.Β
The current draft also limits tool names to 128 characters and allows ASCII letters and numbers plus underscores, hyphens, and periods. Keeping names short and descriptive makes the exposed interface easier to inspect and maintain.Β
Make the tool description precise enough for an agent
A technically valid tool can still be a poor agent interface if its description is vague. βTask toolβ tells an agent almost nothing, while βAdd a new task to the user's task listβ identifies the action and its purpose. The same principle applies to parameters: title with a description is more useful than an unexplained string field.
Think about the description as part of your application's interface contract. Say what the tool does, what information it needs, and whether it changes state. Do not put unrelated instructions, hidden prompts, or business rules into the description. The specification explicitly identifies tool descriptions and tool responses as security-relevant surfaces because agents can encounter untrusted or misleading content.Β
description: "Search published products by name and return matching product IDs."
inputSchema: {
type: "object",
properties: {
query: {
type: "string",
description: "Product name or keyword to search for."
}
},
required: ["query"],
additionalProperties: false
}This style also gives you a cleaner boundary between what the agent decides and what your application validates. The model can select a product-search tool, but the application remains responsible for validating the actual input and enforcing access rules.
Inspect the tools your page exposes
WebMCP provides getTools() for in-page agents to retrieve registered tools. The browser's own agent can use a separate internal mechanism, so calling getTools() from DevTools should be treated as a developer inspection technique rather than proof that a particular AI product will automatically invoke your tools.
After registering the task tool, run this from the browser console:
const tools = await document.modelContext.getTools();
console.table(
tools.map(tool => ({
name: tool.name,
title: tool.title,
description: tool.description
}))
);You should see the registered add-task tool in the resulting list. The current specification says getTools() returns registered tools from the document and exposed descendant documents, subject to the relevant origin and permissions rules.
Microsoft's latest Edge developer update also points developers toward WebMCP samples and a browser extension for running and debugging WebMCP tools. Edge 153 documentation lists WebMCP among the origin-trial features that allow a site to register tools for an in-browser agent. ([Windows Blog][2])
Execute a registered tool during testing
You can also exercise a registered tool directly from JavaScript. First retrieve the tool descriptor, then pass the descriptor and an input object to executeTool(). This is useful for verifying your schema and execution code before testing an actual agent workflow.
const tools = await document.modelContext.getTools();
const addTaskTool = tools.find(
tool => tool.name === "add-task"
);
if (addTaskTool) {
const result = await document.modelContext.executeTool(
addTaskTool,
{ title: "Review WebMCP integration" }
);
console.log(result);
}The result returned by executeTool() is the stringified result of the tool execution. The specification also supports cancellation through an AbortSignal, which gives applications a way to stop a pending operation when the caller cancels it.Β
Use AbortController when a tool should be removable
WebMCP's current registration API does not require a separate unregister method. Instead, an AbortSignal can be supplied when registering the tool, and aborting that signal unregisters the tool. This is useful for pages where tools exist only while a particular component, account state, or workflow is active.Β
const controller = new AbortController();
await document.modelContext.registerTool(
{
name: "add-task",
title: "Add task",
description: "Add a new task to the user's task list.",
inputSchema: {
type: "object",
properties: {
title: { type: "string" }
},
required: ["title"],
additionalProperties: false
},
async execute({ title }) {
await addTask(title);
```
return {
content: [
{
type: "text",
text: `Added task: ${title}`
}
]
};
}
```
},
{
signal: controller.signal
}
);
// Later, remove the tool.
controller.abort();This pattern becomes particularly useful in single-page applications. A tool can be registered after a user signs in, removed after sign-out, or scoped to a part of the application without leaving stale capabilities exposed.
Mark read-only and consequential tools differently
Not every tool has the same risk. A product search may only read information, while a checkout, account deletion, booking, or money transfer changes the real world. WebMCP therefore defines annotations including readOnlyHint, untrustedContentHint, and consequentialHint. These are metadata signals about how the tool behaves; they are not substitutes for authorization or application-side safeguards.Β
await document.modelContext.registerTool({
name: "delete-task",
title: "Delete task",
description: "Delete a task from the user's task list.",
inputSchema: {
type: "object",
properties: {
taskId: {
type: "string",
description: "ID of the task to delete."
}
},
required: ["taskId"],
additionalProperties: false
},
annotations: {
readOnlyHint: false,
consequentialHint: true
},
async execute({ taskId }) {
await deleteTask(taskId);
```
return {
content: [
{
type: "text",
text: `Deleted task ${taskId}.`
}
]
};
```
}
});The consequentialHint is appropriate when an execution can produce significant, real-world, or hard-to-reverse effects. The specification gives examples such as booking a flight and transferring money. A site should still require whatever confirmation and authorization its own operation needs; declaring a tool consequential does not magically create a safe approval system. ([Web Machine Learning][1])
Do not expose every button on your website
WebMCP makes it tempting to turn every useful JavaScript function into an agent tool. That is usually the wrong starting point. Expose a small set of well-defined actions that represent meaningful user goals, such as searching products, checking an order, creating a draft, or preparing a report.
Be particularly careful with tools that accept broad text or return large amounts of page data. The current WebMCP specification discusses prompt injection, misleading tool descriptions, untrusted tool output, privacy leakage through over-parameterized tools, and agent misrepresentation of user intent. Those risks exist because an agent is not just reading your page; it can potentially invoke the capabilities you deliberately expose.Β
Write tools so the application validates permissions independently of the agent. A tool called delete-account should verify the authenticated user and authorization on the server before deleting anything. The fact that an agent successfully supplied a valid JSON object is not evidence that the requested action is authorized.
Test against the current Edge implementation, not an old WebMCP example
Microsoft's September 21 developer update says its Edge WebMCP implementation is ready for testing, while Edge 153 lists WebMCP as an origin-trial feature. The current WebMCP specification was published September 17, 2026 and remains a Community Group Draft rather than a finished web standard.Β
That status changes how you should maintain an integration. Keep your WebMCP registration code in one module, feature-detect the API, preserve the ordinary UI, and test the tool schema whenever the browser or specification changes. If an older guide uses a different API surface, compare its code with the current draft before adapting it.
The practical architecture is straightforward: your website continues to own the business logic, WebMCP describes selected capabilities in a structured form, and a compatible browser agent can use those capabilities instead of guessing how your interface works. Start with read-only tools, add carefully scoped mutations later, and treat every exposed function as a new interface that deserves the same validation and security review as any other public entry point.
Written by


