@mcp-b/transports 0.1.4 → 0.1.5

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/README.md CHANGED
@@ -1,147 +1,246 @@
1
1
  # MCP Browser Transports
2
2
 
3
- This library provides MCP `Transport` implementations for use in browser environments, enabling communication between MCP clients and servers within a web page or across a browser extension.
3
+ This library provides MCP `Transport` implementations for browser environments, enabling communication between MCP clients and servers within web pages and browser extensions.
4
4
 
5
- ### Installation
5
+ ## Installation
6
6
 
7
7
  ```bash
8
8
  npm install @mcp-b/transports
9
9
  ```
10
10
 
11
- ## Tab Transport (In-Page)
11
+ ## Transport Types
12
12
 
13
- Use `TabServerTransport` and `TabClientTransport` when your MCP server and client are running in the same browser tab. The transport uses a `MessageChannel` and a global `window.mcp` object for communication.
13
+ ### Tab Transports (In-Page Communication)
14
14
 
15
- ### Quick Start: Tab Transport
15
+ Use `TabServerTransport` and `TabClientTransport` when your MCP server and client are running in the same browser tab. The transport uses `window.postMessage` for secure communication with origin validation.
16
16
 
17
- **1. Server Setup**
17
+ ### Extension Transports (Cross-Context Communication)
18
18
 
19
- Create an MCP server and connect it to a `TabServerTransport`. This will expose it on `window.mcp`.
19
+ Use `ExtensionClientTransport` and `ExtensionServerTransport` for communication between browser extension components (sidebar, popup, background) and web pages with MCP servers.
20
+
21
+ ## Tab Transport Examples
22
+
23
+ ### Server Setup (Web Page)
24
+
25
+ Create an MCP server in your web page and expose it via `TabServerTransport`:
20
26
 
21
27
  ```typescript
22
- // my-mcp-server.js
23
28
  import { TabServerTransport } from '@mcp-b/transports';
24
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp';
29
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
25
30
  import { z } from 'zod';
26
31
 
27
- // 1. Create an MCP server
32
+ // Create MCP server with tools
28
33
  const server = new McpServer({
29
- name: 'WebAppServer',
34
+ name: 'TODO-APP',
30
35
  version: '1.0.0',
36
+ }, {
37
+ instructions: 'You are a helpful assistant that can create, update, and delete todos.'
31
38
  });
32
39
 
33
- // 2. Add a tool
34
- server.tool('add', { a: z.number(), b: z.number() }, async ({ a, b }) => ({
35
- content: [{ type: 'text', text: String(a + b) }],
36
- }));
40
+ // Register a tool
41
+ server.tool(
42
+ 'createTodo',
43
+ 'Creates a new todo item for the current user',
44
+ {
45
+ todoText: z.string().describe('The content of the todo item.')
46
+ },
47
+ async (args) => {
48
+ // Implementation here
49
+ return {
50
+ content: [{
51
+ type: 'text',
52
+ text: `Todo created: "${args.todoText}"`
53
+ }]
54
+ };
55
+ }
56
+ );
37
57
 
38
- // 3. Create the transport and connect it to the server
39
- const transport = new TabServerTransport();
58
+ // Connect to transport with CORS configuration
59
+ const transport = new TabServerTransport({
60
+ allowedOrigins: ['*'] // Configure based on your security needs
61
+ });
40
62
  await server.connect(transport);
41
-
42
- console.log('MCP Tab Server is running.');
43
63
  ```
44
64
 
45
- **2. Client Setup**
65
+ ### Client Setup (Same Page)
46
66
 
47
- In your application code, create a client that connects to the server running on the page.
67
+ Connect to the server from within the same page or from an extension content script:
48
68
 
49
69
  ```typescript
50
- // my-app.js
51
70
  import { TabClientTransport } from '@mcp-b/transports';
52
- import { Client } from '@modelcontextprotocol/sdk/client';
71
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
53
72
 
54
- // 1. Create a transport that connects to the global 'mcp' namespace
55
- const transport = new TabClientTransport('mcp', {
56
- clientInstanceId: 'my-web-app-client',
73
+ // Create transport with target origin
74
+ const transport = new TabClientTransport({
75
+ targetOrigin: window.location.origin
57
76
  });
58
77
 
59
- // 2. Create the client
78
+ // Discover available servers
79
+ const availableServers = await transport.discover();
80
+ if (availableServers.length > 0) {
81
+ console.log(`Found server: ${availableServers[0].implementation.name}`);
82
+ }
83
+
84
+ // Create and connect client
60
85
  const client = new Client({
61
- name: 'WebAppClient',
62
- version: '1.0.0',
86
+ name: 'ExtensionProxyClient',
87
+ version: '1.0.0'
63
88
  });
64
89
 
65
- // 3. Connect and use the client
66
90
  await client.connect(transport);
91
+
92
+ // Use the client
67
93
  const result = await client.callTool({
68
- name: 'add',
69
- arguments: { a: 5, b: 10 },
94
+ name: 'createTodo',
95
+ arguments: { todoText: 'Buy groceries' }
70
96
  });
71
-
72
- console.log('Result of add(5, 10):', result.content[0].text); // "15"
73
97
  ```
74
98
 
75
- ## Extension Transport
99
+ ## Extension Transport Examples
76
100
 
77
- Use `ExtensionClientTransport` to allow a browser extension's UI (like a popup or sidebar) to communicate with an MCP server running in a page. This works via a relay through the extension's background script.
101
+ ### Background Script Setup
78
102
 
79
- ### Architecture
103
+ The extension background script acts as a hub, aggregating tools from multiple tabs:
80
104
 
81
- `Extension UI <-> Background Script <-> Content Script <-> Page Script`
82
-
83
- ### Quick Start: Extension Transport
105
+ ```typescript
106
+ import { ExtensionServerTransport } from '@mcp-b/transports';
107
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
108
+
109
+ class McpHub {
110
+ private server: McpServer;
111
+
112
+ constructor() {
113
+ this.server = new McpServer({
114
+ name: 'Extension-Hub',
115
+ version: '1.0.0'
116
+ });
117
+
118
+ this.setupConnections();
119
+ }
120
+
121
+ private setupConnections() {
122
+ chrome.runtime.onConnect.addListener((port) => {
123
+ if (port.name === 'mcp') {
124
+ this.handleUiConnection(port);
125
+ } else if (port.name === 'mcp-content-script-proxy') {
126
+ this.handleContentScriptConnection(port);
127
+ }
128
+ });
129
+ }
130
+
131
+ private async handleUiConnection(port: chrome.runtime.Port) {
132
+ const transport = new ExtensionServerTransport(port);
133
+ await this.server.connect(transport);
134
+ }
135
+ }
136
+ ```
84
137
 
85
- **1. Background Script Setup (`background.ts`)**
138
+ ### Content Script Bridge
86
139
 
87
- Set up the central bridge to route messages between the extension UI and content scripts.
140
+ Content scripts act as a bridge between the page's MCP server and the extension:
88
141
 
89
142
  ```typescript
90
- import { setupBackgroundBridge } from '@mcp-b/transports/extension';
91
-
92
- // This function listens for connections from UI and content scripts
93
- // and relays messages between them.
94
- setupBackgroundBridge();
95
- ```
143
+ import { TabClientTransport } from '@mcp-b/transports';
144
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
96
145
 
97
- **2. Content Script Setup (`contentScript.ts`)**
146
+ // Connect to the page's MCP server
147
+ const transport = new TabClientTransport({
148
+ targetOrigin: window.location.origin
149
+ });
98
150
 
99
- Inject a content script into the target page to relay messages between the page's `window` and the background script.
151
+ const client = new Client({
152
+ name: 'ExtensionProxyClient',
153
+ version: '1.0.0'
154
+ });
100
155
 
101
- ```typescript
102
- import { mcpRelay } from '@mcp-b/transports/extension';
156
+ // Connect to extension background
157
+ const backgroundPort = chrome.runtime.connect({
158
+ name: 'mcp-content-script-proxy'
159
+ });
103
160
 
104
- // The relay forwards messages from the page to the background script
105
- // and vice-versa.
106
- mcpRelay();
107
- ```
161
+ // Discover and connect to page server
162
+ await client.connect(transport);
163
+ const pageTools = await client.listTools();
108
164
 
109
- **3. Page Script Setup**
165
+ // Register tools with background hub
166
+ backgroundPort.postMessage({
167
+ type: 'register-tools',
168
+ tools: pageTools.tools
169
+ });
110
170
 
111
- Your web application still needs to run a `TabServerTransport` as shown in the "Tab Transport" example above. The content script will automatically connect to it.
171
+ // Handle tool execution requests from background
172
+ backgroundPort.onMessage.addListener(async (message) => {
173
+ if (message.type === 'execute-tool') {
174
+ const result = await client.callTool({
175
+ name: message.toolName,
176
+ arguments: message.args || {}
177
+ });
178
+
179
+ backgroundPort.postMessage({
180
+ type: 'tool-result',
181
+ requestId: message.requestId,
182
+ data: { success: true, payload: result }
183
+ });
184
+ }
185
+ });
186
+ ```
112
187
 
113
- **4. Extension UI Client (`popup.tsx` or `sidebar.tsx`)**
188
+ ### Extension UI Client
114
189
 
115
- Finally, your extension's UI can connect to the page's MCP server.
190
+ Connect from the extension's sidebar or popup to use tools from all connected pages:
116
191
 
117
192
  ```typescript
