@mcp-b/webmcp-ts-sdk exports BrowserMcpServer, a WebMCP adapter that composes the official MCP TypeScript SDK v2 McpServer.
Most applications should use
@mcp-b/global, which creates and
installs the adapter on document.modelContext.Minimal example
BrowserMcpServer
Constructor
BrowserMcpServerOptions extends the upstream ServerOptions type.
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
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 is authoritative for the proposed browser API.registerTool(tool, options?)
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.
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
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?)
native.getTools(options). The supplied context owns discovery, frame access, and Permissions Policy checks.
executeTool(tool, input, options?)
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)
document.modelContext without changing the package’s global declaration or creating a second runtime handle.
listTools()
outputSchema when registered.
syncNativeTools()
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 recommended by Chrome’s WebMCP documentation to inspect native and mirrored tools.
registerResource(descriptor)
McpServer. The returned handle removes the registration through unregister().
A URI containing
{ registers a ResourceTemplate; its variables are passed to read as params. Other URIs register fixed resources.
registerPrompt(descriptor)
McpServer. The returned handle removes the registration through unregister().
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 for usage.
Transport lifecycle
connect(transport)
close()
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 demonstrates this pattern;
@mcp-b/react-webmcp 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 exportsBrowserMcpServer, 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 documents browser transports. WebMCP and MCP-B extensions defines the boundary between the proposal and this adapter.