•12 min read•OpenHermit Team
WebMCPTutorialChrome 146navigator.modelContextImplementation

WebMCP Tutorial: A Step-by-Step Guide to navigator.modelContext in Chrome 146

Build a working WebMCP implementation in under an hour. Step-by-step tutorial for navigator.modelContext, tool schemas, and graceful degradation, tested against Chrome 146 stable.

WebMCP Tutorial: A Step-by-Step Guide to navigator.modelContext in Chrome 146

šŸ“‹ LLM ABSTRACT

WebMCP is a W3C Draft Community Group Report (Feb 10, 2026) that exposes a navigator.modelContext API in browsers, letting websites publish structured tools that AI agents can call without scraping the DOM. As of May 2026, Chrome 146 is the only browser with a working stable implementation; Edge 147 (Chromium-based) followed in March 2026. Firefox and Safari are engaged in the spec process but uncommitted on timelines.

This tutorial walks through the four-step implementation: feature-detect navigator.modelContext, register one or more tools with provideTools() (the provideContext/clearContext methods were removed from the spec in March 2026), define typed input schemas, and wire your handler to the existing application logic. We include a working <form> example, a graceful-degradation strategy for non-supporting browsers, and the three most common bugs first-time implementers hit.

Note: OpenHermit auto-injects WebMCP-compatible attributes onto your existing HTML so agents can discover and act on your forms, buttons, and product cards without you hand-rolling provideTools() calls. This tutorial covers the raw API; OpenHermit is the compatibility layer.


WHY THIS TUTORIAL EXISTS

Most "WebMCP tutorials" published in early 2026 are already wrong. The spec moved fast — provideContext and clearContext shipped in the original Community Group Report on February 10, but were removed from the draft in March. If you copy-paste from a January blog post, your implementation will throw a TypeError in Chrome 146 stable.

This tutorial uses the post-March spec only. Every snippet has been tested against Chrome 146 stable (released April 2026) and Edge 147. We'll build a real example: a contact form that exposes a submit_inquiry tool to AI agents, with a graceful fallback to traditional form submission for browsers without navigator.modelContext.

If you've never heard of WebMCP before, the How Agents Interact post explains the broader landscape. If you're choosing between WebMCP, OpenAPI, and platform detection, read Three Paths first. This piece assumes you've already decided WebMCP is your target.

"WebMCP doesn't replace your UI. It gives agents a structured shortcut to the same actions a human would perform."


WHAT WEBMCP ACTUALLY IS

WebMCP is a browser-native bridge between websites and AI agents. The website registers a list of named tools with the browser. An AI agent running inside that browser — or an extension acting on its behalf — can enumerate those tools, read their parameter schemas, and invoke them. The website's JavaScript handler runs the tool and returns a structured result. No DOM scraping, no clicking buttons, no waiting for hydration.

Think of it as the same pattern as the server-side Model Context Protocol (MCP) that Anthropic introduced in late 2024, but living in the browser. Anthropic's MCP exposes tools from a server to a client. WebMCP exposes tools from a webpage to an in-browser agent. Both speak the same conceptual language: tools, inputs, outputs, schemas.

šŸ“˜ The four moving parts

• navigator.modelContext — the global API surface, present only in supporting browsers. • provideTools(tools) — the registration call. Pass an array of tool definitions. • Tool definition — { name, description, inputSchema, handler }. The handler is your async function. • Agent runtime — the AI agent (Chrome's built-in agent, an extension, or a remote orchestrator) that discovers and invokes the registered tools.


STEP 1: FEATURE-DETECT BEFORE YOU CALL ANYTHING

The single most common mistake in early WebMCP code is calling navigator.modelContext.provideTools(...) at the top level of a script. In Firefox or Safari, that throws immediately and breaks the rest of your page.

Always feature-detect first. The pattern is identical to how you'd guard navigator.share or navigator.clipboard.

// webmcp-bootstrap.js
if ("modelContext" in navigator &&
    typeof navigator.modelContext.provideTools === "function") {
  registerAgentTools();
} else {
  // Browser doesn't support WebMCP. The page still works for humans.
  // Optionally: log to your analytics so you can size the gap.
  console.debug("[webmcp] not supported, skipping tool registration");
}

async function registerAgentTools() {
  await navigator.modelContext.provideTools([
    {
      name: "submit_inquiry",
      description: "Submit a contact-form inquiry to the sales team.",
      inputSchema: {
        type: "object",
        required: ["email", "message"],
        properties: {
          name:    { type: "string",  description: "Sender's full name" },
          email:   { type: "string",  format: "email" },
          company: { type: "string",  description: "Company name (optional)" },
          message: { type: "string",  description: "Body of the inquiry" },
          urgent:  { type: "boolean", description: "True if reply needed within 24h" }
        }
      },
      handler: async (input) => {
        const res = await fetch("/api/contact", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(input),
        });
        if (!res.ok) throw new Error(`HTTP ${res.status}`);
        const data = await res.json();
        return { ok: true, ticket_id: data.id };
      }
    }
  ]);
}