118
193
  import { ExtensionClientTransport } from '@mcp-b/transports';
119
- import { Client } from '@modelcontextprotocol/sdk/client';
194
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
120
195
 
121
- // 1. Use the ExtensionClientTransport in your UI code
196
+ // Create transport - connects to the extension's background script
122
197
  const transport = new ExtensionClientTransport({
123
- clientInstanceId: 'my-extension-ui-client',
198
+ portName: 'mcp'
124
199
  });
125
200
 
126
- // 2. Create the MCP client
201
+ // Create MCP client
127
202
  const client = new Client({
128
- name: 'MyExtensionUI',
129
- version: '1.0.0',
203
+ name: 'Extension Sidepanel',
204
+ version: '1.0.0'
130
205
  });
131
206
 
132
- // 3. Connect and use the client
133
- async function callPageTool() {
134
- try {
135
- await client.connect(transport);
136
- const result = await client.callTool({
137
- name: 'add',
138
- arguments: { a: 20, b: 22 },
139
- });
140
- console.log('Result from page tool:', result.content[0].text); // "42"
141
- } catch (error) {
142
- console.error('Failed to call tool via extension bridge:', error);
143
- }
144
- }
207
+ // Connect and use
208
+ await client.connect(transport);
209
+
210
+ // List all available tools from all connected tabs
211
+ const tools = await client.listTools();
145
212
 
