Use WebMCP on Any Browser with a Polyfill
Short answer: WebMCP ships today only in recent Chrome, behind a testing flag — which makes it hard to build, demo, or test on the browsers your teammates and users actually run. @mcptrail/webmcp-polyfill fixes that: it installs a spec-shaped navigator.modelContext only when the browser doesn’t already have one, so your registerTool calls work everywhere. It also exposes tools() and invoke() so DevTools panels, test harnesses, and bridges can list your tools and call them without a live agent. When a real WebMCP-capable browser shows up, the polyfill steps aside and the native API takes over.
If you’re not yet sure what navigator.modelContext even is, read What is WebMCP? first. This post is about running WebMCP code now, on any browser.
The gap: WebMCP exists, but almost nowhere
WebMCP is an experimental browser API. As of mid-2026 it’s available in recent Chrome behind a flag, and most other browsers — Firefox, Safari, older Chrome — have no navigator.modelContext at all. That’s a chicken-and-egg trap: you want to instrument your site ahead of the agents, but you can’t reliably develop against an API that isn’t present on your machine.
The classic answer to “the platform API isn’t here yet” is a polyfill — a drop-in that provides the real API’s shape until the browser ships it natively. That’s exactly what webmcp-polyfill does for navigator.modelContext.
Install it — but only when it’s missing
The one rule of a good polyfill: never clobber the real thing. installModelContextPolyfill() checks for a native navigator.modelContext first and installs its own implementation only if none exists:
npm install @mcptrail/webmcp-polyfill
import { installModelContextPolyfill } from "@mcptrail/webmcp-polyfill";
// no-op on browsers that already implement WebMCP natively
installModelContextPolyfill();
// from here on, navigator.modelContext exists everywhere
navigator.modelContext.registerTool({
name: "search_docs",
description: "Search the documentation",
inputSchema: {
type: "object",
properties: { query: { type: "string" } },
required: ["query"],
},
async execute(args) {
const results = await search(args.query);
return { content: [{ type: "text", text: results.join("\n") }] };
},
});
Call installModelContextPolyfill() once, early — before any of your tool registration runs. On a native WebMCP browser it does nothing and your tools bind to the real API; everywhere else it provides a faithful stand-in. Your registration code is identical in both cases.
tools() and invoke(): drive tools without an agent
Registering tools is only half the story. During development you have no agent flipping through your toolset — so the polyfill exposes two extra methods that let you (or a tool, or a test) inspect and drive the registered tools:
// list everything currently registered
const registered = navigator.modelContext.tools();
console.log(registered.map((t) => t.name)); // ["search_docs", ...]
// call a tool by name, exactly as an agent would
const result = await navigator.modelContext.invoke("search_docs", {
query: "webmcp polyfill",
});
console.log(result.content);
tools() returns the live registry so a DevTools panel can render the current toolset, and invoke(name, args) calls a tool’s execute with the same envelope an agent uses. Together they make it trivial to build a “run this tool” button, drive tools from a test, or bridge the polyfilled tools out to another surface.
| Method | Purpose |
|---|---|
registerTool(tool) | native API — publish a tool |
installModelContextPolyfill() | install the API only if it’s absent |
tools() | list registered tools (polyfill extension) |
invoke(name, args) | call a tool directly, agent-style (polyfill extension) |
The polyfill is MIT-licensed and open source at github.com/ElBartoTn/webmcp-polyfill.
Build now, ship agent-ready
The payoff is timing. You don’t have to wait for WebMCP to reach every browser to start instrumenting your site. Add the polyfill, register your tools, and:
- Demo your WebMCP experience to anyone on any browser.
- Test tool behavior in CI where no Chrome flag is set (see How to test WebMCP tools).
- Inspect the live toolset from a panel while you develop (see Debug WebMCP tools with a DevTools panel).
When native support broadens, you delete nothing — the polyfill quietly yields to the browser, and the tools you wrote today keep working.
FAQ
Will the polyfill override a browser that already supports WebMCP?
No. installModelContextPolyfill() checks for an existing navigator.modelContext and returns without doing anything if the browser already provides one. Your tools bind to the native implementation on capable browsers and to the polyfill everywhere else, so there’s no risk of shadowing the real API or double-registering.
Can a real AI agent call polyfilled tools?
The polyfill provides the publisher side — registration plus tools()/invoke() — so you can drive tools yourself and bridge them to other surfaces. A mainstream agent that only speaks native WebMCP won’t discover polyfilled tools directly, but a bridge (like the MCP Trail extension) can read them via tools() and expose them through a governed gateway. That’s how a polyfilled site becomes agent-reachable before native clients arrive.
Is the polyfill a full implementation of the WebMCP spec?
It implements the parts you need to register, list, and call tools, plus change notifications so panels and bridges can react when the toolset updates. It intentionally adds tools() and invoke() on top of the spec for tooling. As the specification evolves, the polyfill tracks it — and because it defers to native support, any spec gaps disappear the moment the browser ships the real API.
Explore features · Open MCP Trail
Related articles
- What is WebMCP? — the plain-English explainer for the API this polyfill fills in.
- Debug WebMCP tools with a DevTools panel — use
tools()to render your live toolset while developing. - How to test WebMCP tools — drive tools with
invoke()in CI, no Chrome flag required. - Turn a WebMCP site into a guarded MCP server — bridge your tools through a governed gateway.