# @mcp-b/global

> WebMCP runtime - Let Claude, ChatGPT, Gemini, and other AI agents interact with your website

[![npm version](https://img.shields.io/npm/v/@mcp-b/global?style=flat-square)](https://www.npmjs.com/package/@mcp-b/global)
[![npm downloads](https://img.shields.io/npm/dm/@mcp-b/global?style=flat-square)](https://www.npmjs.com/package/@mcp-b/global)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](https://opensource.org/licenses/MIT)
[![W3C](https://img.shields.io/badge/W3C-Web_Model_Context-005A9C?style=flat-square)](https://webmachinelearning.github.io/webmcp/)

**[Reference](https://docs.mcp-b.ai/packages/global/reference)** | **[First Tool Tutorial](https://docs.mcp-b.ai/tutorials/first-tool)** | **[Add Tools to an App](https://docs.mcp-b.ai/how-to/add-tools-to-an-existing-app)**

**@mcp-b/global** implements the [WebMCP API](https://webmachinelearning.github.io/webmcp/) (`document.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 WebMCP 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 current WebMCP draft (`document.modelContext`, `registerTool(tool, { signal })`, and `getTools()`) and object-input `executeTool(...)`. |
| **Works with Any AI**        | Claude, ChatGPT, Gemini, Cursor, Copilot, and any MCP client                                                                                       |

## Package Selection

- Use upstream `webmcp-types` for standard browser contracts and `@mcp-b/webmcp-ts-sdk` for MCP-B extensions.
- 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, extension APIs).

## Upstream runtime

When no context is installed, `@mcp-b/global` calls `installWebMCP()` from
`@mcp-b/webmcp-polyfill`. That package bundles the upstream
[WebMCP polyfill](https://github.com/webmachinelearning/webmcp-polyfill) at
revision `6bf6c57bbaf3d1173d7737cfb79572632d9b7871`. It depends on the
upstream `webmcp-types` declarations. Consumers do not install or build a Git
dependency.

`@mcp-b/webmcp-polyfill` retains the declarative API and its `SubmitEvent` extensions
until upstream supports them. `@mcp-b/global` adds MCP-B transports, prompts,
resources, MCP `outputSchema` metadata, and structured MCP responses. Existing native
contexts take precedence and receive the MCP-B adapter when wrapped,
including when the upstream polyfill was installed before `@mcp-b/global`.
`cleanupWebModelContext()` removes the MCP-B adapter; the polyfill context and
declarative layer remain installed for the document lifetime. Existing native
`SubmitEvent` hooks are preserved.

`executeTool(tool, inputObject)` follows the current draft and returns JSON.

## Quick Start

### Via IIFE Script Tag (No Build Required)

```html
<!DOCTYPE html>
<html>
  <head>
    <script src="https://unpkg.com/@mcp-b/global@6/dist/index.iife.js"></script>
  </head>
  <body>
    <h1>My AI-Powered App</h1>

    <script>
      void document.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 }],
            };
          },
        })
        .catch(console.error);
    </script>
  </body>
</html>
```

- **Self-contained** - All dependencies bundled
- **Auto-initializes** - `document.modelContext` ready immediately
- **No build step** - Just drop it in your HTML

### Via ES Module

```html
<script type="module">
  import '@mcp-b/global';
  await document.modelContext.registerTool({
    /* your tool */
  });
</script>
```

The ESM entry keeps `@mcp-b/transports`, `@mcp-b/webmcp-polyfill`, and
`@mcp-b/webmcp-ts-sdk` external; only the IIFE bundles them.

### Via NPM

```bash
npm install @mcp-b/global
```

```javascript
import '@mcp-b/global';

await document.modelContext.registerTool({
  /* your tool */
});
```

## API Reference

### Functions

#### `initializeWebModelContext(options?)`

Initializes the global adapter. Replaces `document.modelContext` with a `BrowserMcpServer` instance that bridges WebMCP tools to the MCP protocol layer.

```typescript
window.__webModelContextOptions = { autoInitialize: false };

const { initializeWebModelContext } = await import('@mcp-b/global');

initializeWebModelContext({
  transport: {
    tabServer: { allowedOrigins: ['https://example.com'] },
  },
});

await document.modelContext.registerTool({
  /* your tool */
});
```

**Behavior:**

- Only operates in browser environments
- Returns nothing; use `document.modelContext` after initialization
- Idempotent - repeated and cross-bundle calls are no-ops
- Wraps the native context when present; otherwise installs and wraps the polyfill
- Cleanup restores the captured native or polyfilled context
- Auto-called on import unless `window.__webModelContextOptions.autoInitialize` is `false`

#### `cleanupWebModelContext()`

Tears down the adapter and restores `document.modelContext` to the captured native or polyfilled
context. Closing the adapter aborts every registration made through `document.modelContext` while
it was installed; re-initializing does not restore them. Tools registered directly on the underlying
context, including declarative tools, survive.

```typescript
import { cleanupWebModelContext, initializeWebModelContext } from '@mcp-b/global';

initializeWebModelContext();
// ... use tools ...
cleanupWebModelContext();

// Can re-initialize after cleanup
initializeWebModelContext();
```

### `document.modelContext` Methods

After initialization, `document.modelContext` exposes these methods:

#### `registerTool(tool, options?)`

Registers a single tool. The tool name must be unique. Duplicate and invalid
registrations reject the returned promise. The recommended unregistration path
is `options.signal` (`AbortSignal`):

```typescript
const ac = new AbortController();
await document.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();
```

`registerTool` resolves `undefined`, matching current Chromium and the WebMCP spec. Use `AbortSignal` cleanup for dynamic tools.

#### `getTools()`

Returns WebMCP tool descriptors for all registered tools.

```typescript
const tools = await document.modelContext.getTools();
// [{ name: 'search-products', inputSchema: { type: 'object', ... }, ... }, ...]
```

#### `executeTool(tool, inputObject)`

Execute a discovered descriptor with an input object. The result is serialized JSON.

```typescript
import '@mcp-b/global';

const modelContext = document.modelContext;
if (!modelContext) throw new Error('WebMCP is unavailable');

const tools = await modelContext.getTools();
const searchTool = tools.find((tool) => tool.name === 'search-products');
if (!searchTool) throw new Error('search-products is not available');

const result = JSON.parse(
  await modelContext.executeTool(searchTool, { query: 'laptop', limit: 5 })
);
// { content: [{ type: 'text', text: '...' }] }
```

#### `listTools()`

This MCP-B helper exposes MCP metadata. In-page WebMCP consumers should use
`getTools()` and `executeTool(tool, inputObject)`.

Prompt, resource, and lower-level MCP helpers are MCP-B extensions. Narrow
`document.modelContext` with `isBrowserMcpServer()` from
`@mcp-b/webmcp-ts-sdk` before using them. Install that package directly when
importing the guard; do not add extensions to the standard global type.

### `BrowserMcpServer` Tool Descriptor

The MCP-B runtime consumes `outputSchema` when a separately typed descriptor is
passed to `document.modelContext.registerTool()`. Native and strict polyfill
runtimes ignore the extra field.

| 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       | MCP-B helper metadata for output typing and structured MCP responses                    |
| `annotations`  | `ToolAnnotations`                       | No       | Hints about tool behavior for LLM planners                                              |
| `execute`      | `(args) => unknown \| Promise<unknown>` | Yes      | Function implementing the tool logic                                                    |

### Tool Response Format

Tools may return an MCP `CallToolResult` object:

```typescript
// Success
{
  content: [{ type: 'text', text: 'Result here' }]
}

// Error
{
  content: [{ type: 'text', text: 'Something went wrong' }],
  isError: true
}
```

## Configuration

### `WebModelContextInitOptions`

```typescript
interface WebModelContextInitOptions {
  transport?: TransportConfiguration;
  autoInitialize?: boolean;
}
```

| Option           | Default     | Description                                              |
| ---------------- | ----------- | -------------------------------------------------------- |
| `transport`      | Auto-detect | Transport layer configuration (tab server and/or iframe) |
| `autoInitialize` | `true`      | Whether to auto-initialize on import                     |

### Transport Configuration

The transport is auto-selected based on context:

```typescript
interface TransportConfiguration {
  tabServer?: TabServerTransportOptions | false;
  iframeServer?: 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

```typescript
window.__webModelContextOptions = { autoInitialize: false };

const { initializeWebModelContext } = await import('@mcp-b/global');
initializeWebModelContext({
  transport: {
    tabServer: { allowedOrigins: ['https://myapp.com'] },
  },
});
```

Inside an iframe, pass `transport: { tabServer: false }` to require the iframe transport.

### Auto-Initialization

The package auto-initializes on import in browser environments. To customize before initialization:

```html
<script>
  window.__webModelContextOptions = {
    autoInitialize: true,
    transport: {
      tabServer: { allowedOrigins: ['https://myapp.com'] },
    },
  };
</script>
<script src="https://unpkg.com/@mcp-b/global@6/dist/index.iife.js"></script>
```

To prevent auto-initialization:

```html
<script>
  window.__webModelContextOptions = { autoInitialize: false };
</script>
<script src="https://unpkg.com/@mcp-b/global@6/dist/index.iife.js"></script>
<script>
  // Initialize manually later
  WebMCP.initializeWebModelContext();
</script>
```

## Feature Detection

```javascript
if ('modelContext' in document) {
  void document.modelContext
    .registerTool({
      /* your tool */
    })
    .catch(console.error);
}
```

## Examples

### E-commerce: Product Search and Cart

```typescript
import '@mcp-b/global';

await document.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() }] };
  },
});

await document.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

```typescript
import '@mcp-b/global';

// Start with base tools
const userToolController = new AbortController();
await document.modelContext.registerTool(
  {
    name: 'get-user',
    description: 'Get current user info',
    inputSchema: { type: 'object', properties: {} },
    async execute() {
      return { content: [{ type: 'text', text: JSON.stringify(currentUser) }] };
    },
  },
  { signal: userToolController.signal }
);

let adminToolController: AbortController | undefined;

async function registerAdminTool() {
  adminToolController?.abort();
  adminToolController = new AbortController();

  await document.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' }] };
      },
    },
    { signal: adminToolController.signal }
  );
}

function unregisterAdminTool() {
  adminToolController?.abort();
  adminToolController = undefined;
}

// Add tools dynamically based on user role
if (currentUser.isAdmin) {
  await registerAdminTool();
}

// Remove tools when permissions change
function onLogout() {
  userToolController.abort();
  unregisterAdminTool();
}
```

### Form Interaction

```typescript
import '@mcp-b/global';

await document.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' }] };
  },
});

await document.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

Native WebMCP needs Chrome 155 or newer with the WebMCP feature enabled; the
runtime wraps the native context when it exists. Everywhere else the bundled
polyfill runs. Its `installWebMCP()` installs only when the engine provides
every API the vendored core calls. Otherwise it returns without defining
`document.modelContext`, so `if (!document.modelContext)` remains a valid
feature check.

| API                             | Chrome | Firefox | Safari |
| ------------------------------- | ------ | ------- | ------ |
| `String.prototype.toWellFormed` | 111    | 119     | 16.4   |
| `AbortSignal.any()`             | 116    | 124     | 17.4   |
| `Promise.withResolvers()`       | 119    | 121     | 17.4   |
| `URL.parse()`                   | 126    | 126     | 18     |

The resulting floor is Chrome 126, Firefox 126, and Safari 18.

## Schema Compatibility

The browser API accepts plain JSON Schema and does not require Zod. Direct MCP SDK v2
registrations accept Standard Schema implementations that emit JSON Schema, including Zod 4.2 or
newer. Zod 3 is unsupported.

## Type exports

This package exports its initialization options:

```typescript
import type { TransportConfiguration, WebModelContextInitOptions } from '@mcp-b/global';
```

Import core browser contracts from upstream `webmcp-types`. Use
`@mcp-b/webmcp-ts-sdk` for MCP-B extensions.

## Tool Routing Contract

- MCP `tools/list`, `tools/call`, and tool list update notifications are sourced from the `BrowserMcpServer` registry.
- Native and polyfill tool backfill use `getTools()` and object-input `executeTool()`.

## Related Packages

- [`@mcp-b/transports`](https://docs.mcp-b.ai/packages/transports/reference) - MCP transport implementations
- [`@mcp-b/react-webmcp`](https://docs.mcp-b.ai/packages/react-webmcp/reference) - React hooks for MCP
- [`chrome-devtools-mcp`](https://github.com/ChromeDevTools/chrome-devtools-mcp) - Upstream Chrome DevTools MCP server

## Resources

- [WebMCP Documentation](https://docs.mcp-b.ai)
- [WebMCP specification](https://webmachinelearning.github.io/webmcp/)
- [Model Context Protocol Spec](https://modelcontextprotocol.io/)

## License

MIT - see [LICENSE](../../LICENSE) for details