146
- callPageTool();
213
+ // Call a tool from a specific website
214
+ const result = await client.callTool({
215
+ name: 'website_tool_example_com_createTodo',
216
+ arguments: { todoText: 'Review PR' }
217
+ });
147
218
  ```
219
+
220
+ ## Architecture Overview
221
+
222
+ ### Tab Transport Flow
223
+ 1. Server creates `TabServerTransport` and listens for messages via `window.postMessage`
224
+ 2. Client creates `TabClientTransport` to connect to the server using the same channel
225
+ 3. Communication happens securely with origin validation and message direction tracking
226
+
227
+ ### Extension Transport Flow
228
+ 1. Web pages run MCP servers with `TabServerTransport`
229
+ 2. Content scripts discover these servers and relay to background script
230
+ 3. Background script aggregates tools from all tabs using `ExtensionServerTransport`
231
+ 4. Extension UI connects via `ExtensionClientTransport` to access all tools
232
+
233
+ ## Key Features
234
+
235
+ - **Automatic Server Discovery**: Tab clients can discover available servers
236
+ - **Cross-Origin Support**: Configure CORS for tab transports
237
+ - **Tool Namespacing**: Extension hub prefixes tools to avoid conflicts
238
+ - **Connection Management**: Automatic cleanup when tabs close
239
+ - **Type Safety**: Full TypeScript support with proper typing
240
+
241
+ ## Security Considerations
242
+
243
+ - Tab transports respect origin restrictions
244
+ - Extension transports use Chrome's secure message passing
245
+ - Configure `allowedOrigins` appropriately for your use case
246
+ - Tools execute in their original context (web page)
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import {JSONRPCMessageSchema}from'@modelcontextprotocol/sdk/types.js';var a=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 t=JSONRPCMessageSchema.parse(e.data.payload);this.onmessage?.(t);}catch(t){this.onerror?.(new Error(`Invalid message: ${t}`));}},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 c=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 t=JSONRPCMessageSchema.parse(e.data.payload);this.onmessage?.(t);}catch(t){this.onerror?.(new Error(`Invalid message: ${t}`));}}},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 h=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,t)=>{if(!chrome?.runtime?.connect){t(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 r=chrome.runtime.lastError;if(r){this._cleanup(),t(new Error(`Connection failed: ${r.message}`));return}e();}catch(r){t(r);}})}async send(e,t){if(!this._port)throw new Error("Not connected");try{this._port.postMessage(e);}catch(r){throw new Error(`Failed to send message: ${r}`)}}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 t=JSONRPCMessageSchema.parse(e);this.onmessage?.(t);}catch(t){this.onerror?.(new Error(`Failed to parse message: ${t}`));}},this._disconnectHandler=()=>{this._cleanup(),this.onclose?.();},this._port.onMessage.addListener(this._messageHandler),this._port.onDisconnect.addListener(this._disconnectHandler);}async send(e,t){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(r){throw new Error(`Failed to send message: ${r}`)}}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 l="mcp",m=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,t)=>{this._socket=new this._WebSocket(this._url.toString(),{perMessageDeflate:false,...{protocol:l}}),this._socket.on("error",r=>{t(r),this.onerror?.(r);}),this._socket.on("open",()=>{e();}),this._socket.on("close",()=>{this.onclose?.();}),this._socket.on("message",r=>{let s;try{let o=r instanceof Buffer?r.toString("utf-8"):r.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((t,r)=>{if(!this._socket||!this._WebSocket){r(new Error("Not connected"));return}if(this._socket.readyState!==this._WebSocket.OPEN){r(new Error("WebSocket is not open"));return}this._socket.send(JSON.stringify(e),s=>{s?r(s):t();});})}};var _=class{constructor(e){this._options=e;}_socket;_server;_started=false;_connectionId;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=t=>{let r;try{let s=typeof t.data=="string"?t.data:t.data.toString(),o=JSON.parse(s);if(o.connectionId){this._connectionId=o.connectionId;let{connectionId:u,...n}=o;r=JSONRPCMessageSchema.parse(n);}else r=JSONRPCMessageSchema.parse(o);}catch(s){this.onerror?.(s);return}this.onmessage?.(r);},e.onerror=t=>{let r=new Error(`WebSocket error: ${JSON.stringify(t)}`);this.onerror?.(r);},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((t,r)=>{if(!this._socket||this._socket.readyState!==WebSocket.OPEN){r(new Error("No active WebSocket connection"));return}try{let s=this._connectionId?{...e,connectionId:this._connectionId}:e;this._socket.send(JSON.stringify(s)),t();}catch(s){r(s);}})}get socket(){return this._socket}get isConnected(){return this._socket?.readyState===WebSocket.OPEN}};var g=class{_socket;_url;_connectionId;_options;_retryCount=0;_isClosing=false;_connectionTimeoutId;onclose;onerror;onmessage;constructor(e,t){this._url=e.toString(),this._options={maxRetries:t?.maxRetries??10,initialRetryDelay:t?.initialRetryDelay??1e3,maxRetryDelay:t?.maxRetryDelay??3e4,connectionTimeout:t?.connectionTimeout??1e4,retryMultiplier:t?.retryMultiplier??1.5};}async start(){if(this._socket)throw new Error("BrowserWebSocketClientTransport already started! If using Client class, note that connect() calls start() automatically.");return this._isClosing=false,this._retryCount=0,this._connectWithRetry()}async _connectWithRetry(){for(;this._retryCount<=this._options.maxRetries&&!this._isClosing;)try{await this._attemptConnection();return}catch(e){if(this._retryCount++,this._isClosing)throw new Error("Connection cancelled");if(this._retryCount>this._options.maxRetries)throw new Error(`Failed to connect after ${this._options.maxRetries} retries: ${e}`);let t=Math.min(this._options.initialRetryDelay*Math.pow(this._options.retryMultiplier,this._retryCount-1),this._options.maxRetryDelay);console.log(`[WebSocket] Connection failed, retrying in ${t}ms (attempt ${this._retryCount}/${this._options.maxRetries})`),await this._sleep(t);}}_sleep(e){return new Promise(t=>setTimeout(t,e))}async _attemptConnection(){return new Promise((e,t)=>{try{this._connectionTimeoutId&&clearTimeout(this._connectionTimeoutId),this._socket=new WebSocket(this._url),this._connectionTimeoutId=setTimeout(()=>{this._socket&&this._socket.readyState===WebSocket.CONNECTING&&(this._socket.close(),t(new Error(`Connection timeout after ${this._options.connectionTimeout}ms`)));},this._options.connectionTimeout),this._socket.onopen=()=>{this._connectionTimeoutId&&(clearTimeout(this._connectionTimeoutId),this._connectionTimeoutId=void 0),this._retryCount=0,e();},this._socket.onerror=r=>{let s=new Error("WebSocket connection error");t(s);},this._socket.onclose=()=>{this._socket=void 0,this._connectionId=void 0,!this._isClosing&&this._retryCount===0&&this.onclose?.();},this._socket.onmessage=r=>{let s;try{if(s=JSON.parse(r.data),s.connectionId&&!this._connectionId&&(this._connectionId=s.connectionId),s.connectionId){let{connectionId:u,...n}=s;s=n;}let o=JSONRPCMessageSchema.parse(s);this.onmessage?.(o);}catch(o){this.onerror?.(o);}};}catch(r){t(r);}})}async close(){this._isClosing=true,this._connectionTimeoutId&&(clearTimeout(this._connectionTimeoutId),this._connectionTimeoutId=void 0),this._socket&&this._socket.readyState===WebSocket.OPEN&&this._socket.close();}async send(e){return new Promise((t,r)=>{if(!this._socket||this._socket.readyState!==WebSocket.OPEN){r(new Error("WebSocket is not connected"));return}try{let s=this._connectionId?{...e,connectionId:this._connectionId}:e;this._socket.send(JSON.stringify(s)),t();}catch(s){r(s);}})}get isConnected(){return this._socket?.readyState===WebSocket.OPEN}};export{g as BrowserWebSocketClientTransport,h as ExtensionClientTransport,d as ExtensionServerTransport,a as TabClientTransport,c as TabServerTransport,m as WebSocketClientTransport,_ as WebSocketServerTransport};//# sourceMappingURL=index.js.map
1
+ import {JSONRPCMessageSchema}from'@modelcontextprotocol/sdk/types.js';var a=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 t=JSONRPCMessageSchema.parse(e.data.payload);this.onmessage?.(t);}catch(t){this.onerror?.(new Error(`Invalid message: ${t}`));}},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 c=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 t=JSONRPCMessageSchema.parse(e.data.payload);this.onmessage?.(t);}catch(t){this.onerror?.(new Error(`Invalid message: ${t}`));}}},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 d=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){console.warn("ExtensionClientTransport already started! If using Client class, note that connect() calls start() automatically.");return}return new Promise((e,t)=>{if(!chrome?.runtime?.connect){t(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 r=chrome.runtime.lastError;if(r){this._cleanup(),t(new Error(`Connection failed: ${r.message}`));return}e();}catch(r){t(r);}})}async send(e,t){if(!this._port)throw new Error("Not connected");try{this._port.postMessage(e);}catch(r){throw new Error(`Failed to send message: ${r}`)}}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 h=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 t=JSONRPCMessageSchema.parse(e);this.onmessage?.(t);}catch(t){this.onerror?.(new Error(`Failed to parse message: ${t}`));}},this._disconnectHandler=()=>{this._cleanup(),this.onclose?.();},this._port.onMessage.addListener(this._messageHandler),this._port.onDisconnect.addListener(this._disconnectHandler);}async send(e,t){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(r){throw new Error(`Failed to send message: ${r}`)}}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 l="mcp",m=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,t)=>{this._socket=new this._WebSocket(this._url.toString(),{perMessageDeflate:false,...{protocol:l}}),this._socket.on("error",r=>{t(r),this.onerror?.(r);}),this._socket.on("open",()=>{e();}),this._socket.on("close",()=>{this.onclose?.();}),this._socket.on("message",r=>{let s;try{let o=r instanceof Buffer?r.toString("utf-8"):r.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((t,r)=>{if(!this._socket||!this._WebSocket){r(new Error("Not connected"));return}if(this._socket.readyState!==this._WebSocket.OPEN){r(new Error("WebSocket is not open"));return}this._socket.send(JSON.stringify(e),s=>{s?r(s):t();});})}};var _=class{constructor(e){this._options=e;}_socket;_server;_started=false;_connectionId;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=t=>{let r;try{let s=typeof t.data=="string"?t.data:t.data.toString(),o=JSON.parse(s);if(o.connectionId){this._connectionId=o.connectionId;let{connectionId:u,...n}=o;r=JSONRPCMessageSchema.parse(n);}else r=JSONRPCMessageSchema.parse(o);}catch(s){this.onerror?.(s);return}this.onmessage?.(r);},e.onerror=t=>{let r=new Error(`WebSocket error: ${JSON.stringify(t)}`);this.onerror?.(r);},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((t,r)=>{if(!this._socket||this._socket.readyState!==WebSocket.OPEN){r(new Error("No active WebSocket connection"));return}try{let s=this._connectionId?{...e,connectionId:this._connectionId}:e;this._socket.send(JSON.stringify(s)),t();}catch(s){r(s);}})}get socket(){return this._socket}get isConnected(){return this._socket?.readyState===WebSocket.OPEN}};var g=class{_socket;_url;_connectionId;_options;_retryCount=0;_isClosing=false;_connectionTimeoutId;onclose;onerror;onmessage;constructor(e,t){this._url=e.toString(),this._options={maxRetries:t?.maxRetries??10,initialRetryDelay:t?.initialRetryDelay??1e3,maxRetryDelay:t?.maxRetryDelay??3e4,connectionTimeout:t?.connectionTimeout??1e4,retryMultiplier:t?.retryMultiplier??1.5};}async start(){if(this._socket)throw new Error("BrowserWebSocketClientTransport already started! If using Client class, note that connect() calls start() automatically.");return this._isClosing=false,this._retryCount=0,this._connectWithRetry()}async _connectWithRetry(){for(;this._retryCount<=this._options.maxRetries&&!this._isClosing;)try{await this._attemptConnection();return}catch(e){if(this._retryCount++,this._isClosing)throw new Error("Connection cancelled");if(this._retryCount>this._options.maxRetries)throw new Error(`Failed to connect after ${this._options.maxRetries} retries: ${e}`);let t=Math.min(this._options.initialRetryDelay*Math.pow(this._options.retryMultiplier,this._retryCount-1),this._options.maxRetryDelay);console.log(`[WebSocket] Connection failed, retrying in ${t}ms (attempt ${this._retryCount}/${this._options.maxRetries})`),await this._sleep(t);}}_sleep(e){return new Promise(t=>setTimeout(t,e))}async _attemptConnection(){return new Promise((e,t)=>{try{this._connectionTimeoutId&&clearTimeout(this._connectionTimeoutId),this._socket=new WebSocket(this._url),this._connectionTimeoutId=setTimeout(()=>{this._socket&&this._socket.readyState===WebSocket.CONNECTING&&(this._socket.close(),t(new Error(`Connection timeout after ${this._options.connectionTimeout}ms`)));},this._options.connectionTimeout),this._socket.onopen=()=>{this._connectionTimeoutId&&(clearTimeout(this._connectionTimeoutId),this._connectionTimeoutId=void 0),this._retryCount=0,e();},this._socket.onerror=r=>{let s=new Error("WebSocket connection error");t(s);},this._socket.onclose=()=>{this._socket=void 0,this._connectionId=void 0,!this._isClosing&&this._retryCount===0&&this.onclose?.();},this._socket.onmessage=r=>{let s;try{if(s=JSON.parse(r.data),s.connectionId&&!this._connectionId&&(this._connectionId=s.connectionId),s.connectionId){let{connectionId:u,...n}=s;s=n;}let o=JSONRPCMessageSchema.parse(s);this.onmessage?.(o);}catch(o){this.onerror?.(o);}};}catch(r){t(r);}})}async close(){this._isClosing=true,this._connectionTimeoutId&&(clearTimeout(this._connectionTimeoutId),this._connectionTimeoutId=void 0),this._socket&&this._socket.readyState===WebSocket.OPEN&&this._socket.close();}async send(e){return new Promise((t,r)=>{if(!this._socket||this._socket.readyState!==WebSocket.OPEN){r(new Error("WebSocket is not connected"));return}try{let s=this._connectionId?{...e,connectionId:this._connectionId}:e;this._socket.send(JSON.stringify(s)),t();}catch(s){r(s);}})}get isConnected(){return this._socket?.readyState===WebSocket.OPEN}};export{g as BrowserWebSocketClientTransport,d as ExtensionClientTransport,h as ExtensionServerTransport,a as TabClientTransport,c as TabServerTransport,m as WebSocketClientTransport,_ as WebSocketServerTransport};//# sourceMappingURL=index.js.map
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/TabClientTransport.ts","../src/TabServerTransport.ts","../src/ExtensionClientTransport.ts","../src/ExtensionServerTransport.ts","../src/WebsocketClientTransport.ts","../src/WebsocketServerTransport.ts","../src/BrowserWebSocketClientTransport.ts"],"names":["TabClientTransport","options","event","message","JSONRPCMessageSchema","error","TabServerTransport","ExtensionClientTransport","resolve","reject","mcpMessage","_options","ExtensionServerTransport","port","SUBPROTOCOL","WebSocketClientTransport","url","ws","data","dataStr","WebSocketServerTransport","socket","parsedData","connectionId","actualMessage","messageToSend","BrowserWebSocketClientTransport","delay","ms","_event"],"mappings":"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","file":"index.js","sourcesContent":["import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport { JSONRPCMessage, JSONRPCMessageSchema } from '@modelcontextprotocol/sdk/types.js';\n\nexport interface TabClientTransportOptions {\n targetOrigin: string; // Required for security\n channelId?: string; // Optional channel name\n}\n\nexport class TabClientTransport implements Transport {\n private _started = false;\n private _targetOrigin: string;\n private _channelId: string;\n private _messageHandler?: (event: MessageEvent) => void;\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n\n constructor(options: TabClientTransportOptions) {\n if (!options.targetOrigin) {\n throw new Error('targetOrigin must be explicitly set for security');\n }\n this._targetOrigin = options.targetOrigin;\n this._channelId = options.channelId || 'mcp-default';\n }\n\n async start(): Promise<void> {\n if (this._started) {\n throw new Error('Transport already started');\n }\n\n this._messageHandler = (event: MessageEvent) => {\n // Security: validate origin\n if (event.origin !== this._targetOrigin) {\n return;\n }\n\n // Validate message structure\n if (event.data?.channel !== this._channelId || event.data?.type !== 'mcp') {\n return;\n }\n\n // Only process server-to-client messages to avoid processing own messages\n if (event.data?.direction !== 'server-to-client') {\n return;\n }\n\n try {\n const message = JSONRPCMessageSchema.parse(event.data.payload);\n this.onmessage?.(message);\n } catch (error) {\n this.onerror?.(new Error(`Invalid message: ${error}`));\n }\n };\n\n window.addEventListener('message', this._messageHandler);\n this._started = true;\n }\n\n async send(message: JSONRPCMessage): Promise<void> {\n if (!this._started) {\n throw new Error('Transport not started');\n }\n\n window.postMessage(\n {\n channel: this._channelId,\n type: 'mcp',\n direction: 'client-to-server', // Mark as client-to-server message\n payload: message,\n },\n this._targetOrigin\n );\n }\n\n async close(): Promise<void> {\n if (this._messageHandler) {\n window.removeEventListener('message', this._messageHandler);\n }\n this._started = false;\n this.onclose?.();\n }\n}\n","import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport { JSONRPCMessage, JSONRPCMessageSchema } from '@modelcontextprotocol/sdk/types.js';\n\nexport interface TabServerTransportOptions {\n allowedOrigins: string[]; // Required for security\n channelId?: string; // Optional channel name\n}\n\nexport class TabServerTransport implements Transport {\n private _started = false;\n private _allowedOrigins: string[] | '*';\n private _channelId: string;\n private _messageHandler?: (event: MessageEvent) => void;\n private _clientOrigin?: string;\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n\n constructor(options: TabServerTransportOptions) {\n if (!options.allowedOrigins || options.allowedOrigins.length === 0) {\n throw new Error('At least one allowed origin must be specified');\n }\n this._allowedOrigins = options.allowedOrigins;\n this._channelId = options.channelId || 'mcp-default';\n }\n\n async start(): Promise<void> {\n if (this._started) {\n throw new Error('Transport already started');\n }\n\n this._messageHandler = (event: MessageEvent) => {\n console.log({ event });\n // Security: validate origin\n if (!this._allowedOrigins.includes(event.origin) && !this._allowedOrigins.includes('*')) {\n return;\n }\n\n // Validate message structure\n if (event.data?.channel !== this._channelId || event.data?.type !== 'mcp') {\n return;\n }\n\n // Only process client-to-server messages to avoid processing own messages\n if (event.data?.direction !== 'client-to-server') {\n return;\n }\n\n // Store client origin for responses\n this._clientOrigin = event.origin;\n\n try {\n const message = JSONRPCMessageSchema.parse(event.data.payload);\n this.onmessage?.(message);\n } catch (error) {\n this.onerror?.(new Error(`Invalid message: ${error}`));\n }\n };\n\n window.addEventListener('message', this._messageHandler);\n this._started = true;\n }\n\n async send(message: JSONRPCMessage): Promise<void> {\n if (!this._started) {\n throw new Error('Transport not started');\n }\n\n if (!this._clientOrigin) {\n throw new Error('No client connected');\n }\n\n window.postMessage(\n {\n channel: this._channelId,\n type: 'mcp',\n direction: 'server-to-client', // Mark as server-to-client message\n payload: message,\n },\n this._clientOrigin\n );\n }\n\n async close(): Promise<void> {\n if (this._messageHandler) {\n window.removeEventListener('message', this._messageHandler);\n }\n this._started = false;\n this.onclose?.();\n }\n}\n","import { Transport, TransportSendOptions } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport { JSONRPCMessage, JSONRPCMessageSchema } from '@modelcontextprotocol/sdk/types.js';\n\n/**\n * Configuration options for ExtensionClientTransport\n */\nexport interface ExtensionClientTransportOptions {\n /**\n * The extension ID to connect to (optional for same-extension connections)\n */\n extensionId?: string;\n\n /**\n * Port name for the connection\n * Default: 'mcp'\n */\n portName?: string;\n}\n\n/**\n * Client transport for Chrome extensions using Port-based messaging.\n * This transport can be used in content scripts, popup scripts, or sidepanel scripts\n * to connect to a server running in the background service worker.\n */\nexport class ExtensionClientTransport implements Transport {\n private _port?: chrome.runtime.Port;\n private _extensionId?: string;\n private _portName: string;\n private _messageHandler?: (message: any) => void;\n private _disconnectHandler?: () => void;\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n\n constructor(options: ExtensionClientTransportOptions = {}) {\n this._extensionId = options.extensionId;\n this._portName = options.portName || 'mcp';\n }\n\n /**\n * Starts the transport by connecting to the extension port\n */\n async start(): Promise<void> {\n if (this._port) {\n throw new Error(\n 'ExtensionClientTransport already started! If using Client class, note that connect() calls start() automatically.'\n );\n }\n\n return new Promise((resolve, reject) => {\n if (!chrome?.runtime?.connect) {\n reject(\n new Error(\n 'Chrome runtime API not available. This transport must be used in a Chrome extension context.'\n )\n );\n return;\n }\n\n try {\n // Connect to the extension\n if (this._extensionId) {\n this._port = chrome.runtime.connect(this._extensionId, { name: this._portName });\n } else {\n this._port = chrome.runtime.connect({ name: this._portName });\n }\n\n // Set up message handler\n this._messageHandler = (message: any) => {\n try {\n const mcpMessage = JSONRPCMessageSchema.parse(message);\n this.onmessage?.(mcpMessage);\n } catch (error) {\n this.onerror?.(new Error(`Failed to parse message: ${error}`));\n }\n };\n\n // Set up disconnect handler\n this._disconnectHandler = () => {\n this._cleanup();\n this.onclose?.();\n };\n\n this._port.onMessage.addListener(this._messageHandler);\n this._port.onDisconnect.addListener(this._disconnectHandler);\n\n // Check for immediate connection errors\n const error = chrome.runtime.lastError;\n if (error) {\n this._cleanup();\n reject(new Error(`Connection failed: ${error.message}`));\n return;\n }\n\n resolve();\n } catch (error) {\n reject(error);\n }\n });\n }\n\n /**\n * Sends a message to the server\n */\n async send(message: JSONRPCMessage, _options?: TransportSendOptions): Promise<void> {\n if (!this._port) {\n throw new Error('Not connected');\n }\n\n try {\n this._port.postMessage(message);\n } catch (error) {\n throw new Error(`Failed to send message: ${error}`);\n }\n }\n\n /**\n * Closes the transport\n */\n async close(): Promise<void> {\n if (this._port) {\n try {\n this._port.disconnect();\n } catch (error) {\n // Port might already be disconnected\n }\n }\n\n this._cleanup();\n this.onclose?.();\n }\n\n /**\n * Cleans up event listeners and references\n */\n private _cleanup(): void {\n if (this._port) {\n if (this._messageHandler) {\n this._port.onMessage.removeListener(this._messageHandler);\n }\n if (this._disconnectHandler) {\n this._port.onDisconnect.removeListener(this._disconnectHandler);\n }\n }\n this._port = undefined;\n }\n}\n","import { Transport, TransportSendOptions } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport { JSONRPCMessage, JSONRPCMessageSchema } from '@modelcontextprotocol/sdk/types.js';\n\n/**\n * Server transport for Chrome extensions using Port-based messaging.\n * This transport handles a single client connection through Chrome's port messaging API.\n * It should be used in the extension's background service worker.\n */\nexport class ExtensionServerTransport implements Transport {\n private _port: chrome.runtime.Port;\n private _started = false;\n private _messageHandler?: (message: any, port: chrome.runtime.Port) => void;\n private _disconnectHandler?: (port: chrome.runtime.Port) => void;\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n\n constructor(port: chrome.runtime.Port) {\n this._port = port;\n }\n\n /**\n * Starts the transport and begins handling messages\n */\n async start(): Promise<void> {\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 if (!this._port) {\n throw new Error('Port not available');\n }\n\n this._started = true;\n\n // Set up message handler\n this._messageHandler = (message: any) => {\n try {\n const mcpMessage = JSONRPCMessageSchema.parse(message);\n this.onmessage?.(mcpMessage);\n } catch (error) {\n this.onerror?.(new Error(`Failed to parse message: ${error}`));\n }\n };\n\n // Set up disconnect handler\n this._disconnectHandler = () => {\n this._cleanup();\n this.onclose?.();\n };\n\n this._port.onMessage.addListener(this._messageHandler);\n this._port.onDisconnect.addListener(this._disconnectHandler);\n }\n\n /**\n * Sends a message to the client\n */\n async send(message: JSONRPCMessage, _options?: TransportSendOptions): Promise<void> {\n if (!this._started) {\n throw new Error('Transport not started');\n }\n\n if (!this._port) {\n throw new Error('Not connected to client');\n }\n\n try {\n this._port.postMessage(message);\n } catch (error) {\n throw new Error(`Failed to send message: ${error}`);\n }\n }\n\n /**\n * Closes the transport\n */\n async close(): Promise<void> {\n this._started = false;\n\n if (this._port) {\n try {\n this._port.disconnect();\n } catch (error) {\n // Port might already be disconnected\n }\n }\n\n this._cleanup();\n this.onclose?.();\n }\n\n /**\n * Cleans up event listeners and references\n */\n private _cleanup(): void {\n if (this._port) {\n if (this._messageHandler) {\n this._port.onMessage.removeListener(this._messageHandler);\n }\n if (this._disconnectHandler) {\n this._port.onDisconnect.removeListener(this._disconnectHandler);\n }\n }\n }\n}\n","import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport { JSONRPCMessage, JSONRPCMessageSchema } from '@modelcontextprotocol/sdk/types.js';\n\nconst SUBPROTOCOL = 'mcp';\n\n/**\n * Client transport for WebSocket: this will connect to a server over the WebSocket protocol.\n * This transport is Node.js specific and requires the 'ws' package to be installed.\n */\nexport class WebSocketClientTransport implements Transport {\n private _socket?: any; // WebSocket type from 'ws' package\n private _url: URL;\n private _WebSocket?: typeof import('ws').WebSocket;\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n\n constructor(url: URL) {\n this._url = url;\n }\n\n async start(): Promise<void> {\n if (this._socket) {\n throw new Error(\n 'WebSocketClientTransport already started! If using Client class, note that connect() calls start() automatically.'\n );\n }\n\n // Dynamically import the WebSocket class from 'ws' package\n if (!this._WebSocket) {\n try {\n const ws = await import('ws');\n this._WebSocket = ws.WebSocket;\n } catch (error) {\n throw new Error(\"Failed to import 'ws' package. Please install it with: npm install ws\");\n }\n }\n\n return new Promise((resolve, reject) => {\n // Create WebSocket instance with Node.js specific options\n this._socket = new this._WebSocket!(this._url.toString(), {\n perMessageDeflate: false,\n // Add the subprotocol in the options\n ...(SUBPROTOCOL ? { protocol: SUBPROTOCOL } : {}),\n });\n\n // Node.js WebSocket error event handler\n this._socket.on('error', (error: Error) => {\n reject(error);\n this.onerror?.(error);\n });\n\n // Node.js WebSocket open event handler\n this._socket.on('open', () => {\n resolve();\n });\n\n // Node.js WebSocket close event handler\n this._socket.on('close', () => {\n this.onclose?.();\n });\n\n // Node.js WebSocket message event handler\n this._socket.on('message', (data: Buffer | ArrayBuffer | Buffer[]) => {\n let message: JSONRPCMessage;\n try {\n // Convert Buffer to string before parsing\n const dataStr = data instanceof Buffer ? data.toString('utf-8') : data.toString();\n message = JSONRPCMessageSchema.parse(JSON.parse(dataStr));\n } catch (error) {\n this.onerror?.(error as Error);\n return;\n }\n this.onmessage?.(message);\n });\n });\n }\n\n async close(): Promise<void> {\n if (this._socket && this._WebSocket) {\n // Check the readyState before closing\n if (this._socket.readyState === this._WebSocket.OPEN) {\n this._socket.close();\n }\n }\n }\n\n async send(message: JSONRPCMessage): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this._socket || !this._WebSocket) {\n reject(new Error('Not connected'));\n return;\n }\n\n // Check if the socket is open before sending\n if (this._socket.readyState !== this._WebSocket.OPEN) {\n reject(new Error('WebSocket is not open'));\n return;\n }\n\n // In Node.js, send accepts a callback for error handling\n this._socket.send(JSON.stringify(message), (error?: Error) => {\n if (error) {\n reject(error);\n } else {\n resolve();\n }\n });\n });\n }\n}\n","import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport { JSONRPCMessage, JSONRPCMessageSchema } from '@modelcontextprotocol/sdk/types.js';\n\n/**\n * Server transport for WebSocket: this will accept connections from MCP clients over WebSocket protocol.\n * Designed to work in browser extension environments.\n */\nexport class WebSocketServerTransport implements Transport {\n private _socket?: WebSocket;\n private _server?: any; // For environments that support WebSocketServer\n private _started = false;\n private _connectionId?: string;\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n\n constructor(\n private _options: {\n /**\n * For browser extensions: provide an existing WebSocket connection\n */\n socket?: WebSocket;\n /**\n * For Node.js: provide port to create WebSocketServer\n */\n port?: number;\n }\n ) {}\n\n /**\n * Starts the transport. In browser extension context, this validates the socket.\n */\n async start(): Promise<void> {\n if (this._started) {\n throw new Error(\n 'WebSocketServerTransport already started! If using Server class, note that connect() calls start() automatically.'\n );\n }\n this._started = true;\n\n if (this._options.socket) {\n this._socket = this._options.socket;\n this.setupSocketHandlers(this._socket);\n } else {\n throw new Error('WebSocketServerTransport requires either a socket ');\n }\n }\n\n private setupSocketHandlers(socket: WebSocket): void {\n socket.onmessage = (event: MessageEvent) => {\n let message: JSONRPCMessage;\n try {\n const data = typeof event.data === 'string' ? event.data : event.data.toString();\n const parsedData = JSON.parse(data);\n\n // Handle bridge protocol - extract connectionId if present\n if (parsedData.connectionId) {\n this._connectionId = parsedData.connectionId;\n const { connectionId, ...actualMessage } = parsedData;\n message = JSONRPCMessageSchema.parse(actualMessage);\n } else {\n message = JSONRPCMessageSchema.parse(parsedData);\n }\n } catch (error) {\n this.onerror?.(error as Error);\n return;\n }\n this.onmessage?.(message);\n };\n\n socket.onerror = (event: Event) => {\n const error = new Error(`WebSocket error: ${JSON.stringify(event)}`);\n this.onerror?.(error);\n };\n\n socket.onclose = () => {\n this._socket = undefined;\n this.onclose?.();\n };\n }\n\n async close(): Promise<void> {\n if (this._socket) {\n this._socket.close();\n this._socket = undefined;\n }\n\n if (this._server) {\n return new Promise((resolve) => {\n this._server.close(() => {\n this._server = undefined;\n resolve();\n });\n });\n }\n }\n\n send(message: JSONRPCMessage): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this._socket || this._socket.readyState !== WebSocket.OPEN) {\n reject(new Error('No active WebSocket connection'));\n return;\n }\n\n try {\n // If we have a connectionId from the bridge, include it in the response\n const messageToSend = this._connectionId\n ? { ...message, connectionId: this._connectionId }\n : message;\n\n this._socket.send(JSON.stringify(messageToSend));\n resolve();\n } catch (error) {\n reject(error);\n }\n });\n }\n\n /**\n * Get the current WebSocket connection (if any)\n */\n get socket(): WebSocket | undefined {\n return this._socket;\n }\n\n /**\n * Check if transport has an active connection\n */\n get isConnected(): boolean {\n return this._socket?.readyState === WebSocket.OPEN;\n }\n}\n","import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport { JSONRPCMessage, JSONRPCMessageSchema } from '@modelcontextprotocol/sdk/types.js';\n\nexport interface BrowserWebSocketClientTransportOptions {\n maxRetries?: number;\n initialRetryDelay?: number;\n maxRetryDelay?: number;\n connectionTimeout?: number;\n retryMultiplier?: number;\n}\n\n/**\n * Browser-compatible WebSocket client transport for connecting from browser extensions to a bridge server.\n * This transport uses the native browser WebSocket API and doesn't require any Node.js dependencies.\n */\nexport class BrowserWebSocketClientTransport implements Transport {\n private _socket?: WebSocket;\n private _url: string;\n private _connectionId?: string;\n private _options: Required<BrowserWebSocketClientTransportOptions>;\n private _retryCount = 0;\n private _isClosing = false;\n private _connectionTimeoutId?: number;\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n\n constructor(url: string | URL, options?: BrowserWebSocketClientTransportOptions) {\n this._url = url.toString();\n this._options = {\n maxRetries: options?.maxRetries ?? 10,\n initialRetryDelay: options?.initialRetryDelay ?? 1000,\n maxRetryDelay: options?.maxRetryDelay ?? 30000,\n connectionTimeout: options?.connectionTimeout ?? 10000,\n retryMultiplier: options?.retryMultiplier ?? 1.5,\n };\n }\n\n async start(): Promise<void> {\n if (this._socket) {\n throw new Error(\n 'BrowserWebSocketClientTransport already started! If using Client class, note that connect() calls start() automatically.'\n );\n }\n\n this._isClosing = false;\n this._retryCount = 0;\n return this._connectWithRetry();\n }\n\n private async _connectWithRetry(): Promise<void> {\n while (this._retryCount <= this._options.maxRetries && !this._isClosing) {\n try {\n await this._attemptConnection();\n return; // Success!\n } catch (error) {\n this._retryCount++;\n\n if (this._isClosing) {\n throw new Error('Connection cancelled');\n }\n\n if (this._retryCount > this._options.maxRetries) {\n throw new Error(`Failed to connect after ${this._options.maxRetries} retries: ${error}`);\n }\n\n const delay = Math.min(\n this._options.initialRetryDelay *\n Math.pow(this._options.retryMultiplier, this._retryCount - 1),\n this._options.maxRetryDelay\n );\n\n console.log(\n `[WebSocket] Connection failed, retrying in ${delay}ms (attempt ${this._retryCount}/${this._options.maxRetries})`\n );\n await this._sleep(delay);\n }\n }\n }\n\n private _sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n private async _attemptConnection(): Promise<void> {\n return new Promise((resolve, reject) => {\n try {\n // Clear any existing timeout\n if (this._connectionTimeoutId) {\n clearTimeout(this._connectionTimeoutId);\n }\n\n // Create WebSocket with browser API\n this._socket = new WebSocket(this._url);\n\n // Set connection timeout\n this._connectionTimeoutId = setTimeout(() => {\n if (this._socket && this._socket.readyState === WebSocket.CONNECTING) {\n this._socket.close();\n reject(new Error(`Connection timeout after ${this._options.connectionTimeout}ms`));\n }\n }, this._options.connectionTimeout) as unknown as number;\n\n this._socket.onopen = () => {\n // Clear timeout on successful connection\n if (this._connectionTimeoutId) {\n clearTimeout(this._connectionTimeoutId);\n this._connectionTimeoutId = undefined;\n }\n this._retryCount = 0; // Reset retry count on successful connection\n resolve();\n };\n\n this._socket.onerror = (_event) => {\n const error = new Error(`WebSocket connection error`);\n reject(error);\n // Don't call this.onerror during connection attempts\n };\n\n this._socket.onclose = () => {\n this._socket = undefined;\n this._connectionId = undefined;\n\n // Only call onclose if we're not in the middle of retrying\n if (!this._isClosing && this._retryCount === 0) {\n this.onclose?.();\n }\n };\n\n this._socket.onmessage = (event: MessageEvent) => {\n let data: any;\n try {\n data = JSON.parse(event.data);\n\n // Handle bridge-specific messages\n if (data.connectionId && !this._connectionId) {\n // Store connectionId for future messages\n this._connectionId = data.connectionId;\n }\n\n // Extract the actual message\n if (data.connectionId) {\n const { connectionId, ...message } = data;\n data = message;\n }\n\n const message = JSONRPCMessageSchema.parse(data);\n this.onmessage?.(message);\n } catch (error) {\n this.onerror?.(error as Error);\n }\n };\n } catch (error) {\n reject(error);\n }\n });\n }\n\n async close(): Promise<void> {\n this._isClosing = true;\n\n // Clear any pending connection timeout\n if (this._connectionTimeoutId) {\n clearTimeout(this._connectionTimeoutId);\n this._connectionTimeoutId = undefined;\n }\n\n if (this._socket && this._socket.readyState === WebSocket.OPEN) {\n this._socket.close();\n }\n }\n\n async send(message: JSONRPCMessage): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this._socket || this._socket.readyState !== WebSocket.OPEN) {\n reject(new Error('WebSocket is not connected'));\n return;\n }\n\n try {\n // If we have a connectionId from the bridge, include it\n const messageToSend = this._connectionId\n ? { ...message, connectionId: this._connectionId }\n : message;\n\n this._socket.send(JSON.stringify(messageToSend));\n resolve();\n } catch (error) {\n reject(error);\n }\n });\n }\n\n /**\n * Check if transport is connected\n */\n get isConnected(): boolean {\n return this._socket?.readyState === WebSocket.OPEN;\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/TabClientTransport.ts","../src/TabServerTransport.ts","../src/ExtensionClientTransport.ts","../src/ExtensionServerTransport.ts","../src/WebsocketClientTransport.ts","../src/WebsocketServerTransport.ts","../src/BrowserWebSocketClientTransport.ts"],"names":["TabClientTransport","options","event","message","JSONRPCMessageSchema","error","TabServerTransport","ExtensionClientTransport","resolve","reject","mcpMessage","_options","ExtensionServerTransport","port","SUBPROTOCOL","WebSocketClientTransport","url","ws","data","dataStr","WebSocketServerTransport","socket","parsedData","connectionId","actualMessage","messageToSend","BrowserWebSocketClientTransport","delay","ms","_event"],"mappings":"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","file":"index.js","sourcesContent":["import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport { JSONRPCMessage, JSONRPCMessageSchema } from '@modelcontextprotocol/sdk/types.js';\n\nexport interface TabClientTransportOptions {\n targetOrigin: string; // Required for security\n channelId?: string; // Optional channel name\n}\n\nexport class TabClientTransport implements Transport {\n private _started = false;\n private _targetOrigin: string;\n private _channelId: string;\n private _messageHandler?: (event: MessageEvent) => void;\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n\n constructor(options: TabClientTransportOptions) {\n if (!options.targetOrigin) {\n throw new Error('targetOrigin must be explicitly set for security');\n }\n this._targetOrigin = options.targetOrigin;\n this._channelId = options.channelId || 'mcp-default';\n }\n\n async start(): Promise<void> {\n if (this._started) {\n throw new Error('Transport already started');\n }\n\n this._messageHandler = (event: MessageEvent) => {\n // Security: validate origin\n if (event.origin !== this._targetOrigin) {\n return;\n }\n\n // Validate message structure\n if (event.data?.channel !== this._channelId || event.data?.type !== 'mcp') {\n return;\n }\n\n // Only process server-to-client messages to avoid processing own messages\n if (event.data?.direction !== 'server-to-client') {\n return;\n }\n\n try {\n const message = JSONRPCMessageSchema.parse(event.data.payload);\n this.onmessage?.(message);\n } catch (error) {\n this.onerror?.(new Error(`Invalid message: ${error}`));\n }\n };\n\n window.addEventListener('message', this._messageHandler);\n this._started = true;\n }\n\n async send(message: JSONRPCMessage): Promise<void> {\n if (!this._started) {\n throw new Error('Transport not started');\n }\n\n window.postMessage(\n {\n channel: this._channelId,\n type: 'mcp',\n direction: 'client-to-server', // Mark as client-to-server message\n payload: message,\n },\n this._targetOrigin\n );\n }\n\n async close(): Promise<void> {\n if (this._messageHandler) {\n window.removeEventListener('message', this._messageHandler);\n }\n this._started = false;\n this.onclose?.();\n }\n}\n","import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport { JSONRPCMessage, JSONRPCMessageSchema } from '@modelcontextprotocol/sdk/types.js';\n\nexport interface TabServerTransportOptions {\n allowedOrigins: string[]; // Required for security\n channelId?: string; // Optional channel name\n}\n\nexport class TabServerTransport implements Transport {\n private _started = false;\n private _allowedOrigins: string[] | '*';\n private _channelId: string;\n private _messageHandler?: (event: MessageEvent) => void;\n private _clientOrigin?: string;\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n\n constructor(options: TabServerTransportOptions) {\n if (!options.allowedOrigins || options.allowedOrigins.length === 0) {\n throw new Error('At least one allowed origin must be specified');\n }\n this._allowedOrigins = options.allowedOrigins;\n this._channelId = options.channelId || 'mcp-default';\n }\n\n async start(): Promise<void> {\n if (this._started) {\n throw new Error('Transport already started');\n }\n\n this._messageHandler = (event: MessageEvent) => {\n console.log({ event });\n // Security: validate origin\n if (!this._allowedOrigins.includes(event.origin) && !this._allowedOrigins.includes('*')) {\n return;\n }\n\n // Validate message structure\n if (event.data?.channel !== this._channelId || event.data?.type !== 'mcp') {\n return;\n }\n\n // Only process client-to-server messages to avoid processing own messages\n if (event.data?.direction !== 'client-to-server') {\n return;\n }\n\n // Store client origin for responses\n this._clientOrigin = event.origin;\n\n try {\n const message = JSONRPCMessageSchema.parse(event.data.payload);\n this.onmessage?.(message);\n } catch (error) {\n this.onerror?.(new Error(`Invalid message: ${error}`));\n }\n };\n\n window.addEventListener('message', this._messageHandler);\n this._started = true;\n }\n\n async send(message: JSONRPCMessage): Promise<void> {\n if (!this._started) {\n throw new Error('Transport not started');\n }\n\n if (!this._clientOrigin) {\n throw new Error('No client connected');\n }\n\n window.postMessage(\n {\n channel: this._channelId,\n type: 'mcp',\n direction: 'server-to-client', // Mark as server-to-client message\n payload: message,\n },\n this._clientOrigin\n );\n }\n\n async close(): Promise<void> {\n if (this._messageHandler) {\n window.removeEventListener('message', this._messageHandler);\n }\n this._started = false;\n this.onclose?.();\n }\n}\n","import { Transport, TransportSendOptions } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport { JSONRPCMessage, JSONRPCMessageSchema } from '@modelcontextprotocol/sdk/types.js';\n\n/**\n * Configuration options for ExtensionClientTransport\n */\nexport interface ExtensionClientTransportOptions {\n /**\n * The extension ID to connect to (optional for same-extension connections)\n */\n extensionId?: string;\n\n /**\n * Port name for the connection\n * Default: 'mcp'\n */\n portName?: string;\n}\n\n/**\n * Client transport for Chrome extensions using Port-based messaging.\n * This transport can be used in content scripts, popup scripts, or sidepanel scripts\n * to connect to a server running in the background service worker.\n */\nexport class ExtensionClientTransport implements Transport {\n private _port?: chrome.runtime.Port;\n private _extensionId?: string;\n private _portName: string;\n private _messageHandler?: (message: any) => void;\n private _disconnectHandler?: () => void;\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n\n constructor(options: ExtensionClientTransportOptions = {}) {\n this._extensionId = options.extensionId;\n this._portName = options.portName || 'mcp';\n }\n\n /**\n * Starts the transport by connecting to the extension port\n */\n async start(): Promise<void> {\n if (this._port) {\n console.warn(\n 'ExtensionClientTransport already started! If using Client class, note that connect() calls start() automatically.'\n );\n // throw new Error(\n // 'ExtensionClientTransport already started! If using Client class, note that connect() calls start() automatically.'\n // );\n return;\n }\n\n return new Promise((resolve, reject) => {\n if (!chrome?.runtime?.connect) {\n reject(\n new Error(\n 'Chrome runtime API not available. This transport must be used in a Chrome extension context.'\n )\n );\n return;\n }\n\n try {\n // Connect to the extension\n if (this._extensionId) {\n this._port = chrome.runtime.connect(this._extensionId, { name: this._portName });\n } else {\n this._port = chrome.runtime.connect({ name: this._portName });\n }\n\n // Set up message handler\n this._messageHandler = (message: any) => {\n try {\n const mcpMessage = JSONRPCMessageSchema.parse(message);\n this.onmessage?.(mcpMessage);\n } catch (error) {\n this.onerror?.(new Error(`Failed to parse message: ${error}`));\n }\n };\n\n // Set up disconnect handler\n this._disconnectHandler = () => {\n this._cleanup();\n this.onclose?.();\n };\n\n this._port.onMessage.addListener(this._messageHandler);\n this._port.onDisconnect.addListener(this._disconnectHandler);\n\n // Check for immediate connection errors\n const error = chrome.runtime.lastError;\n if (error) {\n this._cleanup();\n reject(new Error(`Connection failed: ${error.message}`));\n return;\n }\n\n resolve();\n } catch (error) {\n reject(error);\n }\n });\n }\n\n /**\n * Sends a message to the server\n */\n async send(message: JSONRPCMessage, _options?: TransportSendOptions): Promise<void> {\n if (!this._port) {\n throw new Error('Not connected');\n }\n\n try {\n this._port.postMessage(message);\n } catch (error) {\n throw new Error(`Failed to send message: ${error}`);\n }\n }\n\n /**\n * Closes the transport\n */\n async close(): Promise<void> {\n if (this._port) {\n try {\n this._port.disconnect();\n } catch (error) {\n // Port might already be disconnected\n }\n }\n\n this._cleanup();\n this.onclose?.();\n }\n\n /**\n * Cleans up event listeners and references\n */\n private _cleanup(): void {\n if (this._port) {\n if (this._messageHandler) {\n this._port.onMessage.removeListener(this._messageHandler);\n }\n if (this._disconnectHandler) {\n this._port.onDisconnect.removeListener(this._disconnectHandler);\n }\n }\n this._port = undefined;\n }\n}\n","import { Transport, TransportSendOptions } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport { JSONRPCMessage, JSONRPCMessageSchema } from '@modelcontextprotocol/sdk/types.js';\n\n/**\n * Server transport for Chrome extensions using Port-based messaging.\n * This transport handles a single client connection through Chrome's port messaging API.\n * It should be used in the extension's background service worker.\n */\nexport class ExtensionServerTransport implements Transport {\n private _port: chrome.runtime.Port;\n private _started = false;\n private _messageHandler?: (message: any, port: chrome.runtime.Port) => void;\n private _disconnectHandler?: (port: chrome.runtime.Port) => void;\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n\n constructor(port: chrome.runtime.Port) {\n this._port = port;\n }\n\n /**\n * Starts the transport and begins handling messages\n */\n async start(): Promise<void> {\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 if (!this._port) {\n throw new Error('Port not available');\n }\n\n this._started = true;\n\n // Set up message handler\n this._messageHandler = (message: any) => {\n try {\n const mcpMessage = JSONRPCMessageSchema.parse(message);\n this.onmessage?.(mcpMessage);\n } catch (error) {\n this.onerror?.(new Error(`Failed to parse message: ${error}`));\n }\n };\n\n // Set up disconnect handler\n this._disconnectHandler = () => {\n this._cleanup();\n this.onclose?.();\n };\n\n this._port.onMessage.addListener(this._messageHandler);\n this._port.onDisconnect.addListener(this._disconnectHandler);\n }\n\n /**\n * Sends a message to the client\n */\n async send(message: JSONRPCMessage, _options?: TransportSendOptions): Promise<void> {\n if (!this._started) {\n throw new Error('Transport not started');\n }\n\n if (!this._port) {\n throw new Error('Not connected to client');\n }\n\n try {\n this._port.postMessage(message);\n } catch (error) {\n throw new Error(`Failed to send message: ${error}`);\n }\n }\n\n /**\n * Closes the transport\n */\n async close(): Promise<void> {\n this._started = false;\n\n if (this._port) {\n try {\n this._port.disconnect();\n } catch (error) {\n // Port might already be disconnected\n }\n }\n\n this._cleanup();\n this.onclose?.();\n }\n\n /**\n * Cleans up event listeners and references\n */\n private _cleanup(): void {\n if (this._port) {\n if (this._messageHandler) {\n this._port.onMessage.removeListener(this._messageHandler);\n }\n if (this._disconnectHandler) {\n this._port.onDisconnect.removeListener(this._disconnectHandler);\n }\n }\n }\n}\n","import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport { JSONRPCMessage, JSONRPCMessageSchema } from '@modelcontextprotocol/sdk/types.js';\n\nconst SUBPROTOCOL = 'mcp';\n\n/**\n * Client transport for WebSocket: this will connect to a server over the WebSocket protocol.\n * This transport is Node.js specific and requires the 'ws' package to be installed.\n */\nexport class WebSocketClientTransport implements Transport {\n private _socket?: any; // WebSocket type from 'ws' package\n private _url: URL;\n private _WebSocket?: typeof import('ws').WebSocket;\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n\n constructor(url: URL) {\n this._url = url;\n }\n\n async start(): Promise<void> {\n if (this._socket) {\n throw new Error(\n 'WebSocketClientTransport already started! If using Client class, note that connect() calls start() automatically.'\n );\n }\n\n // Dynamically import the WebSocket class from 'ws' package\n if (!this._WebSocket) {\n try {\n const ws = await import('ws');\n this._WebSocket = ws.WebSocket;\n } catch (error) {\n throw new Error(\"Failed to import 'ws' package. Please install it with: npm install ws\");\n }\n }\n\n return new Promise((resolve, reject) => {\n // Create WebSocket instance with Node.js specific options\n this._socket = new this._WebSocket!(this._url.toString(), {\n perMessageDeflate: false,\n // Add the subprotocol in the options\n ...(SUBPROTOCOL ? { protocol: SUBPROTOCOL } : {}),\n });\n\n // Node.js WebSocket error event handler\n this._socket.on('error', (error: Error) => {\n reject(error);\n this.onerror?.(error);\n });\n\n // Node.js WebSocket open event handler\n this._socket.on('open', () => {\n resolve();\n });\n\n // Node.js WebSocket close event handler\n this._socket.on('close', () => {\n this.onclose?.();\n });\n\n // Node.js WebSocket message event handler\n this._socket.on('message', (data: Buffer | ArrayBuffer | Buffer[]) => {\n let message: JSONRPCMessage;\n try {\n // Convert Buffer to string before parsing\n const dataStr = data instanceof Buffer ? data.toString('utf-8') : data.toString();\n message = JSONRPCMessageSchema.parse(JSON.parse(dataStr));\n } catch (error) {\n this.onerror?.(error as Error);\n return;\n }\n this.onmessage?.(message);\n });\n });\n }\n\n async close(): Promise<void> {\n if (this._socket && this._WebSocket) {\n // Check the readyState before closing\n if (this._socket.readyState === this._WebSocket.OPEN) {\n this._socket.close();\n }\n }\n }\n\n async send(message: JSONRPCMessage): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this._socket || !this._WebSocket) {\n reject(new Error('Not connected'));\n return;\n }\n\n // Check if the socket is open before sending\n if (this._socket.readyState !== this._WebSocket.OPEN) {\n reject(new Error('WebSocket is not open'));\n return;\n }\n\n // In Node.js, send accepts a callback for error handling\n this._socket.send(JSON.stringify(message), (error?: Error) => {\n if (error) {\n reject(error);\n } else {\n resolve();\n }\n });\n });\n }\n}\n","import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport { JSONRPCMessage, JSONRPCMessageSchema } from '@modelcontextprotocol/sdk/types.js';\n\n/**\n * Server transport for WebSocket: this will accept connections from MCP clients over WebSocket protocol.\n * Designed to work in browser extension environments.\n */\nexport class WebSocketServerTransport implements Transport {\n private _socket?: WebSocket;\n private _server?: any; // For environments that support WebSocketServer\n private _started = false;\n private _connectionId?: string;\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n\n constructor(\n private _options: {\n /**\n * For browser extensions: provide an existing WebSocket connection\n */\n socket?: WebSocket;\n /**\n * For Node.js: provide port to create WebSocketServer\n */\n port?: number;\n }\n ) {}\n\n /**\n * Starts the transport. In browser extension context, this validates the socket.\n */\n async start(): Promise<void> {\n if (this._started) {\n throw new Error(\n 'WebSocketServerTransport already started! If using Server class, note that connect() calls start() automatically.'\n );\n }\n this._started = true;\n\n if (this._options.socket) {\n this._socket = this._options.socket;\n this.setupSocketHandlers(this._socket);\n } else {\n throw new Error('WebSocketServerTransport requires either a socket ');\n }\n }\n\n private setupSocketHandlers(socket: WebSocket): void {\n socket.onmessage = (event: MessageEvent) => {\n let message: JSONRPCMessage;\n try {\n const data = typeof event.data === 'string' ? event.data : event.data.toString();\n const parsedData = JSON.parse(data);\n\n // Handle bridge protocol - extract connectionId if present\n if (parsedData.connectionId) {\n this._connectionId = parsedData.connectionId;\n const { connectionId, ...actualMessage } = parsedData;\n message = JSONRPCMessageSchema.parse(actualMessage);\n } else {\n message = JSONRPCMessageSchema.parse(parsedData);\n }\n } catch (error) {\n this.onerror?.(error as Error);\n return;\n }\n this.onmessage?.(message);\n };\n\n socket.onerror = (event: Event) => {\n const error = new Error(`WebSocket error: ${JSON.stringify(event)}`);\n this.onerror?.(error);\n };\n\n socket.onclose = () => {\n this._socket = undefined;\n this.onclose?.();\n };\n }\n\n async close(): Promise<void> {\n if (this._socket) {\n this._socket.close();\n this._socket = undefined;\n }\n\n if (this._server) {\n return new Promise((resolve) => {\n this._server.close(() => {\n this._server = undefined;\n resolve();\n });\n });\n }\n }\n\n send(message: JSONRPCMessage): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this._socket || this._socket.readyState !== WebSocket.OPEN) {\n reject(new Error('No active WebSocket connection'));\n return;\n }\n\n try {\n // If we have a connectionId from the bridge, include it in the response\n const messageToSend = this._connectionId\n ? { ...message, connectionId: this._connectionId }\n : message;\n\n this._socket.send(JSON.stringify(messageToSend));\n resolve();\n } catch (error) {\n reject(error);\n }\n });\n }\n\n /**\n * Get the current WebSocket connection (if any)\n */\n get socket(): WebSocket | undefined {\n return this._socket;\n }\n\n /**\n * Check if transport has an active connection\n */\n get isConnected(): boolean {\n return this._socket?.readyState === WebSocket.OPEN;\n }\n}\n","import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport { JSONRPCMessage, JSONRPCMessageSchema } from '@modelcontextprotocol/sdk/types.js';\n\nexport interface BrowserWebSocketClientTransportOptions {\n maxRetries?: number;\n initialRetryDelay?: number;\n maxRetryDelay?: number;\n connectionTimeout?: number;\n retryMultiplier?: number;\n}\n\n/**\n * Browser-compatible WebSocket client transport for connecting from browser extensions to a bridge server.\n * This transport uses the native browser WebSocket API and doesn't require any Node.js dependencies.\n */\nexport class BrowserWebSocketClientTransport implements Transport {\n private _socket?: WebSocket;\n private _url: string;\n private _connectionId?: string;\n private _options: Required<BrowserWebSocketClientTransportOptions>;\n private _retryCount = 0;\n private _isClosing = false;\n private _connectionTimeoutId?: number;\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n\n constructor(url: string | URL, options?: BrowserWebSocketClientTransportOptions) {\n this._url = url.toString();\n this._options = {\n maxRetries: options?.maxRetries ?? 10,\n initialRetryDelay: options?.initialRetryDelay ?? 1000,\n maxRetryDelay: options?.maxRetryDelay ?? 30000,\n connectionTimeout: options?.connectionTimeout ?? 10000,\n retryMultiplier: options?.retryMultiplier ?? 1.5,\n };\n }\n\n async start(): Promise<void> {\n if (this._socket) {\n throw new Error(\n 'BrowserWebSocketClientTransport already started! If using Client class, note that connect() calls start() automatically.'\n );\n }\n\n this._isClosing = false;\n this._retryCount = 0;\n return this._connectWithRetry();\n }\n\n private async _connectWithRetry(): Promise<void> {\n while (this._retryCount <= this._options.maxRetries && !this._isClosing) {\n try {\n await this._attemptConnection();\n return; // Success!\n } catch (error) {\n this._retryCount++;\n\n if (this._isClosing) {\n throw new Error('Connection cancelled');\n }\n\n if (this._retryCount > this._options.maxRetries) {\n throw new Error(`Failed to connect after ${this._options.maxRetries} retries: ${error}`);\n }\n\n const delay = Math.min(\n this._options.initialRetryDelay *\n Math.pow(this._options.retryMultiplier, this._retryCount - 1),\n this._options.maxRetryDelay\n );\n\n console.log(\n `[WebSocket] Connection failed, retrying in ${delay}ms (attempt ${this._retryCount}/${this._options.maxRetries})`\n );\n await this._sleep(delay);\n }\n }\n }\n\n private _sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n private async _attemptConnection(): Promise<void> {\n return new Promise((resolve, reject) => {\n try {\n // Clear any existing timeout\n if (this._connectionTimeoutId) {\n clearTimeout(this._connectionTimeoutId);\n }\n\n // Create WebSocket with browser API\n this._socket = new WebSocket(this._url);\n\n // Set connection timeout\n this._connectionTimeoutId = setTimeout(() => {\n if (this._socket && this._socket.readyState === WebSocket.CONNECTING) {\n this._socket.close();\n reject(new Error(`Connection timeout after ${this._options.connectionTimeout}ms`));\n }\n }, this._options.connectionTimeout) as unknown as number;\n\n this._socket.onopen = () => {\n // Clear timeout on successful connection\n if (this._connectionTimeoutId) {\n clearTimeout(this._connectionTimeoutId);\n this._connectionTimeoutId = undefined;\n }\n this._retryCount = 0; // Reset retry count on successful connection\n resolve();\n };\n\n this._socket.onerror = (_event) => {\n const error = new Error(`WebSocket connection error`);\n reject(error);\n // Don't call this.onerror during connection attempts\n };\n\n this._socket.onclose = () => {\n this._socket = undefined;\n this._connectionId = undefined;\n\n // Only call onclose if we're not in the middle of retrying\n if (!this._isClosing && this._retryCount === 0) {\n this.onclose?.();\n }\n };\n\n this._socket.onmessage = (event: MessageEvent) => {\n let data: any;\n try {\n data = JSON.parse(event.data);\n\n // Handle bridge-specific messages\n if (data.connectionId && !this._connectionId) {\n // Store connectionId for future messages\n this._connectionId = data.connectionId;\n }\n\n // Extract the actual message\n if (data.connectionId) {\n const { connectionId, ...message } = data;\n data = message;\n }\n\n const message = JSONRPCMessageSchema.parse(data);\n this.onmessage?.(message);\n } catch (error) {\n this.onerror?.(error as Error);\n }\n };\n } catch (error) {\n reject(error);\n }\n });\n }\n\n async close(): Promise<void> {\n this._isClosing = true;\n\n // Clear any pending connection timeout\n if (this._connectionTimeoutId) {\n clearTimeout(this._connectionTimeoutId);\n this._connectionTimeoutId = undefined;\n }\n\n if (this._socket && this._socket.readyState === WebSocket.OPEN) {\n this._socket.close();\n }\n }\n\n async send(message: JSONRPCMessage): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this._socket || this._socket.readyState !== WebSocket.OPEN) {\n reject(new Error('WebSocket is not connected'));\n return;\n }\n\n try {\n // If we have a connectionId from the bridge, include it\n const messageToSend = this._connectionId\n ? { ...message, connectionId: this._connectionId }\n : message;\n\n this._socket.send(JSON.stringify(messageToSend));\n resolve();\n } catch (error) {\n reject(error);\n }\n });\n }\n\n /**\n * Check if transport is connected\n */\n get isConnected(): boolean {\n return this._socket?.readyState === WebSocket.OPEN;\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-b/transports",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Transports for the in browser transport protocols",
5
5
  "license": "MIT",
6
6
  "author": "Alex Nahas",
@@ -34,7 +34,7 @@
34
34
  "dist"
35
35
  ],
36
36
  "dependencies": {
37
- "@modelcontextprotocol/sdk": "^1.12.1",
37
+ "@modelcontextprotocol/sdk": "^1.13.2",
38
38
  "@types/cheerio": "^1.0.0",
39
39
  "cheerio": "^1.1.0",
40
40
  "zod": "^3.24.1"