W3C DraftMarch 2026
Complete quick reference to the W3C navigator.modelContext API - register JavaScript functions as AI-callable browser tools.
≈ 8 min read
W3C Community Group Report
9 March 2026
What Is WebMCP?
The browser API that turns your web app into an AI-callable tool server
The Core Idea
Web pages that use WebMCP can be thought of as MCP servers that implement tools in client-side script instead of on a backend server. AI agents invoke your JavaScript functions directly, reusing your existing frontend logic with no backend required.
// WebMCP lives on the Navigator object navigator.modelContext.registerTool({ name: "searchProducts", description: "Search the product catalog by keyword and category", inputSchema: { type: "object", properties: { query: { type: "string" } } }, execute: async (input) => await searchCatalog(input.query) });
The Problem Today
UI Actuation (Screenshots)
- Screenshots cost ~2,000 tokens each
- Fragile to layout changes
- Slow multi-step interactions
- Agents guess what to do
Backend-only MCP
- No shared UI context with user
- Auth/session must be managed separately
- Can't reuse frontend logic
The WebMCP Solution
Runs client-side in JavaScript - no backend needed
Reuses your existing frontend business logic
User and agent share the same live page
Session/auth is naturally shared
Structured JSON responses - low token cost
Browser enforces security boundaries
Key Terminology
Agent
An autonomous LLM-based assistant that understands user goals and takes actions on their behalf via chat interfaces.
Model Context Provider
A browser tab navigated to a page that uses the WebMCP API to provide tools to agents.
Tool Definition
A struct with: name, description, inputSchema, execute steps, and a readOnlyHint annotation.
Actuation
An agent interacting with a webpage by simulating user input: clicking, scrolling, typing. WebMCP replaces this.
Browser's Agent
An agent provided by or through the browser itself - built-in, or via a browser extension/plug-in.
Backend Integration
An API integration where an AI platform talks directly to a service's backend without a live UI. WebMCP complements, not replaces, these.
Goals
Human-in-the-loop - users retain visibility and control
Simplify AI integration - structured tools vs UI scraping
Minimize dev burden - reuse existing JS code
Improve accessibility - standardized tool access for AT
Non-Goals
Headless browsing - not for no-human-present scenarios
Autonomous agents - use A2A protocol instead
Replacing backend MCP - WebMCP is complementary
Replacing human UI - agent tools augment, not replace
Site discoverability - discovery is out of scope for now
Core API
The official W3C WebIDL interfaces
Navigator Extension
WebMCP extends the browser's Navigator interface with a single new attribute:
// Official W3C IDL partial interface Navigator { [SecureContext, SameObject] readonly attribute ModelContext modelContext; };
SecureContext
Only available on HTTPS pages. Will not work on HTTP.
SameObject
The same ModelContext instance is always returned per Navigator - it's a singleton.
ModelContext Interface
[Exposed=Window, SecureContext] interface ModelContext { undefined registerTool(ModelContextTool tool); undefined unregisterTool(DOMString name); };
registerTool(tool)
Registers a single tool with the browser. Throws InvalidStateError if:
- A tool with the same name already exists
- The inputSchema is invalid
- The name or description is an empty string
unregisterTool(name)
Removes the named tool. Throws InvalidStateError if the tool does not exist.
ModelContextClient Interface
[Exposed=Window, SecureContext] interface ModelContextClient { Promise<any> requestUserInteraction( UserInteractionCallback callback ); }; callback UserInteractionCallback = Promise<any> ();
requestUserInteraction - pauses tool execution to show the user a dialog, confirmation, or other interaction. The agent waits for the user to respond before the tool continues.
Complete IDL Index (Official)
partial interface Navigator { [SecureContext, SameObject] readonly attribute ModelContext modelContext; }; [Exposed=Window, SecureContext] interface ModelContext { undefined registerTool(ModelContextTool tool); undefined unregisterTool(DOMString name); }; dictionary ModelContextTool { required DOMString name; required DOMString description; object inputSchema; required ToolExecuteCallback execute; ToolAnnotations annotations; }; dictionary ToolAnnotations { boolean readOnlyHint = false; }; callback ToolExecuteCallback = Promise<object> (object input, ModelContextClient client); [Exposed=Window, SecureContext] interface ModelContextClient { Promise<any> requestUserInteraction(UserInteractionCallback callback); }; callback UserInteractionCallback = Promise<any> ();
Tool Definition
All fields of ModelContextTool and how to use them
| Field | Type | Required | Description |
|---|---|---|---|
name | DOMString | Yes | Unique identifier used by agents to reference the tool. Must not be empty. Must be unique - registering a duplicate name throws InvalidStateError. |
description | DOMString | Yes | Natural language description. Helps the agent understand when and how to use this tool. Must not be empty. Write it as if explaining to a person. |
inputSchema | object | No | A JSON Schema object describing the expected input parameters. Validates what the agent passes to your execute function. Invalid schemas throw InvalidStateError. |
execute | ToolExecuteCallback | Yes | The callback invoked when an agent calls the tool. Receives input (agent params) and client (for user interaction). Can be async. |
annotations | ToolAnnotations | No | Optional metadata about the tool's behavior. Currently contains readOnlyHint. Helps agents decide when it's safe to call the tool. |
inputSchema Examples
Simple string param
{
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Search term"
}
},
"required": ["query"]
}Multiple typed params
{
"type": "object",
"properties": {
"productId": { "type": "string" },
"quantity": { "type": "integer", "minimum": 1 },
"size": {
"type": "string",
"enum": ["S", "M", "L", "XL"]
}
}
}execute & annotations
execute callback signature
// input = agent-supplied params (matches inputSchema) // client = ModelContextClient for user interaction async (input, client) => { // Do work... return { result: "data" }; // must return object }
ToolAnnotations - readOnlyHint
annotations: { readOnlyHint: true } When readOnlyHint: true, the agent knows the tool won't modify any state. The browser/agent can safely call it without user confirmation prompts.
Internal Data Model
Model Context (struct)
A model context is a struct with one item:
tool map - an ordered map whose keys are tool names (strings) and whose values are tool definition structs.
Tool Definition (struct)
namestring - unique identifier
descriptionstring - natural language
input schemastring - stringified JSON Schema
execute stepssteps - invoked when tool is called
read-only hintboolean - initially false
Code Examples
Real-world patterns for registering and managing WebMCP tools
1
Basic Tool Registration
Register a read-only search tool that returns structured data to agents:
// Requires HTTPS (SecureContext) navigator.modelContext.registerTool({ name: "searchProducts", description: "Search the product catalog by keyword. Returns a list of matching products with name, price, and availability.", inputSchema: { type: "object", properties: { query: { type: "string", description: "The search keyword or phrase" }, category: { type: "string", enum: ["all", "clothing", "electronics", "books"], description: "Product category to filter by" } }, required: ["query"] }, execute: async (input) => { // Reuses your existing frontend search logic! const results = await productStore.search(input.query, input.category); return { products: results, total: results.length }; }, annotations: { readOnlyHint: true } // Does not modify state });
2
State-Modifying Tool
Tool that changes page state (no readOnlyHint):
navigator.modelContext.registerTool({ name: "addToCart", description: "Add a product to the user's shopping cart", inputSchema: { type: "object", properties: { productId: { type: "string" }, quantity: { type: "integer", minimum: 1 } }, required: ["productId", "quantity"] }, execute: async (input) => { await cart.add(input.productId, input.quantity); return { success: true, cartTotal: cart.total }; } // readOnlyHint omitted → defaults to false });
3
User Interaction (Confirmation)
Pause execution to get user confirmation:
execute: async (input, client) => { // Pause and ask user to confirm const confirmed = await client.requestUserInteraction( async () => { // Show your UI confirmation dialog return await showConfirmDialog( `Delete ${input.filename}?` ); } ); if (!confirmed) { return { cancelled: true }; } await deleteFile(input.filename); return { deleted: true }; }
4
Dynamic Registration
Register/unregister tools based on app state:
// Register when user logs in function onUserLogin(user) { navigator.modelContext.registerTool({ name: "getUserOrders", description: "Get the current user's order history", execute: async () => ({ orders: await api.getOrders(user.id) }), annotations: { readOnlyHint: true } }); } // Unregister when user logs out function onUserLogout() { navigator.modelContext.unregisterTool("getUserOrders"); }
5
Updating a Tool
Tools cannot be re-registered - unregister first:
// ❌ Throws InvalidStateError - name already exists navigator.modelContext.registerTool({ name: "myTool", ... }); navigator.modelContext.registerTool({ name: "myTool", ... }); // ✅ Correct - unregister first, then re-register navigator.modelContext.unregisterTool("myTool"); navigator.modelContext.registerTool({ name: "myTool", ... });
6
Registering Multiple Tools
Register all your app's tools on page load:
const tools = [ { name: "getProducts", description: "...", execute: getProducts, annotations: { readOnlyHint: true } }, { name: "addToCart", description: "...", execute: addToCart }, { name: "checkout", description: "...", execute: startCheckout }, ]; tools.forEach(tool => navigator.modelContext.registerTool(tool) );
Testing & Debugging
Inspect, execute, and verify your WebMCP tools during development
navigator.modelContextTesting
Chrome 146+ · Behind flag
A second Navigator attribute available in Chrome 146+ when the WebMCP for testing flag is enabled. It is a developer-only surface - not available in production - for inspecting and manipulating all registered tools without going through the page's own JavaScript.
What it is
A testing-only companion to navigator.modelContext. It can read back and bulk-replace every tool registered on the current page.
Who uses it
Browser extensions, DevTools panels, and automated test scripts that need to inspect or stub tools without modifying page source.
Production availability
Not exposed in stable Chrome. Only active when the #enable-webmcp-for-testing flag is on in chrome://flags.
// Enable in chrome://flags → search "WebMCP for testing" // Then available on every HTTPS page (SecureContext) // Production API - used by your app navigator.modelContext.registerTool({ name: "myTool", ... }); // Testing API - used by dev tools / test scripts const tools = await navigator.modelContextTesting.getTools(); console.log(tools); // → [{ name: "myTool", description: "...", inputSchema: {...} }]
Feature Detection
Always check for both APIs before use - neither is universally available:
// Check production API if ('modelContext' in navigator) { // Safe to call registerTool / unregisterTool navigator.modelContext.registerTool({ ... }); } // Check testing API (dev environments only) if ('modelContextTesting' in navigator) { // Safe to call getTools / provideContext / clearContext const tools = await navigator.modelContextTesting.getTools(); }
provideContext()
Replace the full set of registered tools in one atomic call - useful for test setup or stubbing:
// Replaces ALL currently registered tools await navigator.modelContextTesting.provideContext({ tools: [ { name: "stubSearch", description: "Returns mock search results", inputSchema: { type: "object", properties: { query: { type: "string" } } }, execute: async () => ({ results: ["mock-item-1"] }) } ] });
Unlike calling unregisterTool + registerTool in a loop, provideContext is atomic - no intermediate state is visible to agents during the swap.
clearContext()
Remove every registered tool at once - typically used in test teardown:
// Tear down after each test afterEach(async () => { if ('modelContextTesting' in navigator) { await navigator.modelContextTesting.clearContext(); } }); // Or reset before each scenario beforeEach(async () => { await navigator.modelContextTesting.clearContext(); // Then re-register exactly the tools this test needs navigator.modelContext.registerTool({ name: "myTool", ... }); });
Executing Tools Directly
Call a registered tool by name with custom JSON input - without an actual AI agent:
// List all registered tools first const tools = await navigator.modelContextTesting.getTools(); console.log(tools.map(t => t.name)); // → ["searchProducts", "addToCart", "checkout"] // Execute a specific tool with test input const result = await navigator.modelContextTesting .executeTool("searchProducts", { query: "shoes" }); console.log(result); // → { products: [...], total: 12 }
Model Context Tool Inspector
A Chrome extension built on navigator.modelContextTesting that provides a visual UI for everything above:
Lists all tools registered on the current page with full schema
Execute any tool with a JSON input editor
View execution output in real time
Optionally chain calls through Gemini for agent-style testing
Source: github.com/beaufortfrancois/model-context-tool-inspector
Testing API - IDL Summary
// Navigator is extended with a second attribute (dev/testing only) partial interface Navigator { [SecureContext] readonly attribute ModelContextTesting modelContextTesting; }; [Exposed=Window, SecureContext] interface ModelContextTesting { // Read back all tools currently registered on this page Promise<FrozenArray<ModelContextToolInfo>> getTools(); // Replace the entire tool set atomically undefined provideContext(ModelContextInit init); // Remove all registered tools undefined clearContext(); // Invoke a tool by name with JSON input Promise<object> executeTool(DOMString name, object input); }; dictionary ModelContextInit { required sequence<ModelContextTool> tools; // Same ModelContextTool as registerTool() };
Note: the spec for ModelContextTesting is still in early preview and subject to change. Always verify against the latest Chrome implementation when building tooling on top of it.
Canonical Use Cases
Three official examples from the W3C WebMCP explainer
Creative Applications
Graphic design platform - human + agent collaboration
Scenario
A user collaborates with a browser agent on a graphic design platform. The agent helps find templates and make design changes while the user retains full review and control. The agent surfaces hidden features (like a print ordering service) the user might not have discovered.
Example Tools
filterTemplates(description)
editDesign(instructions)
orderPrint(designId, format)
Agent discovers features the user didn't know existed
E-Commerce / Shopping
Clothing store - structured data vs screen-scraping
Scenario
A user browses a clothing store. The agent calls getDresses(size) to receive a structured JSON list of products with descriptions and images, filters them by criteria, then calls showDresses(product_ids) to update the UI - far more reliable than screen-scraping.
Example Tools
getDresses(size, color?, priceMax?)
showDresses(productIds)
addToCart(productId, size)
Structured JSON response vs thousands of screenshot tokens
Developer Tooling (Code Review)
Complex code review platform (e.g., Gerrit) - domain-specific tools
Scenario
On a complex code review platform, domain-specific tools allow an agent to retrieve test failure data and apply code suggestions in a structured way. The agent handles repetitive tasks while the developer reviews and approves changes.
Agent handles repetitive work; human reviews and approves
Example Tools
getTryRunStatuses(patchsetId)
Returns test failure data from CI
addSuggestedEdit(filename, patch)
Applies code suggestion to review
getOpenComments()
Lists all unresolved review comments
Accessibility Benefits
Traditional AT Problem
Traditional accessibility trees aren't widely implemented. Assistive technologies struggle to interact with complex web applications via DOM-level interactions.
What WebMCP Provides
Standardized higher-level application actions mapped to meaningful operations (e.g., "add todo item") rather than low-level DOM interactions.
Unified Surface
The same tools available to AI agents are available to accessibility tools - one implementation serves both audiences with no extra work.
WebMCP vs. Other Approaches
How WebMCP compares to backend MCP, UI actuation, OpenAPI, and A2A
WebMCP vs. Backend MCP Integration
| Aspect | WebMCP | Backend MCP |
|---|---|---|
| Where code runs | Client-side JavaScript in the browser | Server-side (Python, Node.js, etc.) |
| Requires page navigation | Yes - page must be open in a tab | No - always available if server is running |
| Shared UI context | Yes - user and agent share the same page | No - agent interacts without a UI |
| Auth / session | Naturally shared with user's browser session | Must be managed separately |
| Best for | Collaborative, human-in-the-loop scenarios | Autonomous, headless, server-to-server |
These are complementary, not competing. WebMCP and backend MCP serve different scenarios. Many applications will use both.
WebMCP vs. UI Actuation
| Aspect | WebMCP | UI Actuation |
|---|---|---|
| Reliability | High | Low - fragile to UI changes |
| Speed | Fast - direct function calls | Slow - multi-step |
| Dev control | Full - you define tools | None - agent guesses |
| Token cost | Low - compact JSON | ~2,000 tokens/screenshot |
vs. OpenAPI & A2A
vs. OpenAPI
OpenAPI describes HTTP-based APIs for function-calling (ChatGPT Actions, Gemini Function Calling).
WebMCP runs in the browser using JavaScript, not HTTP. Tools execute client-side and can interact with page state directly.
vs. Agent2Agent (A2A)
A2A connects AI agents to each other, providing capability advertisement, long-running interactions, and multimodal I/O.
WebMCP is for connecting AI agents to web applications with a human in the loop. A2A is for fully autonomous agent-to-agent workflows. Different problems, both needed.
Which Approach to Use?
Use WebMCP when...
- • User is actively present in tab
- • You want to reuse frontend code
- • Human oversight is required
- • Shared UI context matters
Use Backend MCP when...
- • No browser tab needed
- • Server-to-server workflows
- • Fully autonomous pipelines
- • Always-available tools
Use OpenAPI when...
- • Exposing HTTP REST APIs
- • ChatGPT/Gemini integration
- • Standard web API consumers
- • No frontend code access
Use A2A when...
- • Agent-to-agent communication
- • Long-running agent tasks
- • Multimodal agent I/O
- • No human in the loop
Security & Privacy
Trust boundaries, permission model, and open security questions
Two Trust Boundaries
When a website registers tools via WebMCP, it exposes information about its capabilities to the browser.
Browser should prompt users to grant permission and provide visibility into what tools are being exposed.
2
Agent → Site (via Browser)
When an agent wants to use those tools, the site receives untrusted input in parameters and outputs may contain sensitive user data.
Browser mediates this boundary. Users can "always allow" tool calls for trusted site+agent pairs.
HTTPS Required (SecureContext)
navigator.modelContext is decorated with [SecureContext] - it only exists on HTTPS pages.
// On HTTP - undefined console.log(navigator.modelContext); // undefined // On HTTPS - works navigator.modelContext.registerTool(...);
Check window.isSecureContext before calling any WebMCP API to gracefully handle HTTP environments during development.
Top-Level Browsing Contexts Only
Only a top-level browsing context (a browser tab) can be a Model Context Provider. iframes are excluded.
Why iframes are excluded
- Maintains a clear security boundary
- Prevents embedded content from exposing tools without the parent page's knowledge
- Embedders manage capabilities of embedded contexts
Model Poisoning Risk
The Risk
Malicious web developers could craft tools to expose content the user would not normally see, or manipulate agent behavior through carefully crafted tool descriptions and responses.
This is an open question in the spec - actively under investigation by the W3C community group.
Cross-Origin Isolation
The Concern
Data output from one app's tools could be passed as input to another app's tools. While legitimate cross-origin workflows exist, this requires careful browser mediation.
Browser Responsibility
The browser must clearly indicate which web applications are being invoked and with what data, so users can intervene in cross-origin tool chains.
Developer Security Checklist
Input Validation
- Always validate agent-supplied inputs against your inputSchema
- Treat input as untrusted (like user input from a form)
- Sanitize strings before using in queries or HTML
Sensitive Data
- Only return data the user has permission to see
- Don't expose PII in tool responses unnecessarily
- Use requestUserInteraction for destructive operations
Auth & State
- Verify user is authenticated before registering sensitive tools
- Unregister tools when user logs out
- Mark read-only tools with readOnlyHint: true
Future Roadmap
Planned extensions and open design questions from the official W3C spec
Declarative WebMCP
TODO in spec - deferred
The Concept
Register tools via HTML elements instead of JavaScript registerTool() calls. Tools would be derived from HTML forms and their associated elements.
<!-- Conceptual future syntax --> <form webmcp-tool="searchProducts" webmcp-description="Search for products"> <input name="query" type="text"> </form>
A "synthesize a declarative JSON Schema" algorithm would derive the inputSchema from the form elements automatically.
Progressive Web Apps (PWAs)
Future exploration
Always-Available PWA Tools
A PWA with an app manifest could declare tools available "offline" - before the PWA is launched. When an agent calls the tool, the host system auto-launches the PWA and navigates to the appropriate page.
This would bridge the gap between WebMCP (tab must be open) and backend MCP (always available).
Background Model Context Providers
Future exploration
No UI Required
Some tools may not require a browser UI at all. A to-do app could expose an "add item" tool that runs without showing a window - showing only a notification when triggered.
Integration Point
Could combine with the web app launch handler API for seamless background tool execution.
Tool Discovery
Out of scope now
Current Limitation
Tools are only discoverable once a page is navigated to. An agent can't know what tools a site offers without loading it first.
Future: Manifest-Based Discovery
Declarative tool definitions in the app manifest would let agents discover a site's capabilities via a simple HTTP GET - without full page navigation. Agents would still navigate to use tools.
Open Design Questions
Main Thread Performance
Tool calls run on the main thread. When agents request many tool calls in sequence or tools are computationally expensive, this may cause performance issues. Under investigation.
Permission Model Details
The exact UX for browser permission prompts - when to ask, how to phrase it, what granularity of "always allow" - needs further community input and browser vendor coordination.
iframe Capabilities
How should embedders (parent pages) manage capabilities provided to embedded iframes? The current proposal limits this to top-level contexts but future work may define delegation models.
Spec Status & Governance
Current Status
Draft Community Group Report - 9 March 2026. NOT a W3C Standard, nor on the W3C Standards Track. Under incubation.
Editors
Brandon Walderman - Microsoft
Khushal Sagar - Google
Dominic Farolino - Google
Governing Body
W3C Web Machine Learning Community Group. License: W3C Community Contributor License Agreement (CLA). First published: August 13, 2025.
Resources & Related Specs
Official sources, related work, and prior art
Official W3C Sources
Referenced Specifications
Tools & Utilities
WebMCP Inspector
Inspect & augment WebMCP tools on any page. Browse, test, and debug tools registered via navigator.modelContext directly in your browser.
Prior Art & Related Projects
MCP-B (WebMCP by MiguelsPizza)
Open-source project with similar motivation. Extends MCP with tab transports for in-page communication and extension transports for cross-extension communication. Enables tool caching and cross-site tool composition.
github.com/MiguelsPizza/WebMCP
Acknowledged as directly related prior art by the W3C spec.
OpenAPI
Standard for describing HTTP APIs. Used by ChatGPT Actions and Gemini Function Calling. Unlike WebMCP, OpenAPI tools are backend HTTP services - no browser required.
Relationship: complementary - OpenAPI for server-side, WebMCP for client-side
Agent2Agent (A2A)
Protocol for AI agent-to-agent communication. Provides capability advertisement, long-running interactions, and multimodal I/O. Addresses agent orchestration, not web app integration.
Relationship: different problem - A2A for agent mesh, WebMCP for human+agent web workflows
Key Design Rationale
Why Align with MCP?
- Any MCP-compatible agent works with minimal translation
- Developers can reuse code between WebMCP and backend MCP
- Browser can evolve MCP compatibility independently
- Web-platform security policies can be applied
Why Not Static Manifests?
- Would limit WebMCP to installed PWAs only
- A new manifest format still needs implementation
- Cannot execute code or update tools dynamically
- Dynamic registration is a critical developer capability
Why Top-Level Only?
- Clear, understandable security boundary
- Prevents iframe-based capability leakage
- Embedders manage iframe capabilities
- Simpler mental model for users and developers
More Cheatsheets
Other quick-reference guides you might find useful.