@mcp-b/transports 0.0.1 → 0.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.d.ts CHANGED
@@ -1,6 +1,177 @@
1
- import { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js';
2
1
  import { Transport, TransportSendOptions } from '@modelcontextprotocol/sdk/shared/transport.js';
3
- import { AuthInfo } from '@modelcontextprotocol/sdk/server/auth/types.js';
2
+ import { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js';
3
+
4
+ interface TabClientTransportOptions {
5
+ targetOrigin: string;
6
+ channelId?: string;
7
+ }
8
+ declare class TabClientTransport implements Transport {
9
+ private _started;
10
+ private _targetOrigin;
11
+ private _channelId;
12
+ private _messageHandler?;
13
+ onclose?: () => void;
14
+ onerror?: (error: Error) => void;
15
+ onmessage?: (message: JSONRPCMessage) => void;
16
+ constructor(options: TabClientTransportOptions);
17
+ start(): Promise<void>;
18
+ send(message: JSONRPCMessage): Promise<void>;
19
+ close(): Promise<void>;
20
+ }
21
+
22
+ interface TabServerTransportOptions {
23
+ allowedOrigins: string[];
24
+ channelId?: string;
25
+ }
26
+ declare class TabServerTransport implements Transport {
27
+ private _started;
28
+ private _allowedOrigins;
29
+ private _channelId;
30
+ private _messageHandler?;
31
+ private _clientOrigin?;
32
+ onclose?: () => void;
33
+ onerror?: (error: Error) => void;
34
+ onmessage?: (message: JSONRPCMessage) => void;
35
+ constructor(options: TabServerTransportOptions);
36
+ start(): Promise<void>;
37
+ send(message: JSONRPCMessage): Promise<void>;
38
+ close(): Promise<void>;
39
+ }
40
+
41
+ /**
42
+ * Configuration options for ExtensionClientTransport
43
+ */
44
+ interface ExtensionClientTransportOptions {
45
+ /**
46
+ * The extension ID to connect to (optional for same-extension connections)
47
+ */
48
+ extensionId?: string;
49
+ /**
50
+ * Port name for the connection
51
+ * Default: 'mcp'
52
+ */
53
+ portName?: string;
54
+ }
55
+ /**
56
+ * Client transport for Chrome extensions using Port-based messaging.
57
+ * This transport can be used in content scripts, popup scripts, or sidepanel scripts
58
+ * to connect to a server running in the background service worker.
59
+ */
60
+ declare class ExtensionClientTransport implements Transport {
61
+ private _port?;
62
+ private _extensionId?;
63
+ private _portName;
64
+ private _messageHandler?;
65
+ private _disconnectHandler?;
66
+ onclose?: () => void;
67
+ onerror?: (error: Error) => void;
68
+ onmessage?: (message: JSONRPCMessage) => void;
69
+ constructor(options?: ExtensionClientTransportOptions);
70
+ /**
71
+ * Starts the transport by connecting to the extension port
72
+ */
73
+ start(): Promise<void>;
74
+ /**
75
+ * Sends a message to the server
76
+ */
77
+ send(message: JSONRPCMessage, _options?: TransportSendOptions): Promise<void>;
78
+ /**
79
+ * Closes the transport
80
+ */
81
+ close(): Promise<void>;
82
+ /**
83
+ * Cleans up event listeners and references
84
+ */
85
+ private _cleanup;
86
+ }
87
+
88
+ /**
89
+ * Server transport for Chrome extensions using Port-based messaging.
90
+ * This transport handles a single client connection through Chrome's port messaging API.
91
+ * It should be used in the extension's background service worker.
92
+ */
93
+ declare class ExtensionServerTransport implements Transport {
94
+ private _port;
95
+ private _started;
96
+ private _messageHandler?;
97
+ private _disconnectHandler?;
98
+ onclose?: () => void;
99
+ onerror?: (error: Error) => void;
100
+ onmessage?: (message: JSONRPCMessage) => void;
101
+ constructor(port: chrome.runtime.Port);
102
+ /**
103
+ * Starts the transport and begins handling messages
104
+ */
105
+ start(): Promise<void>;
106
+ /**
107
+ * Sends a message to the client
108
+ */
109
+ send(message: JSONRPCMessage, _options?: TransportSendOptions): Promise<void>;
110
+ /**
111
+ * Closes the transport
112
+ */
113
+ close(): Promise<void>;
114
+ /**
115
+ * Cleans up event listeners and references
116
+ */
117
+ private _cleanup;
118
+ }
119
+
120
+ /**
121
+ * Client transport for WebSocket: this will connect to a server over the WebSocket protocol.
122
+ * This transport is Node.js specific and requires the 'ws' package to be installed.
123
+ */
124
+ declare class WebSocketClientTransport implements Transport {
125
+ private _socket?;
126
+ private _url;
127
+ private _WebSocket?;
128
+ onclose?: () => void;
129
+ onerror?: (error: Error) => void;
130
+ onmessage?: (message: JSONRPCMessage) => void;
131
+ constructor(url: URL);
132
+ start(): Promise<void>;
133
+ close(): Promise<void>;
134
+ send(message: JSONRPCMessage): Promise<void>;
135
+ }
136
+
137
+ /**
138
+ * Server transport for WebSocket: this will accept connections from MCP clients over WebSocket protocol.
139
+ * Designed to work in browser extension environments.
140
+ */
141
+ declare class WebSocketServerTransport implements Transport {
142
+ private _options;
143
+ private _socket?;
144
+ private _server?;
145
+ private _started;
146
+ onclose?: () => void;
147
+ onerror?: (error: Error) => void;
148
+ onmessage?: (message: JSONRPCMessage) => void;
149
+ constructor(_options: {
150
+ /**
151
+ * For browser extensions: provide an existing WebSocket connection
152
+ */
153
+ socket?: WebSocket;
154
+ /**
155
+ * For Node.js: provide port to create WebSocketServer
156
+ */
157
+ port?: number;
158
+ });
159
+ /**
160
+ * Starts the transport. In browser extension context, this validates the socket.
161
+ */
162
+ start(): Promise<void>;
163
+ private setupSocketHandlers;
164
+ close(): Promise<void>;
165
+ send(message: JSONRPCMessage): Promise<void>;
166
+ /**
167
+ * Get the current WebSocket connection (if any)
168
+ */
169
+ get socket(): WebSocket | undefined;
170
+ /**
171
+ * Check if transport has an active connection
172
+ */
173
+ get isConnected(): boolean;
174
+ }
4
175
 
5
176
  /**
6
177
  * Unique identifier for an event in the event store
@@ -129,386 +300,4 @@ interface StoredEvent {
129
300
  clientId: string;
130
301
  }
131
302
 
132
- /**
133
- * Browser-specific error class to match StreamableHTTPError
134
- */
135
- declare class BrowserTransportError extends Error {
136
- readonly code: string | undefined;
137
- constructor(code: string | undefined, message: string | undefined);
138
- }
139
- /**
140
- * Configuration options for the BrowserClientTransport, matching StreamableHTTPClientTransportOptions style
141
- */
142
- interface TabClientTransportOptions {
143
- /**
144
- * A unique identifier for this client instance. If not provided, one will be generated.
145
- * This is similar to a persistent client identifier.
146
- */
147
- clientInstanceId?: string;
148
- /**
149
- * Global namespace to look for MCP server (defaults to 'mcp')
150
- */
151
- globalNamespace?: string;
152
- /**
153
- * Options to configure the reconnection behavior.
154
- */
155
- reconnectionOptions?: BrowserReconnectionOptions;
156
- /**
157
- * Timeout for initial connection handshake (ms). Default is 30000 (30 seconds).
158
- */
159
- connectionTimeout?: number;
160
- }
161
- /**
162
- * Configuration options for reconnection behavior
163
- */
164
- interface BrowserReconnectionOptions {
165
- /**
166
- * Maximum backoff time between reconnection attempts in milliseconds.
167
- * Default is 30000 (30 seconds).
168
- */
169
- maxReconnectionDelay: number;
170
- /**
171
- * Initial backoff time between reconnection attempts in milliseconds.
172
- * Default is 1000 (1 second).
173
- */
174
- initialReconnectionDelay: number;
175
- /**
176
- * The factor by which the reconnection delay increases after each attempt.
177
- * Default is 1.5.
178
- */
179
- reconnectionDelayGrowFactor: number;
180
- /**
181
- * Maximum number of reconnection attempts before giving up.
182
- * Default is 2.
183
- */
184
- maxRetries: number;
185
- }
186
- /**
187
- * Client transport for browser environments using window.mcp global.
188
- * This implementation follows the same patterns as StreamableHTTPClientTransport.
189
- */
190
- declare class TabClientTransport implements Transport {
191
- private _globalNamespace;
192
- /**
193
- * The client's persistent instance ID
194
- */
195
- clientInstanceId: string;
196
- /**
197
- * The session ID provided by the server during connection
198
- */
199
- sessionId?: string;
200
- private _reconnectionOptions;
201
- private _connectionTimeout;
202
- private _port?;
203
- /**
204
- * The server's instance ID, received during handshake.
205
- */
206
- serverInstanceId?: string;
207
- hasEventStore: boolean;
208
- streamId?: string;
209
- /**
210
- * Indicates whether the transport is currently connected.
211
- */
212
- isConnected: boolean;
213
- private _abortController?;
214
- private _connectionPromise?;
215
- /**
216
- * The last event ID received from the server.
217
- */
218
- lastEventId?: EventId;
219
- private _reconnectAttempt;
220
- onclose?: () => void;
221
- onerror?: (error: Error) => void;
222
- onmessage?: (message: JSONRPCMessage) => void;
223
- constructor(opts?: TabClientTransportOptions);
224
- /**
225
- * Start the transport connection
226
- */
227
- start(): Promise<void>;
228
- /**
229
- * Internal method to establish connection with retry logic
230
- */
231
- private _connectWithRetry;
232
- /**
233
- * Handle incoming messages with error handling
234
- */
235
- private _handleMessage;
236
- /**
237
- * Handle connection errors with retry logic
238
- */
239
- private _handleConnectionError;
240
- /**
241
- * Calculate the next reconnection delay using exponential backoff
242
- */
243
- private _getNextReconnectionDelay;
244
- /**
245
- * Schedule a reconnection attempt with exponential backoff
246
- */
247
- private _scheduleReconnection;
248
- /**
249
- * Send a message over the transport
250
- */
251
- send(message: JSONRPCMessage, options?: {
252
- resumptionToken?: string;
253
- onresumptiontoken?: (token: string) => void;
254
- }): Promise<void>;
255
- /**
256
- * Close the transport connection
257
- */
258
- close(): Promise<void>;
259
- /**
260
- * Terminate the current session explicitly
261
- * Similar to StreamableHTTP's terminateSession
262
- */
263
- terminateSession(): Promise<void>;
264
- /**
265
- * Static helper to check if an MCP server is available
266
- * Similar to checking server availability before connection
267
- */
268
- static isServerAvailable(namespace?: string): boolean;
269
- }
270
-
271
- /**
272
- * Configuration options for BrowserServerTransport
273
- */
274
- interface BrowserServerTransportOptions {
275
- /**
276
- * Function that generates a session ID for each client connection.
277
- * Return undefined to operate in stateless mode.
278
- * If not provided, defaults to undefined (stateless mode).
279
- */
280
- sessionIdGenerator?: (() => string) | undefined;
281
- /**
282
- * Callback for session initialization events
283
- */
284
- onsessioninitialized?: (clientSessionId: string | undefined, clientInstanceId: string) => void;
285
- /**
286
- * Optional namespace to use instead of window.mcp
287
- */
288
- globalNamespace?: string;
289
- /**
290
- * Enable event storage for resumability (only works in stateful mode)
291
- * Default is true in stateful mode, false in stateless mode
292
- */
293
- enableEventStore?: boolean;
294
- /**
295
- * Maximum number of events to store per client (default: 1000)
296
- */
297
- maxEventsPerClient?: number;
298
- }
299
- /**
300
- * Server transport for browser environments using window.mcp global
301
- * Supports multiple concurrent client connections via MessageChannel
302
- */
303
- declare class TabServerTransport implements Transport {
304
- private _serverInstanceId;
305
- private _sessionIdGenerator;
306
- private _onsessioninitialized?;
307
- private _isStarted;
308
- private _clients;
309
- private _globalNamespace;
310
- private _eventStore?;
311
- private _enableEventStore;
312
- onclose?: () => void;
313
- onerror?: (error: Error) => void;
314
- onmessage?: (message: JSONRPCMessage, extra?: {
315
- authInfo?: AuthInfo | undefined;
316
- }) => void;
317
- constructor(options?: BrowserServerTransportOptions);
318
- start(): Promise<void>;
319
- private _setupClientConnection;
320
- send(message: JSONRPCMessage, options?: TransportSendOptions & {
321
- targetClientId?: string;
322
- }): Promise<void>;
323
- close(): Promise<void>;
324
- get clientCount(): number;
325
- get clients(): ReadonlyArray<{
326
- clientId: string;
327
- initialized: boolean;
328
- serverSessionId?: string;
329
- }>;
330
- }
331
-
332
- /**
333
- * In your background script:
334
- * import { setupBackgroundBridge } from '@modelcontextprotocol/sdk/extension-bridge/backgroundBridge'
335
- * // Dynamically inject pageBridge and contentScript into matching pages
336
- * setupBackgroundBridge({
337
- * matches: ['https://your-app.com/*'],
338
- * pageBridgeRunAt: 'document_start',
339
- * contentScriptRunAt: 'document_idle'
340
- * });
341
- */
342
- declare function setupBackgroundBridge(_?: {
343
- /** Host-match patterns for injection, defaults to ['<all_urls>'] */
344
- matches?: string[];
345
- /** When to inject the pageBridge (MAIN world) */
346
- pageBridgeRunAt?: 'document_start' | 'document_end' | 'document_idle';
347
- /** When to inject the content script (ISOLATED world) */
348
- contentScriptRunAt?: 'document_start' | 'document_end' | 'document_idle';
349
- }): Promise<void>;
350
-
351
- /**
352
- * Sets up a content script bridge that relays messages between the page context
353
- * and the extension background script. This enables communication between
354
- * the injected page bridge and the extension's background script.
355
- *
356
- * @param port - Optional Chrome runtime port. If not provided, creates a default port with name 'cs'
357
- * @returns The Chrome runtime port used for communication
358
- */
359
- declare function mcpRelay(port?: chrome.runtime.Port): chrome.runtime.Port;
360
-
361
- declare const isJSONRPCMessage: (value: unknown) => value is JSONRPCMessage;
362
- /**
363
- * Configuration options for reconnection behavior, mirroring BrowserReconnectionOptions
364
- */
365
- interface ExtensionReconnectionOptions {
366
- maxReconnectionDelay: number;
367
- initialReconnectionDelay: number;
368
- reconnectionDelayGrowFactor: number;
369
- maxRetries: number;
370
- }
371
- /**
372
- * Configuration options for the ExtensionClientTransport
373
- */
374
- interface ExtensionClientTransportOptions {
375
- clientInstanceId?: string;
376
- reconnectionOptions?: ExtensionReconnectionOptions;
377
- connectionTimeout?: number;
378
- }
379
- declare class ExtensionClientTransport implements Transport {
380
- onclose?: () => void;
381
- onerror?: (err: Error) => void;
382
- onmessage?: (message: JSONRPCMessage, extra?: {
383
- authInfo?: any;
384
- }) => void;
385
- isConnected: boolean;
386
- sessionId?: string;
387
- lastEventId?: EventId;
388
- serverInstanceId?: string;
389
- hasEventStore: boolean;
390
- streamId?: string;
391
- private clientId;
392
- private bridge;
393
- private _reconnectionOptions;
394
- private _connectionTimeout;
395
- private _abortController?;
396
- private _connectionPromise?;
397
- private _reconnectAttempt;
398
- private _startPromise?;
399
- constructor(options?: ExtensionClientTransportOptions & {
400
- port?: chrome.runtime.Port;
401
- });
402
- private _setupMessageHandler;
403
- start(): Promise<void>;
404
- private _connectWithRetry;
405
- private _handleConnectionError;
406
- private _getNextReconnectionDelay;
407
- private _scheduleReconnection;
408
- send(message: JSONRPCMessage, _?: TransportSendOptions): Promise<void>;
409
- close(): Promise<void>;
410
- }
411
-
412
- /**
413
- * Union type representing all possible message types that can be sent through the page bridge.
414
- * This includes MCP server information, events, replay events, and JSON-RPC messages.
415
- */
416
- type PageBridgeMessageType = MCPServerInfoMessage | MCPEventMessage | MCPReplayEventMessage | JSONRPCMessage | {
417
- type: 'mcp-server-disconnected';
418
- reason: string;
419
- };
420
- /**
421
- * Response structure for messages received from the bridge.
422
- */
423
- interface BridgeResponse {
424
- /** Unique identifier for the client connection */
425
- clientId: string;
426
- /** The actual message payload */
427
- msg: PageBridgeMessageType;
428
- }
429
- /**
430
- * Message structure for commands sent to the bridge.
431
- */
432
- interface BridgeMessage {
433
- /** Command type - either 'connect' to establish a connection or 'send' to send a message */
434
- cmd: 'connect' | 'send' | 'disconnect';
435
- /** Unique identifier for the client connection */
436
- clientId: string;
437
- /** Connection options, required when cmd is 'connect' */
438
- options?: MCPConnectOptions;
439
- /** JSON-RPC message to send, required when cmd is 'send' */
440
- message?: JSONRPCMessage;
441
- }
442
- /**
443
- * Creates a UI bridge for communicating with MCP servers from browser extension UI components.
444
- *
445
- * This function establishes a connection to the extension's background script via Chrome's
446
- * runtime messaging API, allowing UI components like sidebars and popups to interact with
447
- * MCP servers.
448
- *
449
- * @param port - Optional Chrome runtime port to use for communication. If not provided,
450
- * a new port will be created with the name 'extensionUI'.
451
- *
452
- * The port should be connected to your background script's chrome.runtime.onConnect listener.
453
- *
454
- * @returns An object with methods to interact with MCP servers:
455
- * - connect: Establish a connection to an MCP server
456
- * - send: Send JSON-RPC messages to a connected server
457
- * - onMessage: Listen for responses and events from the server
458
- *
459
- * @example
460
- * ```typescript
461
- * // In your sidebar/popup code:
462
- * import { createUIBridge } from '@modelcontextprotocol/sdk/extension-bridge/uiConnector';
463
- *
464
- * // Using default port
465
- * const bridge = createUIBridge();
466
- *
467
- * // Or using a custom port
468
- * const customPort = chrome.runtime.connect({ name: 'sidebar' });
469
- * const bridge = createUIBridge(customPort);
470
- *
471
- * // Connect to an MCP server
472
- * bridge.connect('my-client-id', {
473
- * serverName: 'my-server',
474
- * command: 'node',
475
- * args: ['server.js']
476
- * });
477
- *
478
- * // Listen for messages
479
- * bridge.onMessage((response) => {
480
- * console.log('Received:', response.msg);
481
- * });
482
- *
483
- * // Send a message
484
- * bridge.send('my-client-id', {
485
- * jsonrpc: '2.0',
486
- * id: 1,
487
- * method: 'tools/list'
488
- * });
489
- * ```
490
- */
491
- declare function createUIBridge(port?: chrome.runtime.Port): {
492
- /**
493
- * Establishes a connection to an MCP server.
494
- *
495
- * @param clientId - Unique identifier for this client connection
496
- * @param options - Configuration options for the MCP server connection
497
- */
498
- connect: (clientId: string, options?: MCPConnectOptions) => void;
499
- /**
500
- * Sends a JSON-RPC message to a connected MCP server.
501
- *
502
- * @param clientId - The client ID of the connection to send the message through
503
- * @param message - The JSON-RPC message to send
504
- */
505
- send: (clientId: string, message: JSONRPCMessage) => void;
506
- /**
507
- * Registers a handler for incoming messages from MCP servers.
508
- *
509
- * @param handler - Function to handle incoming bridge responses
510
- */
511
- onMessage: (handler: (resp: BridgeResponse) => void) => void;
512
- };
513
-
514
- export { type BridgeMessage, type BridgeResponse, type BrowserReconnectionOptions, type BrowserServerTransportOptions, BrowserTransportError, type EventId, ExtensionClientTransport, type ExtensionClientTransportOptions, type ExtensionReconnectionOptions, type MCPBrowserInterface, type MCPConnectOptions, type MCPEventMessage, type MCPEventStore, type MCPReplayEventMessage, type MCPServerInfo, type MCPServerInfoMessage, type MCPWindow, type PageBridgeMessageType, type StoredEvent, type StreamId, TabClientTransport, type TabClientTransportOptions, TabServerTransport, createUIBridge, isJSONRPCMessage, mcpRelay, setupBackgroundBridge };
303
+ export { type EventId, ExtensionClientTransport, type ExtensionClientTransportOptions, ExtensionServerTransport, type MCPBrowserInterface, type MCPConnectOptions, type MCPEventMessage, type MCPEventStore, type MCPReplayEventMessage, type MCPServerInfo, type MCPServerInfoMessage, type MCPWindow, type StoredEvent, type StreamId, TabClientTransport, type TabClientTransportOptions, TabServerTransport, type TabServerTransportOptions, WebSocketClientTransport, WebSocketServerTransport };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import {JSONRPCMessageSchema,isInitializeRequest,isJSONRPCResponse,isJSONRPCError}from'@modelcontextprotocol/sdk/types.js';var m=class extends Error{constructor(t,s){super(`Browser transport error: ${s}`);this.code=t;}},P={initialReconnectionDelay:1e3,maxReconnectionDelay:3e4,reconnectionDelayGrowFactor:1.5,maxRetries:2},f=class{_globalNamespace;clientInstanceId;sessionId;_reconnectionOptions;_connectionTimeout;_port;serverInstanceId;hasEventStore=false;streamId;isConnected=false;_abortController;_connectionPromise;lastEventId;_reconnectAttempt=0;onclose;onerror;onmessage;constructor(e){this._globalNamespace=e?.globalNamespace??"mcp",this._reconnectionOptions=e?.reconnectionOptions??P,this._connectionTimeout=e?.connectionTimeout??3e4,this.clientInstanceId=e?.clientInstanceId||(typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():`client-${Date.now()}-${Math.random().toString(36).substr(2,9)}`);}async start(){if(this._abortController)throw new Error("TabClientTransport already started! If using Client class, note that connect() calls start() automatically.");this._abortController=new AbortController,await this._connectWithRetry();}async _connectWithRetry(e){return new Promise((t,s)=>{this._connectionPromise={resolve:t,reject:s};let n=window,o=this._globalNamespace==="mcp"?n.mcp:n[this._globalNamespace];if(!o?.isServerAvailable?.()){let l=new m("NO_SERVER",`No MCP server found at window.${this._globalNamespace}`);this._handleConnectionError(l,e);return}let r=o.getServerInfo();this.serverInstanceId=r.instanceId;let i=e?{resumeFrom:e}:void 0,c=o.connect(this.clientInstanceId,i);if(!c){let l=new m("CONNECTION_FAILED","Failed to connect to MCP server");this._handleConnectionError(l,e);return}this._port=c;let p=false,g;this._port.onmessage=l=>{let h=l.data;if(!p&&h.type==="mcp-server-info"){let d=h;clearTimeout(g),p=true,this.sessionId=d.serverSessionId,this.hasEventStore=d.hasEventStore||false,this.streamId=d.streamId,this.isConnected=true,this._reconnectAttempt=0,this._connectionPromise&&(this._connectionPromise.resolve(),this._connectionPromise=void 0);return}if(h.type==="mcp-replay-event"){let d=h;this.lastEventId=d.eventId,this._handleMessage(d.message);return}if(h.type==="mcp-event"){let d=h;this.lastEventId=d.eventId,this._handleMessage(d.message);return}p&&this._handleMessage(h);},this._port.onmessageerror=()=>{let l=new m("MESSAGE_ERROR","MessagePort error");this.onerror?.(l),this._connectionPromise&&(this._connectionPromise.reject(l),this._connectionPromise=void 0);},this._port.start(),g=setTimeout(()=>{if(!p){let l=new m("HANDSHAKE_TIMEOUT","Server handshake timeout");this._handleConnectionError(l,e);}},this._connectionTimeout);})}_handleMessage(e){try{let t=JSONRPCMessageSchema.parse(e);this.onmessage?.(t);}catch(t){this.onerror?.(new Error(`Failed to parse message: ${t}`));}}_handleConnectionError(e,t){this.onerror?.(e),this._connectionPromise&&(this._connectionPromise.reject(e),this._connectionPromise=void 0),this._abortController&&!this._abortController.signal.aborted&&t&&this._scheduleReconnection(t);}_getNextReconnectionDelay(e){let t=this._reconnectionOptions.initialReconnectionDelay,s=this._reconnectionOptions.reconnectionDelayGrowFactor,n=this._reconnectionOptions.maxReconnectionDelay;return Math.min(t*Math.pow(s,e),n)}_scheduleReconnection(e){let t=this._reconnectionOptions.maxRetries;if(t>0&&this._reconnectAttempt>=t){this.onerror?.(new Error(`Maximum reconnection attempts (${t}) exceeded.`));return}let s=this._getNextReconnectionDelay(this._reconnectAttempt);setTimeout(()=>{this._reconnectAttempt++,this._connectWithRetry(e).catch(n=>{this.onerror?.(new Error(`Failed to reconnect: ${n instanceof Error?n.message:String(n)}`));});},s);}async send(e,t){if(!this.isConnected||!this._port)if(t?.resumptionToken){if(await this._connectWithRetry(t.resumptionToken).catch(s=>{throw this.onerror?.(new Error(`Failed to reconnect with token: ${s instanceof Error?s.message:String(s)}`)),s}),!this.isConnected||!this._port)throw new Error("Not connected after attempting reconnection with token.")}else if(this._reconnectionOptions.maxRetries>0&&this.lastEventId&&this._abortController&&!this._abortController.signal.aborted){if(await this._connectWithRetry(this.lastEventId).catch(s=>{let n=new Error(`Failed to auto-reconnect: ${s instanceof Error?s.message:String(s)}`);throw this.onerror?.(n),n}),!this.isConnected||!this._port)throw new Error("Not connected after attempting auto-reconnection.")}else throw new Error("Not connected");this._port.postMessage(e);}async close(){this._abortController?.abort(),this._abortController=void 0,this._connectionPromise&&(this._connectionPromise.reject(new Error("Transport closed")),this._connectionPromise=void 0),this._port&&(this._port.close(),this._port=void 0);let e=window,t=this._globalNamespace==="mcp"?e.mcp:e[this._globalNamespace];t?.disconnect&&t.disconnect(this.clientInstanceId),this.isConnected=false,this.onclose?.();}async terminateSession(){if(this.sessionId)try{let e=window,t=this._globalNamespace==="mcp"?e.mcp:e[this._globalNamespace];t?.terminateSession?t.terminateSession(this.clientInstanceId):t?.events?.clearEvents&&t.events.clearEvents(this.clientInstanceId),this.sessionId=void 0,this.lastEventId=void 0;}catch(e){throw this.onerror?.(e),e}}static isServerAvailable(e="mcp"){let t=window;return !!(e==="mcp"?t.mcp:t[e])?.isServerAvailable?.()}};var v=class{_events=[];_eventCounter=0;_maxEventsPerClient;constructor(e=1e3){this._maxEventsPerClient=e;}async storeEvent(e,t,s){let n=`evt_${++this._eventCounter}_${Date.now()}`,o={eventId:n,streamId:e,message:s,timestamp:Date.now(),clientId:t};this._events.push(o);let r=this._events.filter(i=>i.clientId===t);if(r.length>this._maxEventsPerClient){let i=r.slice(0,r.length-this._maxEventsPerClient);this._events=this._events.filter(c=>!i.includes(c));}return n}async replayEventsAfter(e,t,s){let n=this._events.filter(i=>i.clientId===e),o=0;if(t){let i=n.findIndex(c=>c.eventId===t);i>=0&&(o=i+1);}let r=n.slice(o);for(let i of r)await s(i.eventId,i.message);return r.length>0?r[r.length-1].streamId:`stream_${Date.now()}`}getEvents(e,t,s=100){let n=e?this._events.filter(o=>o.clientId===e):this._events;if(t){let o=n.findIndex(r=>r.eventId===t);o>=0&&(n=n.slice(o+1));}return n.slice(0,s)}getLastEventId(e){let t=e?this._events.filter(s=>s.clientId===e):this._events;return t.length>0?t[t.length-1].eventId:null}clearEvents(e){e?this._events=this._events.filter(t=>t.clientId!==e):this._events=[];}removeClientEvents(e){this._events=this._events.filter(t=>t.clientId!==e);}},u=class{_serverInstanceId;_sessionIdGenerator;_onsessioninitialized;_isStarted=false;_clients=new Map;_globalNamespace;_eventStore;_enableEventStore;onclose;onerror;onmessage;constructor(e){this._sessionIdGenerator=e?.sessionIdGenerator,this._onsessioninitialized=e?.onsessioninitialized,this._globalNamespace=e?.globalNamespace||"mcp",this._enableEventStore=e?.enableEventStore??this._sessionIdGenerator!==void 0,this._enableEventStore&&this._sessionIdGenerator&&(this._eventStore=new v(e?.maxEventsPerClient)),this._serverInstanceId=typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():`server-${Date.now()}-${Math.random().toString(36).substr(2,9)}`;}async start(){if(this._isStarted)throw new Error("BrowserServerTransport already started");let e=window;if((this._globalNamespace==="mcp"?e.mcp:e[this._globalNamespace])?.isServerAvailable?.())throw new Error(`Another MCP server is already registered at window.${this._globalNamespace}`);let s={connect:(n,o)=>{if(!this._isStarted)return console.error("MCP server not started"),null;if(this._clients.has(n))return console.error(`Client ${n} already connected`),null;let r=new MessageChannel,i=r.port1,c=r.port2,p=this._sessionIdGenerator?.(),g=`stream_${n}_${Date.now()}`,l={port:i,clientInstanceId:n,serverSessionId:p,streamId:g,initialized:false,requestIds:new Set};return this._setupClientConnection(l,o?.resumeFrom),this._clients.set(n,l),c},disconnect:n=>{let o=this._clients.get(n);o&&(o.port.close(),this._clients.delete(n));},isServerAvailable:()=>this._isStarted,getServerInfo:()=>({instanceId:this._serverInstanceId,stateful:this._sessionIdGenerator!==void 0,hasEventStore:this._eventStore!==void 0}),terminateSession:n=>{let o=this._clients.get(n);o&&(o.port.close(),this._clients.delete(n)),this._eventStore?.removeClientEvents(n);}};this._eventStore&&(s.events={getEvents:(n,o,r)=>this._eventStore.getEvents(n,o,r),getLastEventId:n=>this._eventStore.getLastEventId(n),clearEvents:n=>{this._eventStore.clearEvents(n);}}),this._globalNamespace==="mcp"?window.mcp=s:window[this._globalNamespace]=s,this._isStarted=true;}async _setupClientConnection(e,t){let{port:s,clientInstanceId:n}=e;s.onmessage=r=>{try{let i=JSONRPCMessageSchema.parse(r.data);if("id"in i&&i.id!==void 0&&"method"in i&&e.requestIds.add(i.id),isInitializeRequest(i)){if(e.initialized){this.onerror?.(new Error(`Client ${n} attempted to re-initialize`));return}e.initialized=!0,this._onsessioninitialized?.(e.serverSessionId,n);}else if(!e.initialized){let p=`Client ${n} sent message before initialization`;this.onerror?.(new Error(p));return}this.onmessage?.(i,{authInfo:{clientId:n,token:"N/A",scopes:["browser"]}});}catch(i){this.onerror?.(i instanceof Error?i:new Error(String(i)));}},s.onmessageerror=()=>{this.onerror?.(new Error(`MessagePort error for client ${n}`));},s.start();let o={type:"mcp-server-info",serverInstanceId:this._serverInstanceId,serverSessionId:e.serverSessionId,hasEventStore:this._eventStore!==void 0,streamId:e.streamId};s.postMessage(o),t&&this._eventStore&&await this._eventStore.replayEventsAfter(n,t,async(r,i)=>{let c={type:"mcp-replay-event",eventId:r,message:i};s.postMessage(c);});}async send(e,t){if(!this._isStarted)throw new Error("BrowserServerTransport not started");let s=[];if(t?.relatedRequestId){for(let[n,o]of this._clients)if(o.requestIds.has(t.relatedRequestId)){s=[o],(isJSONRPCResponse(e)||isJSONRPCError(e))&&o.requestIds.delete(t.relatedRequestId);break}}else if(t?.targetClientId){let n=this._clients.get(t.targetClientId);n&&(s=[n]);}else s=Array.from(this._clients.values()).filter(n=>n.initialized);if(s.length===0){this.onerror?.(new Error("No suitable clients found for message"));return}for(let n of s){let o;if(this._eventStore&&(o=await this._eventStore.storeEvent(n.streamId,n.clientInstanceId,e)),o){let r={type:"mcp-event",eventId:o,message:e};n.port.postMessage(r);}else n.port.postMessage(e);}}async close(){if(this._isStarted){for(let e of this._clients.values())e.port.close();this._clients.clear(),this._eventStore?.clearEvents(),this._globalNamespace==="mcp"?delete window.mcp:delete window[this._globalNamespace],this._isStarted=false,this.onclose?.();}}get clientCount(){return this._clients.size}get clients(){return Array.from(this._clients.entries()).map(([e,t])=>({clientId:e,initialized:t.initialized,serverSessionId:t.serverSessionId}))}};async function G(a){let e=null,t=new Map,s=new Map;console.log("BSGW: Background bridge initialized"),chrome.runtime.onConnect.addListener(n=>{if(n.name==="extensionUI")console.log("BSGW: Extension UI connected"),e=n,n.onMessage.addListener(o=>{if(console.log("BSGW: Message from extension",o),o.cmd==="connect"){let r=Array.from(t.keys())[0];if(r!==void 0){s.set(o.clientId,r);let i=t.get(r);i&&(console.log(`BSGW: Routing connect for client ${o.clientId} to tab ${r}`),i.postMessage(o));}else console.error("BSGW: No content script tabs available for connection");}else if(o.cmd==="send"){let r=s.get(o.clientId);if(r!==void 0){let i=t.get(r);i?(console.log(`BSGW: Routing send for client ${o.clientId} to tab ${r}`),i.postMessage(o)):(console.error(`BSGW: Tab ${r} no longer available for client ${o.clientId}`),s.delete(o.clientId));}else console.error(`BSGW: No tab mapping found for client ${o.clientId}`);}}),n.onDisconnect.addListener(()=>{console.log("BSGW: Extension UI disconnected");let o=new Set;for(let[r,i]of s.entries())o.add(r);o.forEach(r=>{let i=s.get(r);if(i!==void 0){let c=t.get(i);c&&(console.log(`BSGW: Relaying disconnect for client ${r} to tab ${i}`),c.postMessage({cmd:"disconnect",clientId:r}));}s.delete(r);}),e=null;});else if(n.name==="cs"){let o=n.sender?.tab?.id;typeof o=="number"&&(console.log(`BSGW: Content script connected from tab ${o}`),t.set(o,n),n.onMessage.addListener(r=>{console.log(`BSGW: Message from tab ${o}`,r);let i=s.get(r.clientId);i===o?e?.postMessage(r):console.warn(`BSGW: Ignoring response from tab ${o} for client ${r.clientId} (expected tab ${i})`);}),n.onDisconnect.addListener(()=>{console.log(`BSGW: Content script disconnected from tab ${o}`),t.delete(o);for(let[r,i]of s.entries())i===o&&(console.log(`BSGW: Removing client ${r} mapping to disconnected tab ${o}`),e&&(console.log(`BSGW: Notifying UI about disconnected client ${r}`),e.postMessage({clientId:r,msg:{type:"mcp-server-disconnected",reason:"The tab hosting the server was refreshed or closed."}})),s.delete(r));}));}});}function U(a){let e=a??chrome.runtime.connect({name:"cs"});return window.addEventListener("message",t=>{if(t.source===window&&t.data?.source==="EXT-PAGE"){console.log("MCP relay: received from tab",t.data.cmd,t.data.clientId);let{clientId:s,msg:n}=t.data;e.postMessage({clientId:s,msg:n});}}),e.onMessage.addListener(t=>{console.log("MCP relay: received from extension",t.cmd,t.clientId),window.postMessage({source:"EXT-CS",...t},"*");}),e}function _(a=chrome.runtime.connect({name:"extensionUI"})){return {connect:(e,t)=>{console.log({clientId:e,options:t}),a.postMessage({cmd:"connect",clientId:e,options:t});},send:(e,t)=>{console.log({clientId:e,message:t}),a.postMessage({cmd:"send",clientId:e,message:t});},onMessage:e=>{a.onMessage.addListener(e);}}}var I=a=>JSONRPCMessageSchema.safeParse(a).success,R={initialReconnectionDelay:1e3,maxReconnectionDelay:3e4,reconnectionDelayGrowFactor:1.5,maxRetries:2},C=class{onclose;onerror;onmessage;isConnected=false;sessionId;lastEventId;serverInstanceId;hasEventStore=false;streamId;clientId;bridge;_reconnectionOptions;_connectionTimeout;_abortController;_connectionPromise;_reconnectAttempt=0;_startPromise;constructor(e){this.clientId=e?.clientInstanceId||(crypto.randomUUID?.()??`ext-${Date.now()}-${Math.random().toString(36).slice(2,8)}`),this._reconnectionOptions=e?.reconnectionOptions??R,this._connectionTimeout=e?.connectionTimeout??3e4,this.bridge=_(e?.port),this._setupMessageHandler();}_setupMessageHandler(){this.bridge.onMessage(e=>{if(console.log(`ExtensionClientTransport: Received message for client ${e.clientId}`,"Expected client:",this.clientId,"Match:",e.clientId===this.clientId,"Message type:",e.msg&&"type"in e.msg?e.msg.type:"unknown"),e.clientId!==this.clientId){console.log(`ExtensionClientTransport: Ignoring message for different client. Expected ${this.clientId}, got ${e.clientId}`);return}let t=e.msg;try{if("type"in t&&t.type==="mcp-server-disconnected"){if(console.warn(`ExtensionClientTransport: Server for client ${this.clientId} disconnected unexpectedly.`),this.isConnected){this.isConnected=!1;let s=new Error("The server-side transport has disconnected.");this._handleConnectionError(s,this.lastEventId);}return}if("type"in t&&t.type==="mcp-server-info"){let s=t;if(console.log(`ExtensionClientTransport: Received server info for client ${this.clientId}`,s),this._connectionPromise){this.sessionId=s.serverSessionId,this.serverInstanceId=s.serverInstanceId,this.hasEventStore=s.hasEventStore||!1,this.streamId=s.streamId,this.isConnected=!0,this._reconnectAttempt=0;let n=this._connectionPromise;this._connectionPromise=void 0,n.resolve();}else console.warn("ExtensionClientTransport: Received server info but no connection promise pending");return}if(!this.isConnected){console.warn("ExtensionClientTransport: Received message while not connected",t);return}if("type"in t&&(t.type==="mcp-replay-event"||t.type==="mcp-event")){let s=t;if(this.lastEventId=s.eventId,I(s.message))this.onmessage?.(s.message);else {let n=JSONRPCMessageSchema.parse(s.message);this.onmessage?.(n);}}else I(t)?this.onmessage?.(t):console.warn("ExtensionClientTransport received unknown message type:",t);}catch(s){let n=s;console.error("Error processing message in ExtensionClientTransport:",n,"Data:",t),this.onerror?.(n);}});}async start(){return this._startPromise?(console.warn("ExtensionClientTransport already started, returning existing connection promise"),this._startPromise):this._abortController&&(console.warn("ExtensionClientTransport already started!"),this.isConnected)?Promise.resolve():(this._abortController=new AbortController,this._startPromise=this._connectWithRetry().finally(()=>{this._startPromise=void 0;}),this._startPromise)}async _connectWithRetry(e){return new Promise((t,s)=>{this._connectionPromise={resolve:t,reject:s};let n,o=this._abortController?.signal;if(o?.aborted)return s(new Error("Connection aborted"));console.log(`ExtensionClientTransport: Starting connection for client ${this.clientId}`,e?`with resumption token ${e}`:"without resumption token");let r=e?{resumeFrom:e}:void 0;console.log(`ExtensionClientTransport: Sending connect command for client ${this.clientId}`,r),this.bridge.connect(this.clientId,r),n=setTimeout(()=>{if(!this.isConnected&&this._connectionPromise){let c=new Error(`ExtensionClientTransport: Server handshake timeout for client ${this.clientId}`);this._handleConnectionError(c,e);}},this._connectionTimeout);let i=this._connectionPromise.resolve;this._connectionPromise.resolve=()=>{n&&clearTimeout(n),i();},o?.addEventListener("abort",()=>{n&&clearTimeout(n),this._connectionPromise&&(this._connectionPromise.reject(new Error("Connection aborted during handshake")),this._connectionPromise=void 0);});})}_handleConnectionError(e,t){this.onerror?.(e),this.isConnected=false,this._connectionPromise&&(this._connectionPromise.reject(e),this._connectionPromise=void 0),this._abortController&&!this._abortController.signal.aborted&&(t||this.lastEventId)&&this._reconnectionOptions.maxRetries>0&&this._reconnectAttempt<this._reconnectionOptions.maxRetries?this._scheduleReconnection(t||this.lastEventId):this._abortController&&(this.onclose?.(),this._abortController=void 0);}_getNextReconnectionDelay(e){let{initialReconnectionDelay:t,maxReconnectionDelay:s,reconnectionDelayGrowFactor:n}=this._reconnectionOptions;return Math.min(t*Math.pow(n,e),s)}_scheduleReconnection(e){let t=this._getNextReconnectionDelay(this._reconnectAttempt);this._reconnectAttempt++,console.log(`ExtensionClientTransport: Scheduling reconnection attempt ${this._reconnectAttempt} in ${t}ms`),setTimeout(()=>{if(this._abortController?.signal.aborted){console.log("ExtensionClientTransport: Reconnection aborted.");return}console.log(`ExtensionClientTransport: Attempting reconnection (attempt ${this._reconnectAttempt})`),this._connectWithRetry(e).catch(s=>{console.error("ExtensionClientTransport: Scheduled reconnection attempt failed:",s);});},t);}async send(e,t){if(this._startPromise){console.log("ExtensionClientTransport: Waiting for initial connection before send.");try{await this._startPromise;}catch(s){let n=new Error(`ExtensionClientTransport: Failed to establish initial connection: ${s instanceof Error?s.message:String(s)}`);throw this.onerror?.(n),n}}if(!this.isConnected)if(this._reconnectionOptions.maxRetries>0&&this.lastEventId&&this._abortController&&!this._abortController.signal.aborted){console.log("ExtensionClientTransport: Not connected. Attempting auto-reconnect before send.");try{await this._connectWithRetry(this.lastEventId);}catch(n){let o=new Error(`ExtensionClientTransport: Failed to auto-reconnect before send: ${n instanceof Error?n.message:String(n)}`);throw this.onerror?.(o),o}if(!this.isConnected)throw new Error("ExtensionClientTransport: Not connected after attempting auto-reconnection.")}else throw new Error("ExtensionClientTransport: Not connected and cannot auto-reconnect.");this.bridge.send(this.clientId,e);}async close(){this._abortController&&(this._abortController.abort(),this._abortController=void 0),this._connectionPromise&&(this._connectionPromise.reject(new Error("ExtensionClientTransport: Transport closed by client.")),this._connectionPromise=void 0),this._startPromise=void 0,this.isConnected=false,this.onclose?.();}};export{m as BrowserTransportError,C as ExtensionClientTransport,f as TabClientTransport,u as TabServerTransport,_ as createUIBridge,I as isJSONRPCMessage,U as mcpRelay,G as setupBackgroundBridge};//# sourceMappingURL=index.js.map
1
+ import {JSONRPCMessageSchema}from'@modelcontextprotocol/sdk/types.js';var n=class{_started=false;_targetOrigin;_channelId;_messageHandler;onclose;onerror;onmessage;constructor(e){if(!e.targetOrigin)throw new Error("targetOrigin must be explicitly set for security");this._targetOrigin=e.targetOrigin,this._channelId=e.channelId||"mcp-default";}async start(){if(this._started)throw new Error("Transport already started");this._messageHandler=e=>{if(e.origin===this._targetOrigin&&!(e.data?.channel!==this._channelId||e.data?.type!=="mcp")&&e.data?.direction==="server-to-client")try{let r=JSONRPCMessageSchema.parse(e.data.payload);this.onmessage?.(r);}catch(r){this.onerror?.(new Error(`Invalid message: ${r}`));}},window.addEventListener("message",this._messageHandler),this._started=true;}async send(e){if(!this._started)throw new Error("Transport not started");window.postMessage({channel:this._channelId,type:"mcp",direction:"client-to-server",payload:e},this._targetOrigin);}async close(){this._messageHandler&&window.removeEventListener("message",this._messageHandler),this._started=false,this.onclose?.();}};var a=class{_started=false;_allowedOrigins;_channelId;_messageHandler;_clientOrigin;onclose;onerror;onmessage;constructor(e){if(!e.allowedOrigins||e.allowedOrigins.length===0)throw new Error("At least one allowed origin must be specified");this._allowedOrigins=e.allowedOrigins,this._channelId=e.channelId||"mcp-default";}async start(){if(this._started)throw new Error("Transport already started");this._messageHandler=e=>{if(console.log({event:e}),!(!this._allowedOrigins.includes(e.origin)&&!this._allowedOrigins.includes("*"))&&!(e.data?.channel!==this._channelId||e.data?.type!=="mcp")&&e.data?.direction==="client-to-server"){this._clientOrigin=e.origin;try{let r=JSONRPCMessageSchema.parse(e.data.payload);this.onmessage?.(r);}catch(r){this.onerror?.(new Error(`Invalid message: ${r}`));}}},window.addEventListener("message",this._messageHandler),this._started=true;}async send(e){if(!this._started)throw new Error("Transport not started");if(!this._clientOrigin)throw new Error("No client connected");window.postMessage({channel:this._channelId,type:"mcp",direction:"server-to-client",payload:e},this._clientOrigin);}async close(){this._messageHandler&&window.removeEventListener("message",this._messageHandler),this._started=false,this.onclose?.();}};var c=class{_port;_extensionId;_portName;_messageHandler;_disconnectHandler;onclose;onerror;onmessage;constructor(e={}){this._extensionId=e.extensionId,this._portName=e.portName||"mcp";}async start(){if(this._port)throw new Error("ExtensionClientTransport already started! If using Client class, note that connect() calls start() automatically.");return new Promise((e,r)=>{if(!chrome?.runtime?.connect){r(new Error("Chrome runtime API not available. This transport must be used in a Chrome extension context."));return}try{this._extensionId?this._port=chrome.runtime.connect(this._extensionId,{name:this._portName}):this._port=chrome.runtime.connect({name:this._portName}),this._messageHandler=s=>{try{let o=JSONRPCMessageSchema.parse(s);this.onmessage?.(o);}catch(o){this.onerror?.(new Error(`Failed to parse message: ${o}`));}},this._disconnectHandler=()=>{this._cleanup(),this.onclose?.();},this._port.onMessage.addListener(this._messageHandler),this._port.onDisconnect.addListener(this._disconnectHandler);let t=chrome.runtime.lastError;if(t){this._cleanup(),r(new Error(`Connection failed: ${t.message}`));return}e();}catch(t){r(t);}})}async send(e,r){if(!this._port)throw new Error("Not connected");try{this._port.postMessage(e);}catch(t){throw new Error(`Failed to send message: ${t}`)}}async close(){if(this._port)try{this._port.disconnect();}catch{}this._cleanup(),this.onclose?.();}_cleanup(){this._port&&(this._messageHandler&&this._port.onMessage.removeListener(this._messageHandler),this._disconnectHandler&&this._port.onDisconnect.removeListener(this._disconnectHandler)),this._port=void 0;}};var d=class{_port;_started=false;_messageHandler;_disconnectHandler;onclose;onerror;onmessage;constructor(e){this._port=e;}async start(){if(this._started)throw new Error("ExtensionServerTransport already started! If using Server class, note that connect() calls start() automatically.");if(!this._port)throw new Error("Port not available");this._started=true,this._messageHandler=e=>{try{let r=JSONRPCMessageSchema.parse(e);this.onmessage?.(r);}catch(r){this.onerror?.(new Error(`Failed to parse message: ${r}`));}},this._disconnectHandler=()=>{this._cleanup(),this.onclose?.();},this._port.onMessage.addListener(this._messageHandler),this._port.onDisconnect.addListener(this._disconnectHandler);}async send(e,r){if(!this._started)throw new Error("Transport not started");if(!this._port)throw new Error("Not connected to client");try{this._port.postMessage(e);}catch(t){throw new Error(`Failed to send message: ${t}`)}}async close(){if(this._started=false,this._port)try{this._port.disconnect();}catch{}this._cleanup(),this.onclose?.();}_cleanup(){this._port&&(this._messageHandler&&this._port.onMessage.removeListener(this._messageHandler),this._disconnectHandler&&this._port.onDisconnect.removeListener(this._disconnectHandler));}};var h="mcp",l=class{_socket;_url;_WebSocket;onclose;onerror;onmessage;constructor(e){this._url=e;}async start(){if(this._socket)throw new Error("WebSocketClientTransport already started! If using Client class, note that connect() calls start() automatically.");if(!this._WebSocket)try{let e=await import('ws');this._WebSocket=e.WebSocket;}catch{throw new Error("Failed to import 'ws' package. Please install it with: npm install ws")}return new Promise((e,r)=>{this._socket=new this._WebSocket(this._url.toString(),{perMessageDeflate:false,...{protocol:h}}),this._socket.on("error",t=>{r(t),this.onerror?.(t);}),this._socket.on("open",()=>{e();}),this._socket.on("close",()=>{this.onclose?.();}),this._socket.on("message",t=>{let s;try{let o=t instanceof Buffer?t.toString("utf-8"):t.toString();s=JSONRPCMessageSchema.parse(JSON.parse(o));}catch(o){this.onerror?.(o);return}this.onmessage?.(s);});})}async close(){this._socket&&this._WebSocket&&this._socket.readyState===this._WebSocket.OPEN&&this._socket.close();}async send(e){return new Promise((r,t)=>{if(!this._socket||!this._WebSocket){t(new Error("Not connected"));return}if(this._socket.readyState!==this._WebSocket.OPEN){t(new Error("WebSocket is not open"));return}this._socket.send(JSON.stringify(e),s=>{s?t(s):r();});})}};var p=class{constructor(e){this._options=e;}_socket;_server;_started=false;onclose;onerror;onmessage;async start(){if(this._started)throw new Error("WebSocketServerTransport already started! If using Server class, note that connect() calls start() automatically.");if(this._started=true,this._options.socket)this._socket=this._options.socket,this.setupSocketHandlers(this._socket);else throw new Error("WebSocketServerTransport requires either a socket ")}setupSocketHandlers(e){e.onmessage=r=>{let t;try{let s=typeof r.data=="string"?r.data:r.data.toString();t=JSONRPCMessageSchema.parse(JSON.parse(s));}catch(s){this.onerror?.(s);return}this.onmessage?.(t);},e.onerror=r=>{let t=new Error(`WebSocket error: ${JSON.stringify(r)}`);this.onerror?.(t);},e.onclose=()=>{this._socket=void 0,this.onclose?.();};}async close(){if(this._socket&&(this._socket.close(),this._socket=void 0),this._server)return new Promise(e=>{this._server.close(()=>{this._server=void 0,e();});})}send(e){return new Promise((r,t)=>{if(!this._socket||this._socket.readyState!==WebSocket.OPEN){t(new Error("No active WebSocket connection"));return}try{this._socket.send(JSON.stringify(e)),r();}catch(s){t(s);}})}get socket(){return this._socket}get isConnected(){return this._socket?.readyState===WebSocket.OPEN}};export{c as ExtensionClientTransport,d as ExtensionServerTransport,n as TabClientTransport,a as TabServerTransport,l as WebSocketClientTransport,p as WebSocketServerTransport};//# sourceMappingURL=index.js.map
2
2
  //# sourceMappingURL=index.js.map