> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mcp-b.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# @mcp-b/webmcp-ts-sdk reference

> Reference for BrowserMcpServer, the WebMCP adapter over MCP TypeScript SDK v2.

`@mcp-b/webmcp-ts-sdk` exports `BrowserMcpServer`, a WebMCP adapter that composes the official MCP TypeScript SDK v2 `McpServer`.

```bash "Terminal" theme={null}
pnpm add @mcp-b/webmcp-ts-sdk @mcp-b/transports
```

<Note>
  Most applications should use [`@mcp-b/global`](/packages/global/overview), which creates and
  installs the adapter on `document.modelContext`.
</Note>

## Minimal example

```ts "Register a tool that an AbortSignal removes" theme={null}
import { TabServerTransport } from '@mcp-b/transports';
import { BrowserMcpServer } from '@mcp-b/webmcp-ts-sdk';

const server = new BrowserMcpServer({ name: 'my-web-app', version: '1.0.0' });
const transport = new TabServerTransport({ allowedOrigins: [window.location.origin] });

await server.connect(transport);

const controller = new AbortController();

await server.registerTool(
  {
    name: 'echo',
    description: 'Echo a message',
    inputSchema: {
      type: 'object',
      properties: {
        message: { type: 'string' },
      },
      required: ['message'],
      additionalProperties: false,
    },
    async execute({ message }) {
      return {
        content: [{ type: 'text', text: `Echo: ${String(message)}` }],
      };
    },
  },
  { signal: controller.signal }
);

controller.abort();
```

## `BrowserMcpServer`

### Constructor

```ts "Constructor" theme={null}
new BrowserMcpServer(serverInfo: Implementation, options?: BrowserMcpServerOptions)
```

`BrowserMcpServerOptions` extends the upstream `ServerOptions` type.

| Property | Type | Description |
| - | - | - |
| `native` | `ModelContext?` | Optional context override; defaults to the page's WebMCP context |

The constructor uses `document.modelContext`, installing the bundled `@mcp-b/webmcp-polyfill` (about 28 KB minified) when needed. It leaves that document property pointing to the underlying context. Use `@mcp-b/global` to install the extended API on the document and connect its default transport.

Without a WebMCP context, which is the case with no `document` (server-side module evaluation) or on an insecure page (the polyfill does not install), the constructor still succeeds and the server serves MCP only: `registerTool()`, `registerPrompt()`, `registerResource()`, `listTools()`, `connect()`, and `close()` work, tools are not mirrored to a browser context, `getTools()` and `executeTool()` reject with `InvalidStateError`, `syncNativeTools()` resolves without effect, and no `toolchange` events fire. In service workers or Node.js, use the official `McpServer` directly.

The constructor passes the remaining options to `McpServer` and enables list-change support for tools, resources, and prompts.

### `mcpServer`

```ts "MCP server property" theme={null}
readonly mcpServer: McpServer
```

The composed official server. Use it for upstream registration APIs and other MCP SDK v2 features that `BrowserMcpServer` does not wrap.

Protocol-owned sampling and elicitation APIs are not direct `BrowserMcpServer` methods. Legacy push-style calls live on `browserServer.mcpServer.server` and depend on the negotiated protocol revision.

WebMCP descriptor callbacks are single-round over MCP. An `input_required` result becomes a tool error. Register multi-round tools directly with `browserServer.mcpServer.registerTool()` so the official handler context owns the interaction.

## WebMCP tool surface

