mcp-use 1.34.2 → 1.34.3

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.
Files changed (41) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/{chunk-QNMWBM3V.js → chunk-4GUL52TF.js} +1 -1
  3. package/dist/{chunk-3L7F47SI.js → chunk-BJIP3NFE.js} +4 -4
  4. package/dist/{chunk-Z3NHDOLS.js → chunk-CQPVGLM4.js} +1 -1
  5. package/dist/{chunk-P2JIVJY6.js → chunk-CXHJETCQ.js} +4 -4
  6. package/dist/{chunk-VJAQ2A7N.js → chunk-GTLSCTCO.js} +1 -1
  7. package/dist/{chunk-EJOL74UE.js → chunk-J67NWN5H.js} +1 -1
  8. package/dist/{chunk-6DJM56C3.js → chunk-KWYZMUVB.js} +2 -2
  9. package/dist/chunk-KX7P6L43.js +171 -0
  10. package/dist/{chunk-VT4KQHJI.js → chunk-MCYFETK3.js} +2 -2
  11. package/dist/{chunk-DENFIIV2.js → chunk-NJPLVA2I.js} +1 -1
  12. package/dist/chunk-NQYSWKWJ.js +2609 -0
  13. package/dist/{chunk-OYFQOSSW.js → chunk-NYIMA6MZ.js} +1 -1
  14. package/dist/chunk-PJB2MFKZ.js +954 -0
  15. package/dist/chunk-PSD6ATRG.js +562 -0
  16. package/dist/{chunk-T3QVQIYU.js → chunk-QLKY4PK3.js} +5 -5
  17. package/dist/chunk-SUVUANZ3.js +594 -0
  18. package/dist/chunk-XHP35F4S.js +204 -0
  19. package/dist/chunk-ZLHL3BY6.js +2690 -0
  20. package/dist/{client-24ODJASB.js → client-VKWZPQCL.js} +4 -4
  21. package/dist/index.cjs +1 -1
  22. package/dist/index.js +6 -6
  23. package/dist/src/agents/index.cjs +1 -1
  24. package/dist/src/agents/index.js +4 -4
  25. package/dist/src/browser-agent.cjs +1 -1
  26. package/dist/src/browser-agent.js +2 -2
  27. package/dist/src/browser.cjs +1 -1
  28. package/dist/src/browser.js +4 -4
  29. package/dist/src/client.cjs +1 -1
  30. package/dist/src/client.js +4 -4
  31. package/dist/src/react/index.cjs +1 -1
  32. package/dist/src/react/index.js +4 -4
  33. package/dist/src/server/index.cjs +1 -1
  34. package/dist/src/server/index.js +7 -7
  35. package/dist/src/version.d.ts +1 -1
  36. package/dist/{stdio-CG6YESIU.js → stdio-37X7ETM7.js} +3 -3
  37. package/dist/{stdio-KZLWEEYB.js → stdio-CSGVA5IJ.js} +2 -2
  38. package/dist/stdio-FUBSRH66.js +13 -0
  39. package/dist/{tool-execution-helpers-GVP22F32.js → tool-execution-helpers-4LIAFMYM.js} +2 -2
  40. package/dist/tool-execution-helpers-HPVG5WVB.js +27 -0
  41. package/package.json +3 -3
