> ## 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/transports reference

> Reference for browser transport classes, options, and transport-specific security boundaries.

Browser transport implementations for the Model Context Protocol. Each class
implements the MCP `Transport` interface and handles JSON-RPC message parsing
and connection lifecycle. The `postMessage` transports validate configured
origins; extension transports operate on Chrome `runtime.Port` objects accepted
by application code.

## Installation

```bash title="Install transports" theme={null}
pnpm add @mcp-b/transports @modelcontextprotocol/server
```

Install `@modelcontextprotocol/client` when you use a client transport.

The package does not install Chrome extension globals. Extension applications
must declare their own types with either `@types/chrome` or `chrome-types` as a
development dependency. Page and iframe transports need neither.

## Minimal example

```ts title="Serve MCP in the current window" theme={null}
import { TabServerTransport } from '@mcp-b/transports';
import { McpServer } from '@modelcontextprotocol/server';

const server = new McpServer({ name: 'my-app', version: '1.0.0' });
const transport = new TabServerTransport({
  allowedOrigins: ['*'],
});

await server.connect(transport);
```

## Which transport to use

| Scenario | Server | Client |
| - | - | - |
| Same-window communication | `TabServerTransport` | `TabClientTransport` |
| Parent page to iframe | `IframeChildTransport` in the iframe | `IframeParentTransport` in the parent |
| Extension component to background | `ExtensionServerTransport` in the receiver | `ExtensionClientTransport` in the initiating context |
| External extension or allowed page | `ExtensionServerTransport` in the host extension | `ExtensionClientTransport` with the host extension ID |

***

## Tab transports

In-page communication via `window.postMessage`. Both server and client run in
the same `Window`; these classes do not discover or connect to another tab.

### TabServerTransport

Listens for MCP messages on the current window. Broadcasts a `mcp-server-ready` signal on start.

```typescript title="Tab server transport" theme={null}
import { TabServerTransport } from '@mcp-b/transports';

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

#### TabServerTransportOptions

| Option | Type | Required | Default | Description |
| - | - | - | - | - |
| `allowedOrigins` | `string[]` | Yes | -- | Origins permitted to send messages. Use `["*"]` to allow any origin. |
| `channelId` | `string` | No | `"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.
* Posts `mcp-server-stopped` on close.

### TabClientTransport

Connects to a `TabServerTransport` in the same window. Waits for a server-ready handshake before sending messages.

```typescript title="Tab client transport" theme={null}
import { TabClientTransport } from '@mcp-b/transports';

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

#### TabClientTransportOptions

| Option | Type | Required | Default | Description |
| - | - | - | - | - |
| `targetOrigin` | `string` | Yes | -- | Expected origin of the server window. Pass `"*"` only to disable origin validation deliberately. |
| `channelId` | `string` | No | `"mcp-default"` | Channel identifier for message routing. |

#### Properties

| Property | Type | Description |
| - | - | - |
| `serverReadyPromise` | `Promise<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

Same-origin or cross-origin communication between a parent page and an iframe.
It uses `window.postMessage` with a ready handshake to handle iframe loading
timing.

### IframeParentTransport

Client-side transport for the parent page. Sends messages into the iframe's `contentWindow`.

```typescript title="Iframe parent transport" theme={null}
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

| Option | Type | Required | Default | Description |
| - | - | - | - | - |
| `iframe` | `HTMLIFrameElement` | Yes | -- | Reference to the iframe element. |
| `targetOrigin` | `string` | Yes | -- | Expected origin of the iframe. Pass `"*"` only to disable origin validation deliberately. |
| `channelId` | `string` | No | `"mcp-iframe"` | Channel identifier for message routing. |
| `checkReadyRetryMs` | `number` | No | `250` | Retry interval while `iframe.contentWindow` is unavailable. |

#### Properties

| Property | Type | Description |
| - | - | - |
| `serverReadyPromise` | `Promise<void>` | Resolves when the iframe's server signals readiness. |

### IframeChildTransport

Server-side transport for code running inside an iframe. Sends messages to `window.parent`.

```typescript title="Iframe child transport" theme={null}
import { IframeChildTransport } from '@mcp-b/transports';

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

#### IframeChildTransportOptions

| Option | Type | Required | Default | Description |
| - | - | - | - | - |
| `allowedOrigins` | `string[]` | Yes | -- | Parent origins permitted to connect. Use `["*"]` to allow any origin. |
| `channelId` | `string` | No | `"mcp-iframe"` | Channel identifier for message routing. |

#### Properties

| Property | Type | Description |
| - | - | - |
| `clientOrigin` | `string \| undefined` | Origin of the connected parent, `undefined` until its first message arrives. |
| `onclientorigin` | `((origin: string) => void)?` | Called when the parent origin is first established, and again on a takeover. |

[`BrowserMcpServer`](/packages/webmcp-ts-sdk/reference) reads both to scope tools
registered with `exposedTo` to the embedder actually connected.

***

## Extension transports

