mcp-use 1.2.1-canary.0 → 1.2.2-canary.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.
@@ -0,0 +1,1339 @@
1
+ import {
2
+ logger
3
+ } from "./chunk-VPPILX7B.js";
4
+ import {
5
+ __name
6
+ } from "./chunk-SHUYVCID.js";
7
+
8
+ // src/session.ts
9
+ var MCPSession = class {
10
+ static {
11
+ __name(this, "MCPSession");
12
+ }
13
+ connector;
14
+ autoConnect;
15
+ constructor(connector, autoConnect = true) {
16
+ this.connector = connector;
17
+ this.autoConnect = autoConnect;
18
+ }
19
+ async connect() {
20
+ await this.connector.connect();
21
+ }
22
+ async disconnect() {
23
+ await this.connector.disconnect();
24
+ }
25
+ async initialize() {
26
+ if (!this.isConnected && this.autoConnect) {
27
+ await this.connect();
28
+ }
29
+ await this.connector.initialize();
30
+ }
31
+ get isConnected() {
32
+ return this.connector && this.connector.isClientConnected;
33
+ }
34
+ };
35
+
36
+ // src/connectors/base.ts
37
+ var BaseConnector = class {
38
+ static {
39
+ __name(this, "BaseConnector");
40
+ }
41
+ client = null;
42
+ connectionManager = null;
43
+ toolsCache = null;
44
+ capabilitiesCache = null;
45
+ connected = false;
46
+ opts;
47
+ constructor(opts = {}) {
48
+ this.opts = opts;
49
+ }
50
+ /** Disconnect and release resources. */
51
+ async disconnect() {
52
+ if (!this.connected) {
53
+ logger.debug("Not connected to MCP implementation");
54
+ return;
55
+ }
56
+ logger.debug("Disconnecting from MCP implementation");
57
+ await this.cleanupResources();
58
+ this.connected = false;
59
+ logger.debug("Disconnected from MCP implementation");
60
+ }
61
+ /** Check if the client is connected */
62
+ get isClientConnected() {
63
+ return this.client != null;
64
+ }
65
+ /**
66
+ * Initialise the MCP session **after** `connect()` has succeeded.
67
+ *
68
+ * In the SDK, `Client.connect(transport)` automatically performs the
69
+ * protocol‑level `initialize` handshake, so we only need to cache the list of
70
+ * tools and expose some server info.
71
+ */
72
+ async initialize(defaultRequestOptions = this.opts.defaultRequestOptions ?? {}) {
73
+ if (!this.client) {
74
+ throw new Error("MCP client is not connected");
75
+ }
76
+ logger.debug("Caching server capabilities & tools");
77
+ const capabilities = this.client.getServerCapabilities();
78
+ this.capabilitiesCache = capabilities;
79
+ const listToolsRes = await this.client.listTools(void 0, defaultRequestOptions);
80
+ this.toolsCache = listToolsRes.tools ?? [];
81
+ logger.debug(`Fetched ${this.toolsCache.length} tools from server`);
82
+ logger.debug("Server capabilities:", capabilities);
83
+ return capabilities;
84
+ }
85
+ /** Lazily expose the cached tools list. */
86
+ get tools() {
87
+ if (!this.toolsCache) {
88
+ throw new Error("MCP client is not initialized; call initialize() first");
89
+ }
90
+ return this.toolsCache;
91
+ }
92
+ /** Call a tool on the server. */
93
+ async callTool(name, args, options) {
94
+ if (!this.client) {
95
+ throw new Error("MCP client is not connected");
96
+ }
97
+ logger.debug(`Calling tool '${name}' with args`, args);
98
+ const res = await this.client.callTool({ name, arguments: args }, void 0, options);
99
+ logger.debug(`Tool '${name}' returned`, res);
100
+ return res;
101
+ }
102
+ /**
103
+ * List resources from the server with optional pagination
104
+ *
105
+ * @param cursor - Optional cursor for pagination
106
+ * @param options - Request options
107
+ * @returns Resource list with optional nextCursor for pagination
108
+ */
109
+ async listResources(cursor, options) {
110
+ if (!this.client) {
111
+ throw new Error("MCP client is not connected");
112
+ }
113
+ logger.debug("Listing resources", cursor ? `with cursor: ${cursor}` : "");
114
+ return await this.client.listResources({ cursor }, options);
115
+ }
116
+ /**
117
+ * List all resources from the server, automatically handling pagination
118
+ *
119
+ * @param options - Request options
120
+ * @returns Complete list of all resources
121
+ */
122
+ async listAllResources(options) {
123
+ if (!this.client) {
124
+ throw new Error("MCP client is not connected");
125
+ }
126
+ if (!this.capabilitiesCache?.resources) {
127
+ logger.debug("Server does not advertise resources capability, skipping");
128
+ return { resources: [] };
129
+ }
130
+ try {
131
+ logger.debug("Listing all resources (with auto-pagination)");
132
+ const allResources = [];
133
+ let cursor = void 0;
134
+ do {
135
+ const result = await this.client.listResources({ cursor }, options);
136
+ allResources.push(...result.resources || []);
137
+ cursor = result.nextCursor;
138
+ } while (cursor);
139
+ return { resources: allResources };
140
+ } catch (err) {
141
+ if (err.code === -32601) {
142
+ logger.debug("Server advertised resources but method not found");
143
+ return { resources: [] };
144
+ }
145
+ throw err;
146
+ }
147
+ }
148
+ /**
149
+ * List resource templates from the server
150
+ *
151
+ * @param options - Request options
152
+ * @returns List of available resource templates
153
+ */
154
+ async listResourceTemplates(options) {
155
+ if (!this.client) {
156
+ throw new Error("MCP client is not connected");
157
+ }
158
+ logger.debug("Listing resource templates");
159
+ return await this.client.listResourceTemplates(void 0, options);
160
+ }
161
+ /** Read a resource by URI. */
162
+ async readResource(uri, options) {
163
+ if (!this.client) {
164
+ throw new Error("MCP client is not connected");
165
+ }
166
+ logger.debug(`Reading resource ${uri}`);
167
+ const res = await this.client.readResource({ uri }, options);
168
+ return res;
169
+ }
170
+ /**
171
+ * Subscribe to resource updates
172
+ *
173
+ * @param uri - URI of the resource to subscribe to
174
+ * @param options - Request options
175
+ */
176
+ async subscribeToResource(uri, options) {
177
+ if (!this.client) {
178
+ throw new Error("MCP client is not connected");
179
+ }
180
+ logger.debug(`Subscribing to resource: ${uri}`);
181
+ return await this.client.subscribeResource({ uri }, options);
182
+ }
183
+ /**
184
+ * Unsubscribe from resource updates
185
+ *
186
+ * @param uri - URI of the resource to unsubscribe from
187
+ * @param options - Request options
188
+ */
189
+ async unsubscribeFromResource(uri, options) {
190
+ if (!this.client) {
191
+ throw new Error("MCP client is not connected");
192
+ }
193
+ logger.debug(`Unsubscribing from resource: ${uri}`);
194
+ return await this.client.unsubscribeResource({ uri }, options);
195
+ }
196
+ async listPrompts() {
197
+ if (!this.client) {
198
+ throw new Error("MCP client is not connected");
199
+ }
200
+ if (!this.capabilitiesCache?.prompts) {
201
+ logger.debug("Server does not advertise prompts capability, skipping");
202
+ return { prompts: [] };
203
+ }
204
+ try {
205
+ logger.debug("Listing prompts");
206
+ return await this.client.listPrompts();
207
+ } catch (err) {
208
+ if (err.code === -32601) {
209
+ logger.debug("Server advertised prompts but method not found");
210
+ return { prompts: [] };
211
+ }
212
+ throw err;
213
+ }
214
+ }
215
+ async getPrompt(name, args) {
216
+ if (!this.client) {
217
+ throw new Error("MCP client is not connected");
218
+ }
219
+ logger.debug(`Getting prompt ${name}`);
220
+ return await this.client.getPrompt({ name, arguments: args });
221
+ }
222
+ /** Send a raw request through the client. */
223
+ async request(method, params = null, options) {
224
+ if (!this.client) {
225
+ throw new Error("MCP client is not connected");
226
+ }
227
+ logger.debug(`Sending raw request '${method}' with params`, params);
228
+ return await this.client.request({ method, params: params ?? {} }, void 0, options);
229
+ }
230
+ /**
231
+ * Helper to tear down the client & connection manager safely.
232
+ */
233
+ async cleanupResources() {
234
+ const issues = [];
235
+ if (this.client) {
236
+ try {
237
+ if (typeof this.client.close === "function") {
238
+ await this.client.close();
239
+ }
240
+ } catch (e) {
241
+ const msg = `Error closing client: ${e}`;
242
+ logger.warn(msg);
243
+ issues.push(msg);
244
+ } finally {
245
+ this.client = null;
246
+ }
247
+ }
248
+ if (this.connectionManager) {
249
+ try {
250
+ await this.connectionManager.stop();
251
+ } catch (e) {
252
+ const msg = `Error stopping connection manager: ${e}`;
253
+ logger.warn(msg);
254
+ issues.push(msg);
255
+ } finally {
256
+ this.connectionManager = null;
257
+ }
258
+ }
259
+ this.toolsCache = null;
260
+ if (issues.length) {
261
+ logger.warn(`Resource cleanup finished with ${issues.length} issue(s)`);
262
+ }
263
+ }
264
+ };
265
+
266
+ // src/connectors/http.ts
267
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
268
+ import { StreamableHTTPError } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
269
+
270
+ // src/task_managers/sse.ts
271
+ import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
272
+
273
+ // src/task_managers/base.ts
274
+ var ConnectionManager = class {
275
+ static {
276
+ __name(this, "ConnectionManager");
277
+ }
278
+ _readyPromise;
279
+ _readyResolver;
280
+ _donePromise;
281
+ _doneResolver;
282
+ _exception = null;
283
+ _connection = null;
284
+ _task = null;
285
+ _abortController = null;
286
+ constructor() {
287
+ this.reset();
288
+ }
289
+ /**
290
+ * Start the connection manager and establish a connection.
291
+ *
292
+ * @returns The established connection.
293
+ * @throws If the connection cannot be established.
294
+ */
295
+ async start() {
296
+ this.reset();
297
+ logger.debug(`Starting ${this.constructor.name}`);
298
+ this._task = this.connectionTask();
299
+ await this._readyPromise;
300
+ if (this._exception) {
301
+ throw this._exception;
302
+ }
303
+ if (this._connection === null) {
304
+ throw new Error("Connection was not established");
305
+ }
306
+ return this._connection;
307
+ }
308
+ /**
309
+ * Stop the connection manager and close the connection.
310
+ */
311
+ async stop() {
312
+ if (this._task && this._abortController) {
313
+ logger.debug(`Cancelling ${this.constructor.name} task`);
314
+ this._abortController.abort();
315
+ try {
316
+ await this._task;
317
+ } catch (e) {
318
+ if (e instanceof Error && e.name === "AbortError") {
319
+ logger.debug(`${this.constructor.name} task aborted successfully`);
320
+ } else {
321
+ logger.warn(`Error stopping ${this.constructor.name} task: ${e}`);
322
+ }
323
+ }
324
+ }
325
+ await this._donePromise;
326
+ logger.debug(`${this.constructor.name} task completed`);
327
+ }
328
+ /**
329
+ * Reset all internal state.
330
+ */
331
+ reset() {
332
+ this._readyPromise = new Promise((res) => this._readyResolver = res);
333
+ this._donePromise = new Promise((res) => this._doneResolver = res);
334
+ this._exception = null;
335
+ this._connection = null;
336
+ this._task = null;
337
+ this._abortController = new AbortController();
338
+ }
339
+ /**
340
+ * The background task responsible for establishing and maintaining the
341
+ * connection until it is cancelled.
342
+ */
343
+ async connectionTask() {
344
+ logger.debug(`Running ${this.constructor.name} task`);
345
+ try {
346
+ this._connection = await this.establishConnection();
347
+ logger.debug(`${this.constructor.name} connected successfully`);
348
+ this._readyResolver();
349
+ await this.waitForAbort();
350
+ } catch (err) {
351
+ this._exception = err;
352
+ logger.error(`Error in ${this.constructor.name} task: ${err}`);
353
+ this._readyResolver();
354
+ } finally {
355
+ if (this._connection !== null) {
356
+ try {
357
+ await this.closeConnection(this._connection);
358
+ } catch (closeErr) {
359
+ logger.warn(`Error closing connection in ${this.constructor.name}: ${closeErr}`);
360
+ }
361
+ this._connection = null;
362
+ }
363
+ this._doneResolver();
364
+ }
365
+ }
366
+ /**
367
+ * Helper that returns a promise which resolves when the abort signal fires.
368
+ */
369
+ async waitForAbort() {
370
+ return new Promise((_resolve, _reject) => {
371
+ if (!this._abortController) {
372
+ return;
373
+ }
374
+ const signal = this._abortController.signal;
375
+ if (signal.aborted) {
376
+ _resolve();
377
+ return;
378
+ }
379
+ const onAbort = /* @__PURE__ */ __name(() => {
380
+ signal.removeEventListener("abort", onAbort);
381
+ _resolve();
382
+ }, "onAbort");
383
+ signal.addEventListener("abort", onAbort);
384
+ });
385
+ }
386
+ };
387
+
388
+ // src/task_managers/sse.ts
389
+ var SseConnectionManager = class extends ConnectionManager {
390
+ static {
391
+ __name(this, "SseConnectionManager");
392
+ }
393
+ url;
394
+ opts;
395
+ _transport = null;
396
+ /**
397
+ * Create an SSE connection manager.
398
+ *
399
+ * @param url The SSE endpoint URL.
400
+ * @param opts Optional transport options (auth, headers, etc.).
401
+ */
402
+ constructor(url, opts) {
403
+ super();
404
+ this.url = typeof url === "string" ? new URL(url) : url;
405
+ this.opts = opts;
406
+ }
407
+ /**
408
+ * Spawn a new `SSEClientTransport` and start the connection.
409
+ */
410
+ async establishConnection() {
411
+ this._transport = new SSEClientTransport(this.url, this.opts);
412
+ logger.debug(`${this.constructor.name} connected successfully`);
413
+ return this._transport;
414
+ }
415
+ /**
416
+ * Close the underlying transport and clean up resources.
417
+ */
418
+ async closeConnection(_connection) {
419
+ if (this._transport) {
420
+ try {
421
+ await this._transport.close();
422
+ } catch (e) {
423
+ logger.warn(`Error closing SSE transport: ${e}`);
424
+ } finally {
425
+ this._transport = null;
426
+ }
427
+ }
428
+ }
429
+ };
430
+
431
+ // src/task_managers/streamable_http.ts
432
+ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
433
+ var StreamableHttpConnectionManager = class extends ConnectionManager {
434
+ static {
435
+ __name(this, "StreamableHttpConnectionManager");
436
+ }
437
+ url;
438
+ opts;
439
+ _transport = null;
440
+ /**
441
+ * Create a Streamable HTTP connection manager.
442
+ *
443
+ * @param url The HTTP endpoint URL.
444
+ * @param opts Optional transport options (auth, headers, etc.).
445
+ */
446
+ constructor(url, opts) {
447
+ super();
448
+ this.url = typeof url === "string" ? new URL(url) : url;
449
+ this.opts = opts;
450
+ }
451
+ /**
452
+ * Spawn a new `StreamableHTTPClientTransport` and return it.
453
+ * The Client.connect() method will handle starting the transport.
454
+ */
455
+ async establishConnection() {
456
+ this._transport = new StreamableHTTPClientTransport(this.url, this.opts);
457
+ logger.debug(`${this.constructor.name} created successfully`);
458
+ return this._transport;
459
+ }
460
+ /**
461
+ * Close the underlying transport and clean up resources.
462
+ */
463
+ async closeConnection(_connection) {
464
+ if (this._transport) {
465
+ try {
466
+ await this._transport.close();
467
+ } catch (e) {
468
+ logger.warn(`Error closing Streamable HTTP transport: ${e}`);
469
+ } finally {
470
+ this._transport = null;
471
+ }
472
+ }
473
+ }
474
+ /**
475
+ * Get the session ID from the transport if available.
476
+ */
477
+ get sessionId() {
478
+ return this._transport?.sessionId;
479
+ }
480
+ };
481
+
482
+ // src/connectors/http.ts
483
+ var HttpConnector = class extends BaseConnector {
484
+ static {
485
+ __name(this, "HttpConnector");
486
+ }
487
+ baseUrl;
488
+ headers;
489
+ timeout;
490
+ sseReadTimeout;
491
+ clientInfo;
492
+ preferSse;
493
+ transportType = null;
494
+ constructor(baseUrl, opts = {}) {
495
+ super(opts);
496
+ this.baseUrl = baseUrl.replace(/\/$/, "");
497
+ this.headers = { ...opts.headers ?? {} };
498
+ if (opts.authToken) {
499
+ this.headers.Authorization = `Bearer ${opts.authToken}`;
500
+ }
501
+ this.timeout = opts.timeout ?? 3e4;
502
+ this.sseReadTimeout = opts.sseReadTimeout ?? 3e5;
503
+ this.clientInfo = opts.clientInfo ?? { name: "http-connector", version: "1.0.0" };
504
+ this.preferSse = opts.preferSse ?? false;
505
+ }
506
+ /** Establish connection to the MCP implementation via HTTP (streamable or SSE). */
507
+ async connect() {
508
+ if (this.connected) {
509
+ logger.debug("Already connected to MCP implementation");
510
+ return;
511
+ }
512
+ const baseUrl = this.baseUrl;
513
+ if (this.preferSse) {
514
+ logger.debug(`Connecting to MCP implementation via HTTP/SSE: ${baseUrl}`);
515
+ await this.connectWithSse(baseUrl);
516
+ return;
517
+ }
518
+ logger.debug(`Connecting to MCP implementation via HTTP: ${baseUrl}`);
519
+ try {
520
+ logger.info("\u{1F504} Attempting streamable HTTP transport...");
521
+ await this.connectWithStreamableHttp(baseUrl);
522
+ logger.info("\u2705 Successfully connected via streamable HTTP");
523
+ } catch (err) {
524
+ let fallbackReason = "Unknown error";
525
+ let is401Error = false;
526
+ if (err instanceof StreamableHTTPError) {
527
+ is401Error = err.code === 401;
528
+ if (err.code === 400 && err.message.includes("Missing session ID")) {
529
+ fallbackReason = "Server requires session ID (FastMCP compatibility) - using SSE transport";
530
+ logger.warn(`\u26A0\uFE0F ${fallbackReason}`);
531
+ } else if (err.code === 404 || err.code === 405) {
532
+ fallbackReason = `Server returned ${err.code} - server likely doesn't support streamable HTTP`;
533
+ logger.debug(fallbackReason);
534
+ } else {
535
+ fallbackReason = `Server returned ${err.code}: ${err.message}`;
536
+ logger.debug(fallbackReason);
537
+ }
538
+ } else if (err instanceof Error) {
539
+ const errorStr = err.toString();
540
+ const errorMsg = err.message || "";
541
+ is401Error = errorStr.includes("401") || errorMsg.includes("Unauthorized");
542
+ if (errorStr.includes("Missing session ID") || errorStr.includes("Bad Request: Missing session ID") || errorMsg.includes("FastMCP session ID error")) {
543
+ fallbackReason = "Server requires session ID (FastMCP compatibility) - using SSE transport";
544
+ logger.warn(`\u26A0\uFE0F ${fallbackReason}`);
545
+ } else if (errorStr.includes("405 Method Not Allowed") || errorStr.includes("404 Not Found")) {
546
+ fallbackReason = "Server doesn't support streamable HTTP (405/404)";
547
+ logger.debug(fallbackReason);
548
+ } else {
549
+ fallbackReason = `Streamable HTTP failed: ${err.message}`;
550
+ logger.debug(fallbackReason);
551
+ }
552
+ }
553
+ if (is401Error) {
554
+ logger.info("Authentication required - skipping SSE fallback");
555
+ await this.cleanupResources();
556
+ const authError = new Error("Authentication required");
557
+ authError.code = 401;
558
+ throw authError;
559
+ }
560
+ logger.info("\u{1F504} Falling back to SSE transport...");
561
+ try {
562
+ await this.connectWithSse(baseUrl);
563
+ } catch (sseErr) {
564
+ logger.error(`Failed to connect with both transports:`);
565
+ logger.error(` Streamable HTTP: ${fallbackReason}`);
566
+ logger.error(` SSE: ${sseErr}`);
567
+ await this.cleanupResources();
568
+ const sseIs401 = sseErr?.message?.includes("401") || sseErr?.message?.includes("Unauthorized");
569
+ if (sseIs401) {
570
+ const authError = new Error("Authentication required");
571
+ authError.code = 401;
572
+ throw authError;
573
+ }
574
+ throw new Error("Could not connect to server with any available transport");
575
+ }
576
+ }
577
+ }
578
+ async connectWithStreamableHttp(baseUrl) {
579
+ try {
580
+ this.connectionManager = new StreamableHttpConnectionManager(
581
+ baseUrl,
582
+ {
583
+ authProvider: this.opts.authProvider,
584
+ // ← Pass OAuth provider to SDK
585
+ requestInit: {
586
+ headers: this.headers
587
+ },
588
+ // Pass through timeout and other options
589
+ reconnectionOptions: {
590
+ maxReconnectionDelay: 3e4,
591
+ initialReconnectionDelay: 1e3,
592
+ reconnectionDelayGrowFactor: 1.5,
593
+ maxRetries: 2
594
+ }
595
+ }
596
+ );
597
+ const transport = await this.connectionManager.start();
598
+ this.client = new Client(this.clientInfo, this.opts.clientOptions);
599
+ try {
600
+ await this.client.connect(transport);
601
+ } catch (connectErr) {
602
+ if (connectErr instanceof Error) {
603
+ const errMsg = connectErr.message || connectErr.toString();
604
+ if (errMsg.includes("Missing session ID") || errMsg.includes("Bad Request: Missing session ID")) {
605
+ const wrappedError = new Error(`FastMCP session ID error: ${errMsg}`);
606
+ wrappedError.cause = connectErr;
607
+ throw wrappedError;
608
+ }
609
+ }
610
+ throw connectErr;
611
+ }
612
+ this.connected = true;
613
+ this.transportType = "streamable-http";
614
+ logger.debug(`Successfully connected to MCP implementation via streamable HTTP: ${baseUrl}`);
615
+ } catch (err) {
616
+ await this.cleanupResources();
617
+ throw err;
618
+ }
619
+ }
620
+ async connectWithSse(baseUrl) {
621
+ try {
622
+ this.connectionManager = new SseConnectionManager(
623
+ baseUrl,
624
+ {
625
+ requestInit: {
626
+ headers: this.headers
627
+ }
628
+ }
629
+ );
630
+ const transport = await this.connectionManager.start();
631
+ this.client = new Client(this.clientInfo, this.opts.clientOptions);
632
+ await this.client.connect(transport);
633
+ this.connected = true;
634
+ this.transportType = "sse";
635
+ logger.debug(`Successfully connected to MCP implementation via HTTP/SSE: ${baseUrl}`);
636
+ } catch (err) {
637
+ await this.cleanupResources();
638
+ throw err;
639
+ }
640
+ }
641
+ get publicIdentifier() {
642
+ return {
643
+ type: "http",
644
+ url: this.baseUrl,
645
+ transport: this.transportType || "unknown"
646
+ };
647
+ }
648
+ /**
649
+ * Get the transport type being used (streamable-http or sse)
650
+ */
651
+ getTransportType() {
652
+ return this.transportType;
653
+ }
654
+ };
655
+
656
+ // src/connectors/websocket.ts
657
+ import { v4 as uuidv4 } from "uuid";
658
+
659
+ // src/task_managers/websocket.ts
660
+ import WS from "ws";
661
+ var WebSocketConnectionManager = class extends ConnectionManager {
662
+ static {
663
+ __name(this, "WebSocketConnectionManager");
664
+ }
665
+ url;
666
+ headers;
667
+ _ws = null;
668
+ /**
669
+ * @param url The WebSocket URL to connect to.
670
+ * @param headers Optional headers to include in the connection handshake.
671
+ */
672
+ constructor(url, headers = {}) {
673
+ super();
674
+ this.url = url;
675
+ this.headers = headers;
676
+ }
677
+ /** Establish a WebSocket connection and wait until it is open. */
678
+ async establishConnection() {
679
+ logger.debug(`Connecting to WebSocket: ${this.url}`);
680
+ return new Promise((resolve, reject) => {
681
+ const ws = new WS(this.url, { headers: this.headers });
682
+ this._ws = ws;
683
+ const onOpen = /* @__PURE__ */ __name(() => {
684
+ cleanup();
685
+ logger.debug("WebSocket connected successfully");
686
+ resolve(ws);
687
+ }, "onOpen");
688
+ const onError = /* @__PURE__ */ __name((err) => {
689
+ cleanup();
690
+ logger.error(`Failed to connect to WebSocket: ${err}`);
691
+ reject(err);
692
+ }, "onError");
693
+ const cleanup = /* @__PURE__ */ __name(() => {
694
+ ws.off("open", onOpen);
695
+ ws.off("error", onError);
696
+ }, "cleanup");
697
+ ws.on("open", onOpen);
698
+ ws.on("error", onError);
699
+ });
700
+ }
701
+ /** Cleanly close the WebSocket connection. */
702
+ async closeConnection(connection) {
703
+ logger.debug("Closing WebSocket connection");
704
+ return new Promise((resolve) => {
705
+ const onClose = /* @__PURE__ */ __name(() => {
706
+ connection.off("close", onClose);
707
+ this._ws = null;
708
+ resolve();
709
+ }, "onClose");
710
+ if (connection.readyState === WS.CLOSED) {
711
+ onClose();
712
+ return;
713
+ }
714
+ connection.on("close", onClose);
715
+ try {
716
+ connection.close();
717
+ } catch (e) {
718
+ logger.warn(`Error closing WebSocket connection: ${e}`);
719
+ onClose();
720
+ }
721
+ });
722
+ }
723
+ };
724
+
725
+ // src/connectors/websocket.ts
726
+ var WebSocketConnector = class extends BaseConnector {
727
+ static {
728
+ __name(this, "WebSocketConnector");
729
+ }
730
+ url;
731
+ headers;
732
+ connectionManager = null;
733
+ ws = null;
734
+ receiverTask = null;
735
+ pending = /* @__PURE__ */ new Map();
736
+ toolsCache = null;
737
+ constructor(url, opts = {}) {
738
+ super();
739
+ this.url = url;
740
+ this.headers = { ...opts.headers ?? {} };
741
+ if (opts.authToken)
742
+ this.headers.Authorization = `Bearer ${opts.authToken}`;
743
+ }
744
+ async connect() {
745
+ if (this.connected) {
746
+ logger.debug("Already connected to MCP implementation");
747
+ return;
748
+ }
749
+ logger.debug(`Connecting via WebSocket: ${this.url}`);
750
+ try {
751
+ this.connectionManager = new WebSocketConnectionManager(this.url, this.headers);
752
+ this.ws = await this.connectionManager.start();
753
+ this.receiverTask = this.receiveLoop();
754
+ this.connected = true;
755
+ logger.debug("WebSocket connected successfully");
756
+ } catch (e) {
757
+ logger.error(`Failed to connect: ${e}`);
758
+ await this.cleanupResources();
759
+ throw e;
760
+ }
761
+ }
762
+ async disconnect() {
763
+ if (!this.connected) {
764
+ logger.debug("Not connected to MCP implementation");
765
+ return;
766
+ }
767
+ logger.debug("Disconnecting \u2026");
768
+ await this.cleanupResources();
769
+ this.connected = false;
770
+ }
771
+ sendRequest(method, params = null) {
772
+ if (!this.ws)
773
+ throw new Error("WebSocket is not connected");
774
+ const id = uuidv4();
775
+ const payload = JSON.stringify({ id, method, params: params ?? {} });
776
+ return new Promise((resolve, reject) => {
777
+ this.pending.set(id, { resolve, reject });
778
+ this.ws.send(payload, (err) => {
779
+ if (err) {
780
+ this.pending.delete(id);
781
+ reject(err);
782
+ }
783
+ });
784
+ });
785
+ }
786
+ async receiveLoop() {
787
+ if (!this.ws)
788
+ return;
789
+ const socket = this.ws;
790
+ const onMessage = /* @__PURE__ */ __name((msg) => {
791
+ let data;
792
+ try {
793
+ data = JSON.parse(msg.data ?? msg);
794
+ } catch (e) {
795
+ logger.warn("Received non\u2011JSON frame", e);
796
+ return;
797
+ }
798
+ const id = data.id;
799
+ if (id && this.pending.has(id)) {
800
+ const { resolve, reject } = this.pending.get(id);
801
+ this.pending.delete(id);
802
+ if ("result" in data)
803
+ resolve(data.result);
804
+ else if ("error" in data)
805
+ reject(data.error);
806
+ } else {
807
+ logger.debug("Received unsolicited message", data);
808
+ }
809
+ }, "onMessage");
810
+ if (socket.addEventListener) {
811
+ socket.addEventListener("message", onMessage);
812
+ } else {
813
+ socket.on("message", onMessage);
814
+ }
815
+ return new Promise((resolve) => {
816
+ const onClose = /* @__PURE__ */ __name(() => {
817
+ if (socket.removeEventListener) {
818
+ socket.removeEventListener("message", onMessage);
819
+ } else {
820
+ socket.off("message", onMessage);
821
+ }
822
+ this.rejectAll(new Error("WebSocket closed"));
823
+ resolve();
824
+ }, "onClose");
825
+ if (socket.addEventListener) {
826
+ socket.addEventListener("close", onClose);
827
+ } else {
828
+ socket.on("close", onClose);
829
+ }
830
+ });
831
+ }
832
+ rejectAll(err) {
833
+ for (const { reject } of this.pending.values()) reject(err);
834
+ this.pending.clear();
835
+ }
836
+ async initialize() {
837
+ logger.debug("Initializing MCP session over WebSocket");
838
+ const result = await this.sendRequest("initialize");
839
+ const toolsList = await this.listTools();
840
+ this.toolsCache = toolsList.map((t) => t);
841
+ logger.debug(`Initialized with ${this.toolsCache.length} tools`);
842
+ return result;
843
+ }
844
+ async listTools() {
845
+ const res = await this.sendRequest("tools/list");
846
+ return res.tools ?? [];
847
+ }
848
+ async callTool(name, args) {
849
+ return await this.sendRequest("tools/call", { name, arguments: args });
850
+ }
851
+ async listResources() {
852
+ const resources = await this.sendRequest("resources/list");
853
+ return { resources: Array.isArray(resources) ? resources : [] };
854
+ }
855
+ async readResource(uri) {
856
+ const res = await this.sendRequest("resources/read", { uri });
857
+ return res;
858
+ }
859
+ async request(method, params = null) {
860
+ return await this.sendRequest(method, params);
861
+ }
862
+ get tools() {
863
+ if (!this.toolsCache)
864
+ throw new Error("MCP client is not initialized");
865
+ return this.toolsCache;
866
+ }
867
+ async cleanupResources() {
868
+ if (this.receiverTask)
869
+ await this.receiverTask.catch(() => {
870
+ });
871
+ this.receiverTask = null;
872
+ this.rejectAll(new Error("WebSocket disconnected"));
873
+ if (this.connectionManager) {
874
+ await this.connectionManager.stop();
875
+ this.connectionManager = null;
876
+ this.ws = null;
877
+ }
878
+ this.toolsCache = null;
879
+ }
880
+ get publicIdentifier() {
881
+ return {
882
+ type: "websocket",
883
+ url: this.url
884
+ };
885
+ }
886
+ };
887
+
888
+ // src/auth/browser-provider.ts
889
+ import { sanitizeUrl } from "strict-url-sanitise";
890
+ var BrowserOAuthClientProvider = class {
891
+ static {
892
+ __name(this, "BrowserOAuthClientProvider");
893
+ }
894
+ serverUrl;
895
+ storageKeyPrefix;
896
+ serverUrlHash;
897
+ clientName;
898
+ clientUri;
899
+ callbackUrl;
900
+ preventAutoAuth;
901
+ onPopupWindow;
902
+ constructor(serverUrl, options = {}) {
903
+ this.serverUrl = serverUrl;
904
+ this.storageKeyPrefix = options.storageKeyPrefix || "mcp:auth";
905
+ this.serverUrlHash = this.hashString(serverUrl);
906
+ this.clientName = options.clientName || "mcp-use";
907
+ this.clientUri = options.clientUri || (typeof window !== "undefined" ? window.location.origin : "");
908
+ this.callbackUrl = sanitizeUrl(
909
+ options.callbackUrl || (typeof window !== "undefined" ? new URL("/oauth/callback", window.location.origin).toString() : "/oauth/callback")
910
+ );
911
+ this.preventAutoAuth = options.preventAutoAuth;
912
+ this.onPopupWindow = options.onPopupWindow;
913
+ }
914
+ // --- SDK Interface Methods ---
915
+ get redirectUrl() {
916
+ return sanitizeUrl(this.callbackUrl);
917
+ }
918
+ get clientMetadata() {
919
+ return {
920
+ redirect_uris: [this.redirectUrl],
921
+ token_endpoint_auth_method: "none",
922
+ // Public client
923
+ grant_types: ["authorization_code", "refresh_token"],
924
+ response_types: ["code"],
925
+ client_name: this.clientName,
926
+ client_uri: this.clientUri
927
+ // scope: 'openid profile email mcp', // Example scopes, adjust as needed
928
+ };
929
+ }
930
+ async clientInformation() {
931
+ const key = this.getKey("client_info");
932
+ const data = localStorage.getItem(key);
933
+ if (!data) return void 0;
934
+ try {
935
+ return JSON.parse(data);
936
+ } catch (e) {
937
+ console.warn(`[${this.storageKeyPrefix}] Failed to parse client information:`, e);
938
+ localStorage.removeItem(key);
939
+ return void 0;
940
+ }
941
+ }
942
+ // NOTE: The SDK's auth() function uses this if dynamic registration is needed.
943
+ // Ensure your OAuthClientInformationFull matches the expected structure if DCR is used.
944
+ async saveClientInformation(clientInformation) {
945
+ const key = this.getKey("client_info");
946
+ localStorage.setItem(key, JSON.stringify(clientInformation));
947
+ }
948
+ async tokens() {
949
+ const key = this.getKey("tokens");
950
+ const data = localStorage.getItem(key);
951
+ if (!data) return void 0;
952
+ try {
953
+ return JSON.parse(data);
954
+ } catch (e) {
955
+ console.warn(`[${this.storageKeyPrefix}] Failed to parse tokens:`, e);
956
+ localStorage.removeItem(key);
957
+ return void 0;
958
+ }
959
+ }
960
+ async saveTokens(tokens) {
961
+ const key = this.getKey("tokens");
962
+ localStorage.setItem(key, JSON.stringify(tokens));
963
+ localStorage.removeItem(this.getKey("code_verifier"));
964
+ localStorage.removeItem(this.getKey("last_auth_url"));
965
+ }
966
+ async saveCodeVerifier(codeVerifier) {
967
+ const key = this.getKey("code_verifier");
968
+ localStorage.setItem(key, codeVerifier);
969
+ }
970
+ async codeVerifier() {
971
+ const key = this.getKey("code_verifier");
972
+ const verifier = localStorage.getItem(key);
973
+ if (!verifier) {
974
+ throw new Error(
975
+ `[${this.storageKeyPrefix}] Code verifier not found in storage for key ${key}. Auth flow likely corrupted or timed out.`
976
+ );
977
+ }
978
+ return verifier;
979
+ }
980
+ /**
981
+ * Generates and stores the authorization URL with state, without opening a popup.
982
+ * Used when preventAutoAuth is enabled to provide the URL for manual navigation.
983
+ * @param authorizationUrl The fully constructed authorization URL from the SDK.
984
+ * @returns The full authorization URL with state parameter.
985
+ */
986
+ async prepareAuthorizationUrl(authorizationUrl) {
987
+ const state = globalThis.crypto.randomUUID();
988
+ const stateKey = `${this.storageKeyPrefix}:state_${state}`;
989
+ const stateData = {
990
+ serverUrlHash: this.serverUrlHash,
991
+ expiry: Date.now() + 1e3 * 60 * 10,
992
+ // State expires in 10 minutes
993
+ // Store provider options needed to reconstruct on callback
994
+ providerOptions: {
995
+ serverUrl: this.serverUrl,
996
+ storageKeyPrefix: this.storageKeyPrefix,
997
+ clientName: this.clientName,
998
+ clientUri: this.clientUri,
999
+ callbackUrl: this.callbackUrl
1000
+ }
1001
+ };
1002
+ localStorage.setItem(stateKey, JSON.stringify(stateData));
1003
+ authorizationUrl.searchParams.set("state", state);
1004
+ const authUrlString = authorizationUrl.toString();
1005
+ const sanitizedAuthUrl = sanitizeUrl(authUrlString);
1006
+ localStorage.setItem(this.getKey("last_auth_url"), sanitizedAuthUrl);
1007
+ return sanitizedAuthUrl;
1008
+ }
1009
+ /**
1010
+ * Redirects the user agent to the authorization URL, storing necessary state.
1011
+ * This now adheres to the SDK's void return type expectation for the interface.
1012
+ * @param authorizationUrl The fully constructed authorization URL from the SDK.
1013
+ */
1014
+ async redirectToAuthorization(authorizationUrl) {
1015
+ if (this.preventAutoAuth) return;
1016
+ const sanitizedAuthUrl = await this.prepareAuthorizationUrl(authorizationUrl);
1017
+ const popupFeatures = "width=600,height=700,resizable=yes,scrollbars=yes,status=yes";
1018
+ try {
1019
+ const popup = window.open(sanitizedAuthUrl, `mcp_auth_${this.serverUrlHash}`, popupFeatures);
1020
+ if (this.onPopupWindow) {
1021
+ this.onPopupWindow(sanitizedAuthUrl, popupFeatures, popup);
1022
+ }
1023
+ if (!popup || popup.closed || typeof popup.closed === "undefined") {
1024
+ console.warn(
1025
+ `[${this.storageKeyPrefix}] Popup likely blocked by browser. Manual navigation might be required using the stored URL.`
1026
+ );
1027
+ } else {
1028
+ popup.focus();
1029
+ console.info(`[${this.storageKeyPrefix}] Redirecting to authorization URL in popup.`);
1030
+ }
1031
+ } catch (e) {
1032
+ console.error(`[${this.storageKeyPrefix}] Error opening popup window:`, e);
1033
+ }
1034
+ }
1035
+ // --- Helper Methods ---
1036
+ /**
1037
+ * Retrieves the last URL passed to `redirectToAuthorization`. Useful for manual fallback.
1038
+ */
1039
+ getLastAttemptedAuthUrl() {
1040
+ const storedUrl = localStorage.getItem(this.getKey("last_auth_url"));
1041
+ return storedUrl ? sanitizeUrl(storedUrl) : null;
1042
+ }
1043
+ clearStorage() {
1044
+ const prefixPattern = `${this.storageKeyPrefix}_${this.serverUrlHash}_`;
1045
+ const statePattern = `${this.storageKeyPrefix}:state_`;
1046
+ const keysToRemove = [];
1047
+ let count = 0;
1048
+ for (let i = 0; i < localStorage.length; i++) {
1049
+ const key = localStorage.key(i);
1050
+ if (!key) continue;
1051
+ if (key.startsWith(prefixPattern)) {
1052
+ keysToRemove.push(key);
1053
+ } else if (key.startsWith(statePattern)) {
1054
+ try {
1055
+ const item = localStorage.getItem(key);
1056
+ if (item) {
1057
+ const state = JSON.parse(item);
1058
+ if (state.serverUrlHash === this.serverUrlHash) {
1059
+ keysToRemove.push(key);
1060
+ }
1061
+ }
1062
+ } catch (e) {
1063
+ console.warn(`[${this.storageKeyPrefix}] Error parsing state key ${key} during clearStorage:`, e);
1064
+ }
1065
+ }
1066
+ }
1067
+ const uniqueKeysToRemove = [...new Set(keysToRemove)];
1068
+ uniqueKeysToRemove.forEach((key) => {
1069
+ localStorage.removeItem(key);
1070
+ count++;
1071
+ });
1072
+ return count;
1073
+ }
1074
+ hashString(str) {
1075
+ let hash = 0;
1076
+ for (let i = 0; i < str.length; i++) {
1077
+ const char = str.charCodeAt(i);
1078
+ hash = (hash << 5) - hash + char;
1079
+ hash = hash & hash;
1080
+ }
1081
+ return Math.abs(hash).toString(16);
1082
+ }
1083
+ getKey(keySuffix) {
1084
+ return `${this.storageKeyPrefix}_${this.serverUrlHash}_${keySuffix}`;
1085
+ }
1086
+ };
1087
+
1088
+ // src/auth/callback.ts
1089
+ import { auth } from "@modelcontextprotocol/sdk/client/auth.js";
1090
+ async function onMcpAuthorization() {
1091
+ const queryParams = new URLSearchParams(window.location.search);
1092
+ const code = queryParams.get("code");
1093
+ const state = queryParams.get("state");
1094
+ const error = queryParams.get("error");
1095
+ const errorDescription = queryParams.get("error_description");
1096
+ const logPrefix = "[mcp-callback]";
1097
+ console.log(`${logPrefix} Handling callback...`, { code, state, error, errorDescription });
1098
+ let provider = null;
1099
+ let storedStateData = null;
1100
+ const stateKey = state ? `mcp:auth:state_${state}` : null;
1101
+ try {
1102
+ if (error) {
1103
+ throw new Error(`OAuth error: ${error} - ${errorDescription || "No description provided."}`);
1104
+ }
1105
+ if (!code) {
1106
+ throw new Error("Authorization code not found in callback query parameters.");
1107
+ }
1108
+ if (!state || !stateKey) {
1109
+ throw new Error("State parameter not found or invalid in callback query parameters.");
1110
+ }
1111
+ const storedStateJSON = localStorage.getItem(stateKey);
1112
+ if (!storedStateJSON) {
1113
+ throw new Error(`Invalid or expired state parameter "${state}". No matching state found in storage.`);
1114
+ }
1115
+ try {
1116
+ storedStateData = JSON.parse(storedStateJSON);
1117
+ } catch (e) {
1118
+ throw new Error("Failed to parse stored OAuth state.");
1119
+ }
1120
+ if (!storedStateData.expiry || storedStateData.expiry < Date.now()) {
1121
+ localStorage.removeItem(stateKey);
1122
+ throw new Error("OAuth state has expired. Please try initiating authentication again.");
1123
+ }
1124
+ if (!storedStateData.providerOptions) {
1125
+ throw new Error("Stored state is missing required provider options.");
1126
+ }
1127
+ const { serverUrl, ...providerOptions } = storedStateData.providerOptions;
1128
+ console.log(`${logPrefix} Re-instantiating provider for server: ${serverUrl}`);
1129
+ provider = new BrowserOAuthClientProvider(serverUrl, providerOptions);
1130
+ console.log(`${logPrefix} Calling SDK auth() to exchange code...`);
1131
+ const baseUrl = new URL(serverUrl).origin;
1132
+ const authResult = await auth(provider, { serverUrl: baseUrl, authorizationCode: code });
1133
+ if (authResult === "AUTHORIZED") {
1134
+ console.log(`${logPrefix} Authorization successful via SDK auth(). Notifying opener...`);
1135
+ if (window.opener && !window.opener.closed) {
1136
+ window.opener.postMessage({ type: "mcp_auth_callback", success: true }, window.location.origin);
1137
+ window.close();
1138
+ } else {
1139
+ console.warn(`${logPrefix} No opener window detected. Redirecting to root.`);
1140
+ const pathParts = window.location.pathname.split("/").filter(Boolean);
1141
+ const basePath = pathParts.length > 0 && pathParts[pathParts.length - 1] === "callback" ? "/" + pathParts.slice(0, -2).join("/") : "/";
1142
+ window.location.href = basePath || "/";
1143
+ }
1144
+ localStorage.removeItem(stateKey);
1145
+ } else {
1146
+ console.warn(`${logPrefix} SDK auth() returned unexpected status: ${authResult}`);
1147
+ throw new Error(`Unexpected result from authentication library: ${authResult}`);
1148
+ }
1149
+ } catch (err) {
1150
+ console.error(`${logPrefix} Error during OAuth callback handling:`, err);
1151
+ const errorMessage = err instanceof Error ? err.message : String(err);
1152
+ if (window.opener && !window.opener.closed) {
1153
+ window.opener.postMessage({ type: "mcp_auth_callback", success: false, error: errorMessage }, window.location.origin);
1154
+ }
1155
+ try {
1156
+ document.body.innerHTML = `
1157
+ <div style="font-family: sans-serif; padding: 20px;">
1158
+ <h1>Authentication Error</h1>
1159
+ <p style="color: red; background-color: #ffebeb; border: 1px solid red; padding: 10px; border-radius: 4px;">
1160
+ ${errorMessage}
1161
+ </p>
1162
+ <p>You can close this window or <a href="#" onclick="window.close(); return false;">click here to close</a>.</p>
1163
+ <pre style="font-size: 0.8em; color: #555; margin-top: 20px; white-space: pre-wrap;">${err instanceof Error ? err.stack : ""}</pre>
1164
+ </div>
1165
+ `;
1166
+ } catch (displayError) {
1167
+ console.error(`${logPrefix} Could not display error in callback window:`, displayError);
1168
+ }
1169
+ if (stateKey) {
1170
+ localStorage.removeItem(stateKey);
1171
+ }
1172
+ if (provider) {
1173
+ localStorage.removeItem(provider.getKey("code_verifier"));
1174
+ localStorage.removeItem(provider.getKey("last_auth_url"));
1175
+ }
1176
+ }
1177
+ }
1178
+ __name(onMcpAuthorization, "onMcpAuthorization");
1179
+
1180
+ // src/client/base.ts
1181
+ var BaseMCPClient = class {
1182
+ static {
1183
+ __name(this, "BaseMCPClient");
1184
+ }
1185
+ config = {};
1186
+ sessions = {};
1187
+ activeSessions = [];
1188
+ constructor(config) {
1189
+ if (config) {
1190
+ this.config = config;
1191
+ }
1192
+ }
1193
+ static fromDict(_cfg) {
1194
+ throw new Error("fromDict must be implemented by concrete class");
1195
+ }
1196
+ addServer(name, serverConfig) {
1197
+ this.config.mcpServers = this.config.mcpServers || {};
1198
+ this.config.mcpServers[name] = serverConfig;
1199
+ }
1200
+ removeServer(name) {
1201
+ if (this.config.mcpServers?.[name]) {
1202
+ delete this.config.mcpServers[name];
1203
+ this.activeSessions = this.activeSessions.filter((n) => n !== name);
1204
+ }
1205
+ }
1206
+ getServerNames() {
1207
+ return Object.keys(this.config.mcpServers ?? {});
1208
+ }
1209
+ getServerConfig(name) {
1210
+ return this.config.mcpServers?.[name];
1211
+ }
1212
+ getConfig() {
1213
+ return this.config ?? {};
1214
+ }
1215
+ async createSession(serverName, autoInitialize = true) {
1216
+ const servers = this.config.mcpServers ?? {};
1217
+ if (Object.keys(servers).length === 0) {
1218
+ logger.warn("No MCP servers defined in config");
1219
+ }
1220
+ if (!servers[serverName]) {
1221
+ throw new Error(`Server '${serverName}' not found in config`);
1222
+ }
1223
+ const connector = this.createConnectorFromConfig(servers[serverName]);
1224
+ const session = new MCPSession(connector);
1225
+ if (autoInitialize) {
1226
+ await session.initialize();
1227
+ }
1228
+ this.sessions[serverName] = session;
1229
+ if (!this.activeSessions.includes(serverName)) {
1230
+ this.activeSessions.push(serverName);
1231
+ }
1232
+ return session;
1233
+ }
1234
+ async createAllSessions(autoInitialize = true) {
1235
+ const servers = this.config.mcpServers ?? {};
1236
+ if (Object.keys(servers).length === 0) {
1237
+ logger.warn("No MCP servers defined in config");
1238
+ }
1239
+ for (const name of Object.keys(servers)) {
1240
+ await this.createSession(name, autoInitialize);
1241
+ }
1242
+ return this.sessions;
1243
+ }
1244
+ getSession(serverName) {
1245
+ const session = this.sessions[serverName];
1246
+ if (!session) {
1247
+ return null;
1248
+ }
1249
+ return session;
1250
+ }
1251
+ getAllActiveSessions() {
1252
+ return Object.fromEntries(
1253
+ this.activeSessions.map((n) => [n, this.sessions[n]])
1254
+ );
1255
+ }
1256
+ async closeSession(serverName) {
1257
+ const session = this.sessions[serverName];
1258
+ if (!session) {
1259
+ logger.warn(`No session exists for server ${serverName}, nothing to close`);
1260
+ return;
1261
+ }
1262
+ try {
1263
+ logger.debug(`Closing session for server ${serverName}`);
1264
+ await session.disconnect();
1265
+ } catch (e) {
1266
+ logger.error(`Error closing session for server '${serverName}': ${e}`);
1267
+ } finally {
1268
+ delete this.sessions[serverName];
1269
+ this.activeSessions = this.activeSessions.filter((n) => n !== serverName);
1270
+ }
1271
+ }
1272
+ async closeAllSessions() {
1273
+ const serverNames = Object.keys(this.sessions);
1274
+ const errors = [];
1275
+ for (const serverName of serverNames) {
1276
+ try {
1277
+ logger.debug(`Closing session for server ${serverName}`);
1278
+ await this.closeSession(serverName);
1279
+ } catch (e) {
1280
+ const errorMsg = `Failed to close session for server '${serverName}': ${e}`;
1281
+ logger.error(errorMsg);
1282
+ errors.push(errorMsg);
1283
+ }
1284
+ }
1285
+ if (errors.length) {
1286
+ logger.error(`Encountered ${errors.length} errors while closing sessions`);
1287
+ } else {
1288
+ logger.debug("All sessions closed successfully");
1289
+ }
1290
+ }
1291
+ };
1292
+
1293
+ // src/client/browser.ts
1294
+ var BrowserMCPClient = class _BrowserMCPClient extends BaseMCPClient {
1295
+ static {
1296
+ __name(this, "BrowserMCPClient");
1297
+ }
1298
+ constructor(config) {
1299
+ super(config);
1300
+ }
1301
+ static fromDict(cfg) {
1302
+ return new _BrowserMCPClient(cfg);
1303
+ }
1304
+ /**
1305
+ * Create a connector from server configuration (Browser version)
1306
+ * Supports HTTP and WebSocket connectors only
1307
+ */
1308
+ createConnectorFromConfig(serverConfig) {
1309
+ const { url, transport, headers, authToken, authProvider } = serverConfig;
1310
+ if (!url) {
1311
+ throw new Error("Server URL is required");
1312
+ }
1313
+ const connectorOptions = {
1314
+ headers,
1315
+ authToken,
1316
+ authProvider
1317
+ // ← Pass OAuth provider to connector
1318
+ };
1319
+ if (transport === "websocket" || url.startsWith("ws://") || url.startsWith("wss://")) {
1320
+ return new WebSocketConnector(url, connectorOptions);
1321
+ } else if (transport === "http" || url.startsWith("http://") || url.startsWith("https://")) {
1322
+ return new HttpConnector(url, connectorOptions);
1323
+ } else {
1324
+ return new HttpConnector(url, connectorOptions);
1325
+ }
1326
+ }
1327
+ };
1328
+
1329
+ export {
1330
+ MCPSession,
1331
+ BaseMCPClient,
1332
+ ConnectionManager,
1333
+ BaseConnector,
1334
+ HttpConnector,
1335
+ WebSocketConnector,
1336
+ BrowserOAuthClientProvider,
1337
+ onMcpAuthorization,
1338
+ BrowserMCPClient
1339
+ };