@@ -0,0 +1,2609 @@
1
+ import {
2
+ ConnectionManager
3
+ } from "./chunk-GKPKUKD6.js";
4
+ import {
5
+ CodeModeConnector
6
+ } from "./chunk-J67NWN5H.js";
7
+ import {
8
+ BaseConnector
9
+ } from "./chunk-PSD6ATRG.js";
10
+ import {
11
+ Tel,
12
+ getPackageVersion
13
+ } from "./chunk-BJIP3NFE.js";
14
+ import {
15
+ JSONSchemaToZod
16
+ } from "./chunk-LG5NSHEL.js";
17
+ import {
18
+ logger
19
+ } from "./chunk-QWQYAQCK.js";
20
+ import {
21
+ __name,
22
+ __require
23
+ } from "./chunk-3GQAWCBQ.js";
24
+
25
+ // src/client.ts
26
+ import fs from "fs";
27
+ import path from "path";
28
+
29
+ // src/session.ts
30
+ var MCPSession = class {
31
+ static {
32
+ __name(this, "MCPSession");
33
+ }
34
+ /**
35
+ * The underlying connector managing the transport layer.
36
+ * This is the Stdio, HTTP, or WebSocket connector handling actual communication.
37
+ */
38
+ connector;
39
+ /**
40
+ * Whether to automatically connect when initializing.
41
+ * @internal
42
+ */
43
+ autoConnect;
44
+ /**
45
+ * Creates a new MCP session.
46
+ *
47
+ * @param connector - The connector to use for communication (Stdio, HTTP, WebSocket)
48
+ * @param autoConnect - Whether to automatically connect during initialization (default: true)
49
+ *
50
+ * @example
51
+ * ```typescript
52
+ * const connector = new HttpConnector({ url: 'http://localhost:3000/mcp' });
53
+ * const session = new MCPSession(connector);
54
+ * await session.initialize(); // Auto-connects and initializes
55
+ * ```
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * // Manual connection control
60
+ * const session = new MCPSession(connector, false);
61
+ * await session.connect();
62
+ * await session.initialize();
63
+ * ```
64
+ */
65
+ constructor(connector, autoConnect = true) {
66
+ this.connector = connector;
67
+ this.autoConnect = autoConnect;
68
+ }
69
+ /**
70
+ * Establishes the connection to the MCP server.
71
+ *
72
+ * This method starts the underlying transport (spawns process for Stdio,
73
+ * opens WebSocket, etc.) but does not perform the MCP initialization
74
+ * handshake. Call {@link initialize} after connecting.
75
+ *
76
+ * @returns Promise that resolves when connected
77
+ *
78
+ * @example
79
+ * ```typescript
80
+ * await session.connect();
81
+ * await session.initialize();
82
+ * ```
83
+ *
84
+ * @see {@link initialize} for performing the MCP handshake
85
+ * @see {@link disconnect} for closing the connection
86
+ */
87
+ async connect() {
88
+ await this.connector.connect();
89
+ }
90
+ /**
91
+ * Closes the connection to the MCP server.
92
+ *
93
+ * This method gracefully shuts down the transport and cleans up resources.
94
+ * After disconnecting, the session cannot be used until reconnected.
95
+ *
96
+ * @returns Promise that resolves when disconnected
97
+ *
98
+ * @example
99
+ * ```typescript
100
+ * await session.disconnect();
101
+ * console.log('Session closed');
102
+ * ```
103
+ *
104
+ * @see {@link connect} for establishing connections
105
+ */
106
+ async disconnect() {
107
+ await this.connector.disconnect();
108
+ }
109
+ /**
110
+ * Initializes the MCP session with the server.
111
+ *
112
+ * This method performs the MCP initialization handshake, exchanging
113
+ * capabilities and metadata with the server. If `autoConnect` is true
114
+ * and the session is not yet connected, it will connect first.
115
+ *
116
+ * After initialization, you can list and call tools, read resources, etc.
117
+ *
118
+ * @returns Promise that resolves when initialized
119
+ *
120
+ * @example
121
+ * ```typescript
122
+ * const session = await client.createSession('my-server', false);
123
+ * await session.connect();
124
+ * await session.initialize();
125
+ * // Now ready to use
126
+ * const tools = await session.listTools();
127
+ * ```
128
+ *
129
+ * @see {@link connect} for establishing the connection first
130
+ */
131
+ async initialize() {
132
+ if (!this.isConnected && this.autoConnect) {
133
+ await this.connect();
134
+ }
135
+ await this.connector.initialize();
136
+ }
137
+ /**
138
+ * Checks if the session is currently connected to the server.
139
+ *
140
+ * @returns True if connected, false otherwise
141
+ *
142
+ * @example
143
+ * ```typescript
144
+ * if (session.isConnected) {
145
+ * const tools = await session.listTools();
146
+ * }
147
+ * ```
148
+ */
149
+ get isConnected() {
150
+ return this.connector && this.connector.isClientConnected;
151
+ }
152
+ /**
153
+ * Register an event handler for session events
154
+ *
155
+ * @param event - The event type to listen for
156
+ * @param handler - The handler function to call when the event occurs
157
+ *
158
+ * @example
159
+ * ```typescript
160
+ * session.on("notification", async (notification) => {
161
+ * console.log(`Received: ${notification.method}`, notification.params);
162
+ *
163
+ * if (notification.method === "notifications/tools/list_changed") {
164
+ * // Refresh tools list
165
+ * }
166
+ * });
167
+ * ```
168
+ */
169
+ on(event, handler) {
170
+ if (event === "notification") {
171
+ this.connector.onNotification(handler);
172
+ }
173
+ }
174
+ /**
175
+ * Set roots and notify the server.
176
+ * Roots represent directories or files that the client has access to.
177
+ *
178
+ * @param roots - Array of Root objects with `uri` (must start with "file://") and optional `name`
179
+ *
180
+ * @example
181
+ * ```typescript
182
+ * await session.setRoots([
183
+ * { uri: "file:///home/user/project", name: "My Project" },
184
+ * { uri: "file:///home/user/data" }
185
+ * ]);
186
+ * ```
187
+ */
188
+ async setRoots(roots) {
189
+ return this.connector.setRoots(roots);
190
+ }
191
+ /**
192
+ * Gets the current roots advertised to the server.
193
+ *
194
+ * Roots represent directories or files that the client has provided access to.
195
+ * The server may use this information to scope its operations.
196
+ *
197
+ * @returns Array of Root objects
198
+ *
199
+ * @example
200
+ * ```typescript
201
+ * const roots = session.getRoots();
202
+ * console.log(`Current roots: ${roots.map(r => r.uri).join(', ')}`);
203
+ * ```
204
+ *
205
+ * @see {@link setRoots} for updating roots
206
+ */
207
+ getRoots() {
208
+ return this.connector.getRoots();
209
+ }
210
+ /**
211
+ * Get the cached list of tools from the server.
212
+ *
213
+ * @returns Array of available tools
214
+ *
215
+ * @example
216
+ * ```typescript
217
+ * const tools = session.tools;
218
+ * console.log(`Available tools: ${tools.map(t => t.name).join(", ")}`);
219
+ * ```
220
+ */
221
+ get tools() {
222
+ return this.connector.tools;
223
+ }
224
+ /**
225
+ * List all available tools from the MCP server.
226
+ * This method fetches fresh tools from the server, unlike the `tools` getter which returns cached tools.
227
+ *
228
+ * @param options - Optional request options
229
+ * @returns Array of available tools
230
+ *
231
+ * @example
232
+ * ```typescript
233
+ * const tools = await session.listTools();
234
+ * console.log(`Available tools: ${tools.map(t => t.name).join(", ")}`);
235
+ * ```
236
+ */
237
+ async listTools(options) {
238
+ return this.connector.listTools(options);
239
+ }
240
+ /**
241
+ * Get the server capabilities advertised during initialization.
242
+ *
243
+ * @returns Server capabilities object
244
+ */
245
+ get serverCapabilities() {
246
+ return this.connector.serverCapabilities;
247
+ }
248
+ /**
249
+ * Get the server information (name and version).
250
+ *
251
+ * @returns Server info object or null if not available
252
+ */
253
+ get serverInfo() {
254
+ return this.connector.serverInfo;
255
+ }
256
+ /**
257
+ * Call a tool on the server.
258
+ *
259
+ * @param name - Name of the tool to call
260
+ * @param args - Arguments to pass to the tool (defaults to empty object)
261
+ * @param options - Optional request options (timeout, progress handlers, etc.)
262
+ * @returns Result from the tool execution
263
+ *
264
+ * @example
265
+ * ```typescript
266
+ * const result = await session.callTool("add", { a: 5, b: 3 });
267
+ * console.log(`Result: ${result.content[0].text}`);
268
+ * ```
269
+ */
270
+ async callTool(name, args = {}, options) {
271
+ return this.connector.callTool(name, args, options);
272
+ }
273
+ /**
274
+ * List resources from the server with optional pagination.
275
+ *
276
+ * @param cursor - Optional cursor for pagination
277
+ * @param options - Request options
278
+ * @returns Resource list with optional nextCursor for pagination
279
+ *
280
+ * @example
281
+ * ```typescript
282
+ * const result = await session.listResources();
283
+ * console.log(`Found ${result.resources.length} resources`);
284
+ * ```
285
+ */
286
+ async listResources(cursor, options) {
287
+ return this.connector.listResources(cursor, options);
288
+ }
289
+ /**
290
+ * List all resources from the server, automatically handling pagination.
291
+ *
292
+ * @param options - Request options
293
+ * @returns Complete list of all resources
294
+ *
295
+ * @example
296
+ * ```typescript
297
+ * const result = await session.listAllResources();
298
+ * console.log(`Total resources: ${result.resources.length}`);
299
+ * ```
300
+ */
301
+ async listAllResources(options) {
302
+ return this.connector.listAllResources(options);
303
+ }
304
+ /**
305
+ * List resource templates from the server.
306
+ *
307
+ * @param options - Request options
308
+ * @returns List of available resource templates
309
+ *
310
+ * @example
311
+ * ```typescript
312
+ * const result = await session.listResourceTemplates();
313
+ * console.log(`Available templates: ${result.resourceTemplates.length}`);
314
+ * ```
315
+ */
316
+ async listResourceTemplates(options) {
317
+ return this.connector.listResourceTemplates(options);
318
+ }
319
+ /**
320
+ * Request completion suggestions for a prompt or resource template argument.
321
+ *
322
+ * @param params - Completion request parameters
323
+ * @param options - Request options
324
+ * @returns Completion suggestions from the server
325
+ *
326
+ * @example
327
+ * ```typescript
328
+ * // Complete a prompt argument
329
+ * const result = await session.complete({
330
+ * ref: { type: "ref/prompt", name: "my-prompt" },
331
+ * argument: { name: "language", value: "py" }
332
+ * });
333
+ * console.log(result.completion.values); // ["python"]
334
+ * ```
335
+ */
336
+ async complete(params, options) {
337
+ return this.connector.complete(params, options);
338
+ }
339
+ /**
340
+ * Read a resource by URI.
341
+ *
342
+ * @param uri - URI of the resource to read
343
+ * @param options - Request options
344
+ * @returns Resource content
345
+ *
346
+ * @example
347
+ * ```typescript
348
+ * const resource = await session.readResource("file:///path/to/file.txt");
349
+ * console.log(resource.contents);
350
+ * ```
351
+ */
352
+ async readResource(uri, options) {
353
+ return this.connector.readResource(uri, options);
354
+ }
355
+ /**
356
+ * Subscribe to resource updates.
357
+ *
358
+ * @param uri - URI of the resource to subscribe to
359
+ * @param options - Request options
360
+ *
361
+ * @example
362
+ * ```typescript
363
+ * await session.subscribeToResource("file:///path/to/file.txt");
364
+ * // Now you'll receive notifications when this resource changes
365
+ * ```
366
+ */
367
+ async subscribeToResource(uri, options) {
368
+ return this.connector.subscribeToResource(uri, options);
369
+ }
370
+ /**
371
+ * Unsubscribe from resource updates.
372
+ *
373
+ * @param uri - URI of the resource to unsubscribe from
374
+ * @param options - Request options
375
+ *
376
+ * @example
377
+ * ```typescript
378
+ * await session.unsubscribeFromResource("file:///path/to/file.txt");
379
+ * ```
380
+ */
381
+ async unsubscribeFromResource(uri, options) {
382
+ return this.connector.unsubscribeFromResource(uri, options);
383
+ }
384
+ /**
385
+ * List available prompts from the server.
386
+ *
387
+ * @returns List of available prompts
388
+ *
389
+ * @example
390
+ * ```typescript
391
+ * const result = await session.listPrompts();
392
+ * console.log(`Available prompts: ${result.prompts.length}`);
393
+ * ```
394
+ */
395
+ async listPrompts() {
396
+ return this.connector.listPrompts();
397
+ }
398
+ /**
399
+ * Get a specific prompt with arguments.
400
+ *
401
+ * @param name - Name of the prompt to get
402
+ * @param args - Arguments for the prompt
403
+ * @returns Prompt result
404
+ *
405
+ * @example
406
+ * ```typescript
407
+ * const prompt = await session.getPrompt("greeting", { name: "Alice" });
408
+ * console.log(prompt.messages);
409
+ * ```
410
+ */
411
+ async getPrompt(name, args) {
412
+ return this.connector.getPrompt(name, args);
413
+ }
414
+ /**
415
+ * Send a raw request through the client.
416
+ *
417
+ * @param method - MCP method name
418
+ * @param params - Request parameters
419
+ * @param options - Request options
420
+ * @returns Response from the server
421
+ *
422
+ * @example
423
+ * ```typescript
424
+ * const result = await session.request("custom/method", { key: "value" });
425
+ * ```
426
+ */
427
+ async request(method, params = null, options) {
428
+ return this.connector.request(method, params, options);
429
+ }
430
+ };
431
+
432
+ // src/client/base.ts
433
+ var BaseMCPClient = class {
434
+ static {
435
+ __name(this, "BaseMCPClient");
436
+ }
437
+ /**
438
+ * Internal configuration object containing MCP server definitions.
439
+ * @protected
440
+ */
441
+ config = {};
442
+ /**
443
+ * Map of server names to their active sessions.
444
+ * @protected
445
+ */
446
+ sessions = {};
447
+ /**
448
+ * List of server names that have active sessions.
449
+ * This array is kept in sync with the sessions map and can be used
450
+ * to iterate over active connections.
451
+ *
452
+ * @example
453
+ * ```typescript
454
+ * console.log(`Active servers: ${client.activeSessions.join(', ')}`);
455
+ * ```
456
+ */
457
+ activeSessions = [];
458
+ /**
459
+ * Creates a new BaseMCPClient instance.
460
+ *
461
+ * @param config - Optional configuration object with MCP server definitions
462
+ *
463
+ * @example
464
+ * ```typescript
465
+ * const client = new MCPClient({
466
+ * mcpServers: {
467
+ * 'example': {
468
+ * command: 'node',
469
+ * args: ['server.js']
470
+ * }
471
+ * }
472
+ * });
473
+ * ```
474
+ */
475
+ constructor(config) {
476
+ if (config) {
477
+ this.config = config;
478
+ }
479
+ }
480
+ /**
481
+ * Creates a client instance from a configuration dictionary.
482
+ *
483
+ * This static factory method must be implemented by concrete subclasses
484
+ * to provide proper type information and platform-specific initialization.
485
+ *
486
+ * @param _cfg - Configuration dictionary
487
+ * @returns Client instance
488
+ * @throws {Error} If called on the base class instead of a concrete implementation
489
+ *
490
+ * @example
491
+ * ```typescript
492
+ * const client = MCPClient.fromDict({
493
+ * mcpServers: {
494
+ * 'my-server': { command: 'node', args: ['server.js'] }
495
+ * }
496
+ * });
497
+ * ```
498
+ */
499
+ static fromDict(_cfg) {
500
+ throw new Error("fromDict must be implemented by concrete class");
501
+ }
502
+ /**
503
+ * Adds a new MCP server configuration to the client.
504
+ *
505
+ * This method adds or updates a server configuration dynamically without
506
+ * needing to restart the client. The server can then be used to create
507
+ * new sessions.
508
+ *
509
+ * @param name - Unique name for the server
510
+ * @param serverConfig - Server configuration object (connector type, command, args, etc.)
511
+ *
512
+ * @example
513
+ * ```typescript
514
+ * client.addServer('new-server', {
515
+ * command: 'python',
516
+ * args: ['server.py']
517
+ * });
518
+ *
519
+ * // Now you can create a session
520
+ * const session = await client.createSession('new-server');
521
+ * ```
522
+ *
523
+ * @see {@link removeServer} for removing servers
524
+ * @see {@link getServerConfig} for retrieving configurations
525
+ */
526
+ addServer(name, serverConfig) {
527
+ this.config.mcpServers = this.config.mcpServers || {};
528
+ this.config.mcpServers[name] = serverConfig;
529
+ Tel.getInstance().trackClientAddServer(name, serverConfig);
530
+ }
531
+ /**
532
+ * Removes an MCP server configuration from the client.
533
+ *
534
+ * This method removes a server configuration and cleans up any active
535
+ * sessions associated with that server. If there's an active session,
536
+ * it will be removed from the active sessions list.
537
+ *
538
+ * @param name - Name of the server to remove
539
+ *
540
+ * @example
541
+ * ```typescript
542
+ * // Remove a server configuration
543
+ * client.removeServer('old-server');
544
+ *
545
+ * // The server name will no longer appear in getServerNames()
546
+ * console.log(client.getServerNames()); // 'old-server' is gone
547
+ * ```
548
+ *
549
+ * @see {@link addServer} for adding servers
550
+ * @see {@link closeSession} for properly closing sessions before removal
551
+ */
552
+ removeServer(name) {
553
+ if (this.config.mcpServers?.[name]) {
554
+ delete this.config.mcpServers[name];
555
+ this.activeSessions = this.activeSessions.filter((n) => n !== name);
556
+ Tel.getInstance().trackClientRemoveServer(name);
557
+ }
558
+ }
559
+ /**
560
+ * Gets the names of all configured MCP servers.
561
+ *
562
+ * @returns Array of server names defined in the configuration
563
+ *
564
+ * @example
565
+ * ```typescript
566
+ * const serverNames = client.getServerNames();
567
+ * console.log(`Configured servers: ${serverNames.join(', ')}`);
568
+ *
569
+ * // Create sessions for all servers
570
+ * for (const name of serverNames) {
571
+ * await client.createSession(name);
572
+ * }
573
+ * ```
574
+ *
575
+ * @see {@link activeSessions} for servers with active sessions
576
+ */
577
+ getServerNames() {
578
+ return Object.keys(this.config.mcpServers ?? {});
579
+ }
580
+ /**
581
+ * Gets the configuration for a specific MCP server.
582
+ *
583
+ * @param name - Name of the server
584
+ * @returns Server configuration object, or undefined if not found
585
+ *
586
+ * @example
587
+ * ```typescript
588
+ * const config = client.getServerConfig('my-server');
589
+ * if (config) {
590
+ * console.log(`Command: ${config.command}`);
591
+ * console.log(`Args: ${config.args.join(' ')}`);
592
+ * }
593
+ * ```
594
+ *
595
+ * @see {@link getConfig} for retrieving the entire configuration
596
+ */
597
+ getServerConfig(name) {
598
+ return this.config.mcpServers?.[name];
599
+ }
600
+ /**
601
+ * Gets the complete client configuration.
602
+ *
603
+ * @returns Complete configuration object including all server definitions
604
+ *
605
+ * @example
606
+ * ```typescript
607
+ * const config = client.getConfig();
608
+ * console.log(`Total servers: ${Object.keys(config.mcpServers).length}`);
609
+ * ```
610
+ *
611
+ * @see {@link getServerConfig} for retrieving individual server configurations
612
+ */
613
+ getConfig() {
614
+ return this.config ?? {};
615
+ }
616
+ /**
617
+ * Creates a new session for connecting to an MCP server.
618
+ *
619
+ * This method initializes a connection to the specified server using the
620
+ * configuration provided during client construction. Sessions manage the
621
+ * lifecycle of connections and provide methods for calling tools, listing
622
+ * resources, and more.
623
+ *
624
+ * If a session already exists for the server, it will be replaced with a new one.
625
+ *
626
+ * @param serverName - The name of the server as defined in the client configuration
627
+ * @param autoInitialize - Whether to automatically initialize the session (default: true)
628
+ * @returns A promise that resolves to the created MCPSession instance
629
+ * @throws {Error} If the server is not found in the configuration
630
+ *
631
+ * @example
632
+ * ```typescript
633
+ * // Create and initialize a session
634
+ * const session = await client.createSession('my-server');
635
+ * const tools = await session.listTools();
636
+ *
637
+ * // Create without auto-initialization
638
+ * const session = await client.createSession('my-server', false);
639
+ * await session.connect();
640
+ * await session.initialize();
641
+ * ```
642
+ *
643
+ * @see {@link MCPSession} for session management methods
644
+ * @see {@link closeSession} for closing sessions
645
+ * @see {@link getSession} for retrieving existing sessions
646
+ */
647
+ async createSession(serverName, autoInitialize = true) {
648
+ const servers = this.config.mcpServers ?? {};
649
+ if (Object.keys(servers).length === 0) {
650
+ logger.warn("No MCP servers defined in config");
651
+ }
652
+ if (!servers[serverName]) {
653
+ throw new Error(`Server '${serverName}' not found in config`);
654
+ }
655
+ const connector = await Promise.resolve(
656
+ this.createConnectorFromConfig(servers[serverName])
657
+ );
658
+ const session = new MCPSession(connector);
659
+ if (autoInitialize) {
660
+ await session.initialize();
661
+ }
662
+ this.sessions[serverName] = session;
663
+ if (!this.activeSessions.includes(serverName)) {
664
+ this.activeSessions.push(serverName);
665
+ }
666
+ return session;
667
+ }
668
+ /**
669
+ * Creates sessions for all configured MCP servers.
670
+ *
671
+ * This is a convenience method that iterates through all servers in the
672
+ * configuration and creates a session for each one. Sessions are created
673
+ * sequentially to avoid overwhelming the system.
674
+ *
675
+ * @param autoInitialize - Whether to automatically initialize each session (default: true)
676
+ * @returns A promise that resolves to a map of server names to sessions
677
+ *
678
+ * @example
679
+ * ```typescript
680
+ * // Create sessions for all configured servers
681
+ * const sessions = await client.createAllSessions();
682
+ * console.log(`Created ${Object.keys(sessions).length} sessions`);
683
+ *
684
+ * // List tools from all servers
685
+ * for (const [name, session] of Object.entries(sessions)) {
686
+ * const tools = await session.listTools();
687
+ * console.log(`${name}: ${tools.length} tools`);
688
+ * }
689
+ * ```
690
+ *
691
+ * @see {@link createSession} for creating individual sessions
692
+ * @see {@link closeAllSessions} for closing all sessions
693
+ */
694
+ async createAllSessions(autoInitialize = true) {
695
+ const servers = this.config.mcpServers ?? {};
696
+ if (Object.keys(servers).length === 0) {
697
+ logger.warn("No MCP servers defined in config");
698
+ }
699
+ for (const name of Object.keys(servers)) {
700
+ await this.createSession(name, autoInitialize);
701
+ }
702
+ return this.sessions;
703
+ }
704
+ /**
705
+ * Retrieves an existing session by server name.
706
+ *
707
+ * This method returns null if no session exists, making it safe for
708
+ * checking session existence without throwing errors.
709
+ *
710
+ * @param serverName - Name of the server
711
+ * @returns The session instance or null if not found
712
+ *
713
+ * @example
714
+ * ```typescript
715
+ * const session = client.getSession('my-server');
716
+ * if (session) {
717
+ * const tools = await session.listTools();
718
+ * } else {
719
+ * console.log('Session not found, creating...');
720
+ * await client.createSession('my-server');
721
+ * }
722
+ * ```
723
+ *
724
+ * @see {@link requireSession} for getting a session that throws if not found
725
+ * @see {@link createSession} for creating sessions
726
+ */
727
+ getSession(serverName) {
728
+ const session = this.sessions[serverName];
729
+ if (!session) {
730
+ return null;
731
+ }
732
+ return session;
733
+ }
734
+ /**
735
+ * Retrieves an existing session by server name, throwing if not found.
736
+ *
737
+ * This method is useful when you need to ensure a session exists before
738
+ * proceeding. It throws a descriptive error if the session is not found.
739
+ *
740
+ * @param serverName - Name of the server
741
+ * @returns The session instance
742
+ * @throws {Error} If the session is not found
743
+ *
744
+ * @example
745
+ * ```typescript
746
+ * try {
747
+ * const session = client.requireSession('my-server');
748
+ * const tools = await session.listTools();
749
+ * } catch (error) {
750
+ * console.error('Session not found:', error.message);
751
+ * }
752
+ * ```
753
+ *
754
+ * @see {@link getSession} for a null-returning alternative
755
+ * @see {@link createSession} for creating sessions
756
+ */
757
+ requireSession(serverName) {
758
+ const session = this.sessions[serverName];
759
+ if (!session) {
760
+ throw new Error(
761
+ `Session '${serverName}' not found. Available sessions: ${this.activeSessions.join(", ") || "none"}`
762
+ );
763
+ }
764
+ return session;
765
+ }
766
+ /**
767
+ * Gets all active sessions as a map of server names to sessions.
768
+ *
769
+ * @returns Map of server names to their active sessions
770
+ *
771
+ * @example
772
+ * ```typescript
773
+ * const sessions = client.getAllActiveSessions();
774
+ *
775
+ * // Iterate over all active sessions
776
+ * for (const [name, session] of Object.entries(sessions)) {
777
+ * console.log(`Server: ${name}`);
778
+ * const tools = await session.listTools();
779
+ * console.log(` Tools: ${tools.length}`);
780
+ * }
781
+ * ```
782
+ *
783
+ * @see {@link activeSessions} for just the list of server names
784
+ * @see {@link getSession} for retrieving individual sessions
785
+ */
786
+ getAllActiveSessions() {
787
+ return Object.fromEntries(
788
+ this.activeSessions.map((n) => [n, this.sessions[n]])
789
+ );
790
+ }
791
+ /**
792
+ * Closes a session and cleans up its resources.
793
+ *
794
+ * This method gracefully disconnects from the server and removes the
795
+ * session from the active sessions list. It's safe to call even if
796
+ * the session doesn't exist.
797
+ *
798
+ * @param serverName - Name of the server whose session should be closed
799
+ *
800
+ * @example
801
+ * ```typescript
802
+ * // Close a specific session
803
+ * await client.closeSession('my-server');
804
+ *
805
+ * // Verify it's closed
806
+ * console.log(client.activeSessions.includes('my-server')); // false
807
+ * ```
808
+ *
809
+ * @see {@link closeAllSessions} for closing all sessions at once
810
+ * @see {@link createSession} for creating new sessions
811
+ */
812
+ async closeSession(serverName) {
813
+ const session = this.sessions[serverName];
814
+ if (!session) {
815
+ logger.warn(
816
+ `No session exists for server ${serverName}, nothing to close`
817
+ );
818
+ return;
819
+ }
820
+ try {
821
+ logger.debug(`Closing session for server ${serverName}`);
822
+ await session.disconnect();
823
+ } catch (e) {
824
+ logger.error(`Error closing session for server '${serverName}': ${e}`);
825
+ } finally {
826
+ if (this.sessions[serverName] === session) {
827
+ delete this.sessions[serverName];
828
+ this.activeSessions = this.activeSessions.filter(
829
+ (n) => n !== serverName
830
+ );
831
+ }
832
+ }
833
+ }
834
+ /**
835
+ * Closes all active sessions and cleans up their resources.
836
+ *
837
+ * This method iterates through all sessions and attempts to close each one
838
+ * gracefully. If any session fails to close, the error is logged but the
839
+ * method continues to close remaining sessions.
840
+ *
841
+ * This is particularly useful for cleanup on application shutdown.
842
+ *
843
+ * @example
844
+ * ```typescript
845
+ * // Clean shutdown
846
+ * try {
847
+ * await client.closeAllSessions();
848
+ * console.log('All sessions closed successfully');
849
+ * } catch (error) {
850
+ * console.error('Error during cleanup:', error);
851
+ * }
852
+ * ```
853
+ *
854
+ * @example
855
+ * ```typescript
856
+ * // Use in application shutdown handler
857
+ * process.on('SIGINT', async () => {
858
+ * console.log('Shutting down...');
859
+ * await client.closeAllSessions();
860
+ * process.exit(0);
861
+ * });
862
+ * ```
863
+ *
864
+ * @see {@link closeSession} for closing individual sessions
865
+ * @see {@link createAllSessions} for creating sessions
866
+ */
867
+ async closeAllSessions() {
868
+ const serverNames = Object.keys(this.sessions);
869
+ const errors = [];
870
+ for (const serverName of serverNames) {
871
+ try {
872
+ logger.debug(`Closing session for server ${serverName}`);
873
+ await this.closeSession(serverName);
874
+ } catch (e) {
875
+ const errorMsg = `Failed to close session for server '${serverName}': ${e}`;
876
+ logger.error(errorMsg);
877
+ errors.push(errorMsg);
878
+ }
879
+ }
880
+ if (errors.length) {
881
+ logger.error(
882
+ `Encountered ${errors.length} errors while closing sessions`
883
+ );
884
+ } else {
885
+ logger.debug("All sessions closed successfully");
886
+ }
887
+ }
888
+ };
889
+
890
+ // src/client/executors/base.ts
891
+ var BaseCodeExecutor = class {
892
+ static {
893
+ __name(this, "BaseCodeExecutor");
894
+ }
895
+ client;
896
+ _connecting = false;
897
+ constructor(client) {
898
+ this.client = client;
899
+ }
900
+ /**
901
+ * Ensure all configured MCP servers are connected before execution.
902
+ * Prevents race conditions with a connection lock.
903
+ */
904
+ async ensureServersConnected() {
905
+ const configuredServers = this.client.getServerNames();
906
+ const activeSessions = Object.keys(this.client.getAllActiveSessions());
907
+ const missingServers = configuredServers.filter(
908
+ (s) => !activeSessions.includes(s)
909
+ );
910
+ if (missingServers.length > 0 && !this._connecting) {
911
+ this._connecting = true;
912
+ try {
913
+ logger.debug(
914
+ `Connecting to configured servers for code execution: ${missingServers.join(", ")}`
915
+ );
916
+ await this.client.createAllSessions();
917
+ } finally {
918
+ this._connecting = false;
919
+ }
920
+ } else if (missingServers.length > 0 && this._connecting) {
921
+ logger.debug("Waiting for ongoing server connection...");
922
+ const startWait = Date.now();
923
+ while (this._connecting && Date.now() - startWait < 5e3) {
924
+ await new Promise((resolve) => setTimeout(resolve, 100));
925
+ }
926
+ }
927
+ }
928
+ /**
929
+ * Get tool namespace information from all active MCP sessions.
930
+ * Filters out the internal code_mode server.
931
+ */
932
+ getToolNamespaces() {
933
+ const namespaces = [];
934
+ const activeSessions = this.client.getAllActiveSessions();
935
+ for (const [serverName, session] of Object.entries(activeSessions)) {
936
+ if (serverName === "code_mode") continue;
937
+ try {
938
+ const connector = session.connector;
939
+ let tools;
940
+ try {
941
+ tools = connector.tools;
942
+ } catch (e) {
943
+ logger.warn(`Tools not available for server ${serverName}: ${e}`);
944
+ continue;
945
+ }
946
+ if (!tools || tools.length === 0) continue;
947
+ namespaces.push({ serverName, tools, session });
948
+ } catch (e) {
949
+ logger.warn(`Failed to load tools for server ${serverName}: ${e}`);
950
+ }
951
+ }
952
+ return namespaces;
953
+ }
954
+ /**
955
+ * Create a search function for discovering available MCP tools.
956
+ * Used by code execution environments to find tools at runtime.
957
+ */
958
+ createSearchToolsFunction() {
959
+ return async (query = "", detailLevel = "full") => {
960
+ const allTools = [];
961
+ const allNamespaces = /* @__PURE__ */ new Set();
962
+ const queryLower = query.toLowerCase();
963
+ const activeSessions = this.client.getAllActiveSessions();
964
+ for (const [serverName, session] of Object.entries(activeSessions)) {
965
+ if (serverName === "code_mode") continue;
966
+ try {
967
+ const tools = session.connector.tools;
968
+ if (tools && tools.length > 0) {
969
+ allNamespaces.add(serverName);
970
+ }
971
+ for (const tool of tools) {
972
+ if (detailLevel === "names") {
973
+ allTools.push({ name: tool.name, server: serverName });
974
+ } else if (detailLevel === "descriptions") {
975
+ allTools.push({
976
+ name: tool.name,
977
+ server: serverName,
978
+ description: tool.description
979
+ });
980
+ } else {
981
+ allTools.push({
982
+ name: tool.name,
983
+ server: serverName,
984
+ description: tool.description,
985
+ input_schema: tool.inputSchema
986
+ });
987
+ }
988
+ }
989
+ } catch (e) {
990
+ logger.warn(`Failed to search tools in server ${serverName}: ${e}`);
991
+ }
992
+ }
993
+ let filteredTools = allTools;
994
+ if (query) {
995
+ filteredTools = allTools.filter((tool) => {
996
+ const nameMatch = tool.name.toLowerCase().includes(queryLower);
997
+ const descMatch = tool.description?.toLowerCase().includes(queryLower);
998
+ const serverMatch = tool.server.toLowerCase().includes(queryLower);
999
+ return nameMatch || descMatch || serverMatch;
1000
+ });
1001
+ }
1002
+ return {
1003
+ meta: {
1004
+ total_tools: allTools.length,
1005
+ namespaces: Array.from(allNamespaces).sort(),
1006
+ result_count: filteredTools.length
1007
+ },
1008
+ results: filteredTools
1009
+ };
1010
+ };
1011
+ }
1012
+ };
1013
+
1014
+ // src/client/executors/e2b.ts
1015
+ var E2BCodeExecutor = class extends BaseCodeExecutor {
1016
+ static {
1017
+ __name(this, "E2BCodeExecutor");
1018
+ }
1019
+ e2bApiKey;
1020
+ codeExecSandbox = null;
1021
+ SandboxClass = null;
1022
+ timeoutMs;
1023
+ constructor(client, options) {
1024
+ super(client);
1025
+ this.e2bApiKey = options.apiKey;
1026
+ this.timeoutMs = options.timeoutMs ?? 3e5;
1027
+ }
1028
+ /**
1029
+ * Lazy load E2B Sandbox class.
1030
+ * This allows the library to work without E2B installed.
1031
+ */
1032
+ async ensureSandboxClass() {
1033
+ if (this.SandboxClass) return;
1034
+ try {
1035
+ const e2b = await import("@e2b/code-interpreter");
1036
+ this.SandboxClass = e2b.Sandbox;
1037
+ } catch (error) {
1038
+ throw new Error(
1039
+ "@e2b/code-interpreter is not installed. The E2B code executor requires this optional dependency. Install it with: yarn add @e2b/code-interpreter"
1040
+ );
1041
+ }
1042
+ }
1043
+ /**
1044
+ * Get or create a dedicated sandbox for code execution.
1045
+ */
1046
+ async getOrCreateCodeExecSandbox() {
1047
+ if (this.codeExecSandbox) return this.codeExecSandbox;
1048
+ await this.ensureSandboxClass();
1049
+ logger.debug("Starting E2B sandbox for code execution...");
1050
+ this.codeExecSandbox = await this.SandboxClass.create("base", {
1051
+ apiKey: this.e2bApiKey,
1052
+ timeoutMs: this.timeoutMs
1053
+ });
1054
+ return this.codeExecSandbox;
1055
+ }
1056
+ /**
1057
+ * Generate the shim code that exposes tools to the sandbox environment.
1058
+ * Creates a bridge that intercepts tool calls and sends them back to host.
1059
+ */
1060
+ generateShim(tools) {
1061
+ let shim = `
1062
+ // MCP Bridge Shim
1063
+ global.__callMcpTool = async (server, tool, args) => {
1064
+ const id = Math.random().toString(36).substring(7);
1065
+ console.log(JSON.stringify({
1066
+ type: '__MCP_TOOL_CALL__',
1067
+ id,
1068
+ server,
1069
+ tool,
1070
+ args
1071
+ }));
1072
+
1073
+ const resultPath = \`/tmp/mcp_result_\${id}.json\`;
1074
+ const fs = require('fs');
1075
+
1076
+ // Poll for result file
1077
+ let attempts = 0;
1078
+ while (attempts < 300) { // 30 seconds timeout
1079
+ if (fs.existsSync(resultPath)) {
1080
+ const content = fs.readFileSync(resultPath, 'utf8');
1081
+ const result = JSON.parse(content);
1082
+ fs.unlinkSync(resultPath); // Clean up
1083
+
1084
+ if (result.error) {
1085
+ throw new Error(result.error);
1086
+ }
1087
+ return result.data;
1088
+ }
1089
+ await new Promise(resolve => setTimeout(resolve, 100));
1090
+ attempts++;
1091
+ }
1092
+ throw new Error('Tool execution timed out');
1093
+ };
1094
+
1095
+ // Global search_tools helper
1096
+ global.search_tools = async (query, detailLevel = 'full') => {
1097
+ const allTools = ${JSON.stringify(
1098
+ Object.entries(tools).flatMap(
1099
+ ([server, serverTools]) => serverTools.map((tool) => ({
1100
+ name: tool.name,
1101
+ description: tool.description,
1102
+ server,
1103
+ input_schema: tool.inputSchema
1104
+ }))
1105
+ )
1106
+ )};
1107
+
1108
+ const filtered = allTools.filter(tool => {
1109
+ if (!query) return true;
1110
+ const q = query.toLowerCase();
1111
+ return tool.name.toLowerCase().includes(q) ||
1112
+ (tool.description && tool.description.toLowerCase().includes(q));
1113
+ });
1114
+
1115
+ if (detailLevel === 'names') {
1116
+ return filtered.map(t => ({ name: t.name, server: t.server }));
1117
+ } else if (detailLevel === 'descriptions') {
1118
+ return filtered.map(t => ({ name: t.name, server: t.server, description: t.description }));
1119
+ }
1120
+ return filtered;
1121
+ };
1122
+ `;
1123
+ for (const [serverName, serverTools] of Object.entries(tools)) {
1124
+ if (!serverTools || serverTools.length === 0) continue;
1125
+ const safeServerName = serverName.replace(/[^a-zA-Z0-9_]/g, "_");
1126
+ shim += `
1127
+ global['${serverName}'] = {`;
1128
+ for (const tool of serverTools) {
1129
+ shim += `
1130
+ '${tool.name}': async (args) => await global.__callMcpTool('${serverName}', '${tool.name}', args),`;
1131
+ }
1132
+ shim += `
1133
+ };
1134
+
1135
+ // Also expose as safe name if different
1136
+ if ('${safeServerName}' !== '${serverName}') {
1137
+ global['${safeServerName}'] = global['${serverName}'];
1138
+ }
1139
+ `;
1140
+ }
1141
+ return shim;
1142
+ }
1143
+ /**
1144
+ * Build the tool catalog for the shim.
1145
+ * Returns a map of server names to their available tools.
1146
+ */
1147
+ buildToolCatalog() {
1148
+ const catalog = {};
1149
+ const namespaces = this.getToolNamespaces();
1150
+ for (const { serverName, tools } of namespaces) {
1151
+ catalog[serverName] = tools;
1152
+ }
1153
+ return catalog;
1154
+ }
1155
+ /**
1156
+ * Execute JavaScript/TypeScript code in an E2B sandbox with MCP tool access.
1157
+ * Tool calls are proxied back to the host via the bridge pattern.
1158
+ *
1159
+ * @param code - Code to execute
1160
+ * @param timeout - Execution timeout in milliseconds (default: 30000)
1161
+ */
1162
+ async execute(code, timeout = 3e4) {
1163
+ const startTime = Date.now();
1164
+ let result = null;
1165
+ let error = null;
1166
+ let logs = [];
1167
+ try {
1168
+ await this.ensureServersConnected();
1169
+ const sandbox = await this.getOrCreateCodeExecSandbox();
1170
+ const toolCatalog = this.buildToolCatalog();
1171
+ const shim = this.generateShim(toolCatalog);
1172
+ const wrappedCode = `
1173
+ ${shim}
1174
+
1175
+ (async () => {
1176
+ try {
1177
+ const func = async () => {
1178
+ ${code}
1179
+ };
1180
+ const result = await func();
1181
+ console.log('__MCP_RESULT_START__');
1182
+ console.log(JSON.stringify(result));
1183
+ console.log('__MCP_RESULT_END__');
1184
+ } catch (e) {
1185
+ console.error(e);
1186
+ process.exit(1);
1187
+ }
1188
+ })();
1189
+ `;
1190
+ const filename = `exec_${Date.now()}.js`;
1191
+ await sandbox.files.write(filename, wrappedCode);
1192
+ const execution = await sandbox.commands.run(`node ${filename}`, {
1193
+ timeoutMs: timeout,
1194
+ onStdout: /* @__PURE__ */ __name(async (data) => {
1195
+ try {
1196
+ const lines = data.split("\n");
1197
+ for (const line of lines) {
1198
+ if (line.trim().startsWith('{"type":"__MCP_TOOL_CALL__"')) {
1199
+ const call = JSON.parse(line);
1200
+ if (call.type === "__MCP_TOOL_CALL__") {
1201
+ try {
1202
+ logger.debug(
1203
+ `[E2B Bridge] Calling tool ${call.server}.${call.tool}`
1204
+ );
1205
+ const activeSessions = this.client.getAllActiveSessions();
1206
+ const session = activeSessions[call.server];
1207
+ if (!session) {
1208
+ throw new Error(`Server ${call.server} not found`);
1209
+ }
1210
+ const toolResult = await session.connector.callTool(
1211
+ call.tool,
1212
+ call.args
1213
+ );
1214
+ let extractedResult = toolResult;
1215
+ if (toolResult.content && toolResult.content.length > 0) {
1216
+ const item = toolResult.content[0];
1217
+ if (item.type === "text") {
1218
+ try {
1219
+ extractedResult = JSON.parse(item.text);
1220
+ } catch {
1221
+ extractedResult = item.text;
1222
+ }
1223
+ } else {
1224
+ extractedResult = item;
1225
+ }
1226
+ }
1227
+ const resultPath = `/tmp/mcp_result_${call.id}.json`;
1228
+ await sandbox.files.write(
1229
+ resultPath,
1230
+ JSON.stringify({ data: extractedResult })
1231
+ );
1232
+ } catch (err) {
1233
+ logger.error(
1234
+ `[E2B Bridge] Tool execution failed: ${err.message}`
1235
+ );
1236
+ const resultPath = `/tmp/mcp_result_${call.id}.json`;
1237
+ await sandbox.files.write(
1238
+ resultPath,
1239
+ JSON.stringify({
1240
+ error: err.message || String(err)
1241
+ })
1242
+ );
1243
+ }
1244
+ }
1245
+ }
1246
+ }
1247
+ } catch (e) {
1248
+ }
1249
+ }, "onStdout")
1250
+ });
1251
+ logs = [execution.stdout, execution.stderr].filter(Boolean);
1252
+ if (execution.exitCode !== 0) {
1253
+ error = execution.stderr || "Execution failed";
1254
+ } else {
1255
+ const stdout = execution.stdout;
1256
+ const startMarker = "__MCP_RESULT_START__";
1257
+ const endMarker = "__MCP_RESULT_END__";
1258
+ const startIndex = stdout.indexOf(startMarker);
1259
+ const endIndex = stdout.indexOf(endMarker);
1260
+ if (startIndex !== -1 && endIndex !== -1) {
1261
+ const jsonStr = stdout.substring(startIndex + startMarker.length, endIndex).trim();
1262
+ try {
1263
+ result = JSON.parse(jsonStr);
1264
+ } catch (e) {
1265
+ result = jsonStr;
1266
+ }
1267
+ logs = logs.map((log) => {
1268
+ let cleaned = log.replace(
1269
+ new RegExp(startMarker + "[\\s\\S]*?" + endMarker),
1270
+ "[Result captured]"
1271
+ );
1272
+ cleaned = cleaned.split("\n").filter((l) => !l.includes("__MCP_TOOL_CALL__")).join("\n");
1273
+ return cleaned;
1274
+ });
1275
+ }
1276
+ }
1277
+ } catch (e) {
1278
+ error = e.message || String(e);
1279
+ if (error && (error.includes("timeout") || error.includes("timed out"))) {
1280
+ error = "Script execution timed out";
1281
+ }
1282
+ }
1283
+ return {
1284
+ result,
1285
+ logs,
1286
+ error,
1287
+ execution_time: (Date.now() - startTime) / 1e3
1288
+ };
1289
+ }
1290
+ /**
1291
+ * Clean up the E2B sandbox.
1292
+ * Should be called when the executor is no longer needed.
1293
+ */
1294
+ async cleanup() {
1295
+ if (this.codeExecSandbox) {
1296
+ try {
1297
+ await this.codeExecSandbox.kill();
1298
+ this.codeExecSandbox = null;
1299
+ logger.debug("E2B code execution sandbox stopped");
1300
+ } catch (error) {
1301
+ logger.error("Failed to stop E2B code execution sandbox:", error);
1302
+ }
1303
+ }
1304
+ }
1305
+ };
1306
+
1307
+ // src/client/executors/vm.ts
1308
+ var vm = null;
1309
+ var vmCheckAttempted = false;
1310
+ function getVMModuleName() {
1311
+ return ["node", "vm"].join(":");
1312
+ }
1313
+ __name(getVMModuleName, "getVMModuleName");
1314
+ function tryLoadVM() {
1315
+ if (vmCheckAttempted) {
1316
+ return vm !== null;
1317
+ }
1318
+ vmCheckAttempted = true;
1319
+ try {
1320
+ const nodeRequire = typeof __require !== "undefined" ? __require : null;
1321
+ if (nodeRequire) {
1322
+ vm = nodeRequire(getVMModuleName());
1323
+ return true;
1324
+ }
1325
+ } catch (error) {
1326
+ logger.debug("node:vm module not available via require");
1327
+ }
1328
+ return false;
1329
+ }
1330
+ __name(tryLoadVM, "tryLoadVM");
1331
+ async function tryLoadVMAsync() {
1332
+ if (vm !== null) {
1333
+ return true;
1334
+ }
1335
+ if (!vmCheckAttempted) {
1336
+ if (tryLoadVM()) {
1337
+ return true;
1338
+ }
1339
+ }
1340
+ try {
1341
+ vm = await import(
1342
+ /* @vite-ignore */
1343
+ getVMModuleName()
1344
+ );
1345
+ return true;
1346
+ } catch (error) {
1347
+ logger.debug(
1348
+ "node:vm module not available in this environment (e.g., Deno)"
1349
+ );
1350
+ return false;
1351
+ }
1352
+ }
1353
+ __name(tryLoadVMAsync, "tryLoadVMAsync");
1354
+ function isVMAvailable() {
1355
+ tryLoadVM();
1356
+ return vm !== null;
1357
+ }
1358
+ __name(isVMAvailable, "isVMAvailable");
1359
+ var VMCodeExecutor = class extends BaseCodeExecutor {
1360
+ static {
1361
+ __name(this, "VMCodeExecutor");
1362
+ }
1363
+ defaultTimeout;
1364
+ memoryLimitMb;
1365
+ constructor(client, options) {
1366
+ super(client);
1367
+ this.defaultTimeout = options?.timeoutMs ?? 3e4;
1368
+ this.memoryLimitMb = options?.memoryLimitMb;
1369
+ tryLoadVM();
1370
+ }
1371
+ /**
1372
+ * Ensure VM module is loaded before execution
1373
+ */
1374
+ async ensureVMLoaded() {
1375
+ if (vm !== null) {
1376
+ return;
1377
+ }
1378
+ const loaded = await tryLoadVMAsync();
1379
+ if (!loaded) {
1380
+ throw new Error(
1381
+ "node:vm module is not available in this environment. Please use E2B executor instead or run in a Node.js environment."
1382
+ );
1383
+ }
1384
+ }
1385
+ /**
1386
+ * Execute JavaScript/TypeScript code with access to MCP tools.
1387
+ *
1388
+ * @param code - Code to execute
1389
+ * @param timeout - Execution timeout in milliseconds (default: configured timeout or 30000)
1390
+ */
1391
+ async execute(code, timeout) {
1392
+ const effectiveTimeout = timeout ?? this.defaultTimeout;
1393
+ await this.ensureVMLoaded();
1394
+ await this.ensureServersConnected();
1395
+ const logs = [];
1396
+ const startTime = Date.now();
1397
+ let result = null;
1398
+ let error = null;
1399
+ try {
1400
+ const context = await this._buildContext(logs);
1401
+ const wrappedCode = `
1402
+ (async () => {
1403
+ try {
1404
+ ${code}
1405
+ } catch (e) {
1406
+ throw e;
1407
+ }
1408
+ })()
1409
+ `;
1410
+ const script = new vm.Script(wrappedCode, {
1411
+ filename: "agent_code.js"
1412
+ });
1413
+ const promise = script.runInNewContext(context, {
1414
+ timeout: effectiveTimeout,
1415
+ displayErrors: true
1416
+ });
1417
+ result = await promise;
1418
+ } catch (e) {
1419
+ error = e.message || String(e);
1420
+ if (e.code === "ERR_SCRIPT_EXECUTION_TIMEOUT" || e.message === "Script execution timed out." || typeof error === "string" && (error.includes("timed out") || error.includes("timeout"))) {
1421
+ error = "Script execution timed out";
1422
+ }
1423
+ if (e.stack) {
1424
+ logger.debug(`Code execution error stack: ${e.stack}`);
1425
+ }
1426
+ }
1427
+ const executionTime = (Date.now() - startTime) / 1e3;
1428
+ return {
1429
+ result,
1430
+ logs,
1431
+ error,
1432
+ execution_time: executionTime
1433
+ };
1434
+ }
1435
+ /**
1436
+ * Build the VM execution context with MCP tools and standard globals.
1437
+ *
1438
+ * @param logs - Array to capture console output
1439
+ */
1440
+ async _buildContext(logs) {
1441
+ const logHandler = /* @__PURE__ */ __name((...args) => {
1442
+ logs.push(
1443
+ args.map(
1444
+ (arg) => typeof arg === "object" ? JSON.stringify(arg, null, 2) : String(arg)
1445
+ ).join(" ")
1446
+ );
1447
+ }, "logHandler");
1448
+ const sandbox = {
1449
+ console: {
1450
+ log: logHandler,
1451
+ error: /* @__PURE__ */ __name((...args) => {
1452
+ logHandler("[ERROR]", ...args);
1453
+ }, "error"),
1454
+ warn: /* @__PURE__ */ __name((...args) => {
1455
+ logHandler("[WARN]", ...args);
1456
+ }, "warn"),
1457
+ info: logHandler,
1458
+ debug: logHandler
1459
+ },
1460
+ // Standard globals
1461
+ Object,
1462
+ Array,
1463
+ String,
1464
+ Number,
1465
+ Boolean,
1466
+ Date,
1467
+ Math,
1468
+ JSON,
1469
+ RegExp,
1470
+ Map,
1471
+ Set,
1472
+ Promise,
1473
+ parseInt,
1474
+ parseFloat,
1475
+ isNaN,
1476
+ isFinite,
1477
+ encodeURI,
1478
+ decodeURI,
1479
+ encodeURIComponent,
1480
+ decodeURIComponent,
1481
+ setTimeout,
1482
+ clearTimeout,
1483
+ // Helper for tools
1484
+ search_tools: this.createSearchToolsFunction(),
1485
+ __tool_namespaces: []
1486
+ };
1487
+ const toolNamespaces = {};
1488
+ const namespaceInfos = this.getToolNamespaces();
1489
+ for (const { serverName, tools, session } of namespaceInfos) {
1490
+ const serverNamespace = {};
1491
+ for (const tool of tools) {
1492
+ const toolName = tool.name;
1493
+ serverNamespace[toolName] = async (args) => {
1494
+ const result = await session.connector.callTool(toolName, args || {});
1495
+ if (result.content && result.content.length > 0) {
1496
+ const item = result.content[0];
1497
+ if (item.type === "text") {
1498
+ try {
1499
+ return JSON.parse(item.text);
1500
+ } catch {
1501
+ return item.text;
1502
+ }
1503
+ }
1504
+ return item;
1505
+ }
1506
+ return result;
1507
+ };
1508
+ }
1509
+ sandbox[serverName] = serverNamespace;
1510
+ toolNamespaces[serverName] = true;
1511
+ }
1512
+ sandbox.__tool_namespaces = Object.keys(toolNamespaces);
1513
+ return vm.createContext(sandbox);
1514
+ }
1515
+ /**
1516
+ * Clean up resources.
1517
+ * VM executor doesn't need cleanup, but method kept for interface consistency.
1518
+ */
1519
+ async cleanup() {
1520
+ }
1521
+ };
1522
+
1523
+ // src/connectors/http.ts
1524
+ import {
1525
+ Client
1526
+ } from "@modelcontextprotocol/sdk/client/index.js";
1527
+ import {
1528
+ StreamableHTTPClientTransport,
1529
+ StreamableHTTPError
1530
+ } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
1531
+
1532
+ // src/task_managers/sse.ts
1533
+ import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
1534
+ var SseConnectionManager = class extends ConnectionManager {
1535
+ static {
1536
+ __name(this, "SseConnectionManager");
1537
+ }
1538
+ url;
1539
+ opts;
1540
+ _transport = null;
1541
+ reinitializing = false;
1542
+ /**
1543
+ * Create an SSE connection manager.
1544
+ *
1545
+ * @param url The SSE endpoint URL.
1546
+ * @param opts Optional transport options (auth, headers, etc.).
1547
+ */
1548
+ constructor(url, opts) {
1549
+ super();
1550
+ this.url = typeof url === "string" ? new URL(url) : url;
1551
+ this.opts = opts;
1552
+ }
1553
+ /**
1554
+ * Spawn a new `SSEClientTransport` and wrap it with 404 handling.
1555
+ * Per MCP spec, clients MUST re-initialize when receiving 404 for stale sessions.
1556
+ */
1557
+ async establishConnection() {
1558
+ const transport = new SSEClientTransport(this.url, this.opts);
1559
+ const originalSend = transport.send.bind(transport);
1560
+ transport.send = async (message) => {
1561
+ const sendMessage = /* @__PURE__ */ __name(async (msg) => {
1562
+ if (Array.isArray(msg)) {
1563
+ for (const singleMsg of msg) {
1564
+ await originalSend(singleMsg);
1565
+ }
1566
+ } else {
1567
+ await originalSend(msg);
1568
+ }
1569
+ }, "sendMessage");
1570
+ try {
1571
+ await sendMessage(message);
1572
+ } catch (error) {
1573
+ if (error?.code === 404 && transport.sessionId && !this.reinitializing) {
1574
+ logger.warn(
1575
+ `[SSE] Session not found (404), re-initializing per MCP spec...`
1576
+ );
1577
+ this.reinitializing = true;
1578
+ try {
1579
+ transport.sessionId = void 0;
1580
+ await this.reinitialize(transport);
1581
+ logger.debug(
1582
+ `[SSE] Re-initialization successful, retrying request`
1583
+ );
1584
+ await sendMessage(message);
1585
+ } finally {
1586
+ this.reinitializing = false;
1587
+ }
1588
+ } else {
1589
+ throw error;
1590
+ }
1591
+ }
1592
+ };
1593
+ this._transport = transport;
1594
+ logger.debug(`${this.constructor.name} connected successfully`);
1595
+ return transport;
1596
+ }
1597
+ /**
1598
+ * Re-initialize the transport with a new session
1599
+ * This is called when the server returns 404 for a stale session
1600
+ */
1601
+ async reinitialize(transport) {
1602
+ logger.debug(`[SSE] Re-initialization triggered`);
1603
+ }
1604
+ /**
1605
+ * Close the underlying transport and clean up resources.
1606
+ */
1607
+ async closeConnection(_connection) {
1608
+ if (this._transport) {
1609
+ try {
1610
+ await this._transport.close();
1611
+ } catch (e) {
1612
+ logger.warn(`Error closing SSE transport: ${e}`);
1613
+ } finally {
1614
+ this._transport = null;
1615
+ }
1616
+ }
1617
+ }
1618
+ };
1619
+
1620
+ // src/connectors/http.ts
1621
+ var HttpConnector = class extends BaseConnector {
1622
+ static {
1623
+ __name(this, "HttpConnector");
1624
+ }
1625
+ baseUrl;
1626
+ headers;
1627
+ timeout;
1628
+ sseReadTimeout;
1629
+ customFetch;
1630
+ clientInfo;
1631
+ preferSse;
1632
+ disableSseFallback;
1633
+ gatewayUrl;
1634
+ serverId;
1635
+ reconnectionOptions;
1636
+ transportType = null;
1637
+ streamableTransport = null;
1638
+ constructor(baseUrl, opts = {}) {
1639
+ super(opts);
1640
+ const originalUrl = baseUrl.replace(/\/$/, "");
1641
+ this.gatewayUrl = opts.gatewayUrl;
1642
+ this.serverId = opts.serverId;
1643
+ if (this.gatewayUrl) {
1644
+ this.baseUrl = this.gatewayUrl.replace(/\/$/, "");
1645
+ this.headers = { ...opts.headers ?? {} };
1646
+ this.headers["X-Target-URL"] = originalUrl;
1647
+ if (this.serverId) {
1648
+ this.headers["X-Server-Id"] = this.serverId;
1649
+ }
1650
+ } else {
1651
+ this.baseUrl = originalUrl;
1652
+ this.headers = { ...opts.headers ?? {} };
1653
+ }
1654
+ if (opts.authToken) {
1655
+ this.headers.Authorization = `Bearer ${opts.authToken}`;
1656
+ }
1657
+ this.timeout = opts.timeout ?? 1e4;
1658
+ this.sseReadTimeout = opts.sseReadTimeout ?? 3e5;
1659
+ this.customFetch = opts.fetch;
1660
+ this.clientInfo = opts.clientInfo ?? {
1661
+ name: "http-connector",
1662
+ version: "1.0.0"
1663
+ };
1664
+ this.preferSse = opts.preferSse ?? false;
1665
+ this.disableSseFallback = opts.disableSseFallback ?? false;
1666
+ this.reconnectionOptions = opts.reconnectionOptions;
1667
+ }
1668
+ buildClientOptions() {
1669
+ return {
1670
+ ...this.opts.clientOptions || {},
1671
+ capabilities: {
1672
+ ...this.opts.clientOptions?.capabilities || {},
1673
+ roots: { listChanged: true },
1674
+ ...this.opts.onSampling ? { sampling: {} } : {},
1675
+ ...this.opts.onElicitation ?? this.opts.elicitationCallback ? { elicitation: { form: {}, url: {} } } : {}
1676
+ }
1677
+ };
1678
+ }
1679
+ unwrapStreamableError(err) {
1680
+ if (err instanceof StreamableHTTPError) {
1681
+ return err;
1682
+ }
1683
+ if (err instanceof Error && err.cause instanceof StreamableHTTPError) {
1684
+ return err.cause;
1685
+ }
1686
+ return null;
1687
+ }
1688
+ classifyStreamableHttpFailure(err) {
1689
+ let fallbackReason = "Unknown error";
1690
+ let is401Error = false;
1691
+ let httpStatusCode;
1692
+ const streamableErr = this.unwrapStreamableError(err);
1693
+ if (streamableErr) {
1694
+ is401Error = streamableErr.code === 401;
1695
+ httpStatusCode = streamableErr.code;
1696
+ if (streamableErr.code === 400 && streamableErr.message.includes("Missing session ID")) {
1697
+ fallbackReason = "Server requires session ID (FastMCP compatibility) - using SSE transport";
1698
+ logger.warn(`\u26A0\uFE0F ${fallbackReason}`);
1699
+ } else if (streamableErr.code === 404 || streamableErr.code === 405) {
1700
+ fallbackReason = `Server returned ${streamableErr.code} - server likely doesn't support streamable HTTP`;
1701
+ logger.debug(fallbackReason);
1702
+ } else {
1703
+ fallbackReason = `Server returned ${streamableErr.code}: ${streamableErr.message}`;
1704
+ logger.debug(fallbackReason);
1705
+ }
1706
+ return { fallbackReason, is401Error, httpStatusCode };
1707
+ }
1708
+ if (err instanceof Error) {
1709
+ const errorStr = err.toString();
1710
+ const errorMsg = err.message || "";
1711
+ is401Error = errorStr.includes("401") || errorMsg.includes("Unauthorized");
1712
+ if (errorStr.includes("Missing session ID") || errorStr.includes("Bad Request: Missing session ID") || errorMsg.includes("FastMCP session ID error")) {
1713
+ fallbackReason = "Server requires session ID (FastMCP compatibility) - using SSE transport";
1714
+ logger.warn(`\u26A0\uFE0F ${fallbackReason}`);
1715
+ } else if (errorStr.includes("405 Method Not Allowed") || errorStr.includes("404 Not Found")) {
1716
+ fallbackReason = "Server doesn't support streamable HTTP (405/404)";
1717
+ logger.debug(fallbackReason);
1718
+ } else {
1719
+ fallbackReason = `Streamable HTTP failed: ${err.message}`;
1720
+ logger.debug(fallbackReason);
1721
+ }
1722
+ }
1723
+ return { fallbackReason, is401Error, httpStatusCode };
1724
+ }
1725
+ /** Establish connection to the MCP implementation via HTTP (streamable or SSE). */
1726
+ async connect() {
1727
+ if (this.connected) {
1728
+ logger.debug("Already connected to MCP implementation");
1729
+ return;
1730
+ }
1731
+ const baseUrl = this.baseUrl;
1732
+ if (this.preferSse) {
1733
+ logger.debug(`Connecting to MCP implementation via HTTP/SSE: ${baseUrl}`);
1734
+ await this.connectWithSse(baseUrl);
1735
+ return;
1736
+ }
1737
+ logger.debug(`Connecting to MCP implementation via HTTP: ${baseUrl}`);
1738
+ try {
1739
+ logger.debug("\u{1F504} Attempting streamable HTTP transport...");
1740
+ await this.connectWithStreamableHttp(baseUrl);
1741
+ logger.debug("\u2705 Successfully connected via streamable HTTP");
1742
+ } catch (err) {
1743
+ logger.debug("Streamable HTTP connect failed", err);
1744
+ const { fallbackReason, is401Error, httpStatusCode } = this.classifyStreamableHttpFailure(err);
1745
+ if (is401Error) {
1746
+ logger.info("Authentication required - skipping SSE fallback");
1747
+ await this.cleanupResources();
1748
+ const authError = new Error("Authentication required");
1749
+ authError.code = 401;
1750
+ throw authError;
1751
+ }
1752
+ if (this.disableSseFallback) {
1753
+ logger.info("SSE fallback disabled - failing connection");
1754
+ await this.cleanupResources();
1755
+ throw new Error(`Streamable HTTP connection failed: ${fallbackReason}`);
1756
+ }
1757
+ logger.debug("\u{1F504} Falling back to SSE transport...");
1758
+ try {
1759
+ await this.connectWithSse(baseUrl);
1760
+ } catch (sseErr) {
1761
+ logger.error("Failed to connect with both transports:");
1762
+ logger.error(` Streamable HTTP: ${fallbackReason}`);
1763
+ logger.error(` SSE: ${sseErr}`);
1764
+ await this.cleanupResources();
1765
+ const sseIs401 = sseErr?.message?.includes("401") || sseErr?.message?.includes("Unauthorized");
1766
+ if (sseIs401) {
1767
+ const authError = new Error("Authentication required");
1768
+ authError.code = 401;
1769
+ throw authError;
1770
+ }
1771
+ const finalError = new Error(
1772
+ `Could not connect to server with any available transport. Streamable HTTP: ${fallbackReason}`
1773
+ );
1774
+ if (httpStatusCode !== void 0) {
1775
+ Object.defineProperty(finalError, "code", {
1776
+ value: httpStatusCode,
1777
+ writable: false,
1778
+ enumerable: true,
1779
+ configurable: true
1780
+ });
1781
+ logger.debug(
1782
+ `Preserving HTTP status code ${httpStatusCode} in error for proxy fallback detection`
1783
+ );
1784
+ }
1785
+ throw finalError;
1786
+ }
1787
+ }
1788
+ }
1789
+ async connectWithStreamableHttp(baseUrl) {
1790
+ try {
1791
+ logger.debug("[HttpConnector] Connecting with Streamable HTTP", {
1792
+ baseUrl,
1793
+ originalUrl: this.baseUrl,
1794
+ gatewayUrl: this.gatewayUrl || "none",
1795
+ authProviderUrl: this.opts.authProvider?.serverUrl || "none",
1796
+ headers: this.headers
1797
+ });
1798
+ const streamableTransport = new StreamableHTTPClientTransport(
1799
+ new URL(baseUrl),
1800
+ {
1801
+ authProvider: this.opts.authProvider,
1802
+ // ← Pass OAuth provider to SDK
1803
+ fetch: this.customFetch,
1804
+ requestInit: {
1805
+ headers: this.headers
1806
+ },
1807
+ reconnectionOptions: {
1808
+ maxReconnectionDelay: 3e4,
1809
+ initialReconnectionDelay: 1e3,
1810
+ reconnectionDelayGrowFactor: 1.5,
1811
+ maxRetries: 2,
1812
+ ...this.reconnectionOptions
1813
+ }
1814
+ // Don't pass sessionId - let the SDK generate it automatically during connect()
1815
+ }
1816
+ );
1817
+ let transport = streamableTransport;
1818
+ if (this.opts.wrapTransport) {
1819
+ const serverId = this.baseUrl;
1820
+ transport = this.opts.wrapTransport(
1821
+ transport,
1822
+ serverId
1823
+ );
1824
+ }
1825
+ const clientOptions = this.buildClientOptions();
1826
+ logger.debug(
1827
+ `Creating Client with capabilities:`,
1828
+ JSON.stringify(clientOptions.capabilities, null, 2)
1829
+ );
1830
+ this.client = new Client(this.clientInfo, clientOptions);
1831
+ this.setupRootsHandler();
1832
+ logger.debug("Roots handler registered before connect");
1833
+ try {
1834
+ await this.client.connect(transport, {
1835
+ timeout: this.timeout
1836
+ });
1837
+ const sessionId = streamableTransport.sessionId;
1838
+ if (sessionId) {
1839
+ logger.debug(`Session ID obtained: ${sessionId}`);
1840
+ } else {
1841
+ logger.warn(
1842
+ "Session ID not available after connect - this may cause issues with SSE stream"
1843
+ );
1844
+ }
1845
+ } catch (connectErr) {
1846
+ if (connectErr instanceof Error) {
1847
+ const errMsg = connectErr.message || connectErr.toString();
1848
+ if (errMsg.includes("Missing session ID") || errMsg.includes("Bad Request: Missing session ID") || errMsg.includes("Mcp-Session-Id header is required")) {
1849
+ const wrappedError = new Error(
1850
+ `Session ID error: ${errMsg}. The SDK should automatically extract session ID from initialize response.`
1851
+ );
1852
+ wrappedError.cause = connectErr;
1853
+ throw wrappedError;
1854
+ }
1855
+ }
1856
+ throw connectErr;
1857
+ }
1858
+ this.streamableTransport = streamableTransport;
1859
+ this.connectionManager = {
1860
+ stop: /* @__PURE__ */ __name(async () => {
1861
+ if (this.streamableTransport) {
1862
+ try {
1863
+ await this.streamableTransport.close();
1864
+ } catch (e) {
1865
+ logger.warn(`Error closing Streamable HTTP transport: ${e}`);
1866
+ } finally {
1867
+ this.streamableTransport = null;
1868
+ }
1869
+ }
1870
+ }, "stop")
1871
+ };
1872
+ this.connected = true;
1873
+ this.transportType = "streamable-http";
1874
+ this.setupNotificationHandler();
1875
+ this.setupSamplingHandler();
1876
+ this.setupElicitationHandler();
1877
+ logger.debug(
1878
+ `Successfully connected to MCP implementation via streamable HTTP: ${baseUrl}`
1879
+ );
1880
+ this.trackConnectorInit({
1881
+ serverUrl: this.baseUrl,
1882
+ publicIdentifier: `${this.baseUrl} (streamable-http)`
1883
+ });
1884
+ } catch (err) {
1885
+ await this.cleanupResources();
1886
+ throw err;
1887
+ }
1888
+ }
1889
+ async connectWithSse(baseUrl) {
1890
+ try {
1891
+ this.connectionManager = new SseConnectionManager(baseUrl, {
1892
+ authProvider: this.opts.authProvider,
1893
+ // ← Pass OAuth provider to SDK (same as streamable HTTP)
1894
+ requestInit: {
1895
+ headers: this.headers
1896
+ }
1897
+ });
1898
+ let transport = await this.connectionManager.start();
1899
+ if (this.opts.wrapTransport) {
1900
+ const serverId = this.baseUrl;
1901
+ transport = this.opts.wrapTransport(transport, serverId);
1902
+ }
1903
+ const clientOptions = this.buildClientOptions();
1904
+ logger.debug(
1905
+ `Creating Client with capabilities (SSE):`,
1906
+ JSON.stringify(clientOptions.capabilities, null, 2)
1907
+ );
1908
+ this.client = new Client(this.clientInfo, clientOptions);
1909
+ this.setupRootsHandler();
1910
+ logger.debug("Roots handler registered before connect (SSE)");
1911
+ await this.client.connect(transport);
1912
+ this.connected = true;
1913
+ this.transportType = "sse";
1914
+ this.setupNotificationHandler();
1915
+ this.setupSamplingHandler();
1916
+ this.setupElicitationHandler();
1917
+ logger.debug(
1918
+ `Successfully connected to MCP implementation via HTTP/SSE: ${baseUrl}`
1919
+ );
1920
+ this.trackConnectorInit({
1921
+ serverUrl: this.baseUrl,
1922
+ publicIdentifier: `${this.baseUrl} (sse)`
1923
+ });
1924
+ } catch (err) {
1925
+ await this.cleanupResources();
1926
+ throw err;
1927
+ }
1928
+ }
1929
+ get publicIdentifier() {
1930
+ return {
1931
+ type: "http",
1932
+ url: this.baseUrl,
1933
+ transport: this.transportType || "unknown"
1934
+ };
1935
+ }
1936
+ /**
1937
+ * Get the transport type being used (streamable-http or sse)
1938
+ */
1939
+ getTransportType() {
1940
+ return this.transportType;
1941
+ }
1942
+ // Send the streamable-HTTP DELETE *before* super.cleanupResources() invokes
1943
+ // client.close(). The SDK's transport.close() aborts the shared abort
1944
+ // controller, and terminateSession()'s DELETE fetch reuses that signal —
1945
+ // running it after close() rejects immediately with AbortError.
1946
+ async cleanupResources() {
1947
+ if (this.streamableTransport) {
1948
+ try {
1949
+ await this.streamableTransport.terminateSession();
1950
+ } catch (e) {
1951
+ logger.debug(`Error terminating Streamable HTTP session: ${e}`);
1952
+ }
1953
+ }
1954
+ await super.cleanupResources();
1955
+ }
1956
+ };
1957
+
1958
+ // src/config.ts
1959
+ function resolveCallbacks(perServer, globalDefaults) {
1960
+ const pickSampling = perServer?.onSampling ?? perServer?.samplingCallback ?? globalDefaults?.onSampling ?? globalDefaults?.samplingCallback;
1961
+ const pickElicitation = perServer?.onElicitation ?? perServer?.elicitationCallback ?? globalDefaults?.onElicitation ?? globalDefaults?.elicitationCallback;
1962
+ const pickNotification = perServer?.onNotification ?? globalDefaults?.onNotification;
1963
+ return {
1964
+ onSampling: pickSampling,
1965
+ onElicitation: pickElicitation,
1966
+ onNotification: pickNotification
1967
+ };
1968
+ }
1969
+ __name(resolveCallbacks, "resolveCallbacks");
1970
+ function getDefaultClientInfo() {
1971
+ return {
1972
+ name: "mcp-use",
1973
+ title: "mcp-use",
1974
+ version: getPackageVersion(),
1975
+ description: "mcp-use is a complete TypeScript framework for building and using MCP",
1976
+ icons: [
1977
+ {
1978
+ src: "https://mcp-use.com/logo.png"
1979
+ }
1980
+ ],
1981
+ websiteUrl: "https://mcp-use.com"
1982
+ };
1983
+ }
1984
+ __name(getDefaultClientInfo, "getDefaultClientInfo");
1985
+ function normalizeClientInfo(input) {
1986
+ const fallback = getDefaultClientInfo();
1987
+ if (!input || typeof input !== "object") return fallback;
1988
+ const ci = input;
1989
+ if (!ci.name || !ci.version) return fallback;
1990
+ return { ...fallback, ...ci };
1991
+ }
1992
+ __name(normalizeClientInfo, "normalizeClientInfo");
1993
+ function createConnectorFromConfig(serverConfig, connectorOptions) {
1994
+ const clientInfo = normalizeClientInfo(serverConfig.clientInfo);
1995
+ if ("command" in serverConfig && "args" in serverConfig) {
1996
+ throw new Error(
1997
+ "Stdio connector is not supported in this environment. Stdio connections require Node.js and are only available in the Node.js MCPClient."
1998
+ );
1999
+ }
2000
+ if ("url" in serverConfig) {
2001
+ const transport = serverConfig.transport || "http";
2002
+ return new HttpConnector(serverConfig.url, {
2003
+ headers: serverConfig.headers,
2004
+ fetch: serverConfig.fetch,
2005
+ authToken: serverConfig.auth_token || serverConfig.authToken,
2006
+ authProvider: serverConfig.authProvider,
2007
+ // Only force SSE if explicitly requested
2008
+ preferSse: serverConfig.preferSse || transport === "sse",
2009
+ // Disable SSE fallback if explicitly disabled in config
2010
+ disableSseFallback: serverConfig.disableSseFallback,
2011
+ clientInfo,
2012
+ ...connectorOptions
2013
+ });
2014
+ }
2015
+ throw new Error("Cannot determine connector type from config");
2016
+ }
2017
+ __name(createConnectorFromConfig, "createConnectorFromConfig");
2018
+
2019
+ // src/config-file.ts
2020
+ function loadConfigFile(filepath) {
2021
+ const { readFileSync } = __require("fs");
2022
+ const raw = readFileSync(filepath, "utf-8");
2023
+ return JSON.parse(raw);
2024
+ }
2025
+ __name(loadConfigFile, "loadConfigFile");
2026
+
2027
+ // src/client/elicitation-helpers.ts
2028
+ function hasRequestedSchema(params) {
2029
+ return "requestedSchema" in params && params.requestedSchema != null;
2030
+ }
2031
+ __name(hasRequestedSchema, "hasRequestedSchema");
2032
+ function getDefaults(params) {
2033
+ const content = {};
2034
+ if (!hasRequestedSchema(params)) return content;
2035
+ const schema = params.requestedSchema;
2036
+ const properties = schema.properties ?? {};
2037
+ for (const [fieldName, fieldSchema] of Object.entries(properties)) {
2038
+ const field = fieldSchema;
2039
+ if ("default" in field) {
2040
+ const v = field.default;
2041
+ if (typeof v === "string" || typeof v === "number" || typeof v === "boolean" || Array.isArray(v) && v.every((x) => typeof x === "string")) {
2042
+ content[fieldName] = v;
2043
+ }
2044
+ }
2045
+ }
2046
+ return content;
2047
+ }
2048
+ __name(getDefaults, "getDefaults");
2049
+ function applyDefaults(params, partial) {
2050
+ const defaults = getDefaults(params);
2051
+ if (partial == null) return defaults;
2052
+ return { ...defaults, ...partial };
2053
+ }
2054
+ __name(applyDefaults, "applyDefaults");
2055
+ function acceptWithDefaults(params) {
2056
+ return { action: "accept", content: getDefaults(params) };
2057
+ }
2058
+ __name(acceptWithDefaults, "acceptWithDefaults");
2059
+ function accept(content) {
2060
+ return { action: "accept", content };
2061
+ }
2062
+ __name(accept, "accept");
2063
+ function decline(_reason) {
2064
+ return { action: "decline" };
2065
+ }
2066
+ __name(decline, "decline");
2067
+ function cancel() {
2068
+ return { action: "cancel" };
2069
+ }
2070
+ __name(cancel, "cancel");
2071
+ function reject(reason) {
2072
+ return decline(reason);
2073
+ }
2074
+ __name(reject, "reject");
2075
+ function validate(params, content) {
2076
+ if (!hasRequestedSchema(params)) {
2077
+ return { valid: true };
2078
+ }
2079
+ try {
2080
+ const zodSchema = JSONSchemaToZod.convert(
2081
+ params.requestedSchema
2082
+ );
2083
+ const result = zodSchema.safeParse(content);
2084
+ if (result.success) {
2085
+ return { valid: true };
2086
+ }
2087
+ const messages = result.error.issues.map(
2088
+ (i) => i.path.length > 0 ? `${i.path.join(".")}: ${i.message}` : i.message
2089
+ );
2090
+ return { valid: false, errors: messages };
2091
+ } catch {
2092
+ return { valid: false, errors: ["Unsupported or invalid schema"] };
2093
+ }
2094
+ }
2095
+ __name(validate, "validate");
2096
+
2097
+ // src/client.ts
2098
+ function trackNodeClientInit(config, codeMode, callbacks) {
2099
+ const servers = Object.keys(config.mcpServers ?? {});
2100
+ const hasSamplingCallback = !!(callbacks.onSampling ?? callbacks.samplingCallback);
2101
+ const hasElicitationCallback = !!(callbacks.onElicitation ?? callbacks.elicitationCallback);
2102
+ Tel.getInstance().trackMCPClientInit({
2103
+ codeMode,
2104
+ sandbox: false,
2105
+ allCallbacks: hasSamplingCallback && hasElicitationCallback,
2106
+ verify: false,
2107
+ servers,
2108
+ numServers: servers.length,
2109
+ isBrowser: false
2110
+ }).catch((e) => logger.debug(`Failed to track MCPClient init: ${e}`));
2111
+ }
2112
+ __name(trackNodeClientInit, "trackNodeClientInit");
2113
+ var MCPClient = class _MCPClient extends BaseMCPClient {
2114
+ static {
2115
+ __name(this, "MCPClient");
2116
+ }
2117
+ /**
2118
+ * Gets the mcp-use package version.
2119
+ *
2120
+ * This static method returns the version string of the installed mcp-use package,
2121
+ * which is useful for debugging and compatibility checks.
2122
+ *
2123
+ * @returns The package version string (e.g., "1.13.2")
2124
+ *
2125
+ * @example
2126
+ * ```typescript
2127
+ * console.log(`mcp-use version: ${MCPClient.getPackageVersion()}`);
2128
+ * ```
2129
+ */
2130
+ static getPackageVersion() {
2131
+ return getPackageVersion();
2132
+ }
2133
+ /**
2134
+ * Indicates whether code execution mode is enabled.
2135
+ *
2136
+ * When true, the client provides special tools for executing code dynamically
2137
+ * through the {@link executeCode} and {@link searchTools} methods.
2138
+ *
2139
+ * @example
2140
+ * ```typescript
2141
+ * if (client.codeMode) {
2142
+ * const result = await client.executeCode('return 2 + 2');
2143
+ * console.log(result.output); // "4"
2144
+ * }
2145
+ * ```
2146
+ */
2147
+ codeMode = false;
2148
+ _codeExecutor = null;
2149
+ _customCodeExecutor = null;
2150
+ _codeExecutorConfig = "vm";
2151
+ _executorOptions;
2152
+ _globalCallbacks;
2153
+ /**
2154
+ * Creates a new MCPClient instance.
2155
+ *
2156
+ * The client can be initialized with either a configuration object, a path to
2157
+ * a configuration file, or no configuration at all (servers can be added later
2158
+ * using {@link addServer}).
2159
+ *
2160
+ * @param config - Configuration object or path to JSON config file. If omitted,
2161
+ * starts with empty configuration
2162
+ * @param options - Optional client behavior configuration
2163
+ * @param options.codeMode - Enable code execution mode (boolean or advanced config)
2164
+ * @param options.onSampling - Callback for handling sampling requests from servers
2165
+ * @param options.elicitationCallback - Callback for handling elicitation requests
2166
+ *
2167
+ * @example
2168
+ * ```typescript
2169
+ * // From config file
2170
+ * const client = new MCPClient('./mcp-config.json');
2171
+ * ```
2172
+ *
2173
+ * @example
2174
+ * ```typescript
2175
+ * // From inline config
2176
+ * const client = new MCPClient({
2177
+ * mcpServers: {
2178
+ * 'my-server': {
2179
+ * command: 'node',
2180
+ * args: ['server.js']
2181
+ * }
2182
+ * }
2183
+ * });
2184
+ * ```
2185
+ *
2186
+ * @example
2187
+ * ```typescript
2188
+ * // With code mode enabled
2189
+ * const client = new MCPClient('./config.json', {
2190
+ * codeMode: true
2191
+ * });
2192
+ * ```
2193
+ *
2194
+ * @example
2195
+ * ```typescript
2196
+ * // With sampling callback
2197
+ * const client = new MCPClient('./config.json', {
2198
+ * onSampling: async (params) => {
2199
+ * // Call your LLM here
2200
+ * return anthropic.messages.create(params);
2201
+ * }
2202
+ * });
2203
+ * ```
2204
+ *
2205
+ * @see {@link fromDict} for creating from config object (alternative syntax)
2206
+ * @see {@link fromConfigFile} for creating from file (alternative syntax)
2207
+ */
2208
+ constructor(config, options) {
2209
+ if (config) {
2210
+ if (typeof config === "string") {
2211
+ super(loadConfigFile(config));
2212
+ } else {
2213
+ super(config);
2214
+ }
2215
+ } else {
2216
+ super();
2217
+ }
2218
+ let codeModeEnabled = false;
2219
+ let executorConfig = "vm";
2220
+ let executorOptions;
2221
+ if (options?.codeMode) {
2222
+ if (typeof options.codeMode === "boolean") {
2223
+ codeModeEnabled = options.codeMode;
2224
+ } else {
2225
+ codeModeEnabled = options.codeMode.enabled;
2226
+ executorConfig = options.codeMode.executor ?? "vm";
2227
+ executorOptions = options.codeMode.executorOptions;
2228
+ }
2229
+ }
2230
+ this.codeMode = codeModeEnabled;
2231
+ this._codeExecutorConfig = executorConfig;
2232
+ this._executorOptions = executorOptions;
2233
+ const configRoot = this.config;
2234
+ this._globalCallbacks = {
2235
+ onSampling: options?.onSampling ?? options?.samplingCallback ?? configRoot?.onSampling ?? configRoot?.samplingCallback,
2236
+ samplingCallback: options?.samplingCallback ?? configRoot?.samplingCallback,
2237
+ onElicitation: options?.onElicitation ?? options?.elicitationCallback ?? configRoot?.onElicitation ?? configRoot?.elicitationCallback,
2238
+ elicitationCallback: options?.elicitationCallback ?? configRoot?.elicitationCallback,
2239
+ onNotification: options?.onNotification ?? configRoot?.onNotification
2240
+ };
2241
+ if (options?.samplingCallback && !options?.onSampling) {
2242
+ console.warn(
2243
+ '[MCPClient] The "samplingCallback" option is deprecated. Use "onSampling" instead.'
2244
+ );
2245
+ }
2246
+ if (options?.elicitationCallback && !options?.onElicitation) {
2247
+ console.warn(
2248
+ '[MCPClient] The "elicitationCallback" option is deprecated. Use "onElicitation" instead.'
2249
+ );
2250
+ }
2251
+ if (this.codeMode) {
2252
+ this._setupCodeModeConnector();
2253
+ }
2254
+ trackNodeClientInit(this.config, this.codeMode, this._globalCallbacks);
2255
+ }
2256
+ /**
2257
+ * Creates a client instance from a configuration dictionary.
2258
+ *
2259
+ * This static factory method provides an alternative syntax for creating
2260
+ * a client from an inline configuration object.
2261
+ *
2262
+ * @param cfg - Configuration dictionary with server definitions
2263
+ * @param options - Optional client behavior configuration
2264
+ * @returns New MCPClient instance
2265
+ *
2266
+ * @example
2267
+ * ```typescript
2268
+ * const client = MCPClient.fromDict({
2269
+ * mcpServers: {
2270
+ * 'filesystem': {
2271
+ * command: 'npx',
2272
+ * args: ['-y', '@modelcontextprotocol/server-filesystem', '/tmp']
2273
+ * }
2274
+ * }
2275
+ * });
2276
+ * ```
2277
+ *
2278
+ * @see {@link constructor} for direct instantiation
2279
+ * @see {@link fromConfigFile} for loading from file
2280
+ */
2281
+ static fromDict(cfg, options) {
2282
+ return new _MCPClient(cfg, options);
2283
+ }
2284
+ /**
2285
+ * Creates a client instance from a configuration file.
2286
+ *
2287
+ * This static factory method loads MCP server configurations from a JSON
2288
+ * file and creates a new client instance.
2289
+ *
2290
+ * @param path - Path to the JSON configuration file
2291
+ * @param options - Optional client behavior configuration
2292
+ * @returns New MCPClient instance
2293
+ * @throws {Error} If the file cannot be read or parsed
2294
+ *
2295
+ * @example
2296
+ * ```typescript
2297
+ * const client = MCPClient.fromConfigFile('./mcp-config.json');
2298
+ * await client.createAllSessions();
2299
+ * ```
2300
+ *
2301
+ * @example
2302
+ * ```typescript
2303
+ * // With code mode
2304
+ * const client = MCPClient.fromConfigFile('./config.json', {
2305
+ * codeMode: true
2306
+ * });
2307
+ * ```
2308
+ *
2309
+ * @see {@link constructor} for direct instantiation
2310
+ * @see {@link fromDict} for inline configuration
2311
+ */
2312
+ static fromConfigFile(path2, options) {
2313
+ return new _MCPClient(loadConfigFile(path2), options);
2314
+ }
2315
+ /**
2316
+ * Saves the current configuration to a file.
2317
+ *
2318
+ * This Node.js-specific method writes the client's current configuration
2319
+ * (including all server definitions) to a JSON file. The directory will be
2320
+ * created if it doesn't exist.
2321
+ *
2322
+ * @param filepath - Path where the configuration file should be saved
2323
+ *
2324
+ * @example
2325
+ * ```typescript
2326
+ * const client = new MCPClient();
2327
+ * client.addServer('my-server', {
2328
+ * command: 'node',
2329
+ * args: ['server.js']
2330
+ * });
2331
+ *
2332
+ * // Save configuration for later use
2333
+ * client.saveConfig('./mcp-config.json');
2334
+ * ```
2335
+ *
2336
+ * @see {@link fromConfigFile} for loading configurations
2337
+ */
2338
+ saveConfig(filepath) {
2339
+ const dir = path.dirname(filepath);
2340
+ if (!fs.existsSync(dir)) {
2341
+ fs.mkdirSync(dir, { recursive: true });
2342
+ }
2343
+ fs.writeFileSync(filepath, JSON.stringify(this.config, null, 2), "utf-8");
2344
+ }
2345
+ /**
2346
+ * Create a connector from server configuration (Node.js version)
2347
+ * Supports all connector types including StdioConnector (lazy-loaded to avoid pulling Node-only code into browser bundles).
2348
+ */
2349
+ async createConnectorFromConfig(serverConfig) {
2350
+ const resolved = resolveCallbacks(
2351
+ serverConfig,
2352
+ this._globalCallbacks
2353
+ );
2354
+ const merged = {
2355
+ ...serverConfig,
2356
+ clientInfo: serverConfig.clientInfo ?? this.config.clientInfo
2357
+ };
2358
+ if ("command" in merged && "args" in merged) {
2359
+ const { StdioConnector } = await import("./stdio-FUBSRH66.js");
2360
+ const stdioConfig = merged;
2361
+ return new StdioConnector({
2362
+ command: stdioConfig.command,
2363
+ args: stdioConfig.args,
2364
+ env: stdioConfig.env,
2365
+ cwd: stdioConfig.cwd,
2366
+ clientInfo: normalizeClientInfo(merged.clientInfo),
2367
+ onSampling: resolved.onSampling,
2368
+ onElicitation: resolved.onElicitation,
2369
+ onNotification: resolved.onNotification
2370
+ });
2371
+ }
2372
+ return createConnectorFromConfig(merged, {
2373
+ onSampling: resolved.onSampling,
2374
+ onElicitation: resolved.onElicitation,
2375
+ onNotification: resolved.onNotification
2376
+ });
2377
+ }
2378
+ _setupCodeModeConnector() {
2379
+ logger.debug("Code mode connector initialized as internal meta server");
2380
+ const connector = new CodeModeConnector(this);
2381
+ const session = new MCPSession(connector);
2382
+ this.sessions["code_mode"] = session;
2383
+ this.activeSessions.push("code_mode");
2384
+ }
2385
+ _ensureCodeExecutor() {
2386
+ if (!this._codeExecutor) {
2387
+ const config = this._codeExecutorConfig;
2388
+ if (config instanceof BaseCodeExecutor) {
2389
+ this._codeExecutor = config;
2390
+ } else if (typeof config === "function") {
2391
+ this._customCodeExecutor = config;
2392
+ throw new Error(
2393
+ "Custom executor function should be handled in executeCode"
2394
+ );
2395
+ } else if (config === "e2b") {
2396
+ const opts = this._executorOptions;
2397
+ if (!opts?.apiKey) {
2398
+ logger.warn("E2B executor requires apiKey. Falling back to VM.");
2399
+ try {
2400
+ this._codeExecutor = new VMCodeExecutor(
2401
+ this,
2402
+ this._executorOptions
2403
+ );
2404
+ } catch (error) {
2405
+ throw new Error(
2406
+ "VM executor is not available in this environment and E2B API key is not provided. Please provide an E2B API key or run in a Node.js environment."
2407
+ );
2408
+ }
2409
+ } else {
2410
+ this._codeExecutor = new E2BCodeExecutor(this, opts);
2411
+ }
2412
+ } else {
2413
+ try {
2414
+ this._codeExecutor = new VMCodeExecutor(
2415
+ this,
2416
+ this._executorOptions
2417
+ );
2418
+ } catch (error) {
2419
+ const e2bOpts = this._executorOptions;
2420
+ const e2bApiKey = e2bOpts?.apiKey || process.env.E2B_API_KEY;
2421
+ if (e2bApiKey) {
2422
+ logger.info(
2423
+ "VM executor not available in this environment. Falling back to E2B."
2424
+ );
2425
+ this._codeExecutor = new E2BCodeExecutor(this, {
2426
+ ...e2bOpts,
2427
+ apiKey: e2bApiKey
2428
+ });
2429
+ } else {
2430
+ throw new Error(
2431
+ "VM executor is not available in this environment. Please provide an E2B API key via executorOptions or E2B_API_KEY environment variable, or run in a Node.js environment."
2432
+ );
2433
+ }
2434
+ }
2435
+ }
2436
+ }
2437
+ return this._codeExecutor;
2438
+ }
2439
+ /**
2440
+ * Executes JavaScript/TypeScript code in a sandboxed environment.
2441
+ *
2442
+ * This method is only available when code mode is enabled. It executes the
2443
+ * provided code in an isolated environment (VM or E2B sandbox) and returns
2444
+ * the results including stdout, stderr, and return value.
2445
+ *
2446
+ * @param code - JavaScript/TypeScript code to execute
2447
+ * @param timeout - Optional execution timeout in milliseconds
2448
+ * @returns Execution result with output, errors, and return value
2449
+ * @throws {Error} If code mode is not enabled
2450
+ *
2451
+ * @example
2452
+ * ```typescript
2453
+ * const client = new MCPClient('./config.json', { codeMode: true });
2454
+ *
2455
+ * const result = await client.executeCode(`
2456
+ * console.log('Hello, world!');
2457
+ * return 2 + 2;
2458
+ * `);
2459
+ *
2460
+ * console.log(result.stdout); // "Hello, world!\n"
2461
+ * console.log(result.returnValue); // 4
2462
+ * ```
2463
+ *
2464
+ * @example
2465
+ * ```typescript
2466
+ * // With timeout
2467
+ * try {
2468
+ * const result = await client.executeCode('while(true) {}', 1000);
2469
+ * } catch (error) {
2470
+ * console.log('Execution timed out');
2471
+ * }
2472
+ * ```
2473
+ *
2474
+ * @see {@link searchTools} for discovering available tools in code mode
2475
+ */
2476
+ async executeCode(code, timeout) {
2477
+ if (!this.codeMode) {
2478
+ throw new Error("Code execution mode is not enabled");
2479
+ }
2480
+ if (this._customCodeExecutor) {
2481
+ return this._customCodeExecutor(code, timeout);
2482
+ }
2483
+ return this._ensureCodeExecutor().execute(code, timeout);
2484
+ }
2485
+ /**
2486
+ * Searches for available tools across all MCP servers.
2487
+ *
2488
+ * This method is only available when code mode is enabled. It searches
2489
+ * through tools from all active servers and returns matching tools based
2490
+ * on the query and detail level.
2491
+ *
2492
+ * @param query - Optional search query to filter tools (defaults to empty string for all tools)
2493
+ * @param detailLevel - Level of detail to return: "names", "descriptions", or "full"
2494
+ * @returns Tool search results with matching tools
2495
+ * @throws {Error} If code mode is not enabled
2496
+ *
2497
+ * @example
2498
+ * ```typescript
2499
+ * const client = new MCPClient('./config.json', { codeMode: true });
2500
+ * await client.createAllSessions();
2501
+ *
2502
+ * // Search for all tools
2503
+ * const allTools = await client.searchTools();
2504
+ * console.log(`Found ${allTools.tools.length} tools`);
2505
+ *
2506
+ * // Search for specific tools
2507
+ * const fileTools = await client.searchTools('file', 'descriptions');
2508
+ * ```
2509
+ *
2510
+ * @see {@link executeCode} for executing code in code mode
2511
+ */
2512
+ async searchTools(query = "", detailLevel = "full") {
2513
+ if (!this.codeMode) {
2514
+ throw new Error("Code execution mode is not enabled");
2515
+ }
2516
+ return this._ensureCodeExecutor().createSearchToolsFunction()(
2517
+ query,
2518
+ detailLevel
2519
+ );
2520
+ }
2521
+ /**
2522
+ * Gets the names of all configured MCP servers (excluding internal servers).
2523
+ *
2524
+ * This method overrides the base implementation to filter out internal
2525
+ * meta-servers like the code mode server, which is an implementation detail
2526
+ * not intended for direct user interaction.
2527
+ *
2528
+ * @returns Array of user-configured server names
2529
+ *
2530
+ * @example
2531
+ * ```typescript
2532
+ * const names = client.getServerNames();
2533
+ * console.log(`User servers: ${names.join(', ')}`);
2534
+ * // Note: 'code_mode' is excluded even if code mode is enabled
2535
+ * ```
2536
+ *
2537
+ * @see {@link activeSessions} for servers with active sessions
2538
+ */
2539
+ getServerNames() {
2540
+ const isCodeModeEnabled = this.codeMode;
2541
+ return super.getServerNames().filter((name) => {
2542
+ return !isCodeModeEnabled || name !== "code_mode";
2543
+ });
2544
+ }
2545
+ /**
2546
+ * Closes the client and cleans up all resources.
2547
+ *
2548
+ * This method performs a complete cleanup by:
2549
+ * 1. Shutting down code executors (VM or E2B sandboxes)
2550
+ * 2. Closing all active MCP sessions
2551
+ * 3. Releasing any other held resources
2552
+ *
2553
+ * Always call this method when you're done with the client to ensure
2554
+ * proper resource cleanup, especially when using E2B sandboxes which
2555
+ * have associated costs.
2556
+ *
2557
+ * @example
2558
+ * ```typescript
2559
+ * const client = new MCPClient('./config.json', { codeMode: true });
2560
+ * await client.createAllSessions();
2561
+ *
2562
+ * // Do work...
2563
+ *
2564
+ * // Clean up
2565
+ * await client.close();
2566
+ * ```
2567
+ *
2568
+ * @example
2569
+ * ```typescript
2570
+ * // Use in shutdown handler
2571
+ * process.on('SIGINT', async () => {
2572
+ * console.log('Shutting down...');
2573
+ * await client.close();
2574
+ * process.exit(0);
2575
+ * });
2576
+ * ```
2577
+ *
2578
+ * @see {@link closeAllSessions} for closing just the sessions
2579
+ */
2580
+ async close() {
2581
+ if (this._codeExecutor) {
2582
+ await this._codeExecutor.cleanup();
2583
+ this._codeExecutor = null;
2584
+ }
2585
+ await this.closeAllSessions();
2586
+ }
2587
+ };
2588
+
2589
+ export {
2590
+ MCPSession,
2591
+ BaseMCPClient,
2592
+ BaseCodeExecutor,
2593
+ E2BCodeExecutor,
2594
+ isVMAvailable,
2595
+ VMCodeExecutor,
2596
+ HttpConnector,
2597
+ resolveCallbacks,
2598
+ normalizeClientInfo,
2599
+ loadConfigFile,
2600
+ getDefaults,
2601
+ applyDefaults,
2602
+ acceptWithDefaults,
2603
+ accept,
2604
+ decline,
2605
+ cancel,
2606
+ reject,
2607
+ validate,
2608
+ MCPClient
2609
+ };