webmcp-core
v1.0.0
Published
Drop-in navigator.modelContext polyfill — replaces @mcp-b/global with zero dependencies, payment awareness, and SSR support
Downloads
112
Maintainers
Readme
webmcp-polyfill
Drop-in
navigator.modelContextpolyfill — replaces@mcp-b/globalwith zero dependencies, payment awareness, and SSR support.
Why?
The WebMCP spec adds navigator.modelContext to browsers, letting websites expose tools that AI agents can discover and use. @mcp-b/global provides a polyfill, but at 285KB with external dependencies.
webmcp-polyfill is our zero-dependency, <10KB alternative that:
- ✅ Drop-in compatible with
@mcp-b/global - ✅ Zero external dependencies
- ✅ Payment-aware — tools can declare pricing metadata inline
- ✅ SSR compatible — works in Node.js (Next.js, Nuxt, SvelteKit)
- ✅ TypeScript-first — full type safety, strict mode
- ✅ Feature detection — defers to native
navigator.modelContext(Chrome 146+)
Install
npm install webmcp-polyfillUsage
Drop-in replacement for @mcp-b/global
// Before:
// import '@mcp-b/global';
// After:
import 'webmcp-polyfill';
navigator.modelContext.registerTool({
name: 'weather',
description: 'Get current weather',
inputSchema: {
type: 'object',
properties: { city: { type: 'string' } },
required: ['city'],
},
execute: async ({ city }) => ({
content: [{ type: 'text', text: `Weather in ${city}: Sunny, 72°F` }],
}),
});Script tag (IIFE)
<script src="https://unpkg.com/webmcp-polyfill/dist/webmcp-polyfill.iife.global.js"></script>
<script>
navigator.modelContext.registerTool({ /* ... */ });
</script>Explicit API
import { install, createModelContext, ModelContext } from 'webmcp-polyfill';
// install() — installs on navigator (idempotent)
const ctx = install();
// createModelContext() — standalone instance (great for testing/SSR)
const isolated = createModelContext();API
navigator.modelContext.registerTool(tool)
Register a dynamic tool. Returns { unregister() } for cleanup.
const reg = navigator.modelContext.registerTool({
name: 'translate',
description: 'Translate text',
inputSchema: { type: 'object', properties: { text: { type: 'string' }, to: { type: 'string' } } },
execute: async (args) => ({
content: [{ type: 'text', text: `Translated: ${args.text}` }],
}),
});
// Later:
reg.unregister();navigator.modelContext.provideContext(context)
Set base tools. Replaces all previous base tools. Does NOT affect dynamic tools.
navigator.modelContext.provideContext({
tools: [
{ name: 'search', description: 'Search', inputSchema: { type: 'object' }, execute: async () => ({ content: [] }) },
{ name: 'calc', description: 'Calculate', inputSchema: { type: 'object' }, execute: async () => ({ content: [] }) },
],
});navigator.modelContext.clearContext()
Remove ALL tools (base + dynamic).
navigator.modelContext.listTools()
Enumerate all registered tools with metadata (no execute function exposed).
navigator.modelContext.executeTool(name, args?)
Execute a tool by name. Dynamic tools take priority over base tools.
Two-Bucket Architecture
| Bucket | Set via | Behavior |
|--------|---------|----------|
| Base | provideContext() | Replaced entirely each call |
| Dynamic | registerTool() | Persist across provideContext() calls, individually unregisterable |
Dynamic tools shadow base tools with the same name. Unregistering a dynamic tool reveals the shadowed base tool.
Payment Metadata
Our competitive advantage — tools can declare pricing inline:
navigator.modelContext.registerTool({
name: 'premium-api',
description: 'Premium data endpoint',
inputSchema: { type: 'object', properties: { query: { type: 'string' } } },
execute: async (args) => ({ content: [{ type: 'text', text: 'data...' }] }),
payment: {
price: 10,
currency: 'USDC',
network: 'base',
payTo: '0x1234...',
required: true,
},
});Payment metadata is exposed via listTools() for agents to discover pricing before execution.
Feature Detection
If native navigator.modelContext exists (Chrome 146+), the polyfill defers to it automatically. Use force to override:
import { install } from 'webmcp-polyfill';
install(true); // Force polyfill even if native existsCheck which you're running:
navigator.modelContext.isPolyfill // true = polyfill, undefined = nativeSSR Support
Works in Node.js environments by shimming globalThis.navigator:
// Next.js API route or getServerSideProps
import 'webmcp-polyfill';
// navigator.modelContext is now available server-sideLicense
MIT
