Skip to main content
Browser transport implementations for the Model Context Protocol. Each transport class implements the MCP Transport interface and handles JSON-RPC 2.0 message serialization, origin validation, and connection lifecycle.

Installation

pnpm add @mcp-b/transports
Peer dependency: @mcp-b/webmcp-ts-sdk (included transitively).

Minimal example

Which transport to use

ScenarioServerClient
Same-page communicationTabServerTransportTabClientTransport
Parent page to iframeIframeChildTransport (in iframe)IframeParentTransport (in parent)
Extension to webpageExtensionServerTransport (in background)ExtensionClientTransport (in sidebar/popup)
Extension to extensionExtensionServerTransport (in host extension)ExtensionExternalClientTransport (in guest extension)
User script to extensionUserScriptServerTransport (in background)UserScriptClientTransport (in user script)

Tab transports

In-page communication via window.postMessage. Both server and client run in the same browser tab.

TabServerTransport

Listens for MCP messages on the current window. Broadcasts a mcp-server-ready signal on start.
import { TabServerTransport } from "@mcp-b/transports";

const transport = new TabServerTransport({
  allowedOrigins: ["https://example.com"],
});
await server.connect(transport);

TabServerTransportOptions

OptionTypeRequiredDefaultDescription
allowedOriginsstring[]YesOrigins permitted to send messages. Use ["*"] to allow any origin.
channelIdstringNo"mcp-default"Channel identifier for message routing. Multiple transports can coexist on the same page with different channel IDs.

Behavior

  • Validates event.origin against allowedOrigins for every incoming message.
  • Tracks pending requests and sends interrupted responses during beforeunload if the page navigates while a tool call is in progress.
  • Cleans up stale requests after 5 minutes.
  • Posts mcp-server-stopped on close.

TabClientTransport

Connects to a TabServerTransport in the same window. Waits for a server-ready handshake before sending messages.
import { TabClientTransport } from "@mcp-b/transports";

const transport = new TabClientTransport({
  targetOrigin: window.location.origin,
});
await client.connect(transport);

TabClientTransportOptions

OptionTypeRequiredDefaultDescription
targetOriginstringNo"*"Expected origin of the server window. Messages from other origins are ignored.
channelIdstringNo"mcp-default"Channel identifier for message routing.
requestTimeoutnumberNo10000Timeout in milliseconds for requests. If the server does not respond in time, a JSON-RPC error with code -32000 is synthesized.

Properties

PropertyTypeDescription
serverReadyPromisePromise<void>Resolves when the server signals readiness. send() awaits this automatically.

Server discovery

TabClientTransport does not have a standalone discover() method. The server-ready handshake (mcp-check-ready / mcp-server-ready) is handled internally during start().

Iframe transports

Cross-origin communication between a parent page and an iframe. Uses window.postMessage with retry-based ready handshakes to handle iframe loading timing.

IframeParentTransport

Client-side transport for the parent page. Sends messages into the iframe’s contentWindow.
import { IframeParentTransport } from "@mcp-b/transports";

const iframe = document.querySelector("iframe");
const transport = new IframeParentTransport({
  iframe,
  targetOrigin: "https://iframe-app.com",
});
await client.connect(transport);

IframeParentTransportOptions

OptionTypeRequiredDefaultDescription
iframeHTMLIFrameElementYesReference to the iframe element.
targetOriginstringNo"*"Expected origin of the iframe for postMessage security.
channelIdstringNo"mcp-iframe"Channel identifier for message routing.
checkReadyRetryMsnumberNo250Interval in milliseconds to retry the ready handshake if the iframe is not yet ready.

Properties

PropertyTypeDescription
serverReadyPromisePromise<void>Resolves when the iframe’s server signals readiness.

IframeChildTransport

Server-side transport for code running inside an iframe. Sends messages to window.parent.
import { IframeChildTransport } from "@mcp-b/transports";

const transport = new IframeChildTransport({
  allowedOrigins: ["https://parent-app.com"],
});
await server.connect(transport);

IframeChildTransportOptions

OptionTypeRequiredDefaultDescription
allowedOriginsstring[]YesParent origins permitted to connect. Use ["*"] to allow any origin.
channelIdstringNo"mcp-iframe"Channel identifier for message routing.
serverReadyRetryMsnumberNo250Interval in milliseconds to retry broadcasting the ready signal to the parent.

Extension transports

Communication between Chrome extension components using chrome.runtime.Port. Used for connecting content scripts, sidebars, and popups to the extension’s background service worker.

