navigator.modelContext Explained: The Complete API Reference

The Imperative API of WebMCP lives on navigator.modelContext. This is the full method reference with real code you can paste into a production app today.

navigator.modelContext is the browser-side object exposed to pages that want to describe their capabilities to AI agents. Where the Declarative API uses HTML attributes on forms, the Imperative API uses JavaScript — giving you dynamic, state-aware tool registration.

Surface area

The API has four main methods. Everything else is convenience:

registerTool()

Declares a capability the agent can call. Returns a handle (or unsubscribe function depending on polyfill).

navigator.modelContext.registerTool({
  name: "searchProducts",
  description: "Search the product catalog by keyword, with optional category and price filters.",
  inputSchema: {
    type: "object",
    properties: {
      query:    { type: "string",  description: "Free-text search query." },
      category: { type: "string",  enum: ["shoes","shirts","pants","accessories"] },
      maxPrice: { type: "number",  minimum: 0 }
    },
    required: ["query"]
  },
  execute: async ({ query, category, maxPrice }) => {
    const params = new URLSearchParams({ q: query });
    if (category) params.set("cat", category);
    if (maxPrice) params.set("max", String(maxPrice));
    const res = await fetch(`/api/search?${params}`);
    const data = await res.json();
    return { results: data.items.slice(0, 20) };
  }
});

Schema design matters more than you think

Agents pick tools by matching user intent against tool description first, then inputSchema. If your description is "Searches things," no agent will pick it over a competitor's "Search our running-shoe catalog by size, brand, and color." Be specific and use natural language.

unregisterTool()

Removes a tool. Critical for SPAs — never leave tools registered after their view unmounts:

// React example
useEffect(() => {
  const handle = navigator.modelContext.registerTool({
    name: "saveDraft",
    description: "Save the current document as a draft.",
    inputSchema: { type: "object" },
    execute: () => saveDraftToServer()
  });
  return () => navigator.modelContext.unregisterTool("saveDraft");
}, []);

provideContext()

Tells the agent about the current page state. Call it on route change or when meaningful state shifts. Agents use this for disambiguation ("which product is the user looking at?") and for deciding whether a tool is currently relevant.

navigator.modelContext.provideContext({
  type: "page",
  data: {
    route: "/products/sku-1234",
    title: "Nike Pegasus 40",
    sku: "sku-1234",
    price: 129.99,
    inStock: true,
    userIsLoggedIn: true
  }
});

requestUserInteraction()

The human-in-the-loop primitive. Use it when a tool call needs explicit confirmation — payments, deletes, legal actions. The browser shows a consent UI; the agent cannot bypass it.

const confirmed = await navigator.modelContext.requestUserInteraction({
  type: "confirm",
  title: "Complete purchase?",
  description: "Your card ending 4242 will be charged $129.99.",
  action: "Purchase"
});
if (confirmed) { /* proceed */ }

SubmitEvent.agentInvoked

When an agent submits a form, the browser sets event.agentInvoked = true. Use it for analytics (segment agent vs human traffic), UX adjustments (skip confirmation modals an agent already handled), and abuse tracking. Never use it for authorization — the user's session is the authority.

form.addEventListener("submit", (event) => {
  if (event.agentInvoked) {
    analytics.track("agent_form_submit", { form: form.id });
  }
});

React integration pattern

function useWebMCPTool(tool) {
  useEffect(() => {
    if (!navigator.modelContext) return;
    navigator.modelContext.registerTool(tool);
    return () => navigator.modelContext.unregisterTool(tool.name);
  }, [tool.name]);
}

Browser support and fallbacks

Chrome 146 Canary ships the native API behind the webmcp flag. For everything else, use the @mcp-b/global polyfill. Always feature-detect before calling:

if ("modelContext" in navigator) {
  navigator.modelContext.registerTool({ /* ... */ });
}

Common gotchas

For the bigger picture, see the full implementation guide, the security model, and industry use cases.

Frequently Asked Questions

Is navigator.modelContext the same across browsers?

The spec is — the implementation is not. Chrome 146 Canary is the only native implementation as of April 2026. Other browsers will follow; in the meantime use the polyfill.

Can I register tools before the page is ready?

You can, but you usually shouldn't. Register tools when the UI they map to is mounted so the agent doesn't call actions the user can't see.

How do I test my tools without building an agent?

Open Chrome Canary DevTools → Application → WebMCP panel. You can see registered tools, inspect their schemas, and invoke them manually.

What happens if execute() throws?

The agent receives the error and typically surfaces it to the user. Always throw meaningful messages, and return structured errors (e.g., { success: false, reason: "out_of_stock" }) instead of raw exceptions when possible.

Can tools call other tools?

Yes, from within execute() you can call anything — including other registerTool handlers. Just be careful about infinite loops and about exposing internal tools you didn't mean to chain.

KP

A decade-plus building in technical SEO, AEO, and AI-driven growth. Founder of SEOsmoHub, creator of WebMCP Checker, and publisher across a portfolio of content sites including topinlists.com. Writes about the open web at kulbhushanpareek.com.

Is your site ready for AI agents?

Run a free WebMCP readiness audit in 15 seconds — no signup.

Run Free Audit →