@mcp-b/global
v2.3.2
Published
W3C Web Model Context API polyfill - Let AI agents like Claude, ChatGPT, and Gemini interact with your website via navigator.modelContext
Maintainers
Readme
@mcp-b/global
W3C Web Model Context API polyfill - Let Claude, ChatGPT, Gemini, and other AI agents interact with your website
Full Documentation | Quick Start | Tool Registration
@mcp-b/global implements the W3C Web Model Context API (navigator.modelContext) specification, allowing AI agents like Claude, ChatGPT, Gemini, Cursor, and Copilot to discover and call functions on your website.
Why Use @mcp-b/global?
| Feature | Benefit |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| W3C Standard | Implements the emerging Web Model Context API specification |
| Drop-in IIFE | Add AI capabilities with a single <script> tag - no build step |
| Native Chromium Support | Auto-detects and uses native browser implementation when available |
| Dual Transport | Works with both same-window clients AND parent pages (iframe support) |
| Spec-Aware Compatibility | Tracks the April 23, 2026 WebMCP draft (registerTool(tool, { signal })). Deprecated APIs — provideContext(), clearContext(), unregisterTool(name), and the { unregister } return handle are still functional but emit one-time warnings and will be removed in the next major version. |
| Works with Any AI | Claude, ChatGPT, Gemini, Cursor, Copilot, and any MCP client |
Package Selection
- Use
@mcp-b/webmcp-typeswhen you only need strict WebMCP type definitions. - Use
@mcp-b/webmcp-polyfillwhen you only need strict WebMCP runtime polyfill behavior. - Use
@mcp-b/globalwhen you want MCPB integration features (bridge transport, prompts/resources, testing helpers, extension APIs).
Quick Start
Via IIFE Script Tag (No Build Required)
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/@mcp-b/global@latest/dist/index.iife.js"></script>
</head>
<body>
<h1>My AI-Powered App</h1>
<script>
navigator.modelContext.registerTool({
name: 'get-page-title',
description: 'Get the current page title',
inputSchema: { type: 'object', properties: {} },
async execute() {
return {
content: [{ type: 'text', text: document.title }],
};
},
});
</script>
</body>
</html>- Self-contained - All dependencies bundled (285KB minified)
- Auto-initializes -
navigator.modelContextready immediately - No build step - Just drop it in your HTML
Via ES Module
<script type="module">
import '@mcp-b/global';
navigator.modelContext.registerTool({
/* your tool */
});
</script>The ESM version is smaller (~16KB) but doesn't bundle dependencies.
Via NPM
npm install @mcp-b/globalimport '@mcp-b/global';
navigator.modelContext.registerTool({
/* your tool */
});API Reference
Functions
initializeWebModelContext(options?)
Initializes the global adapter. Replaces navigator.modelContext with a BrowserMcpServer instance that bridges WebMCP tools to the MCP protocol layer.
import { initializeWebModelContext } from '@mcp-b/global';
initializeWebModelContext({
transport: {
tabServer: { allowedOrigins: ['https://example.com'] },
},
nativeModelContextBehavior: 'preserve',
});Behavior:
- Only operates in browser environments
- Idempotent - calling multiple times is a no-op after first initialization
- Preserves native
navigator.modelContextby default (configurable) - Auto-called on import unless
window.__webModelContextOptions.autoInitializeisfalse
cleanupWebModelContext()
Tears down the adapter and restores navigator.modelContext to its original state. Allows re-initialization.
import { cleanupWebModelContext, initializeWebModelContext } from '@mcp-b/global';
initializeWebModelContext();
// ... use tools ...
cleanupWebModelContext();
// Can re-initialize after cleanup
initializeWebModelContext();navigator.modelContext Methods
After initialization, navigator.modelContext exposes these methods:
provideContext(options?)
Deprecated compatibility API. The upstream WebMCP spec removed provideContext() on March 5, 2026. @mcp-b/global keeps it functional for now, but logs a deprecation warning and will remove it in the next major version.
Replaces all currently registered tools with a new set. This is an atomic replacement - all previous tools are removed first.
navigator.modelContext.provideContext({
tools: [
{
name: 'search-products',
description: 'Search the product catalog by query',
inputSchema: {
type: 'object',
properties: {
query: { type: 'string', description: 'Search query' },
limit: { type: 'integer', description: 'Max results' },
},
required: ['query'],
},
async execute(args) {
const results = await searchProducts(args.query, args.limit ?? 10);
return {
content: [{ type: 'text', text: JSON.stringify(results) }],
};
},
},
{
name: 'get-cart',
description: 'Get the current shopping cart contents',
inputSchema: { type: 'object', properties: {} },
async execute() {
return {
content: [{ type: 'text', text: JSON.stringify(getCart()) }],
};
},
},
],
});registerTool(tool, options?)
Registers a single tool. The tool name must be unique, otherwise throws if a tool with the same name already exists. The recommended unregistration path is options.signal (AbortSignal):
const ac = new AbortController();
navigator.modelContext.registerTool(
{
name: 'add-to-cart',
description: 'Add a product to the shopping cart',
inputSchema: {
type: 'object',
properties: {
productId: { type: 'string' },
quantity: { type: 'integer' },
},
required: ['productId'],
},
async execute(args) {
const item = await addToCart(args.productId, args.quantity ?? 1);
return {
content: [{ type: 'text', text: `Added ${item.name} to cart` }],
};
},
},
{ signal: ac.signal }
);
// Later — clean up:
ac.abort();For backwards compatibility, @mcp-b/global also returns a deprecated { unregister } handle so existing MCP-B integrations do not break, even though current Chromium and the WebMCP spec return undefined. The handle will be removed in the next major version.
unregisterTool(nameOrTool) (deprecated)
Removes a tool by name. The April 23, 2026 WebMCP draft removed unregisterTool from the spec in favor of AbortSignal on registerTool. @mcp-b/global keeps unregisterTool functional for compatibility with older native previews and existing MCP-B integrations, and emits a one-time deprecation warning when called. It will be removed in the next major version.
navigator.modelContext.unregisterTool('add-to-cart');clearContext()
Deprecated compatibility API. The upstream WebMCP spec removed clearContext() on March 5, 2026. @mcp-b/global keeps it functional for now, but logs a deprecation warning and will remove it in the next major version.
Removes all registered tools.
navigator.modelContext.clearContext();listTools()
Returns metadata for all registered tools (without execute functions).
const tools = navigator.modelContext.listTools();
// [{ name: 'search-products', description: '...', inputSchema: {...} }, ...]callTool(params)
Executes a registered tool by name.
const result = await navigator.modelContext.callTool({
name: 'search-products',
arguments: { query: 'laptop', limit: 5 },
});
// { content: [{ type: 'text', text: '...' }] }Tool Descriptor
| Property | Type | Required | Description |
| -------------- | ----------------------------------------- | -------- | --------------------------------------------------------------------------------------- |
| name | string | Yes | Unique identifier for the tool |
| description | string | Yes | Natural language description of what the tool does |
| inputSchema | InputSchema | No | JSON Schema describing accepted input. Defaults to { type: 'object', properties: {} } |
| outputSchema | InputSchema | No | JSON Schema describing the output payload shape |
| annotations | ToolAnnotations | No | Hints about tool behavior for LLM planners |
| execute | (args, client) => Promise<ToolResponse> | Yes | Async function implementing the tool logic |
Tool Response Format
Tools return a ToolResponse object:
// Success
{
content: [{ type: 'text', text: 'Result here' }]
}
// Error
{
content: [{ type: 'text', text: 'Something went wrong' }],
isError: true
}Configuration
WebModelContextInitOptions
interface WebModelContextInitOptions {
transport?: TransportConfiguration;
autoInitialize?: boolean;
nativeModelContextBehavior?: 'preserve' | 'patch';
installTestingShim?: boolean | 'always' | 'if-missing';
}| Option | Default | Description |
| ---------------------------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| transport | Auto-detect | Transport layer configuration (tab server and/or iframe) |
| autoInitialize | true | Whether to auto-initialize on import |
| nativeModelContextBehavior | 'preserve' | 'preserve' keeps native implementation untouched. 'patch' replaces it with a BrowserMcpServer that mirrors to the native object |
| installTestingShim | 'if-missing' | Controls navigator.modelContextTesting installation. Only installs when not already present natively |
Transport Configuration
The transport is auto-selected based on context:
interface TransportConfiguration {
tabServer?: Partial<TabServerTransportOptions> | false;
iframeServer?: Partial<IframeChildTransportOptions> | false;
}- In an iframe: Uses
IframeChildTransportto communicate with the parent page - In the main window: Uses
TabServerTransportfor cross-tab communication - Set either to
falseto disable it
// Restrict to specific origins
initializeWebModelContext({
transport: {
tabServer: { allowedOrigins: ['https://myapp.com'] },
},
});
// Disable tab transport (iframe only)
initializeWebModelContext({
transport: {
tabServer: false,
},
});Auto-Initialization
The package auto-initializes on import in browser environments. To customize before initialization:
<script>
window.__webModelContextOptions = {
autoInitialize: true,
transport: {
tabServer: { allowedOrigins: ['https://myapp.com'] },
},
};
</script>
<script src="https://unpkg.com/@mcp-b/global@latest/dist/index.iife.js"></script>To prevent auto-initialization:
<script>
window.__webModelContextOptions = { autoInitialize: false };
</script>
<script src="https://unpkg.com/@mcp-b/global@latest/dist/index.iife.js"></script>
<script>
// Initialize manually later
MCPB.initializeWebModelContext();
</script>Testing
navigator.modelContextTesting provides a testing shim that stays in sync with registered tools:
// List registered tools
const tools = navigator.modelContextTesting?.listTools();
// [{ name: 'search-products', description: '...', inputSchema: '...' }]
// Execute a tool (input args as JSON string)
const result = await navigator.modelContextTesting?.executeTool(
'search-products',
'{"query": "laptop"}'
);Feature Detection
if ('modelContext' in navigator) {
navigator.modelContext.registerTool({
/* your tool */
});
}Examples
E-commerce: Product Search and Cart
import '@mcp-b/global';
navigator.modelContext.registerTool({
name: 'search-products',
description: 'Search products by keyword, category, or price range',
inputSchema: {
type: 'object',
properties: {
query: { type: 'string', description: 'Search terms' },
category: { type: 'string', description: 'Product category' },
maxPrice: { type: 'number', description: 'Maximum price filter' },
},
required: ['query'],
},
async execute(args) {
const results = await fetch(
`/api/products?q=${args.query}&cat=${args.category ?? ''}&max=${args.maxPrice ?? ''}`
);
return { content: [{ type: 'text', text: await results.text() }] };
},
});
navigator.modelContext.registerTool({
name: 'add-to-cart',
description: 'Add a product to the shopping cart',
inputSchema: {
type: 'object',
properties: {
productId: { type: 'string' },
quantity: { type: 'integer' },
},
required: ['productId'],
},
async execute(args) {
await fetch('/api/cart', {
method: 'POST',
body: JSON.stringify({ productId: args.productId, quantity: args.quantity ?? 1 }),
});
return { content: [{ type: 'text', text: `Added to cart` }] };
},
});Dynamic Tool Registration
import '@mcp-b/global';
// Start with base tools
navigator.modelContext.registerTool({
name: 'get-user',
description: 'Get current user info',
inputSchema: { type: 'object', properties: {} },
async execute() {
return { content: [{ type: 'text', text: JSON.stringify(currentUser) }] };
},
});
// Add tools dynamically based on user role
if (currentUser.isAdmin) {
navigator.modelContext.registerTool({
name: 'delete-user',
description: 'Delete a user account (admin only)',
inputSchema: {
type: 'object',
properties: { userId: { type: 'string' } },
required: ['userId'],
},
async execute(args) {
await fetch(`/api/users/${args.userId}`, { method: 'DELETE' });
return { content: [{ type: 'text', text: 'User deleted' }] };
},
});
}
// Remove tools when permissions change
function onLogout() {
navigator.modelContext.unregisterTool('get-user');
}Form Interaction
import '@mcp-b/global';
navigator.modelContext.registerTool({
name: 'fill-contact-form',
description: 'Fill the contact form with provided details',
inputSchema: {
type: 'object',
properties: {
name: { type: 'string' },
email: { type: 'string' },
message: { type: 'string' },
},
required: ['name', 'email', 'message'],
},
async execute(args) {
document.querySelector('#name').value = args.name;
document.querySelector('#email').value = args.email;
document.querySelector('#message').value = args.message;
return { content: [{ type: 'text', text: 'Form filled' }] };
},
});
navigator.modelContext.registerTool({
name: 'submit-form',
description: 'Submit the contact form',
inputSchema: { type: 'object', properties: {} },
async execute() {
document.querySelector('#contact-form').submit();
return { content: [{ type: 'text', text: 'Form submitted' }] };
},
});Browser Compatibility
| Browser | Native Support | Polyfill | | ----------------------- | -------------- | -------- | | Chrome/Edge (with flag) | Yes | N/A | | Chrome/Edge (default) | No | Yes | | Firefox | No | Yes | | Safari | No | Yes |
Zod Version Compatibility
This package supports Zod 3.25.76+ (3.x only). JSON Schema is also supported if you prefer not to use Zod.
Type Exports
All types are re-exported for TypeScript consumers:
import type {
CallToolResult,
InputSchema,
ModelContext,
ModelContextCore,
ModelContextOptions,
NativeModelContextBehavior,
ToolAnnotations,
ToolDescriptor,
ToolListItem,
ToolResponse,
TransportConfiguration,
WebModelContextInitOptions,
} from '@mcp-b/global';Tool Routing Contract
- MCP
tools/list,tools/call, and tool list update notifications are sourced fromnavigator.modelContextTesting. @mcp-b/globalrequiresnavigator.modelContextTestingto be available at initialization time.
Related Packages
@mcp-b/transports- MCP transport implementations@mcp-b/react-webmcp- React hooks for MCP@mcp-b/extension-tools- Chrome Extension API tools@mcp-b/chrome-devtools-mcp- Connect desktop AI agents to browser tools
Resources
License
MIT - see LICENSE for details
