npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@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

Readme

@mcp-b/global

W3C Web Model Context API polyfill - Let Claude, ChatGPT, Gemini, and other AI agents interact with your website

npm version npm downloads License: MIT Bundle Size W3C

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-types when you only need strict WebMCP type definitions.
  • Use @mcp-b/webmcp-polyfill when you only need strict WebMCP runtime polyfill behavior.
  • Use @mcp-b/global when 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.modelContext ready 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/global
import '@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.modelContext by default (configurable)
  • Auto-called on import unless window.__webModelContextOptions.autoInitialize is false

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 IframeChildTransport to communicate with the parent page
  • In the main window: Uses TabServerTransport for cross-tab communication
  • Set either to false to 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 from navigator.modelContextTesting.
  • @mcp-b/global requires navigator.modelContextTesting to be available at initialization time.

Related Packages

Resources

License

MIT - see LICENSE for details