
I was watching Gemini in Chrome try to use a Next.js app I'd built. The agent was taking screenshots, guessing which input field was "search," clicking in the wrong places, hallucinating button labels.
It was painful to watch. Like watching someone read a book through a foggy window.
That's the exact problem WebMCP was built to solve.
WebMCP is a W3C draft standard that adds a navigator.modelContext API to browsers. Instead of an AI agent scraping your DOM and guessing what your app does, your Next.js app tells the agent exactly what it can do — in structured, machine-readable terms. The agent calls a function, gets clean data, moves on.
No screenshots. No broken selectors. No hallucinations.
In this guide: what WebMCP actually is, how it differs from Anthropic's MCP, and a complete step-by-step implementation in Next.js App Router with working code.
WebMCP vs MCP — Clear This Up First
This confuses a lot of developers. One sentence each:
Anthropic's MCP runs server-side. Your AI agent connects to a backend over JSON-RPC to access databases, file systems, or external APIs. Runs outside the browser entirely.
WebMCP runs client-side, inside the browser tab. Your site registers tools with navigator.modelContext, and in-browser agents discover and call them locally. No separate server. No extra auth. The agent uses whatever session the logged-in user already has.
They're complementary, not competing. A flight booking site might use WebMCP so browser agents can search flights on the front end, while also running an MCP server so Claude or GPT can book flights via API on the back end.
| Feature | Anthropic MCP | WebMCP |
|---|---|---|
| Where it runs | Server-side | Browser tab |
| Protocol | JSON-RPC over stdio/HTTP |
navigator.modelContext API |
| Auth | Separate auth required | Uses existing browser session |
| Backend needed? | Yes | No (zero backend) |
| Who made it | Anthropic | W3C (Google + Microsoft) |
| Browser support | N/A | Chrome 146+ (flag), Edge 147+ (flag) |
The W3C published the WebMCP Draft Community Group Report on February 10, 2026. Chrome 146 shipped the first implementation behind a feature flag. All four major browser vendors are at the table — Google, Microsoft, Mozilla, Apple. That's a strong signal.
The Two APIs — Declarative vs Imperative
WebMCP gives you two ways to expose tools.
Declarative API — HTML Attributes (2 minutes to ship)
Already have HTML forms? Add two attributes:
<form
toolname="searchProducts"
tooldescription="Search the product catalog by keyword and category. Returns matching products with price and availability."
>
<input name="query" type="text" placeholder="Search..." />
<select name="category">
<option value="">All categories</option>
<option value="electronics">Electronics</option>
</select>
<input name="maxPrice" type="number" />
<button type="submit">Search</button>
</form>
Done. An agent sees a searchProducts tool with typed parameters inferred from your form fields. Zero JavaScript needed.
Use for: search forms, filters, contact forms, newsletter signups.
Imperative API — registerTool()
For dynamic interactions, stateful logic, or multi-step flows:
if ('modelContext' in navigator) {
navigator.modelContext.registerTool({
name: 'getUserDashboard',
description: "Get the current user's dashboard summary including recent orders, wishlist count, and loyalty points.",
inputSchema: {
type: 'object',
properties: {
includeOrders: {
type: 'boolean',
description: 'Include last 5 orders in the response'
}
},
required: []
},
async execute({ includeOrders }) {
const res = await fetch('/api/dashboard', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ includeOrders })
});
return res.json();
}
});
}
The if ('modelContext' in navigator) check is not optional. Browsers without WebMCP support shouldn't throw errors — they should degrade gracefully. The JSON Schema in inputSchema is the same format Claude, GPT, and Gemini already use for function calling. WebMCP speaks the language LLMs already understand.
Step-by-Step: Next.js App Router Implementation
Let's wire this into a real Next.js 15 project. I'll use a product search app as the example.
Step 1 — Install the Polyfill
Chrome 146+ has WebMCP behind a flag. Edge 147+ too. Everyone else needs the polyfill:
npm install @mcp-b/global @mcp-b/react-webmcp
@mcp-b/react-webmcp gives you the useWebMCP hook, which handles registration and cleanup automatically when components mount/unmount.
Step 2 — Initialize in Root Layout
Create a Client Component that initializes the polyfill once:
// components/WebMCPProvider.tsx
'use client';
import { useEffect } from 'react';
export function WebMCPProvider({ children }: { children: React.ReactNode }) {
useEffect(() => {
if (!('modelContext' in navigator)) {
import('@mcp-b/global').then(({ initializeWebMCPPolyfill }) => {
initializeWebMCPPolyfill();
});
}
}, []);
return <>{children}</>;
}
Add it to app/layout.tsx:
import { WebMCPProvider } from '@/components/WebMCPProvider';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<WebMCPProvider>
{children}
</WebMCPProvider>
</body>
</html>
);
}
Step 3 — Register Tools in Page Components
Key principle: register tools when the UI is visible, unregister when the user leaves. The useWebMCP hook handles this automatically via AbortController.
// app/products/ProductsPageTools.tsx
'use client';
import { useWebMCP } from '@mcp-b/react-webmcp';
import { z } from 'zod';
export function ProductsPageTools() {
useWebMCP({
name: 'searchProducts',
description:
'Search the product catalog. Returns products with name, price, rating, and stock status. Use when the user asks to find, browse, or filter products.',
inputSchema: z.object({
query: z.string().describe('Search keywords — e.g. "wireless headphones"'),
category: z.string().optional().describe('Product category filter'),
maxPrice: z.number().optional().describe('Maximum price in USD'),
inStock: z.boolean().optional().describe('Only return in-stock items'),
}),
execute: async ({ query, category, maxPrice, inStock }) => {
const params = new URLSearchParams({ q: query });
if (category) params.set('cat', category);
if (maxPrice) params.set('max', String(maxPrice));
if (inStock) params.set('inStock', '1');
const res = await fetch(`/api/products?${params}`);
if (!res.ok) throw new Error('Search failed');
return res.json();
},
});
return null; // renders nothing — just manages tool registration
}
Drop <ProductsPageTools /> inside your products page. When the component mounts, the tool registers. Navigate away — it automatically unregisters. Clean, no memory leaks.
Step 4 — Enable the Chrome Flag and Test
- Open Chrome 146+
- Navigate to
chrome://flags - Search for "webmcp" → set "Enable WebMCP for testing" to Enabled
- Relaunch Chrome
- Open your app (localhost or HTTPS — plain HTTP won't work in production)
- Open DevTools (F12) → Console → run:
navigator.modelContext - If you see an object (not
undefined), WebMCP is active ✅ - Install the Model Context Tool Inspector extension from the Chrome Web Store — it shows registered tools and lets you invoke them with test inputs without needing an actual AI agent
That last step saved me ~40 minutes of debugging on my first implementation. Highly recommend.
Gotchas That Bit Me (So They Don't Bite You)
1. Single-Tab Scope
Tools only exist while the page is open. Navigate away — tools are gone. Refresh — they re-register. This is intentional (security), but make your tool descriptions rich and self-explanatory. The agent can't know what tools exist without visiting your page first.
2. provideContext() Is Gone
Earlier drafts had a provideContext() method for passing page state to agents. It was removed in March 2026. If you see tutorials using it, they're outdated.
The current pattern: bake context into your tool's description string directly.
// ❌ Old (removed)
navigator.modelContext.provideContext({ userId: '123' });
// ✅ Current
const userId = getCurrentUser().id;
useWebMCP({
name: 'getUserOrders',
description: `Get recent orders for the currently logged-in user (ID: ${userId}). Returns last 10 orders with status and tracking.`,
// ...
});
3. Tightly Coupled React State Won't Work
Tool handlers don't have access to React state or Zustand stores. If your app logic is buried in component state, handlers will run with stale data.
Fix: put data-fetching logic in a shared service layer (plain functions, not hooks) and call it from both your UI and WebMCP handlers.
4. The 50-Tool Soft Limit
No hard cap in the spec, but stay under 50 tools per page. Each tool definition gets included in the agent's context window. Too many tools → bloated context → slow agent reasoning. Expose the actions that actually matter for agent workflows.
5. HTTPS Is Mandatory in Production
localhost gets a pass in dev. In production — HTTPS only. Custom local domains like myapp.test need a self-signed cert or ngrok.
Browser Support — Honest Picture (June 2026)
| Browser | Status |
|---|---|
| Chrome 146+ Stable | ✅ Behind flag (enable-webmcp-testing) |
| Edge 147+ | ✅ Behind flag (same as Chrome) |
| Chrome 149 | 🔜 Public Origin Trial (no flag needed) |
| Firefox | 🔄 In W3C Working Group, no timeline |
| Safari | 🔄 Bug-tracker entry, no commitment |
Mainstream AI agents (Claude Desktop, ChatGPT, Gemini app) don't call WebMCP tools directly on websites yet as of mid-2026. The working bridge is the MCP-B browser extension, which aggregates WebMCP tools from open tabs and forwards them to local MCP clients.
The realistic timeline: late 2026 for Chrome Stable with WebMCP on by default.
Is It Worth Building Now?
Cost: a few hours of work. The polyfill handles cross-browser fallback. The useWebMCP hook handles cleanup. When Chrome flips the default switch, your app is already ready.
Start with one tool — the most important search or filter your users perform. Add useWebMCP. Test it with the Chrome DevTools inspector. Ship it. Then add more as the spec stabilizes.
The sites that did responsive design early won mobile. The ones that structured their content early won featured snippets. WebMCP is the same dynamic — and the cost of being early is genuinely low this time.
Resources
- W3C WebMCP Draft Spec
- WebMCP-org on GitHub — polyfill, React hooks, DevTools extension
- Model Context Tool Inspector — Chrome extension for testing
Originally published at WebToolsHub — free developer tools and guides.
Top comments (0)