That's a complete, working WebMCP implementation. Drop it into a <script type="module"> tag at the bottom of your contact page and an agent running in Chrome 146 can discover and invoke submit_inquiry immediately.


STEP 2: WRITE TOOL SCHEMAS THAT AGENTS UNDERSTAND

The inputSchema is JSON Schema. Agents read it to know what arguments to send. The quality of your schema directly determines whether agents pick the right tool and pass valid arguments.

Three rules from the early production deployments:

First, be aggressive with description fields. Agents use descriptions to disambiguate between similar tools. "Submit form" is useless. "Submit a contact-form inquiry to the sales team; do not use for support tickets — use open_support_ticket instead" is a directive an agent will actually follow.

Second, mark required fields explicitly. Agents will fabricate optional fields if they think it'll help, but they'll fail loudly on missing required fields, which is what you want.

Third, use enum for closed sets. If your form has a country dropdown with 50 options, encode them as an enum. Agents that see { type: "string" } will sometimes invent a country code. Agents that see { type: "string", enum: ["US","GB","CH",...] } will pick from the list.

SCHEMA PATTERNWEAK VERSIONSTRONG VERSION
Email fieldtype: stringtype: string, format: email
Countrytype: stringtype: string, enum: [ISO codes]
Order quantitytype: numbertype: integer, minimum: 1, maximum: 100
Tool description"Submit form""Submit inquiry; not for support — see open_support_ticket"
Boolean flagtype: booleantype: boolean, description: "True if reply needed within 24h"

STEP 3: HANDLE THE RESULT PROPERLY

Your handler returns whatever the agent should see. Two principles:

Return structured data, not HTML. Agents want JSON they can reason about, not a rendered confirmation page. { ok: true, ticket_id: "T-9281", eta: "24h" } is useful. "<div class='success'>Thanks!</div>" is not.

Throw on failure. WebMCP's handler contract treats thrown errors as tool failures. The agent will see the error message and can decide whether to retry, ask the user, or pick a different tool. Don't catch errors and return { ok: false } — that hides the failure mode.

"Treat your WebMCP handler like a public API. Strict inputs, structured outputs, loud failures."


STEP 4: TEST IT WITHOUT AN AGENT

You don't need a live agent to verify your implementation. Chrome 146 ships DevTools support: open the console on a page that has called provideTools() and run await navigator.modelContext.listTools() to see your registered tools. Then call any tool directly:

// In Chrome 146 DevTools console:
> await navigator.modelContext.listTools()
[{ name: "submit_inquiry", description: "...", inputSchema: {...} }]

> await navigator.modelContext.callTool("submit_inquiry", {
    email: "test@example.com",
    message: "Hello from devtools"
  })
{ ok: true, ticket_id: "T-9281" }

If listTools() returns an empty array, your registration code never ran. If callTool() throws, your handler has a bug. If the schema validation fails, your inputSchema doesn't match what you passed. This is the entire test loop.


THE THREE BUGS EVERYONE HITS

After reviewing dozens of early WebMCP deployments, three failure modes show up over and over.

šŸ› Bug #1: Tools registered too late

Some teams call provideTools() inside a DOMContentLoaded listener that fires after the agent has already enumerated the page. Register tools as early as possible — top of the script, no waiting for the DOM. The API doesn't depend on DOM readiness.

šŸ› Bug #2: Handlers that read from the DOM