The [WebMCP Community Group draft](https://webmachinelearning.github.io/webmcp/) is authoritative for the proposed browser API.

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

```ts "Register tool signature" theme={null}
registerTool(
  tool: ToolDescriptor,
  options?: ModelContextRegisterToolOptions
): Promise<void>
```

Registers a WebMCP descriptor and its MCP representation. Registration succeeds only after the upstream context accepts the tool.

| Option | Type | Description |
| - | - | - |
| `signal` | `AbortSignal?` | Removes the MCP registration and native mirror when aborted |
| `exposedTo` | `string[]?` | Restricts the tool to the listed embedder origins |

A tool registered with a non-empty `exposedTo` is advertised only while the
connected peer's origin appears in that list. `BrowserMcpServer` reads the origin
from transports that report one, such as
[`IframeChildTransport`](/packages/transports/reference#iframechildtransport).
Restricted tools fail closed: they stay hidden before the first peer origin
arrives, and on transports that never report one. The supplied WebMCP context also validates the origin list before the tool is published. The vendored polyfill rejects opaque origins and browser-specific extension schemes.

`exposedTo` only narrows. The child transport's `allowedOrigins` still decides
who may connect, and enforcement is the page's own JavaScript rather than the
user agent. See [Bridge tools across iframes](/how-to/bridge-tools-across-iframes)
for the end-to-end setup.

Invalid descriptors, duplicate names, blocked Permissions Policy, and pre-aborted signals reject the returned promise. Duplicate names count only this document's registrations: a name held by a mirrored child-frame tool passes to the local registration once the upstream context accepts it.

### `getTools(options?)`

```ts "Get tools signature" theme={null}
getTools(options?: ModelContextGetToolOptions): Promise<RegisteredTool[]>
```

Delegates to `native.getTools(options)`. The supplied context owns discovery, frame access, and Permissions Policy checks.

### `executeTool(tool, input, options?)`

```ts "Execute tool signature" theme={null}
executeTool(
  tool: RegisteredTool,
  input: object = {},
  options?: WebMCP.ModelContextExecuteToolOptions
): Promise<string>
```

Executes a descriptor returned by `getTools()` with an input object and returns the underlying context's result string: JSON for imperative tools, plain text for native declarative tools. The optional signal aborts the call.

### Lifecycle events

`BrowserMcpServer` extends `EventTarget` and exposes `ontoolchange`, `ontoolactivated`, and `ontoolcancel`. A native `toolchange` event triggers reconciliation before the adapter dispatches its own `toolchange`; a local registration produces one only after the underlying context reports it. The adapter re-dispatches the underlying context's `toolactivated` and `toolcancel` events on itself as plain `Event` objects that carry the same `toolName`, so listeners on `document.modelContext` see them when `@mcp-b/global` is installed.

## MCP-B extensions

### `isBrowserMcpServer(context)`

```ts "Type guard signature" theme={null}
isBrowserMcpServer(context: unknown): context is BrowserMcpServer
```

Narrows an installed `document.modelContext` without changing the package's global declaration or creating a second runtime handle.

### `listTools()`

```ts "List tools signature" theme={null}
listTools(): ToolListItem[]
```

Returns a clone of the adapter's MCP-B tool metadata, including `outputSchema` when registered.

### `syncNativeTools()`

```ts "Synchronize native tools signature" theme={null}
syncNativeTools(): Promise<void>
```

Reconciles the adapter with tools already visible through `native.getTools()`. A top-level document mirrors its own tools and those of same-origin descendant frames; a framed document (`window.parent !== window`) mirrors only its own tools. Backfill uses the native context’s `executeTool()` with object input. Over MCP, a native result that parses to a JSON object becomes structured content (an MCP result envelope passes through unchanged); any other result, including numbers, quoted strings, booleans, `null`, and arrays, is returned as text exactly as the context produced it. Later native `toolchange` events reconcile additions, metadata changes, and removals; when a mirrored frame is removed, the first failed call to one of its tools drops the stale mirrors.

Use the [Model Context Tool Inspector](https://chromewebstore.google.com/detail/webmcp-model-context-tool/gbpdfapgefenggkahomfgkhfehlcenpd) recommended by Chrome's WebMCP documentation to inspect native and mirrored tools.

### `registerResource(descriptor)`

```ts "Register resource signature" theme={null}
registerResource(descriptor: ResourceDescriptor): RegistrationHandle
```

Registers a fixed URI or URI template with the composed `McpServer`. The returned handle removes the registration through `unregister()`.

| Field | Type | Required |
| - | - | - |
| `uri` | `string` | Yes |
| `name` | `string` | Yes |
| `description` | `string` | No |
| `mimeType` | `string` | No |
| `read` | `(uri: URL, params?: Variables) => Promise<ReadResourceResult>` | Yes |

A URI containing `{` registers a `ResourceTemplate`; its variables are passed to `read` as `params`. Other URIs register fixed resources.

### `registerPrompt(descriptor)`

```ts "Register prompt signature" theme={null}
registerPrompt(descriptor: PromptDescriptor): RegistrationHandle
```

Registers a prompt with the composed `McpServer`. The returned handle removes the registration through `unregister()`.

| Field | Type | Required |
| - | - | - |
| `name` | `string` | Yes |
| `description` | `string` | No |
| `argsSchema` | `InputSchema` | No |
| `get` | `(args: Record<string, string>) => Promise<GetPromptResult>` | Yes |

Prompt and resource discovery and invocation use MCP. Connect an MCP client; `BrowserMcpServer` does not duplicate the official list, read, or get methods. See [Register prompts and resources](/how-to/register-prompts-and-resources) for usage.

## Transport lifecycle

### `connect(transport)`

```ts "Connect signature" theme={null}
connect(transport: Transport): Promise<void>
```

Connects the composed server. Custom tab and iframe transports use SDK v2's legacy 2025-era route; see the upstream [protocol revision guide](https://ts.sdk.modelcontextprotocol.io/v2/migration/support-2026-07-28).

### `close()`

```ts "Close signature" theme={null}
close(): Promise<void>
```

Stops native reconciliation, aborts native mirrors, removes registrations, and closes `mcpServer`. Repeated calls return the same promise.

## Schema boundary

`BrowserMcpServer` normalizes JSON Schema and Standard JSON Schema inputs with `normalizeInputSchema()`.
If the schema also supplies `~standard.validate()`, the adapter preserves it for the composed `McpServer`.
Otherwise the MCP server uses its `fromJsonSchema` adapter.

This validation runs on MCP client calls. Direct `executeTool()` calls and native mirrors execute the
browser callback without passing through MCP validation. Validate in the callback when both paths
can call a tool, and pass plain JSON metadata so the MCP server does not also apply the vendor
transforms. The [schema guide](/how-to/use-schemas-and-structured-output) demonstrates this pattern;
[`@mcp-b/react-webmcp`](/packages/react-webmcp/reference) applies it for React registrations.

A callback result is passed through as an MCP result only when it is a valid `CallToolResult` whose content items use the MCP content types; any other object, such as a rich-text document with its own `type` and `content` fields, becomes text plus `structuredContent`.

`outputSchema` is enforced by the official MCP server on MCP calls. Direct browser calls do not
perform that check. `PromptDescriptor.argsSchema` accepts `InputSchema`. Direct upstream server
registrations support Zod 4.2 or newer and the official `fromJsonSchema` helper; Zod 3 is unsupported.

MCP requires object-root tool input schemas. An array-root WebMCP tool remains available through WebMCP but is omitted from MCP discovery with a warning.

## Exports

The root exports `BrowserMcpServer`, `BrowserMcpServerOptions`, `isBrowserMcpServer`,
`PromptDescriptor`, `ResourceDescriptor`, `ModelContext`, `ModelContextGetToolOptions`,
`ModelContextRegisterToolOptions`, `RegisteredTool`, `ModelContextExtensions`,
`ModelContextWithExtensions`, `ModelContextTool`, `InputSchema`, `WebMcpToolInput`,
`WebMcpToolObjectInput`, `ToolDescriptor`, `ToolDescriptorFromSchema`, `ToolListItem`,
`ToolAnnotations`, `MaybePromise`, `RegistrationHandle`, `JsonSchemaForInference`,
`InferJsonSchema`, `InferArgsFromInputSchema`, and `ToolResultFromOutputSchema`. The MCP result
types `CallToolResult`, `ContentBlock`, `TextContent`, `JsonObject`, and `JsonValue` are
re-exported from `@modelcontextprotocol/server`. `WebMCP` is the upstream namespace from
`webmcp-types`, re-exported through `@mcp-b/webmcp-polyfill` so that the polyfill's `SubmitEvent`
and global `ModelContext` declarations reach consumers that import only this package.

Import MCP clients, protocol schemas, transports, and validators from their official
`@modelcontextprotocol/*` packages. The `@mcp-b/webmcp-ts-sdk/schema` subpath exports
`normalizeInputSchema()`, `normalizeToolResponse()`, `isMcpStandardSchema()`, and the
`ToolInputSchema` and `NormalizedInputSchema` types.

The [`@mcp-b/transports` reference](/packages/transports/reference) documents browser transports. [WebMCP and MCP-B extensions](/explanation/strict-core-vs-mcp-b-extensions) defines the boundary between the proposal and this adapter.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.