@mcp-b/global is 285KB. Our polyfill does the same thing in 2.94KB.
That's not a typo. Same API surface. Same navigator.modelContext interface that Chrome 146 ships natively. 97x smaller.
Why we built it
We were integrating WebMCP into a production site - making it agent-friendly so AI agents could call tools instead of scraping DOM. The spec is solid. Chrome's implementation works. But for browsers that don't support it yet, you need a polyfill.
@mcp-b/global exists for this. It works fine. But 285KB for a polyfill that bridges a single browser API felt wrong. Especially when you're already fighting bundle size on a site that needs to load fast for both humans and agents.
So we wrote our own from scratch.
What it does
The full navigator.modelContext API:
import 'webmcp-polyfill';
// Register a tool
navigator.modelContext.registerTool({
name: 'searchProducts',
description: 'Search the product catalog',
inputSchema: {
type: 'object',
properties: {
query: { type: 'string' }
},
required: ['query']
},
execute: async ({ query }) => {
const results = await searchAPI(query);
return { content: [{ type: 'text', text: JSON.stringify(results) }] };
}
});
Feature detection is built in. If the browser has native navigator.modelContext (Chrome 146+), the polyfill steps aside. No conflicts, no double-registration, no wasted bytes.
The numbers
- 2.94KB IIFE bundle (minified + gzipped)
- 0 dependencies
- 70 tests passing
- TypeScript strict mode throughout
Compare that to @mcp-b/global:
- 285KB bundle
- Multiple transitive dependencies
- Same spec surface
We're not saying @mcp-b/global is bad. The MCP-B team built the reference implementation that helped shape the W3C spec. That matters. But if you're shipping a production site and bundle size is a constraint, 285KB for a polyfill is a lot.
SSR compatibility
This bit matters more than people think. If you're running Next.js, Nuxt, SvelteKit, or anything with server-side rendering, navigator doesn't exist on the server. A polyfill that blows up on import because it touches window.navigator immediately is a problem.
webmcp-polyfill checks the environment before doing anything. Import it in a server component, it stays quiet. Import it on the client, it sets up the polyfill. No typeof window !== 'undefined' guards needed in your code.
The part nobody else does: payment-aware tools
This is where we went beyond the spec.
WebMCP tells agents what tools exist and how to call them. But it doesn't tell them what it costs. If you're running a paid API behind a tool, the agent has no way to know it'll be charged $0.05 per call until after it calls.
We added pricing metadata directly into tool registration:
navigator.modelContext.registerTool({
name: 'generateReport',
description: 'Generate a detailed market report',
inputSchema: { /* ... */ },
annotations: {
pricing: {
amount: '0.05',
currency: 'USD',
protocol: 'x402'
}
},
execute: async (params) => { /* ... */ }
});
Agents see the cost upfront. They can decide whether to call it, find a cheaper alternative, or ask the user for approval. This is how agent commerce should work - transparent pricing before execution.
We built a companion package (webmcp-payments) that handles the actual x402 payment flow, but the polyfill carries the metadata layer on its own.
How to swap
If you're using @mcp-b/global today:
- import '@mcp-b/global';
+ import 'webmcp-polyfill';
That's it. Same API. Your registerTool, provideContext, and unregisterTool calls don't change.
The full stack
We built three packages that layer on each other:
-
webmcp-polyfill - the base layer.
navigator.modelContextin 2.94KB. - webmcp-payments - x402 payment acceptance middleware. Agents pay per tool call.
- webmcp-platform - unified entry point. One import to make your site agent-friendly and monetizable.
They work independently or together. Use just the polyfill if that's all you need. Add payments when you want to charge for tool access. Use the platform package when you want the whole stack in a single import.
Try it
npm install webmcp-polyfill
Source: GitHub
70 tests. Zero dependencies. 2.94KB. If your site is already using @mcp-b/global, the swap takes 10 seconds and saves you 282KB.
Top comments (0)