@mcp-b/transports 5.0.3 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/post-message.ts","../src/ExtensionClientTransport.ts","../src/ExtensionServerTransport.ts","../src/IframeChildTransport.ts","../src/IframeParentTransport.ts","../src/TabClientTransport.ts","../src/TabServerTransport.ts"],"sourcesContent":["/**\n * Channel discriminators. Both ends of a pair must agree or the envelope check in\n * `isMcpMessage` silently drops every message — no error, just a hang.\n */\nexport const DEFAULT_TAB_CHANNEL_ID = 'mcp-default';\nexport const DEFAULT_IFRAME_CHANNEL_ID = 'mcp-iframe';\n\ntype McpMessageDirection = 'client-to-server' | 'server-to-client';\n\ninterface McpMessageEnvelope {\n payload: unknown;\n}\n\nexport function isMcpMessage(\n data: unknown,\n channelId: string,\n direction: McpMessageDirection\n): data is McpMessageEnvelope {\n return (\n typeof data === 'object' &&\n data !== null &&\n Reflect.get(data, 'channel') === channelId &&\n Reflect.get(data, 'type') === 'mcp' &&\n Reflect.get(data, 'direction') === direction &&\n Reflect.has(data, 'payload')\n );\n}\n\nexport function postMcpMessage(\n target: Window,\n targetOrigin: string,\n channelId: string,\n direction: McpMessageDirection,\n payload: unknown\n): void {\n target.postMessage({ channel: channelId, type: 'mcp', direction, payload }, targetOrigin);\n}\n","import { JSONRPCMessageSchema } from '@modelcontextprotocol/core';\nimport type { JSONRPCMessage, Transport, TransportSendOptions } from '@modelcontextprotocol/server';\n\nexport interface ExtensionClientTransportOptions {\n /** Extension ID to connect to. Omit for the current extension. */\n extensionId?: string;\n /** Port name (default: `mcp`). */\n portName?: string;\n}\n\n/** Client transport for Chrome extension Port messaging. */\nexport class ExtensionClientTransport implements Transport {\n private _port: chrome.runtime.Port | undefined;\n private readonly _extensionId: string | undefined;\n private readonly _portName: string;\n private _messageHandler: ((message: unknown) => void) | undefined;\n private _disconnectHandler: (() => void) | undefined;\n private _started = false;\n private _closed = false;\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: Transport['onmessage'];\n\n constructor(options: ExtensionClientTransportOptions = {}) {\n this._extensionId = options.extensionId;\n this._portName = options.portName ?? 'mcp';\n }\n\n async start(): Promise<void> {\n if (this._closed) {\n throw new Error('ExtensionClientTransport cannot be restarted after it closes');\n }\n if (this._started) {\n throw new Error('Transport already started');\n }\n\n const runtime = globalThis.chrome?.runtime;\n if (!runtime?.connect) {\n throw new Error(\n 'Chrome runtime API not available. This transport must be used in a Chrome extension context.'\n );\n }\n\n try {\n const port = this._extensionId\n ? runtime.connect(this._extensionId, { name: this._portName })\n : runtime.connect({ name: this._portName });\n this._port = port;\n this._messageHandler = (message: unknown) => {\n if (\n typeof message === 'object' &&\n message !== null &&\n Reflect.get(message, 'type') === 'keep-alive'\n ) {\n return;\n }\n\n try {\n const mcpMessage = JSONRPCMessageSchema.parse(message);\n this.onmessage?.(mcpMessage);\n } catch (error) {\n this.onerror?.(\n new Error(\n `Failed to parse message: ${error instanceof Error ? error.message : String(error)}`\n )\n );\n }\n };\n this._disconnectHandler = () => this._finishClose(false);\n port.onMessage.addListener(this._messageHandler);\n port.onDisconnect.addListener(this._disconnectHandler);\n this._started = true;\n } catch (error) {\n this._cleanup();\n throw error;\n }\n }\n\n async send(message: JSONRPCMessage, _options?: TransportSendOptions): Promise<void> {\n if (this._closed) throw new Error('Transport is closed');\n if (!this._started) throw new Error('Transport not started');\n\n const port = this._port;\n if (!port) throw new Error('Not connected');\n\n try {\n port.postMessage(message);\n } catch (error) {\n this._finishClose(true);\n throw new Error(`Failed to send message: ${error}`);\n }\n }\n\n async close(): Promise<void> {\n this._finishClose(true);\n }\n\n private _finishClose(disconnectPort: boolean): void {\n if (this._closed) return;\n\n const port = this._port;\n this._closed = true;\n this._started = false;\n this._cleanup();\n\n if (disconnectPort && port) {\n try {\n port.disconnect();\n } catch {\n // The remote endpoint may already have disconnected.\n }\n }\n this.onclose?.();\n }\n\n private _cleanup(): void {\n if (this._port && this._messageHandler) {\n this._port.onMessage.removeListener(this._messageHandler);\n }\n if (this._port && this._disconnectHandler) {\n this._port.onDisconnect.removeListener(this._disconnectHandler);\n }\n this._port = undefined;\n this._messageHandler = undefined;\n this._disconnectHandler = undefined;\n }\n}\n","import { JSONRPCMessageSchema } from '@modelcontextprotocol/core';\nimport type { JSONRPCMessage, Transport, TransportSendOptions } from '@modelcontextprotocol/server';\n\n/** The runtime Port methods used by this transport, independent of Chrome ambient types. */\nexport interface ExtensionPort {\n postMessage(message: unknown): void;\n disconnect(): void;\n onMessage: {\n addListener(callback: (message: unknown) => void): void;\n removeListener(callback: (message: unknown) => void): void;\n };\n onDisconnect: {\n addListener(callback: () => void): void;\n removeListener(callback: () => void): void;\n };\n}\n\nexport interface ExtensionServerTransportOptions {\n /** Send keep-alive messages (default: true). */\n keepAlive?: boolean;\n /** Keep-alive interval in milliseconds (default: 25,000). */\n keepAliveInterval?: number;\n}\n\n/** Server transport for one Chrome extension Port connection. */\nexport class ExtensionServerTransport implements Transport {\n private _port: ExtensionPort | undefined;\n private _started = false;\n private _closed = false;\n private _messageHandler: ((message: unknown) => void) | undefined;\n private _disconnectHandler: (() => void) | undefined;\n private _keepAliveTimer: ReturnType<typeof setInterval> | undefined;\n private readonly _keepAliveInterval: number | undefined;\n private readonly _connectionInfo = {\n connectedAt: Date.now(),\n lastMessageAt: Date.now(),\n messageCount: 0,\n };\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: Transport['onmessage'];\n\n constructor(port: ExtensionPort, options: ExtensionServerTransportOptions = {}) {\n this._port = port;\n this._keepAliveInterval =\n options.keepAlive === false ? undefined : (options.keepAliveInterval ?? 25_000);\n }\n\n async start(): Promise<void> {\n if (this._closed) {\n throw new Error('ExtensionServerTransport cannot be restarted after it closes');\n }\n if (this._started) {\n throw new Error(\n 'ExtensionServerTransport already started! If using Server class, note that connect() calls start() automatically.'\n );\n }\n\n const port = this._port;\n if (!port) throw new Error('Port not available');\n\n this._messageHandler = (message: unknown) => {\n this._connectionInfo.lastMessageAt = Date.now();\n this._connectionInfo.messageCount++;\n try {\n const mcpMessage = JSONRPCMessageSchema.parse(message);\n this.onmessage?.(mcpMessage);\n } catch (error) {\n this.onerror?.(\n new Error(\n `Failed to parse message: ${error instanceof Error ? error.message : String(error)}`\n )\n );\n }\n };\n this._disconnectHandler = () => this._finishClose(false);\n port.onMessage.addListener(this._messageHandler);\n port.onDisconnect.addListener(this._disconnectHandler);\n this._started = true;\n\n if (this._keepAliveInterval !== undefined) {\n this._keepAliveTimer = setInterval(() => {\n try {\n port.postMessage({ type: 'keep-alive', timestamp: Date.now() });\n } catch (error) {\n this.onerror?.(\n new Error(\n `Keep-alive failed: ${error instanceof Error ? error.message : String(error)}`\n )\n );\n this._finishClose(true);\n }\n }, this._keepAliveInterval);\n }\n }\n\n async send(message: JSONRPCMessage, _options?: TransportSendOptions): Promise<void> {\n if (this._closed) throw new Error('Transport is closed');\n if (!this._started) throw new Error('Transport not started');\n\n const port = this._port;\n if (!port) throw new Error('Not connected to client');\n\n try {\n port.postMessage(message);\n } catch (error) {\n this._finishClose(true);\n throw new Error(`Client disconnected: ${error}`);\n }\n }\n\n async close(): Promise<void> {\n this._finishClose(true);\n }\n\n getConnectionInfo() {\n return {\n ...this._connectionInfo,\n uptime: Date.now() - this._connectionInfo.connectedAt,\n isConnected: this._port !== undefined && this._started && !this._closed,\n };\n }\n\n private _finishClose(disconnectPort: boolean): void {\n if (this._closed) return;\n\n const port = this._port;\n this._closed = true;\n this._started = false;\n this._cleanup();\n\n if (disconnectPort && port) {\n try {\n port.disconnect();\n } catch {\n // The remote endpoint may already have disconnected.\n }\n }\n this.onclose?.();\n }\n\n private _cleanup(): void {\n if (this._keepAliveTimer !== undefined) {\n clearInterval(this._keepAliveTimer);\n this._keepAliveTimer = undefined;\n }\n if (this._port && this._messageHandler) {\n this._port.onMessage.removeListener(this._messageHandler);\n }\n if (this._port && this._disconnectHandler) {\n this._port.onDisconnect.removeListener(this._disconnectHandler);\n }\n this._port = undefined;\n this._messageHandler = undefined;\n this._disconnectHandler = undefined;\n }\n}\n","import { JSONRPCMessageSchema } from '@modelcontextprotocol/core';\nimport type { JSONRPCMessage, Transport, TransportSendOptions } from '@modelcontextprotocol/server';\nimport { DEFAULT_IFRAME_CHANNEL_ID, isMcpMessage, postMcpMessage } from './post-message.js';\n\nexport interface IframeChildTransportOptions {\n /** Parent origins allowed to connect. Pass `['*']` only to disable validation deliberately. */\n allowedOrigins: readonly string[];\n /** Channel name (default: `mcp-iframe`). */\n channelId?: string;\n}\n\n/** Server transport for an MCP server running inside an iframe. */\nexport class IframeChildTransport implements Transport {\n private _started = false;\n private _closed = false;\n private readonly _allowedOrigins: ReadonlySet<string>;\n private readonly _channelId: string;\n private _messageHandler: ((event: MessageEvent<unknown>) => void) | undefined;\n private _clientOrigin?: string;\n\n /**\n * Fires when the connected parent's origin is first established, and again if a\n * different parent takes over the channel. Lets a server scope per-tool exposure\n * to the embedder it is actually talking to.\n */\n onclientorigin?: (origin: string) => void;\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: Transport['onmessage'];\n\n constructor(options: IframeChildTransportOptions) {\n if (!options.allowedOrigins?.length) {\n throw new Error('At least one allowed origin must be specified');\n }\n\n this._allowedOrigins = new Set(options.allowedOrigins);\n this._channelId = options.channelId ?? DEFAULT_IFRAME_CHANNEL_ID;\n }\n\n /** Origin of the connected parent, or `undefined` before its first message arrives. */\n get clientOrigin(): string | undefined {\n return this._clientOrigin;\n }\n\n async start(): Promise<void> {\n if (this._closed) {\n throw new Error('IframeChildTransport cannot be restarted after it closes');\n }\n if (this._started) throw new Error('Transport already started');\n\n this._messageHandler = (event: MessageEvent<unknown>) => {\n if (\n event.source !== window.parent ||\n (!this._allowedOrigins.has('*') && !this._allowedOrigins.has(event.origin)) ||\n !isMcpMessage(event.data, this._channelId, 'client-to-server')\n ) {\n return;\n }\n\n if (this._clientOrigin !== event.origin) {\n this._clientOrigin = event.origin;\n this.onclientorigin?.(event.origin);\n }\n const { payload } = event.data;\n if (payload === 'mcp-check-ready') {\n this._broadcastServerReady();\n return;\n }\n\n try {\n const message = JSONRPCMessageSchema.parse(payload);\n this.onmessage?.(message);\n } catch (error) {\n this.onerror?.(\n new Error(`Invalid message: ${error instanceof Error ? error.message : String(error)}`)\n );\n }\n };\n\n window.addEventListener('message', this._messageHandler);\n this._started = true;\n this._broadcastServerReady();\n }\n\n async send(message: JSONRPCMessage, _options?: TransportSendOptions): Promise<void> {\n if (this._closed) throw new Error('Transport is closed');\n if (!this._started) throw new Error('Transport not started');\n if (!this._clientOrigin) {\n console.debug('[IframeChildTransport] No client connected, message not sent');\n return;\n }\n if (window.parent === window) {\n console.debug('[IframeChildTransport] Not running in an iframe, message not sent');\n return;\n }\n\n postMcpMessage(window.parent, this._clientOrigin, this._channelId, 'server-to-client', message);\n }\n\n async close(): Promise<void> {\n if (this._closed) return;\n\n this._closed = true;\n this._started = false;\n if (this._messageHandler) {\n window.removeEventListener('message', this._messageHandler);\n this._messageHandler = undefined;\n }\n if (this._clientOrigin && window.parent !== window) {\n postMcpMessage(\n window.parent,\n this._clientOrigin,\n this._channelId,\n 'server-to-client',\n 'mcp-server-stopped'\n );\n }\n this.onclose?.();\n }\n\n private _broadcastServerReady(): void {\n if (window.parent !== window) {\n postMcpMessage(window.parent, '*', this._channelId, 'server-to-client', 'mcp-server-ready');\n }\n }\n}\n","import { JSONRPCMessageSchema } from '@modelcontextprotocol/core';\nimport type { JSONRPCMessage, Transport, TransportSendOptions } from '@modelcontextprotocol/server';\nimport { DEFAULT_IFRAME_CHANNEL_ID, isMcpMessage, postMcpMessage } from './post-message.js';\n\nexport interface IframeParentTransportOptions {\n iframe: HTMLIFrameElement;\n /** Expected iframe origin. Pass `'*'` only to disable origin validation deliberately. */\n targetOrigin: string;\n /** Channel name (default: `mcp-iframe`). */\n channelId?: string;\n /** Retry interval while `iframe.contentWindow` is unavailable (default: 250ms). */\n checkReadyRetryMs?: number;\n}\n\n/** Client transport for an MCP server running inside an iframe. */\nexport class IframeParentTransport implements Transport {\n private _started = false;\n private _closed = false;\n private readonly _iframe: HTMLIFrameElement;\n private readonly _targetOrigin: string;\n private readonly _channelId: string;\n private readonly _checkReadyRetryMs: number;\n private _messageHandler: ((event: MessageEvent<unknown>) => void) | undefined;\n private _checkReadyTimeout: ReturnType<typeof setTimeout> | undefined;\n private _serverReadyResolve!: () => void;\n private _serverReadyReject!: (reason: unknown) => void;\n private _serverReadySettled = false;\n\n readonly serverReadyPromise: Promise<void>;\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: Transport['onmessage'];\n\n constructor(options: IframeParentTransportOptions) {\n if (!options.iframe) throw new Error('iframe element is required');\n if (!options.targetOrigin) {\n throw new Error('targetOrigin must be explicitly set for security');\n }\n\n this._iframe = options.iframe;\n this._targetOrigin = options.targetOrigin;\n this._channelId = options.channelId ?? DEFAULT_IFRAME_CHANNEL_ID;\n this._checkReadyRetryMs = options.checkReadyRetryMs ?? 250;\n this.serverReadyPromise = new Promise<void>((resolve, reject) => {\n this._serverReadyResolve = resolve;\n this._serverReadyReject = reject;\n });\n // Closing an unused transport is valid; late consumers still observe the rejection.\n void this.serverReadyPromise.catch(() => {});\n }\n\n async start(): Promise<void> {\n if (this._closed) {\n throw new Error('IframeParentTransport cannot be restarted after it closes');\n }\n if (this._started) throw new Error('Transport already started');\n\n this._messageHandler = (event: MessageEvent<unknown>) => {\n if (\n event.source !== this._iframe.contentWindow ||\n (this._targetOrigin !== '*' && event.origin !== this._targetOrigin) ||\n !isMcpMessage(event.data, this._channelId, 'server-to-client')\n ) {\n return;\n }\n\n const { payload } = event.data;\n if (payload === 'mcp-server-ready') {\n this._resolveServerReady();\n this._clearCheckReadyRetry();\n return;\n }\n if (payload === 'mcp-server-stopped') {\n void this.close();\n return;\n }\n\n try {\n const message = JSONRPCMessageSchema.parse(payload);\n this._resolveServerReady();\n this.onmessage?.(message);\n } catch (error) {\n this.onerror?.(\n new Error(`Invalid message: ${error instanceof Error ? error.message : String(error)}`)\n );\n }\n };\n\n window.addEventListener('message', this._messageHandler);\n this._started = true;\n this._sendCheckReady();\n }\n\n async send(message: JSONRPCMessage, _options?: TransportSendOptions): Promise<void> {\n if (this._closed) throw new Error('Transport is closed');\n if (!this._started) throw new Error('Transport not started');\n\n await this.serverReadyPromise;\n if (this._closed) throw new Error('Transport is closed');\n\n const contentWindow = this._iframe.contentWindow;\n if (!contentWindow) throw new Error('iframe.contentWindow not available');\n postMcpMessage(contentWindow, this._targetOrigin, this._channelId, 'client-to-server', message);\n }\n\n async close(): Promise<void> {\n if (this._closed) return;\n\n this._closed = true;\n this._started = false;\n if (this._messageHandler) {\n window.removeEventListener('message', this._messageHandler);\n this._messageHandler = undefined;\n }\n this._clearCheckReadyRetry();\n if (!this._serverReadySettled) {\n this._serverReadySettled = true;\n this._serverReadyReject(new Error('Transport closed before server ready'));\n }\n this.onclose?.();\n }\n\n private _sendCheckReady(): void {\n const contentWindow = this._iframe.contentWindow;\n if (contentWindow) {\n postMcpMessage(\n contentWindow,\n this._targetOrigin,\n this._channelId,\n 'client-to-server',\n 'mcp-check-ready'\n );\n return;\n }\n\n this._checkReadyTimeout ??= setTimeout(() => {\n this._checkReadyTimeout = undefined;\n if (this._started) this._sendCheckReady();\n }, this._checkReadyRetryMs);\n }\n\n private _clearCheckReadyRetry(): void {\n if (this._checkReadyTimeout === undefined) return;\n clearTimeout(this._checkReadyTimeout);\n this._checkReadyTimeout = undefined;\n }\n\n private _resolveServerReady(): void {\n if (this._serverReadySettled) return;\n this._serverReadySettled = true;\n this._serverReadyResolve();\n }\n}\n","import { JSONRPCMessageSchema } from '@modelcontextprotocol/core';\nimport type { JSONRPCMessage, Transport, TransportSendOptions } from '@modelcontextprotocol/server';\nimport { DEFAULT_TAB_CHANNEL_ID, isMcpMessage, postMcpMessage } from './post-message.js';\n\nexport interface TabClientTransportOptions {\n /** Expected server origin. Pass `'*'` only to disable origin validation deliberately. */\n targetOrigin: string;\n /** Channel name (default: `mcp-default`). */\n channelId?: string;\n}\n\n/** Client transport for an MCP server running in the same window. */\nexport class TabClientTransport implements Transport {\n private _started = false;\n private _closed = false;\n private readonly _targetOrigin: string;\n private readonly _channelId: string;\n private _messageHandler: ((event: MessageEvent<unknown>) => void) | undefined;\n private _serverReadyResolve!: () => void;\n private _serverReadyReject!: (reason: unknown) => void;\n private _serverReadySettled = false;\n\n /** Resolves when the server signals readiness. `send()` awaits it automatically. */\n readonly serverReadyPromise: Promise<void>;\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: Transport['onmessage'];\n\n constructor(options: TabClientTransportOptions) {\n if (!options.targetOrigin) {\n throw new Error('targetOrigin must be explicitly set for security');\n }\n\n this._targetOrigin = options.targetOrigin;\n this._channelId = options.channelId ?? DEFAULT_TAB_CHANNEL_ID;\n this.serverReadyPromise = new Promise<void>((resolve, reject) => {\n this._serverReadyResolve = resolve;\n this._serverReadyReject = reject;\n });\n // Closing an unused transport is valid; late consumers still observe the rejection.\n void this.serverReadyPromise.catch(() => {});\n }\n\n async start(): Promise<void> {\n if (this._closed) {\n throw new Error('TabClientTransport cannot be restarted after it closes');\n }\n if (this._started) {\n throw new Error('Transport already started');\n }\n\n this._messageHandler = (event: MessageEvent<unknown>) => {\n if (\n event.source !== window ||\n (this._targetOrigin !== '*' && event.origin !== this._targetOrigin) ||\n !isMcpMessage(event.data, this._channelId, 'server-to-client')\n ) {\n return;\n }\n\n const { payload } = event.data;\n if (payload === 'mcp-server-ready') {\n this._resolveServerReady();\n return;\n }\n if (payload === 'mcp-server-stopped') {\n void this.close();\n return;\n }\n\n try {\n const message = JSONRPCMessageSchema.parse(payload);\n this._resolveServerReady();\n this.onmessage?.(message);\n } catch (error) {\n this.onerror?.(\n new Error(`Invalid message: ${error instanceof Error ? error.message : String(error)}`)\n );\n }\n };\n\n window.addEventListener('message', this._messageHandler);\n this._started = true;\n postMcpMessage(\n window,\n this._targetOrigin,\n this._channelId,\n 'client-to-server',\n 'mcp-check-ready'\n );\n }\n\n async send(message: JSONRPCMessage, _options?: TransportSendOptions): Promise<void> {\n if (this._closed) throw new Error('Transport is closed');\n if (!this._started) throw new Error('Transport not started');\n\n await this.serverReadyPromise;\n if (this._closed) throw new Error('Transport is closed');\n\n postMcpMessage(window, this._targetOrigin, this._channelId, 'client-to-server', message);\n }\n\n async close(): Promise<void> {\n if (this._closed) return;\n\n this._closed = true;\n this._started = false;\n if (this._messageHandler) {\n window.removeEventListener('message', this._messageHandler);\n this._messageHandler = undefined;\n }\n if (!this._serverReadySettled) {\n this._serverReadySettled = true;\n this._serverReadyReject(new Error('Transport closed before server ready'));\n }\n this.onclose?.();\n }\n\n private _resolveServerReady(): void {\n if (this._serverReadySettled) return;\n this._serverReadySettled = true;\n this._serverReadyResolve();\n }\n}\n","import { JSONRPCMessageSchema } from '@modelcontextprotocol/core';\nimport type { JSONRPCMessage, Transport, TransportSendOptions } from '@modelcontextprotocol/server';\nimport { DEFAULT_TAB_CHANNEL_ID, isMcpMessage, postMcpMessage } from './post-message.js';\n\nexport interface TabServerTransportOptions {\n /** Origins allowed to connect. Pass `['*']` only to disable origin validation deliberately. */\n allowedOrigins: readonly string[];\n /** Channel name (default: `mcp-default`). */\n channelId?: string;\n}\n\nexport class TabServerTransport implements Transport {\n private _started = false;\n private _closed = false;\n private readonly _allowedOrigins: ReadonlySet<string>;\n private readonly _channelId: string;\n private _messageHandler: ((event: MessageEvent<unknown>) => void) | undefined;\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: Transport['onmessage'];\n\n constructor(options: TabServerTransportOptions) {\n if (!options.allowedOrigins?.length) {\n throw new Error('At least one allowed origin must be specified');\n }\n\n this._allowedOrigins = new Set(options.allowedOrigins);\n this._channelId = options.channelId ?? DEFAULT_TAB_CHANNEL_ID;\n }\n\n async start(): Promise<void> {\n if (this._closed) {\n throw new Error('TabServerTransport cannot be restarted after it closes');\n }\n if (this._started) {\n throw new Error('Transport already started');\n }\n\n this._messageHandler = (event: MessageEvent<unknown>) => {\n if (\n event.source !== window ||\n (!this._allowedOrigins.has('*') && !this._allowedOrigins.has(event.origin)) ||\n !isMcpMessage(event.data, this._channelId, 'client-to-server')\n ) {\n return;\n }\n\n const { payload } = event.data;\n if (payload === 'mcp-check-ready') {\n postMcpMessage(window, '*', this._channelId, 'server-to-client', 'mcp-server-ready');\n return;\n }\n\n try {\n const message = JSONRPCMessageSchema.parse(payload);\n this.onmessage?.(message);\n } catch (error) {\n this.onerror?.(\n new Error(`Invalid message: ${error instanceof Error ? error.message : String(error)}`)\n );\n }\n };\n\n window.addEventListener('message', this._messageHandler);\n this._started = true;\n postMcpMessage(window, '*', this._channelId, 'server-to-client', 'mcp-server-ready');\n }\n\n async send(message: JSONRPCMessage, _options?: TransportSendOptions): Promise<void> {\n if (this._closed) throw new Error('Transport is closed');\n if (!this._started) throw new Error('Transport not started');\n\n postMcpMessage(window, '*', this._channelId, 'server-to-client', message);\n }\n\n async close(): Promise<void> {\n if (this._closed) return;\n\n this._closed = true;\n if (this._messageHandler) {\n window.removeEventListener('message', this._messageHandler);\n this._messageHandler = undefined;\n }\n if (this._started) {\n postMcpMessage(window, '*', this._channelId, 'server-to-client', 'mcp-server-stopped');\n }\n this._started = false;\n this.onclose?.();\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/post-message.ts","../src/ExtensionClientTransport.ts","../src/ExtensionServerTransport.ts","../src/IframeChildTransport.ts","../src/IframeParentTransport.ts","../src/TabClientTransport.ts","../src/TabServerTransport.ts"],"sourcesContent":["/**\n * Channel discriminators. Both ends of a pair must agree or the envelope check in\n * `isMcpMessage` silently drops every message — no error, just a hang.\n */\nexport const DEFAULT_TAB_CHANNEL_ID = 'mcp-default';\nexport const DEFAULT_IFRAME_CHANNEL_ID = 'mcp-iframe';\n\ntype McpMessageDirection = 'client-to-server' | 'server-to-client';\n\ninterface McpMessageEnvelope {\n payload: unknown;\n}\n\nexport function isMcpMessage(\n data: unknown,\n channelId: string,\n direction: McpMessageDirection\n): data is McpMessageEnvelope {\n return (\n typeof data === 'object' &&\n data !== null &&\n Reflect.get(data, 'channel') === channelId &&\n Reflect.get(data, 'type') === 'mcp' &&\n Reflect.get(data, 'direction') === direction &&\n Reflect.has(data, 'payload')\n );\n}\n\nexport function postMcpMessage(\n target: Window,\n targetOrigin: string,\n channelId: string,\n direction: McpMessageDirection,\n payload: unknown\n): void {\n target.postMessage({ channel: channelId, type: 'mcp', direction, payload }, targetOrigin);\n}\n","import { JSONRPCMessageSchema } from '@modelcontextprotocol/core';\nimport type { JSONRPCMessage, Transport, TransportSendOptions } from '@modelcontextprotocol/server';\n\nexport interface ExtensionClientTransportOptions {\n /** Extension ID to connect to. Omit for the current extension. */\n extensionId?: string;\n /** Port name (default: `mcp`). */\n portName?: string;\n}\n\n/** Client transport for Chrome extension Port messaging. */\nexport class ExtensionClientTransport implements Transport {\n private _port: chrome.runtime.Port | undefined;\n private readonly _extensionId: string | undefined;\n private readonly _portName: string;\n private _messageHandler: ((message: unknown) => void) | undefined;\n private _disconnectHandler: (() => void) | undefined;\n private _started = false;\n private _closed = false;\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: Transport['onmessage'];\n\n constructor(options: ExtensionClientTransportOptions = {}) {\n this._extensionId = options.extensionId;\n this._portName = options.portName ?? 'mcp';\n }\n\n async start(): Promise<void> {\n if (this._closed) {\n throw new Error('ExtensionClientTransport cannot be restarted after it closes');\n }\n if (this._started) {\n throw new Error('Transport already started');\n }\n\n const runtime = globalThis.chrome?.runtime;\n if (!runtime?.connect) {\n throw new Error(\n 'Chrome runtime API not available. This transport must be used in a Chrome extension context.'\n );\n }\n\n try {\n const port = this._extensionId\n ? runtime.connect(this._extensionId, { name: this._portName })\n : runtime.connect({ name: this._portName });\n this._port = port;\n this._messageHandler = (message: unknown) => {\n if (\n typeof message === 'object' &&\n message !== null &&\n Reflect.get(message, 'type') === 'keep-alive'\n ) {\n return;\n }\n\n try {\n const mcpMessage = JSONRPCMessageSchema.parse(message);\n this.onmessage?.(mcpMessage);\n } catch (error) {\n this.onerror?.(\n new Error(\n `Failed to parse message: ${error instanceof Error ? error.message : String(error)}`\n )\n );\n }\n };\n this._disconnectHandler = () => this._finishClose(false);\n port.onMessage.addListener(this._messageHandler);\n port.onDisconnect.addListener(this._disconnectHandler);\n this._started = true;\n } catch (error) {\n this._cleanup();\n throw error;\n }\n }\n\n async send(message: JSONRPCMessage, _options?: TransportSendOptions): Promise<void> {\n if (this._closed) throw new Error('Transport is closed');\n if (!this._started) throw new Error('Transport not started');\n\n const port = this._port;\n if (!port) throw new Error('Not connected');\n\n try {\n port.postMessage(message);\n } catch (error) {\n this._finishClose(true);\n throw new Error(`Failed to send message: ${error}`);\n }\n }\n\n async close(): Promise<void> {\n this._finishClose(true);\n }\n\n private _finishClose(disconnectPort: boolean): void {\n if (this._closed) return;\n\n const port = this._port;\n this._closed = true;\n this._started = false;\n this._cleanup();\n\n if (disconnectPort && port) {\n try {\n port.disconnect();\n } catch {\n // The remote endpoint may already have disconnected.\n }\n }\n this.onclose?.();\n }\n\n private _cleanup(): void {\n if (this._port && this._messageHandler) {\n this._port.onMessage.removeListener(this._messageHandler);\n }\n if (this._port && this._disconnectHandler) {\n this._port.onDisconnect.removeListener(this._disconnectHandler);\n }\n this._port = undefined;\n this._messageHandler = undefined;\n this._disconnectHandler = undefined;\n }\n}\n","import { JSONRPCMessageSchema } from '@modelcontextprotocol/core';\nimport type { JSONRPCMessage, Transport, TransportSendOptions } from '@modelcontextprotocol/server';\n\n/** The runtime Port methods used by this transport, independent of Chrome ambient types. */\nexport interface ExtensionPort {\n postMessage(message: unknown): void;\n disconnect(): void;\n onMessage: {\n addListener(callback: (message: unknown) => void): void;\n removeListener(callback: (message: unknown) => void): void;\n };\n onDisconnect: {\n addListener(callback: () => void): void;\n removeListener(callback: () => void): void;\n };\n}\n\nexport interface ExtensionServerTransportOptions {\n /** Send keep-alive messages (default: true). */\n keepAlive?: boolean;\n /** Keep-alive interval in milliseconds (default: 25,000). */\n keepAliveInterval?: number;\n}\n\n/** Server transport for one Chrome extension Port connection. */\nexport class ExtensionServerTransport implements Transport {\n private _port: ExtensionPort | undefined;\n private _started = false;\n private _closed = false;\n private _messageHandler: ((message: unknown) => void) | undefined;\n private _disconnectHandler: (() => void) | undefined;\n private _keepAliveTimer: ReturnType<typeof setInterval> | undefined;\n private readonly _keepAliveInterval: number | undefined;\n private readonly _connectionInfo = {\n connectedAt: Date.now(),\n lastMessageAt: Date.now(),\n messageCount: 0,\n };\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: Transport['onmessage'];\n\n constructor(port: ExtensionPort, options: ExtensionServerTransportOptions = {}) {\n this._port = port;\n this._keepAliveInterval =\n options.keepAlive === false ? undefined : (options.keepAliveInterval ?? 25_000);\n }\n\n async start(): Promise<void> {\n if (this._closed) {\n throw new Error('ExtensionServerTransport cannot be restarted after it closes');\n }\n if (this._started) {\n throw new Error(\n 'ExtensionServerTransport already started! If using Server class, note that connect() calls start() automatically.'\n );\n }\n\n const port = this._port;\n if (!port) throw new Error('Port not available');\n\n this._messageHandler = (message: unknown) => {\n this._connectionInfo.lastMessageAt = Date.now();\n this._connectionInfo.messageCount++;\n try {\n const mcpMessage = JSONRPCMessageSchema.parse(message);\n this.onmessage?.(mcpMessage);\n } catch (error) {\n this.onerror?.(\n new Error(\n `Failed to parse message: ${error instanceof Error ? error.message : String(error)}`\n )\n );\n }\n };\n this._disconnectHandler = () => this._finishClose(false);\n port.onMessage.addListener(this._messageHandler);\n port.onDisconnect.addListener(this._disconnectHandler);\n this._started = true;\n\n if (this._keepAliveInterval !== undefined) {\n this._keepAliveTimer = setInterval(() => {\n try {\n port.postMessage({ type: 'keep-alive', timestamp: Date.now() });\n } catch (error) {\n this.onerror?.(\n new Error(\n `Keep-alive failed: ${error instanceof Error ? error.message : String(error)}`\n )\n );\n this._finishClose(true);\n }\n }, this._keepAliveInterval);\n }\n }\n\n async send(message: JSONRPCMessage, _options?: TransportSendOptions): Promise<void> {\n if (this._closed) throw new Error('Transport is closed');\n if (!this._started) throw new Error('Transport not started');\n\n const port = this._port;\n if (!port) throw new Error('Not connected to client');\n\n try {\n port.postMessage(message);\n } catch (error) {\n this._finishClose(true);\n throw new Error(`Client disconnected: ${error}`);\n }\n }\n\n async close(): Promise<void> {\n this._finishClose(true);\n }\n\n getConnectionInfo() {\n return {\n ...this._connectionInfo,\n uptime: Date.now() - this._connectionInfo.connectedAt,\n isConnected: this._port !== undefined && this._started && !this._closed,\n };\n }\n\n private _finishClose(disconnectPort: boolean): void {\n if (this._closed) return;\n\n const port = this._port;\n this._closed = true;\n this._started = false;\n this._cleanup();\n\n if (disconnectPort && port) {\n try {\n port.disconnect();\n } catch {\n // The remote endpoint may already have disconnected.\n }\n }\n this.onclose?.();\n }\n\n private _cleanup(): void {\n if (this._keepAliveTimer !== undefined) {\n clearInterval(this._keepAliveTimer);\n this._keepAliveTimer = undefined;\n }\n if (this._port && this._messageHandler) {\n this._port.onMessage.removeListener(this._messageHandler);\n }\n if (this._port && this._disconnectHandler) {\n this._port.onDisconnect.removeListener(this._disconnectHandler);\n }\n this._port = undefined;\n this._messageHandler = undefined;\n this._disconnectHandler = undefined;\n }\n}\n","import { JSONRPCMessageSchema } from '@modelcontextprotocol/core';\nimport type { JSONRPCMessage, Transport, TransportSendOptions } from '@modelcontextprotocol/server';\nimport { DEFAULT_IFRAME_CHANNEL_ID, isMcpMessage, postMcpMessage } from './post-message.js';\n\nexport interface IframeChildTransportOptions {\n /** Parent origins allowed to connect. Pass `['*']` only to disable validation deliberately. */\n allowedOrigins: readonly string[];\n /** Channel name (default: `mcp-iframe`). */\n channelId?: string;\n}\n\n/** Server transport for an MCP server running inside an iframe. */\nexport class IframeChildTransport implements Transport {\n private _started = false;\n private _closed = false;\n private readonly _allowedOrigins: ReadonlySet<string>;\n private readonly _channelId: string;\n private _messageHandler: ((event: MessageEvent<unknown>) => void) | undefined;\n private _clientOrigin?: string;\n\n /**\n * Fires when the connected parent's origin is first established, and again if a\n * different parent takes over the channel. Lets a server scope per-tool exposure\n * to the embedder it is actually talking to.\n */\n onclientorigin?: (origin: string) => void;\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: Transport['onmessage'];\n\n constructor(options: IframeChildTransportOptions) {\n if (!options.allowedOrigins?.length) {\n throw new Error('At least one allowed origin must be specified');\n }\n\n this._allowedOrigins = new Set(options.allowedOrigins);\n this._channelId = options.channelId ?? DEFAULT_IFRAME_CHANNEL_ID;\n }\n\n /** Origin of the connected parent, or `undefined` before its first message arrives. */\n get clientOrigin(): string | undefined {\n return this._clientOrigin;\n }\n\n async start(): Promise<void> {\n if (this._closed) {\n throw new Error('IframeChildTransport cannot be restarted after it closes');\n }\n if (this._started) throw new Error('Transport already started');\n\n this._messageHandler = (event: MessageEvent<unknown>) => {\n if (\n event.source !== window.parent ||\n (!this._allowedOrigins.has('*') && !this._allowedOrigins.has(event.origin)) ||\n !isMcpMessage(event.data, this._channelId, 'client-to-server')\n ) {\n return;\n }\n\n if (this._clientOrigin !== event.origin) {\n this._clientOrigin = event.origin;\n this.onclientorigin?.(event.origin);\n }\n const { payload } = event.data;\n if (payload === 'mcp-check-ready') {\n this._broadcastServerReady();\n return;\n }\n\n try {\n const message = JSONRPCMessageSchema.parse(payload);\n this.onmessage?.(message);\n } catch (error) {\n this.onerror?.(\n new Error(`Invalid message: ${error instanceof Error ? error.message : String(error)}`)\n );\n }\n };\n\n window.addEventListener('message', this._messageHandler);\n this._started = true;\n this._broadcastServerReady();\n }\n\n async send(message: JSONRPCMessage, _options?: TransportSendOptions): Promise<void> {\n if (this._closed) throw new Error('Transport is closed');\n if (!this._started) throw new Error('Transport not started');\n if (!this._clientOrigin) {\n console.debug('[IframeChildTransport] No client connected, message not sent');\n return;\n }\n if (window.parent === window) {\n console.debug('[IframeChildTransport] Not running in an iframe, message not sent');\n return;\n }\n\n postMcpMessage(window.parent, this._clientOrigin, this._channelId, 'server-to-client', message);\n }\n\n async close(): Promise<void> {\n if (this._closed) return;\n\n this._closed = true;\n this._started = false;\n if (this._messageHandler) {\n window.removeEventListener('message', this._messageHandler);\n this._messageHandler = undefined;\n }\n if (this._clientOrigin && window.parent !== window) {\n postMcpMessage(\n window.parent,\n this._clientOrigin,\n this._channelId,\n 'server-to-client',\n 'mcp-server-stopped'\n );\n }\n this.onclose?.();\n }\n\n private _broadcastServerReady(): void {\n if (window.parent !== window) {\n postMcpMessage(window.parent, '*', this._channelId, 'server-to-client', 'mcp-server-ready');\n }\n }\n}\n","import { JSONRPCMessageSchema } from '@modelcontextprotocol/core';\nimport type { JSONRPCMessage, Transport, TransportSendOptions } from '@modelcontextprotocol/server';\nimport { DEFAULT_IFRAME_CHANNEL_ID, isMcpMessage, postMcpMessage } from './post-message.js';\n\nexport interface IframeParentTransportOptions {\n iframe: HTMLIFrameElement;\n /** Expected iframe origin. Pass `'*'` only to disable origin validation deliberately. */\n targetOrigin: string;\n /** Channel name (default: `mcp-iframe`). */\n channelId?: string;\n /** Retry interval while `iframe.contentWindow` is unavailable (default: 250ms). */\n checkReadyRetryMs?: number;\n}\n\n/** Client transport for an MCP server running inside an iframe. */\nexport class IframeParentTransport implements Transport {\n private _started = false;\n private _closed = false;\n private readonly _iframe: HTMLIFrameElement;\n private readonly _targetOrigin: string;\n private readonly _channelId: string;\n private readonly _checkReadyRetryMs: number;\n private _messageHandler: ((event: MessageEvent<unknown>) => void) | undefined;\n private _checkReadyTimeout: ReturnType<typeof setTimeout> | undefined;\n private _serverReadyResolve!: () => void;\n private _serverReadyReject!: (reason: unknown) => void;\n private _serverReadySettled = false;\n\n readonly serverReadyPromise: Promise<void>;\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: Transport['onmessage'];\n\n constructor(options: IframeParentTransportOptions) {\n if (!options.iframe) throw new Error('iframe element is required');\n if (!options.targetOrigin) {\n throw new Error('targetOrigin must be explicitly set for security');\n }\n\n this._iframe = options.iframe;\n this._targetOrigin = options.targetOrigin;\n this._channelId = options.channelId ?? DEFAULT_IFRAME_CHANNEL_ID;\n this._checkReadyRetryMs = options.checkReadyRetryMs ?? 250;\n this.serverReadyPromise = new Promise<void>((resolve, reject) => {\n this._serverReadyResolve = resolve;\n this._serverReadyReject = reject;\n });\n // Closing an unused transport is valid; late consumers still observe the rejection.\n void this.serverReadyPromise.catch(() => {});\n }\n\n async start(): Promise<void> {\n if (this._closed) {\n throw new Error('IframeParentTransport cannot be restarted after it closes');\n }\n if (this._started) throw new Error('Transport already started');\n\n this._messageHandler = (event: MessageEvent<unknown>) => {\n if (\n event.source !== this._iframe.contentWindow ||\n (this._targetOrigin !== '*' && event.origin !== this._targetOrigin) ||\n !isMcpMessage(event.data, this._channelId, 'server-to-client')\n ) {\n return;\n }\n\n const { payload } = event.data;\n if (payload === 'mcp-server-ready') {\n this._resolveServerReady();\n this._clearCheckReadyRetry();\n return;\n }\n if (payload === 'mcp-server-stopped') {\n void this.close();\n return;\n }\n\n try {\n const message = JSONRPCMessageSchema.parse(payload);\n this._resolveServerReady();\n this.onmessage?.(message);\n } catch (error) {\n this.onerror?.(\n new Error(`Invalid message: ${error instanceof Error ? error.message : String(error)}`)\n );\n }\n };\n\n window.addEventListener('message', this._messageHandler);\n this._started = true;\n this._sendCheckReady();\n }\n\n async send(message: JSONRPCMessage, _options?: TransportSendOptions): Promise<void> {\n if (this._closed) throw new Error('Transport is closed');\n if (!this._started) throw new Error('Transport not started');\n\n await this.serverReadyPromise;\n if (this._closed) throw new Error('Transport is closed');\n\n const contentWindow = this._iframe.contentWindow;\n if (!contentWindow) throw new Error('iframe.contentWindow not available');\n postMcpMessage(contentWindow, this._targetOrigin, this._channelId, 'client-to-server', message);\n }\n\n async close(): Promise<void> {\n if (this._closed) return;\n\n this._closed = true;\n this._started = false;\n if (this._messageHandler) {\n window.removeEventListener('message', this._messageHandler);\n this._messageHandler = undefined;\n }\n this._clearCheckReadyRetry();\n if (!this._serverReadySettled) {\n this._serverReadySettled = true;\n this._serverReadyReject(new Error('Transport closed before server ready'));\n }\n this.onclose?.();\n }\n\n private _sendCheckReady(): void {\n const contentWindow = this._iframe.contentWindow;\n if (contentWindow) {\n postMcpMessage(\n contentWindow,\n this._targetOrigin,\n this._channelId,\n 'client-to-server',\n 'mcp-check-ready'\n );\n return;\n }\n\n this._checkReadyTimeout ??= setTimeout(() => {\n this._checkReadyTimeout = undefined;\n if (this._started) this._sendCheckReady();\n }, this._checkReadyRetryMs);\n }\n\n private _clearCheckReadyRetry(): void {\n if (this._checkReadyTimeout === undefined) return;\n clearTimeout(this._checkReadyTimeout);\n this._checkReadyTimeout = undefined;\n }\n\n private _resolveServerReady(): void {\n if (this._serverReadySettled) return;\n this._serverReadySettled = true;\n this._serverReadyResolve();\n }\n}\n","import { JSONRPCMessageSchema } from '@modelcontextprotocol/core';\nimport type { JSONRPCMessage, Transport, TransportSendOptions } from '@modelcontextprotocol/server';\nimport { DEFAULT_TAB_CHANNEL_ID, isMcpMessage, postMcpMessage } from './post-message.js';\n\nexport interface TabClientTransportOptions {\n /** Expected server origin. Pass `'*'` only to disable origin validation deliberately. */\n targetOrigin: string;\n /** Channel name (default: `mcp-default`). */\n channelId?: string;\n}\n\n/** Client transport for an MCP server running in the same window. */\nexport class TabClientTransport implements Transport {\n private _started = false;\n private _closed = false;\n private readonly _targetOrigin: string;\n private readonly _channelId: string;\n private _messageHandler: ((event: MessageEvent<unknown>) => void) | undefined;\n private _serverReadyResolve!: () => void;\n private _serverReadyReject!: (reason: unknown) => void;\n private _serverReadySettled = false;\n\n /** Resolves when the server signals readiness. `send()` awaits it automatically. */\n readonly serverReadyPromise: Promise<void>;\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: Transport['onmessage'];\n\n constructor(options: TabClientTransportOptions) {\n if (!options.targetOrigin) {\n throw new Error('targetOrigin must be explicitly set for security');\n }\n\n this._targetOrigin = options.targetOrigin;\n this._channelId = options.channelId ?? DEFAULT_TAB_CHANNEL_ID;\n this.serverReadyPromise = new Promise<void>((resolve, reject) => {\n this._serverReadyResolve = resolve;\n this._serverReadyReject = reject;\n });\n // Closing an unused transport is valid; late consumers still observe the rejection.\n void this.serverReadyPromise.catch(() => {});\n }\n\n async start(): Promise<void> {\n if (this._closed) {\n throw new Error('TabClientTransport cannot be restarted after it closes');\n }\n if (this._started) {\n throw new Error('Transport already started');\n }\n\n this._messageHandler = (event: MessageEvent<unknown>) => {\n if (\n event.source !== window ||\n (this._targetOrigin !== '*' && event.origin !== this._targetOrigin) ||\n !isMcpMessage(event.data, this._channelId, 'server-to-client')\n ) {\n return;\n }\n\n const { payload } = event.data;\n if (payload === 'mcp-server-ready') {\n this._resolveServerReady();\n return;\n }\n if (payload === 'mcp-server-stopped') {\n void this.close();\n return;\n }\n\n try {\n const message = JSONRPCMessageSchema.parse(payload);\n this._resolveServerReady();\n this.onmessage?.(message);\n } catch (error) {\n this.onerror?.(\n new Error(`Invalid message: ${error instanceof Error ? error.message : String(error)}`)\n );\n }\n };\n\n window.addEventListener('message', this._messageHandler);\n this._started = true;\n postMcpMessage(\n window,\n this._targetOrigin,\n this._channelId,\n 'client-to-server',\n 'mcp-check-ready'\n );\n }\n\n async send(message: JSONRPCMessage, _options?: TransportSendOptions): Promise<void> {\n if (this._closed) throw new Error('Transport is closed');\n if (!this._started) throw new Error('Transport not started');\n\n await this.serverReadyPromise;\n if (this._closed) throw new Error('Transport is closed');\n\n postMcpMessage(window, this._targetOrigin, this._channelId, 'client-to-server', message);\n }\n\n async close(): Promise<void> {\n if (this._closed) return;\n\n this._closed = true;\n this._started = false;\n if (this._messageHandler) {\n window.removeEventListener('message', this._messageHandler);\n this._messageHandler = undefined;\n }\n if (!this._serverReadySettled) {\n this._serverReadySettled = true;\n this._serverReadyReject(new Error('Transport closed before server ready'));\n }\n this.onclose?.();\n }\n\n private _resolveServerReady(): void {\n if (this._serverReadySettled) return;\n this._serverReadySettled = true;\n this._serverReadyResolve();\n }\n}\n","import { JSONRPCMessageSchema } from '@modelcontextprotocol/core';\nimport type { JSONRPCMessage, Transport, TransportSendOptions } from '@modelcontextprotocol/server';\nimport { DEFAULT_TAB_CHANNEL_ID, isMcpMessage, postMcpMessage } from './post-message.js';\n\nexport interface TabServerTransportOptions {\n /** Origins allowed to connect. Pass `['*']` only to disable origin validation deliberately. */\n allowedOrigins: readonly string[];\n /** Channel name (default: `mcp-default`). */\n channelId?: string;\n}\n\nexport class TabServerTransport implements Transport {\n private _started = false;\n private _closed = false;\n private readonly _allowedOrigins: ReadonlySet<string>;\n private readonly _channelId: string;\n private _messageHandler: ((event: MessageEvent<unknown>) => void) | undefined;\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: Transport['onmessage'];\n\n constructor(options: TabServerTransportOptions) {\n if (!options.allowedOrigins?.length) {\n throw new Error('At least one allowed origin must be specified');\n }\n\n this._allowedOrigins = new Set(options.allowedOrigins);\n this._channelId = options.channelId ?? DEFAULT_TAB_CHANNEL_ID;\n }\n\n async start(): Promise<void> {\n if (this._closed) {\n throw new Error('TabServerTransport cannot be restarted after it closes');\n }\n if (this._started) {\n throw new Error('Transport already started');\n }\n\n this._messageHandler = (event: MessageEvent<unknown>) => {\n if (\n event.source !== window ||\n (!this._allowedOrigins.has('*') && !this._allowedOrigins.has(event.origin)) ||\n !isMcpMessage(event.data, this._channelId, 'client-to-server')\n ) {\n return;\n }\n\n const { payload } = event.data;\n if (payload === 'mcp-check-ready') {\n postMcpMessage(window, '*', this._channelId, 'server-to-client', 'mcp-server-ready');\n return;\n }\n\n try {\n const message = JSONRPCMessageSchema.parse(payload);\n this.onmessage?.(message);\n } catch (error) {\n this.onerror?.(\n new Error(`Invalid message: ${error instanceof Error ? error.message : String(error)}`)\n );\n }\n };\n\n window.addEventListener('message', this._messageHandler);\n this._started = true;\n postMcpMessage(window, '*', this._channelId, 'server-to-client', 'mcp-server-ready');\n }\n\n async send(message: JSONRPCMessage, _options?: TransportSendOptions): Promise<void> {\n if (this._closed) throw new Error('Transport is closed');\n if (!this._started) throw new Error('Transport not started');\n\n postMcpMessage(window, '*', this._channelId, 'server-to-client', message);\n }\n\n async close(): Promise<void> {\n if (this._closed) return;\n\n this._closed = true;\n if (this._messageHandler) {\n window.removeEventListener('message', this._messageHandler);\n this._messageHandler = undefined;\n }\n if (this._started) {\n postMcpMessage(window, '*', this._channelId, 'server-to-client', 'mcp-server-stopped');\n }\n this._started = false;\n this.onclose?.();\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-b/transports",
3
- "version": "5.0.3",
3
+ "version": "5.1.0",
4
4
  "description": "Browser transport implementations for Model Context Protocol (MCP) - postMessage, Chrome extension messaging, and iframe communication for AI agents and LLMs",
5
5
  "keywords": [
6
6
  "ai-agents",
@@ -66,15 +66,15 @@
66
66
  },
67
67
  "devDependencies": {
68
68
  "@modelcontextprotocol/client": "2.0.0",
69
- "@types/chrome": "^0.0.326",
70
- "@types/node": "22.17.2",
71
- "@vitest/coverage-v8": "4.1.6",
72
- "chrome-types": "0.1.438",
73
- "playwright": "^1.60.0",
69
+ "@types/chrome": "^0.2.7",
70
+ "@types/node": "22.20.1",
71
+ "@vitest/coverage-v8": "4.1.8",
72
+ "chrome-types": "0.1.440",
73
+ "playwright": "^1.62.1",
74
74
  "typescript": "^5.8.3",
75
- "vite-plus": "0.1.22",
76
- "vitest": "npm:@voidzero-dev/vite-plus-test@0.1.22",
77
- "@mcp-b/webmcp-ts-sdk": "5.0.3"
75
+ "vite-plus": "0.1.24",
76
+ "vitest": "npm:@voidzero-dev/vite-plus-test@0.1.24",
77
+ "@mcp-b/webmcp-ts-sdk": "5.1.0"
78
78
  },
79
79
  "engines": {
80
80
  "node": ">=20"