DEV Community

up2itnow0822
up2itnow0822

Posted on

We Built a 3KB WebMCP Polyfill (97x Smaller Than @mcp-b/global)

@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) }] };
  }
});
Enter fullscreen mode Exit fullscreen mode

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) => { /* ... */ }
});
Enter fullscreen mode Exit fullscreen mode

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';
Enter fullscreen mode Exit fullscreen mode

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:

  1. webmcp-polyfill - the base layer. navigator.modelContext in 2.94KB.
  2. webmcp-payments - x402 payment acceptance middleware. Agents pay per tool call.
  3. 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
Enter fullscreen mode Exit fullscreen mode

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)