Communication over Chrome `runtime.Port` objects. Same-extension content
scripts, sidebars, and popups can initiate a connection to a receiving extension
context. Another extension or a web page allowed by `externally_connectable` can
initiate an external connection by supplying the host extension ID. These
transports do not let an extension initiate a connection into an ordinary web
page, and they are not generic user-script transports. See Chrome's
[message passing documentation](https://developer.chrome.com/docs/extensions/develop/concepts/messaging).

### ExtensionServerTransport

Wraps one accepted `chrome.runtime.Port` and therefore handles one MCP client
session. It commonly runs in the extension's background service worker, but the
class itself accepts a port from any receiving extension context.

```typescript title="Accept same-extension connections" theme={null}
import { ExtensionServerTransport } from '@mcp-b/transports';
import { McpServer } from '@modelcontextprotocol/server';

function createServer() {
  const server = new McpServer({
    name: 'extension-background',
    version: '1.0.0',
  });
  // Register this session's tools, resources, and prompts here.
  return server;
}

chrome.runtime.onConnect.addListener((port) => {
  if (port.name === 'mcp') {
    const server = createServer();
    const transport = new ExtensionServerTransport(port);
    void server.connect(transport);
  }
});
```

An MCP server owns one protocol session. Create a fresh server for every port; keep shared
application state outside `createServer()` when clients need to see the same data.

For external connections, listen with `chrome.runtime.onConnectExternal`, check
`port.sender.id` or `port.sender.url`, and construct the transport only after the
sender passes your allowlist.

#### Constructor

```typescript title="Extension server transport constructor" theme={null}
new ExtensionServerTransport(port: ExtensionPort, options?: ExtensionServerTransportOptions)
```

`ExtensionPort` is exported by `@mcp-b/transports`. It contains `postMessage`,
`disconnect`, and the `addListener`/`removeListener` methods on `onMessage` and
`onDisconnect`. Chrome `runtime.Port` objects from either declaration package
satisfy this interface.

#### ExtensionServerTransportOptions

| Option | Type | Required | Default | Description |
| - | - | - | - | - |
| `keepAlive` | `boolean` | No | `true` | Send periodic keep-alive messages over the port. |
| `keepAliveInterval` | `number` | No | `25000` | Keep-alive interval in milliseconds. |

#### Methods

| Method | Returns | Description |
| - | - | - |
| `getConnectionInfo()` | `{ connectedAt, lastMessageAt, messageCount, uptime, isConnected }` | Connection statistics. |

### ExtensionClientTransport

Calls `chrome.runtime.connect` to open a port. Omit `extensionId` for another
context in the same extension; provide it for an external host extension. The
caller must have access to the Chrome runtime messaging API. A port disconnect
closes the MCP connection; create a new transport and reconnect the client to
start a new server session.

```typescript title="Extension client transport" theme={null}
import { ExtensionClientTransport } from '@mcp-b/transports';

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

#### ExtensionClientTransportOptions

| Option | Type | Required | Default | Description |
| - | - | - | - | - |
| `extensionId` | `string` | No | -- | Host extension ID. Omit for same-extension connections; provide it for external extension or allowed-page connections. |
| `portName` | `string` | No | `"mcp"` | Port name for the connection. |

***

## Security

Security is split between the transport and the code that creates it:

| Transport type | Security mechanism |
| - | - |
| Tab | Server `allowedOrigins`, client `targetOrigin`, channel ID, and same-window source checks |
| Iframe | Child `allowedOrigins`, parent `targetOrigin`, channel ID, and parent/child window source checks |
| Same-extension port | Chrome runtime routing plus the receiving code's port-name and sender validation |
| External port | `externally_connectable` policy when configured, `onConnectExternal`, and receiver validation of `port.sender.id` or `port.sender.url` |

`ExtensionServerTransport` does not maintain its own sender allowlist. Validate
an incoming port before passing it to the constructor. Chrome permits external
extensions by default when `externally_connectable` is absent, while ordinary
web pages require an explicit `matches` entry.

<Warning>
  Setting `allowedOrigins` to `["*"]` or `targetOrigin` to `"*"` disables origin validation. Use
  specific origins in production.
</Warning>

## Common transport interface

Every transport class implements the MCP `Transport` interface:

| Member | Type | Description |
| - | - | - |
| `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) => void` | Callback for incoming messages. |
| `onerror` | `(error: Error) => void` | Callback for errors. |
| `onclose` | `() => void` | Callback when the transport closes. |

## Related

* [Transports and bridges](/explanation/architecture/transports-and-bridges) for the conceptual architecture behind transport design
* [Bridge tools across iframes](/how-to/bridge-tools-across-iframes) for a step-by-step iframe bridging guide
* [Connect desktop agents with local relay](/how-to/connect-desktop-agents-with-local-relay) for stdio-to-browser bridging
* [@mcp-b/mcp-iframe](/packages/mcp-iframe/reference) for the high-level `<mcp-iframe>` custom element that wraps iframe transports
* [WebMCP API sources](/reference/webmcp/standard-api) for native cross-document discovery without an MCP transport
* [Chrome extension message passing](https://developer.chrome.com/docs/extensions/develop/concepts/messaging) for runtime port ownership and external connections


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