Your website already does useful things — searching products, fetching data, processing forms, running calculations. But right now, the only way an AI agent can access any of that is by rendering your page, taking a screenshot, and guessing where to click. That's expensive, slow, and breaks every time you change a button's position.
WebMCP gives your site a way to tell agents exactly what it can do. You register tools — named functions with typed parameters — and any browser-based agent can discover and call them directly. No DOM scraping. No pixel hunting. Just structured input and structured output.
WebMCP adds a new API to the browser: navigator.modelContext. Your JavaScript calls navigator.modelContext.registerTool() with a tool definition, and any AI agent running in that browser can discover and invoke it.
The tool definition includes a name, a human-readable description, a JSON Schema for inputs, and an async handler function that does the actual work. When an agent visits your page, it reads the registered tools, picks the right one based on the description, calls it with structured parameters, and gets JSON back.
The entire exchange happens inside the browser session — the user's cookies, auth tokens, and permissions all apply automatically.
chrome://flags. This is the only browser with native support right now.localhost works fine.If you need to support browsers without native WebMCP, the MCP-B polyfill provides navigator.modelContext as a drop-in shim.
Every WebMCP integration starts with navigator.modelContext.registerTool(). This function takes a single object with four properties:
name — A kebab-case identifier. Keep it short and descriptive.description — A natural language explanation of what the tool does. This is what the agent reads to decide whether to call your tool.inputSchema — A JSON Schema object describing the parameters.execute — An async function that receives validated input and returns the result.navigator.modelContext.registerTool({
name: "get-site-info",
description: "Returns basic information about this website, " +
"including the site name, page count, and contact email.",
inputSchema: {
type: "object",
properties: {}
},
execute: async () => {
return {
siteName: "My Website",
pageCount: 42,
contactEmail: "hello@example.com"
};
}
});
Most tools need parameters. WebMCP uses JSON Schema to define inputs. The browser validates parameters before calling your handler, so you don't need to do manual type checking.
inputSchema: {
type: "object",
properties: {
query: {
type: "string",
description: "Search keyword or phrase"
},
maxResults: {
type: "number",
description: "Maximum results to return (1-50)",
default: 10
}
},
required: ["query"]
}
Be specific in your property descriptions. Agents read these to understand what values to pass. "Search keyword or phrase" is better than just "query."
The execute function is where your tool does its work. It receives the validated input object and should return a JSON-serializable result. Keep it async — most tools will call a fetch or process data.
execute: async ({ query, maxResults = 10 }) => {
const response = await fetch(`/api/search?q=${encodeURIComponent(query)}&limit=${maxResults}`);
if (!response.ok) throw new Error(`Search failed: ${response.status}`);
const data = await response.json();
return {
results: data.items.map(item => ({
title: item.title,
url: item.url,
snippet: item.description
})),
totalCount: data.total
};
}
Return structured data, not raw HTML or giant blobs. Agents work best with clean, typed fields. If your backend returns 50 fields, pick the 5-10 that are actually useful and return those.
Here is a full implementation for a product search tool:
if (navigator.modelContext) {
navigator.modelContext.registerTool({
name: "search-products",
description: "Search the product catalog by keyword. " +
"Returns product name, price, availability, and URL.",
inputSchema: {
type: "object",
properties: {
query: { type: "string", description: "Search term" },
category: { type: "string", description: "Filter by category (optional)" },
maxResults: { type: "number", description: "Max results, 1-50", default: 10 }
},
required: ["query"]
},
execute: async ({ query, category, maxResults = 10 }) => {
const params = new URLSearchParams({ q: query, limit: maxResults });
if (category) params.set("cat", category);
const res = await fetch(`/api/products?${params}`);
if (!res.ok) throw new Error("Search failed");
const data = await res.json();
return {
products: data.items.map(p => ({
name: p.name,
price: `$${p.price}`,
inStock: p.inventory > 0,
url: `${location.origin}/product/${p.slug}`
})),
total: data.total
};
}
});
}
Notice the if (navigator.modelContext) guard — always check for API availability before calling it. This prevents errors in browsers that don't support WebMCP yet.
Most sites should register 2-5 tools. Think about the core actions an agent would want to perform. A restaurant site might have:
search-menu — Find menu items by name or dietary restrictionget-hours — Return operating hours for a given daycheck-reservation — Check availability for a date and party sizeEach tool gets its own registerTool() call. Keep tools focused on a single action — agents perform better when tools do one thing well.
WebMCP tools run in the page's JavaScript context, so they inherit the user's session. This means your tools can access authenticated APIs, but it also means you need to be careful about what you expose.
Read our full guide on WebMCP security and permissions for detailed patterns.
Open Chrome DevTools and run:
const tools = await navigator.modelContext.tools();
console.log(tools.map(t => t.name));
// Call a specific tool
const result = await navigator.modelContext.callTool("search-products", {
query: "laptop",
maxResults: 3
});
console.log(result);
Write integration tests using Playwright or Puppeteer with the WebMCP flag enabled. Test that your tools return expected shapes and handle edge cases (empty queries, invalid parameters, API failures).
Once your implementation is live on HTTPS, submit your site to the webmcplist.com directory. Include your site URL, a description of each tool, and the category that fits best. Submissions are reviewed before publishing.