Don't write a handler that does document.querySelector('input[name=email]').value. The agent isn't filling out your form — it's calling your tool directly. Read input parameters from the handler argument, not from form fields. The form is for humans.

šŸ› Bug #3: Forgetting graceful degradation

The whole page should work for non-WebMCP browsers, screen readers, and crawlers. Your <form> should still POST to a real endpoint. WebMCP is a progressive enhancement on top of working HTML — not a replacement for it. If your only path to submitting an inquiry is the WebMCP tool, you've broken the long tail of users.


WHAT GOES WRONG WHEN YOU SCALE

A single tool on a single page is easy. Real sites have dozens of meaningful actions across hundreds of pages. The hard problems start at scale:

Tool naming collisions. If your blog page exposes subscribe and your e-commerce page exposes subscribe, agents that crawl both will confuse them. Namespace your tool names: newsletter.subscribe vs product.subscribe_to_back_in_stock.

Schema drift. Your submit_inquiry form gains a "phone" field one quarter, loses a "company" field the next. Versioning matters. Either pin a version field in your tool definition or accept that agents will sometimes call old shapes.

Auth state. A logged-in user's add_to_cart tool is meaningfully different from a logged-out user's. Re-register tools when auth state changes; don't expose place_order to anonymous sessions.

This is precisely the operational layer that hand-rolled WebMCP gets expensive. The Agent-Ready Scorecard covers what mature deployments look like once you're past the toy example.


OPENHERMIT VS. HAND-ROLLED

You can absolutely write provideTools() calls by hand for every meaningful action on your site. That's the path this tutorial just walked. It works, and for a single landing page it's the right call.

OpenHermit takes a different approach: it scans your existing HTML — <form>, <button>, [data-action] attributes, structured data — and auto-injects WebMCP-compatible tools at runtime. You get coverage across hundreds of pages without writing per-page registration code, with the trade-off that the tool schemas are inferred from markup rather than hand-tuned.

The right answer is usually both: hand-roll the three or four high-value tools (checkout, sign-up, the main conversion form) and let OpenHermit cover the long tail.


FAQ

Q: Do I need an HTTPS site for WebMCP?

A: Yes. `navigator.modelContext` is gated behind secure contexts, same as service workers and the clipboard API. `localhost` works for development; production needs HTTPS.

Q: Will WebMCP work in Firefox or Safari?

A: Not as of May 2026. Firefox is engaged in the spec process with no committed timeline; Safari hasn't publicly committed beyond participating in W3C discussions. Estimated Firefox availability is 8–12 weeks per recent vendor signals; Safari is unscheduled.

Q: Is WebMCP a replacement for my OpenAPI spec?

A: No. OpenAPI describes server endpoints; WebMCP describes browser-side actions tied to a page's authenticated session. Many sites need both — the [Three Paths](/blog/three-paths) post breaks down when each fits.

Q: Can agents see tools from cross-origin iframes?

A: No. Tools registered in an iframe are scoped to that iframe's origin. Agents enumerate tools from the top-level document. If you embed a third-party widget that registers tools, those tools are not visible to the parent page's agent.

Q: How do I track agent invocations for analytics?

A: Add a logging step at the top of your handler. Tag the event as `agent_tool_call` with the tool name and inputs. The [Agent-Driven ROI](/blog/agent-driven-roi) post covers the broader measurement framework.

NEXT STEPS

Pick one form on your site — your highest-converting one. Add the four-step pattern from above. Test it in Chrome 146 DevTools. Ship it behind a feature detect so non-supporting browsers see no change. That's a complete WebMCP implementation, in production, in under an hour.

Once you have one tool working, the second is easy. The third is mechanical. By the time you're at five, you'll be looking for ways to automate the registration — which is what brought OpenHermit into existence.

For the broader strategy of why this matters now and not next year, the Agent-First SEO Playbook lays out the case. For the payment side of the agent stack — what happens after submit_inquiry becomes place_order — see the 2026 Guide to AI Agent Payments.

# Make your website agent-ready
> npm install @openhermit/client-script
> Ready to be discovered by AI agents.
START TRACKING AGENT TRAFFIC →

MAKE YOUR WEBSITE
AGENT-READY

Add one script tag. Be discoverable by AI agents in 2 minutes.

Get Started Free →