ExtensionServerTransport

Wraps a chrome.runtime.Port to handle a single client connection. Runs in the background service worker.
import { ExtensionServerTransport } from "@mcp-b/transports";

chrome.runtime.onConnect.addListener((port) => {
  if (port.name === "mcp") {
    const transport = new ExtensionServerTransport(port);
    await server.connect(transport);
  }
});

Constructor

new ExtensionServerTransport(port: chrome.runtime.Port, options?: ExtensionServerTransportOptions)

ExtensionServerTransportOptions

OptionTypeRequiredDefaultDescription
keepAlivebooleanNotrueSend periodic keep-alive pings to prevent service worker shutdown.
keepAliveIntervalnumberNo1000Keep-alive interval in milliseconds.

Methods

MethodReturnsDescription
getConnectionInfo(){ connectedAt, lastMessageAt, messageCount, uptime, isConnected }Connection statistics.

ExtensionClientTransport

Connects to the extension’s background service worker via chrome.runtime.connect. Supports automatic reconnection with exponential backoff.
import { ExtensionClientTransport } from "@mcp-b/transports";

const transport = new ExtensionClientTransport({
  portName: "mcp",
});
await client.connect(transport);

ExtensionClientTransportOptions

OptionTypeRequiredDefaultDescription
extensionIdstringNoExtension ID to connect to. Omit for same-extension connections. When provided, uses chrome.runtime.connect(extensionId, ...) for cross-extension communication.
portNamestringNo"mcp"Port name for the connection.
autoReconnectbooleanNotrueReconnect automatically on disconnect.
maxReconnectAttemptsnumberNo10Maximum reconnection attempts before giving up.
reconnectDelaynumberNo1000Initial reconnection delay in milliseconds.
maxReconnectDelaynumberNo30000Maximum reconnection delay in milliseconds.
reconnectBackoffMultipliernumberNo1.5Multiplier applied to the delay after each failed attempt.

User script transports

Communication between Chrome MV3 user scripts and the extension’s background service worker. Connections arrive via chrome.runtime.onUserScriptConnect on the extension side.

UserScriptServerTransport

Identical in API to ExtensionServerTransport. Wraps a chrome.runtime.Port received from a user script connection.
import { UserScriptServerTransport } from "@mcp-b/transports";

chrome.runtime.onUserScriptConnect.addListener((port) => {
  const transport = new UserScriptServerTransport(port);
  await server.connect(transport);
});

UserScriptServerTransportOptions

OptionTypeRequiredDefaultDescription
keepAlivebooleanNotrueSend periodic keep-alive pings.
keepAliveIntervalnumberNo1000Keep-alive interval in milliseconds.

UserScriptClientTransport

Identical in API to ExtensionClientTransport. Connects from a user script context to the extension background.

UserScriptClientTransportOptions

OptionTypeRequiredDefaultDescription
extensionIdstringNoExtension ID to connect to.
portNamestringNo"mcp"Port name for the connection.
autoReconnectbooleanNotrueReconnect automatically on disconnect.
maxReconnectAttemptsnumberNo10Maximum reconnection attempts.
reconnectDelaynumberNo1000Initial reconnection delay in milliseconds.
maxReconnectDelaynumberNo30000Maximum reconnection delay in milliseconds.
reconnectBackoffMultipliernumberNo1.5Backoff multiplier per failed attempt.

Security

All transports enforce origin or connection validation:
Transport typeSecurity mechanism
TaballowedOrigins on server, targetOrigin on client. Messages from non-matching origins are silently dropped.
IframeSame as tab transports. Cross-origin postMessage with origin checks on both sides.
ExtensionChrome’s port-based messaging API. Only code within the extension (or extensions listed in externally_connectable) can connect.
Extension ExternalRequires externally_connectable in manifest.json. Server should validate port.sender.id before accepting connections.
User ScriptChrome’s onUserScriptConnect API. Only registered user scripts can initiate connections.
Setting allowedOrigins to ["*"] or targetOrigin to "*" disables origin validation. Use specific origins in production.

Common transport interface

Every transport class implements the MCP Transport interface:
MemberTypeDescription
start()Promise<void>Begin listening for messages.
send(message)Promise<void>Send a JSON-RPC 2.0 message.
close()Promise<void>Stop listening and clean up resources.
onmessage(message: JSONRPCMessage) => voidCallback for incoming messages.
onerror(error: Error) => voidCallback for errors.
onclose() => voidCallback when the transport closes.