indusagi 0.12.34 → 0.13.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.
Files changed (55) hide show
  1. package/dist/agent.js +1247 -184
  2. package/dist/ai.js +72 -4
  3. package/dist/capabilities.js +69 -2
  4. package/dist/cli.js +83 -13
  5. package/dist/connectors-saas.js +66 -0
  6. package/dist/index.js +83 -13
  7. package/dist/interop.js +66 -0
  8. package/dist/mcp.js +270 -363
  9. package/dist/react-ink.js +15 -11
  10. package/dist/shell-app.js +83 -13
  11. package/dist/smithy.js +69 -2
  12. package/dist/swarm.js +69 -2
  13. package/dist/types/capabilities/backends/node-backends.d.ts +3 -1
  14. package/dist/types/capabilities/files/read-state-gate.d.ts +69 -0
  15. package/dist/types/capabilities/files/read-state-gate.test.d.ts +14 -0
  16. package/dist/types/capabilities/kernel/context.d.ts +4 -0
  17. package/dist/types/capabilities/kernel/index.d.ts +2 -2
  18. package/dist/types/capabilities/kernel/spec.d.ts +55 -0
  19. package/dist/types/facade/bot/actions/bash.d.ts +15 -0
  20. package/dist/types/facade/bot/actions/bash.test.d.ts +1 -0
  21. package/dist/types/facade/bot/actions/checkpoint.d.ts +49 -0
  22. package/dist/types/facade/bot/actions/checkpoint.test.d.ts +1 -0
  23. package/dist/types/facade/bot/actions/edit-utils.d.ts +86 -0
  24. package/dist/types/facade/bot/actions/edit.d.ts +18 -0
  25. package/dist/types/facade/bot/actions/edit.test.d.ts +1 -0
  26. package/dist/types/facade/bot/actions/find.d.ts +2 -0
  27. package/dist/types/facade/bot/actions/find.test.d.ts +1 -0
  28. package/dist/types/facade/bot/actions/grep.d.ts +10 -0
  29. package/dist/types/facade/bot/actions/grep.test.d.ts +1 -0
  30. package/dist/types/facade/bot/actions/index.d.ts +16 -0
  31. package/dist/types/facade/bot/actions/read-state.d.ts +83 -0
  32. package/dist/types/facade/bot/actions/read-state.test.d.ts +1 -0
  33. package/dist/types/facade/bot/actions/read.d.ts +7 -0
  34. package/dist/types/facade/bot/actions/read.test.d.ts +1 -0
  35. package/dist/types/facade/bot/actions/sandbox-backend.d.ts +99 -0
  36. package/dist/types/facade/bot/actions/sandbox-backend.test.d.ts +1 -0
  37. package/dist/types/facade/bot/actions/websearch.d.ts +5 -2
  38. package/dist/types/facade/bot/actions/websearch.test.d.ts +1 -0
  39. package/dist/types/facade/bot/actions/write.d.ts +15 -0
  40. package/dist/types/facade/bot/agent-loop.d.ts +10 -0
  41. package/dist/types/facade/bot/agent-loop.test.d.ts +1 -0
  42. package/dist/types/facade/bot/agent.d.ts +9 -1
  43. package/dist/types/facade/bot/permission-gate.test.d.ts +1 -0
  44. package/dist/types/facade/bot/types.d.ts +60 -0
  45. package/dist/types/facade/mcp-core/client.d.ts +71 -15
  46. package/dist/types/facade/mcp-core/client.test.d.ts +18 -0
  47. package/dist/types/facade/mcp-core/types.d.ts +10 -0
  48. package/dist/types/facade/ml/adapters/anthropic-retry.test.d.ts +1 -0
  49. package/dist/types/facade/ml/adapters/anthropic.d.ts +17 -0
  50. package/dist/types/facade/ml/adapters/simple-options.d.ts +13 -0
  51. package/dist/types/facade/ml/adapters/simple-options.test.d.ts +1 -0
  52. package/dist/types/react-ink/components/StatusLine.d.ts +10 -1
  53. package/dist/types/react-ink/components/ToolEventBlock.d.ts +2 -1
  54. package/dist/types/react-ink/components/ToolEventBlock.test.d.ts +1 -0
  55. package/package.json +1 -1
package/dist/mcp.js CHANGED
@@ -1,12 +1,21 @@
1
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
- }) : x)(function(x) {
4
- if (typeof require !== "undefined") return require.apply(this, arguments);
5
- throw Error('Dynamic require of "' + x + '" is not supported');
6
- });
7
-
8
1
  // src/facade/mcp-core/client.ts
9
- import { spawn } from "child_process";
2
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
3
+ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
4
+ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
5
+ import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
6
+ import {
7
+ ListRootsRequestSchema,
8
+ ElicitRequestSchema,
9
+ CreateMessageRequestSchema,
10
+ PingRequestSchema,
11
+ ToolListChangedNotificationSchema,
12
+ ResourceListChangedNotificationSchema,
13
+ ResourceUpdatedNotificationSchema,
14
+ PromptListChangedNotificationSchema,
15
+ ProgressNotificationSchema,
16
+ LoggingMessageNotificationSchema,
17
+ CancelledNotificationSchema
18
+ } from "@modelcontextprotocol/sdk/types.js";
10
19
 
11
20
  // src/facade/mcp-core/errors.ts
12
21
  var MCPErrorCode = /* @__PURE__ */ ((MCPErrorCode2) => {
@@ -114,6 +123,7 @@ function createSchemaConversionError(toolName, details, serverName) {
114
123
 
115
124
  // src/facade/mcp-core/client.ts
116
125
  var DEFAULT_REQUEST_TIMEOUT = 6e4;
126
+ var DEFAULT_CONNECT_TIMEOUT = 3e4;
117
127
  var MCPClient = class {
118
128
  constructor(options) {
119
129
  this.options = options;
@@ -126,10 +136,8 @@ var MCPClient = class {
126
136
  this._roots = options.roots ?? [];
127
137
  }
128
138
  options;
129
- process;
130
- buffer = "";
131
- messageId = 0;
132
- pendingRequests = /* @__PURE__ */ new Map();
139
+ client;
140
+ transport;
133
141
  isConnected = false;
134
142
  connectionPromise = null;
135
143
  serverCapabilities;
@@ -137,6 +145,19 @@ var MCPClient = class {
137
145
  enableServerLogs;
138
146
  enableProgressTracking;
139
147
  _roots;
148
+ // --- Host-overridable handlers (Core of #13) --------------------------------
149
+ // Server-initiated REQUESTS are answered by request handlers registered on the
150
+ // SDK client; the handlers delegate to these host-supplied callbacks when set,
151
+ // and fall back to sensible defaults otherwise.
152
+ elicitationHandler;
153
+ samplingHandler;
154
+ // Server-initiated NOTIFICATIONS are dispatched to these (so the host can, for
155
+ // example, re-list tools when a `tools/list_changed` arrives).
156
+ resourceUpdatedHandler;
157
+ resourceListChangedHandler;
158
+ toolListChangedHandler;
159
+ promptListChangedHandler;
160
+ progressHandler;
140
161
  /** Server name */
141
162
  serverName;
142
163
  /** Server config */
@@ -163,106 +184,169 @@ var MCPClient = class {
163
184
  }
164
185
  }
165
186
  async doConnect() {
166
- if ("command" in this.config) {
167
- await this.connectStdio(this.config);
168
- } else if ("url" in this.config) {
169
- await this.connectHttp(this.config);
170
- } else {
171
- throw new MCPError(
172
- "Server configuration must include either a command or a url",
173
- "CONFIG_ERROR" /* CONFIG_ERROR */,
174
- void 0,
175
- { serverName: this.serverName }
176
- );
177
- }
178
- const initResult = await this.sendRequest("initialize", {
179
- protocolVersion: "2024-11-05",
180
- capabilities: {
181
- ...this._roots.length > 0 ? { roots: { listChanged: true } } : {},
182
- elicitation: {}
183
- },
184
- clientInfo: {
185
- name: "new_indusvx",
186
- version: "1.0.0"
187
+ const transport = this.createTransport(this.config);
188
+ this.transport = transport;
189
+ const client = new Client(
190
+ { name: "indusagi-coding-agent", version: "0.13.0" },
191
+ {
192
+ capabilities: {
193
+ roots: { listChanged: true },
194
+ elicitation: {},
195
+ sampling: {}
196
+ }
187
197
  }
188
- });
189
- this.serverCapabilities = initResult.capabilities;
198
+ );
199
+ this.client = client;
200
+ this.registerServerHandlers(client);
201
+ const prevOnClose = transport.onclose;
202
+ transport.onclose = () => {
203
+ this.isConnected = false;
204
+ this.log("debug", "Transport closed");
205
+ prevOnClose?.();
206
+ };
207
+ await client.connect(transport, { timeout: DEFAULT_CONNECT_TIMEOUT });
208
+ this.serverCapabilities = client.getServerCapabilities();
190
209
  this.isConnected = true;
191
- await this.sendNotification("notifications/initialized", {});
192
210
  this.log("info", `Connected to MCP server`);
193
211
  }
194
- async connectHttp(config) {
195
- this.log("debug", `Starting HTTP transport: ${config.url.toString()}`);
196
- this.isConnected = true;
197
- }
198
- async connectStdio(config) {
199
- return new Promise((resolve, reject) => {
200
- this.log("debug", `Starting stdio transport: ${config.command} ${(config.args ?? []).join(" ")}`);
201
- this.process = spawn(config.command, config.args ?? [], {
202
- env: { ...process.env, ...config.env },
212
+ /**
213
+ * Build the SDK transport for the configured server.
214
+ *
215
+ * - stdio: `StdioClientTransport` (inherits the parent env, merges config.env).
216
+ * - http: `StreamableHTTPClientTransport` (POST + server-push SSE channel,
217
+ * reconnect + session-expiry handled by the SDK), unless the config
218
+ * asks for the legacy `SSEClientTransport`.
219
+ */
220
+ createTransport(config) {
221
+ if ("command" in config) {
222
+ return new StdioClientTransport({
223
+ command: config.command,
224
+ args: config.args ?? [],
225
+ env: { ...sanitizeEnv(process.env), ...config.env ?? {} },
203
226
  cwd: config.cwd,
204
- stdio: ["pipe", "pipe", "pipe"]
205
- });
206
- this.process.stdout?.on("data", (data) => {
207
- this.buffer += data.toString("utf-8");
208
- this.processBuffer();
209
- });
210
- this.process.stderr?.on("data", (data) => {
211
- const message = data.toString("utf-8").trim();
212
- if (message) {
213
- this.log("debug", `[stderr] ${message}`);
214
- }
227
+ stderr: "pipe"
215
228
  });
216
- this.process.on("error", (error) => {
217
- const mcpError = new MCPError(
218
- `Failed to spawn MCP server: ${error.message}`,
219
- "CONNECTION_FAILED" /* CONNECTION_FAILED */,
220
- { error: error.message },
221
- { serverName: this.serverName, cause: error }
229
+ }
230
+ if ("url" in config) {
231
+ const requestInit = config.headers ? { headers: config.headers } : void 0;
232
+ if (config.transport === "sse") {
233
+ return new SSEClientTransport(config.url, { requestInit });
234
+ }
235
+ return new StreamableHTTPClientTransport(config.url, { requestInit });
236
+ }
237
+ throw new MCPError(
238
+ "Server configuration must include either a command or a url",
239
+ "CONFIG_ERROR" /* CONFIG_ERROR */,
240
+ void 0,
241
+ { serverName: this.serverName }
242
+ );
243
+ }
244
+ /**
245
+ * Register handlers for server-initiated REQUESTS and NOTIFICATIONS.
246
+ *
247
+ * REQUESTS (have both `.method` AND `.id`) must be ANSWERED with a JSON-RPC
248
+ * RESPONSE carrying the matching id — the SDK does this automatically for the
249
+ * value a request handler returns (or rejects). The previous hand-rolled
250
+ * transport routed these into the notification path, so they were never
251
+ * answered (bug #13).
252
+ *
253
+ * Defaults:
254
+ * - ping → {}
255
+ * - roots/list → the configured roots
256
+ * - elicitation/create→ decline ({action:"cancel"}) unless a host handler is set
257
+ * - sampling → reject "Method not found" unless a host handler is set
258
+ */
259
+ registerServerHandlers(client) {
260
+ client.setRequestHandler(PingRequestSchema, async () => ({}));
261
+ client.setRequestHandler(ListRootsRequestSchema, async () => ({
262
+ roots: this._roots.map((r) => ({ uri: r.uri, name: r.name }))
263
+ }));
264
+ client.setRequestHandler(ElicitRequestSchema, async (request) => {
265
+ if (!this.elicitationHandler) {
266
+ return { action: "cancel" };
267
+ }
268
+ const params = request.params;
269
+ const elicitRequest = {
270
+ message: params.message,
271
+ requestedSchema: params.requestedSchema ?? {}
272
+ };
273
+ const result = await this.elicitationHandler(elicitRequest);
274
+ return mapToSdkElicitResult(result);
275
+ });
276
+ client.setRequestHandler(CreateMessageRequestSchema, async (request) => {
277
+ if (!this.samplingHandler) {
278
+ throw new MCPError(
279
+ "Method not found: sampling/createMessage",
280
+ "SERVER_ERROR" /* SERVER_ERROR */,
281
+ { method: "sampling/createMessage" },
282
+ { serverName: this.serverName }
222
283
  );
223
- reject(mcpError);
224
- });
225
- this.process.on("close", (code) => {
226
- this.isConnected = false;
227
- if (code !== 0 && code !== null) {
228
- this.log("warning", `Process exited with code ${code}`);
229
- }
230
- for (const [id, { reject: reqReject, timeout }] of this.pendingRequests) {
231
- clearTimeout(timeout);
232
- reqReject(new MCPError("Connection closed", "NOT_CONNECTED" /* NOT_CONNECTED */));
233
- }
234
- this.pendingRequests.clear();
235
- });
236
- setTimeout(resolve, 500);
284
+ }
285
+ return await this.samplingHandler(request.params);
286
+ });
287
+ client.setNotificationHandler(ToolListChangedNotificationSchema, () => {
288
+ this.log("debug", "Tool list changed");
289
+ this.toolListChangedHandler?.();
290
+ });
291
+ client.setNotificationHandler(ResourceListChangedNotificationSchema, () => {
292
+ this.log("debug", "Resource list changed");
293
+ this.resourceListChangedHandler?.();
294
+ });
295
+ client.setNotificationHandler(ResourceUpdatedNotificationSchema, (n) => {
296
+ const uri = n.params?.uri;
297
+ this.log("debug", `Resource updated: ${uri}`);
298
+ if (uri) this.resourceUpdatedHandler?.({ uri });
299
+ });
300
+ client.setNotificationHandler(PromptListChangedNotificationSchema, () => {
301
+ this.log("debug", "Prompt list changed");
302
+ this.promptListChangedHandler?.();
303
+ });
304
+ client.setNotificationHandler(ProgressNotificationSchema, (n) => {
305
+ const params = n.params;
306
+ this.log("debug", `Progress: ${JSON.stringify(params)}`);
307
+ if (this.progressHandler && params) {
308
+ const notification = {
309
+ progressToken: params.progressToken,
310
+ progress: params.progress,
311
+ total: params.total,
312
+ message: params.message
313
+ };
314
+ this.progressHandler(notification);
315
+ }
316
+ });
317
+ client.setNotificationHandler(LoggingMessageNotificationSchema, (n) => {
318
+ if (this.enableServerLogs && n.params) {
319
+ const { level, ...rest } = n.params;
320
+ this.log(level || "info", "[SERVER]", rest);
321
+ }
322
+ });
323
+ client.setNotificationHandler(CancelledNotificationSchema, (n) => {
324
+ this.log("debug", `Request cancelled: ${JSON.stringify(n.params)}`);
237
325
  });
238
326
  }
239
327
  /**
240
328
  * Disconnect from the MCP server.
329
+ *
330
+ * The SDK's `client.close()` closes the transport and rejects any pending
331
+ * requests — a clean teardown for both stdio (graceful subprocess shutdown)
332
+ * and HTTP (close the push channel / end the session).
241
333
  */
242
334
  async disconnect() {
243
- if ("url" in this.config) {
335
+ if (!this.client) {
336
+ this.log("debug", "Disconnect called but not connected");
244
337
  this.isConnected = false;
245
- this.log("debug", "Disconnected from HTTP MCP server");
246
- return;
247
- }
248
- if (!this.process) {
249
- this.log("debug", "Disconnect called but no process was running");
250
338
  return;
251
339
  }
252
340
  this.log("debug", "Disconnecting from MCP server");
253
341
  try {
254
- this.process.kill();
255
- this.process = void 0;
256
- this.isConnected = false;
257
- for (const [id, { reject, timeout }] of this.pendingRequests) {
258
- clearTimeout(timeout);
259
- reject(new MCPError("Connection closed", "NOT_CONNECTED" /* NOT_CONNECTED */));
260
- }
261
- this.pendingRequests.clear();
262
- this.log("debug", "Successfully disconnected");
342
+ await this.client.close();
263
343
  } catch (error) {
264
344
  this.log("error", `Error during disconnect: ${error}`);
265
- throw error;
345
+ } finally {
346
+ this.client = void 0;
347
+ this.transport = void 0;
348
+ this.isConnected = false;
349
+ this.log("debug", "Successfully disconnected");
266
350
  }
267
351
  }
268
352
  /**
@@ -279,7 +363,9 @@ var MCPClient = class {
279
363
  */
280
364
  async listTools() {
281
365
  this.ensureConnected();
282
- const result = await this.sendRequest("tools/list", {});
366
+ const result = await this.client.listTools(void 0, {
367
+ timeout: this.timeout
368
+ });
283
369
  return result.tools;
284
370
  }
285
371
  /**
@@ -289,15 +375,16 @@ var MCPClient = class {
289
375
  this.ensureConnected();
290
376
  this.log("debug", `Calling tool: ${name}`);
291
377
  try {
292
- const result = await this.sendRequest("tools/call", {
293
- name,
294
- arguments: args
295
- });
378
+ const result = await this.client.callTool(
379
+ { name, arguments: args },
380
+ void 0,
381
+ { timeout: this.timeout }
382
+ );
296
383
  this.log("debug", `Tool ${name} executed successfully`);
297
384
  return result;
298
385
  } catch (error) {
299
386
  this.log("error", `Tool ${name} failed: ${error}`);
300
- throw error;
387
+ throw this.wrapError(error);
301
388
  }
302
389
  }
303
390
  // ========================================================================
@@ -311,7 +398,9 @@ var MCPClient = class {
311
398
  if (!this.serverCapabilities?.resources) {
312
399
  return [];
313
400
  }
314
- const result = await this.sendRequest("resources/list", {});
401
+ const result = await this.client.listResources(void 0, {
402
+ timeout: this.timeout
403
+ });
315
404
  return result.resources;
316
405
  }
317
406
  /**
@@ -319,21 +408,21 @@ var MCPClient = class {
319
408
  */
320
409
  async readResource(uri) {
321
410
  this.ensureConnected();
322
- return await this.sendRequest("resources/read", { uri });
411
+ return await this.client.readResource({ uri }, { timeout: this.timeout });
323
412
  }
324
413
  /**
325
414
  * Subscribe to resource updates.
326
415
  */
327
416
  async subscribeResource(uri) {
328
417
  this.ensureConnected();
329
- await this.sendRequest("resources/subscribe", { uri });
418
+ await this.client.subscribeResource({ uri }, { timeout: this.timeout });
330
419
  }
331
420
  /**
332
421
  * Unsubscribe from resource updates.
333
422
  */
334
423
  async unsubscribeResource(uri) {
335
424
  this.ensureConnected();
336
- await this.sendRequest("resources/unsubscribe", { uri });
425
+ await this.client.unsubscribeResource({ uri }, { timeout: this.timeout });
337
426
  }
338
427
  // ========================================================================
339
428
  // Prompt Operations
@@ -346,7 +435,9 @@ var MCPClient = class {
346
435
  if (!this.serverCapabilities?.prompts) {
347
436
  return [];
348
437
  }
349
- const result = await this.sendRequest("prompts/list", {});
438
+ const result = await this.client.listPrompts(void 0, {
439
+ timeout: this.timeout
440
+ });
350
441
  return result.prompts;
351
442
  }
352
443
  /**
@@ -354,7 +445,10 @@ var MCPClient = class {
354
445
  */
355
446
  async getPrompt(name, args) {
356
447
  this.ensureConnected();
357
- return await this.sendRequest("prompts/get", { name, arguments: args });
448
+ return await this.client.getPrompt(
449
+ { name, arguments: args },
450
+ { timeout: this.timeout }
451
+ );
358
452
  }
359
453
  // ========================================================================
360
454
  // Roots Operations
@@ -371,49 +465,79 @@ var MCPClient = class {
371
465
  async setRoots(roots) {
372
466
  this.log("debug", `Updating roots to ${roots.length} entries`);
373
467
  this._roots = [...roots];
374
- if (this.isConnected) {
375
- await this.sendNotification("notifications/roots/list_changed", {});
468
+ if (this.isConnected && this.client) {
469
+ try {
470
+ await this.client.sendRootsListChanged();
471
+ } catch (error) {
472
+ this.log("debug", `Failed to send roots/list_changed: ${error}`);
473
+ }
376
474
  }
377
475
  }
378
476
  // ========================================================================
379
477
  // Handler Registration
478
+ //
479
+ // Setters STORE the host handler (and, for notifications, the SDK handler
480
+ // registered in `registerServerHandlers` delegates to the stored callback).
481
+ // This is the host-overridable layer at the core of bug #13.
380
482
  // ========================================================================
381
483
  /**
382
484
  * Set a handler for resource updated notifications.
383
485
  */
384
486
  setResourceUpdatedHandler(handler) {
487
+ this.resourceUpdatedHandler = handler;
385
488
  this.log("debug", "Resource updated handler registered");
386
489
  }
387
490
  /**
388
491
  * Set a handler for resource list changed notifications.
389
492
  */
390
493
  setResourceListChangedHandler(handler) {
494
+ this.resourceListChangedHandler = handler;
391
495
  this.log("debug", "Resource list changed handler registered");
392
496
  }
497
+ /**
498
+ * Set a handler for tool list changed notifications.
499
+ */
500
+ setToolListChangedHandler(handler) {
501
+ this.toolListChangedHandler = handler;
502
+ this.log("debug", "Tool list changed handler registered");
503
+ }
393
504
  /**
394
505
  * Set a handler for prompt list changed notifications.
395
506
  */
396
507
  setPromptListChangedHandler(handler) {
508
+ this.promptListChangedHandler = handler;
397
509
  this.log("debug", "Prompt list changed handler registered");
398
510
  }
399
511
  /**
400
- * Set a handler for elicitation requests.
512
+ * Set a handler for elicitation requests. When set, the server's
513
+ * `elicitation/create` REQUEST is answered with the host's decision; when
514
+ * unset the request is declined ({action:"cancel"}).
401
515
  */
402
516
  setElicitationHandler(handler) {
517
+ this.elicitationHandler = handler;
403
518
  this.log("debug", "Elicitation handler registered");
404
519
  }
520
+ /**
521
+ * Set a handler for sampling (`sampling/createMessage`) requests. When set,
522
+ * the server request is answered with the host's result; when unset the
523
+ * request is rejected with "Method not found".
524
+ */
525
+ setSamplingHandler(handler) {
526
+ this.samplingHandler = handler;
527
+ this.log("debug", "Sampling handler registered");
528
+ }
405
529
  /**
406
530
  * Set a handler for progress notifications.
407
531
  */
408
532
  setProgressHandler(handler) {
533
+ this.progressHandler = handler;
409
534
  this.log("debug", "Progress handler registered");
410
535
  }
411
536
  // ========================================================================
412
537
  // Private Methods
413
538
  // ========================================================================
414
539
  ensureConnected() {
415
- const isHttpTransport = "url" in this.config;
416
- if (!this.isConnected || !isHttpTransport && !this.process) {
540
+ if (!this.isConnected || !this.client) {
417
541
  throw new MCPError(
418
542
  "Not connected to MCP server",
419
543
  "NOT_CONNECTED" /* NOT_CONNECTED */,
@@ -422,258 +546,24 @@ var MCPClient = class {
422
546
  );
423
547
  }
424
548
  }
425
- async sendRequest(method, params) {
426
- if (method !== "initialize") {
427
- this.ensureConnected();
428
- }
429
- if ("url" in this.config) {
430
- return this.sendHttpRequest(method, params);
431
- }
432
- if (!this.process) {
433
- throw new MCPError(
434
- "Not connected to MCP server",
435
- "NOT_CONNECTED" /* NOT_CONNECTED */,
436
- void 0,
437
- { serverName: this.serverName }
438
- );
439
- }
440
- return new Promise((resolve, reject) => {
441
- const id = ++this.messageId;
442
- const timeout = setTimeout(() => {
443
- this.pendingRequests.delete(id);
444
- reject(
445
- new MCPError(
446
- `Request timeout: ${method}`,
447
- "TIMEOUT" /* TIMEOUT */,
448
- { method, id },
449
- { serverName: this.serverName }
450
- )
451
- );
452
- }, this.timeout);
453
- this.pendingRequests.set(id, {
454
- resolve,
455
- reject,
456
- timeout
457
- });
458
- const message = {
459
- jsonrpc: "2.0",
460
- id,
461
- method,
462
- params
463
- };
464
- this.log("debug", `Sending request: ${method} (id: ${id})`);
465
- try {
466
- const messageStr = JSON.stringify(message) + "\n";
467
- this.process.stdin.write(messageStr);
468
- } catch (error) {
469
- this.pendingRequests.delete(id);
470
- clearTimeout(timeout);
471
- reject(
472
- new MCPError(
473
- `Failed to send request: ${error}`,
474
- "TRANSPORT_ERROR" /* TRANSPORT_ERROR */,
475
- { error: String(error) },
476
- { serverName: this.serverName }
477
- )
478
- );
479
- }
480
- });
481
- }
482
- async sendHttpRequest(method, params) {
483
- const config = this.config;
484
- const id = ++this.messageId;
485
- const message = {
486
- jsonrpc: "2.0",
487
- id,
488
- method,
489
- params
490
- };
491
- this.log("debug", `Sending HTTP request: ${method} (id: ${id})`);
492
- const controller = new AbortController();
493
- const timeoutId = setTimeout(() => controller.abort(), this.timeout);
494
- try {
495
- const response = await fetch(config.url.toString(), {
496
- method: "POST",
497
- headers: {
498
- "Content-Type": "application/json",
499
- "Accept": "application/json, text/event-stream",
500
- ...config.headers
501
- },
502
- body: JSON.stringify(message),
503
- signal: controller.signal
549
+ /**
550
+ * Normalize an SDK/transport error into an MCPError. Session-expiry style
551
+ * failures (404 session / -32001) are surfaced as SESSION_ERROR so callers
552
+ * (the pool) can decide to reconnect; the SDK's StreamableHTTP transport
553
+ * already attempts a bounded reconnect re-using the session id before this.
554
+ */
555
+ wrapError(error) {
556
+ if (error instanceof MCPError) return error;
557
+ const message = error instanceof Error ? error.message : String(error);
558
+ const code = error.code;
559
+ if (code === -32001 || /\b404\b|session/i.test(message)) {
560
+ return new MCPError(message, "SESSION_ERROR" /* SESSION_ERROR */, { code }, {
561
+ serverName: this.serverName
504
562
  });
505
- clearTimeout(timeoutId);
506
- if (!response.ok) {
507
- const errorText = await response.text();
508
- let errorMessage = `HTTP error: ${response.status} ${response.statusText}`;
509
- try {
510
- const errorJson = JSON.parse(errorText);
511
- if (errorJson.error?.message) {
512
- errorMessage = errorJson.error.message;
513
- }
514
- } catch {
515
- if (errorText) {
516
- errorMessage = errorText;
517
- }
518
- }
519
- throw new MCPError(
520
- errorMessage,
521
- "TRANSPORT_ERROR" /* TRANSPORT_ERROR */,
522
- { status: response.status, body: errorText },
523
- { serverName: this.serverName }
524
- );
525
- }
526
- const responseText = await response.text();
527
- let result;
528
- if (responseText.startsWith("event:") || responseText.startsWith("data:")) {
529
- const lines = responseText.split("\n");
530
- let dataLine = "";
531
- for (const line of lines) {
532
- if (line.startsWith("data:")) {
533
- dataLine = line.slice(5).trim();
534
- break;
535
- }
536
- }
537
- if (dataLine) {
538
- result = JSON.parse(dataLine);
539
- } else {
540
- throw new MCPError(
541
- "Empty SSE response",
542
- "TRANSPORT_ERROR" /* TRANSPORT_ERROR */,
543
- { response: responseText },
544
- { serverName: this.serverName }
545
- );
546
- }
547
- } else {
548
- result = JSON.parse(responseText);
549
- }
550
- if (result.error) {
551
- throw new MCPError(
552
- result.error.message || "MCP error",
553
- "SERVER_ERROR" /* SERVER_ERROR */,
554
- result.error,
555
- { serverName: this.serverName }
556
- );
557
- }
558
- this.log("debug", `HTTP request ${id} succeeded`);
559
- return result.result;
560
- } catch (error) {
561
- clearTimeout(timeoutId);
562
- if (error instanceof MCPError) throw error;
563
- if (error instanceof Error && error.name === "AbortError") {
564
- throw new MCPError(
565
- `Request timeout: ${method}`,
566
- "TIMEOUT" /* TIMEOUT */,
567
- { method, id },
568
- { serverName: this.serverName }
569
- );
570
- }
571
- throw new MCPError(
572
- `HTTP request failed: ${error}`,
573
- "TRANSPORT_ERROR" /* TRANSPORT_ERROR */,
574
- { error: String(error) },
575
- { serverName: this.serverName }
576
- );
577
- }
578
- }
579
- async sendNotification(method, params) {
580
- const message = {
581
- jsonrpc: "2.0",
582
- method,
583
- params
584
- };
585
- this.log("debug", `Sending notification: ${method}`);
586
- if ("url" in this.config) {
587
- const config = this.config;
588
- try {
589
- await fetch(config.url.toString(), {
590
- method: "POST",
591
- headers: {
592
- "Content-Type": "application/json",
593
- ...config.headers
594
- },
595
- body: JSON.stringify(message)
596
- });
597
- } catch (error) {
598
- this.log("error", `Failed to send HTTP notification: ${error}`);
599
- }
600
- return;
601
- }
602
- if (!this.process) {
603
- throw new MCPError(
604
- "Not connected to MCP server",
605
- "NOT_CONNECTED" /* NOT_CONNECTED */,
606
- void 0,
607
- { serverName: this.serverName }
608
- );
609
- }
610
- try {
611
- const messageStr = JSON.stringify(message) + "\n";
612
- this.process.stdin.write(messageStr);
613
- } catch (error) {
614
- this.log("error", `Failed to send notification: ${error}`);
615
- }
616
- }
617
- processBuffer() {
618
- const lines = this.buffer.split("\n");
619
- this.buffer = lines.pop() || "";
620
- for (const line of lines) {
621
- const trimmed = line.trim();
622
- if (!trimmed) continue;
623
- try {
624
- const message = JSON.parse(trimmed);
625
- this.handleMessage(message);
626
- } catch (error) {
627
- this.log("warning", `Failed to parse message: ${trimmed}`);
628
- }
629
- }
630
- }
631
- handleMessage(message) {
632
- if (message.id !== void 0 && this.pendingRequests.has(message.id)) {
633
- const { resolve, reject, timeout } = this.pendingRequests.get(message.id);
634
- this.pendingRequests.delete(message.id);
635
- clearTimeout(timeout);
636
- if (message.error) {
637
- this.log("debug", `Request ${message.id} failed: ${message.error.message}`);
638
- reject(
639
- new MCPError(
640
- message.error.message || "Unknown error",
641
- "SERVER_ERROR" /* SERVER_ERROR */,
642
- message.error,
643
- { serverName: this.serverName }
644
- )
645
- );
646
- } else {
647
- this.log("debug", `Request ${message.id} succeeded`);
648
- resolve(message.result);
649
- }
650
- }
651
- if (message.method) {
652
- this.handleNotification(message.method, message.params);
653
- }
654
- }
655
- handleNotification(method, params) {
656
- this.log("debug", `Received notification: ${method}`);
657
- switch (method) {
658
- case "notifications/message":
659
- if (this.enableServerLogs && params) {
660
- const { level, ...rest } = params;
661
- this.log(level || "info", "[SERVER]", rest);
662
- }
663
- break;
664
- case "notifications/progress":
665
- this.log("debug", `Progress: ${JSON.stringify(params)}`);
666
- break;
667
- case "notifications/resources/updated":
668
- this.log("debug", `Resource updated: ${JSON.stringify(params)}`);
669
- break;
670
- case "notifications/resources/list_changed":
671
- this.log("debug", "Resource list changed");
672
- break;
673
- case "notifications/prompts/list_changed":
674
- this.log("debug", "Prompt list changed");
675
- break;
676
563
  }
564
+ return new MCPError(message, "SERVER_ERROR" /* SERVER_ERROR */, { code }, {
565
+ serverName: this.serverName
566
+ });
677
567
  }
678
568
  log(level, message, details) {
679
569
  const msg = `[${this.serverName}] ${message}`;
@@ -705,6 +595,22 @@ var MCPClient = class {
705
595
  }
706
596
  }
707
597
  };
598
+ function mapToSdkElicitResult(result) {
599
+ if (result.action === "accept") {
600
+ return {
601
+ action: "accept",
602
+ content: result.content ?? {}
603
+ };
604
+ }
605
+ return { action: result.action };
606
+ }
607
+ function sanitizeEnv(env) {
608
+ const out = {};
609
+ for (const [k, v] of Object.entries(env)) {
610
+ if (typeof v === "string") out[k] = v;
611
+ }
612
+ return out;
613
+ }
708
614
 
709
615
  // src/facade/mcp-core/client-pool.ts
710
616
  var MCPClientPool = class {
@@ -906,14 +812,14 @@ var MCPClientPool = class {
906
812
  };
907
813
 
908
814
  // src/facade/mcp-core/config.ts
909
- import { existsSync, readFileSync, mkdirSync } from "fs";
815
+ import { existsSync, readFileSync, mkdirSync, statSync, writeFileSync } from "fs";
910
816
  import { join, dirname } from "path";
911
817
  import { homedir } from "os";
912
818
  function loadMCPConfig(configPathOrCwd = process.cwd()) {
913
819
  const configs = [];
914
820
  if (existsSync(configPathOrCwd)) {
915
821
  try {
916
- const stats = __require("fs").statSync(configPathOrCwd);
822
+ const stats = statSync(configPathOrCwd);
917
823
  if (stats.isFile()) {
918
824
  const fileConfigs = parseConfigFile(configPathOrCwd);
919
825
  configs.push(...fileConfigs);
@@ -991,7 +897,8 @@ function parseConfigFile(path) {
991
897
  name: server.name,
992
898
  config: {
993
899
  url: new URL(server.url),
994
- headers: server.headers
900
+ headers: server.headers,
901
+ ...server.transport ? { transport: server.transport } : {}
995
902
  },
996
903
  timeout: server.timeout
997
904
  };
@@ -1041,7 +948,7 @@ function saveConfig(path, config) {
1041
948
  mkdirSync(dir, { recursive: true });
1042
949
  }
1043
950
  const content = JSON.stringify(config, null, 2);
1044
- __require("fs").writeFileSync(path, content, "utf-8");
951
+ writeFileSync(path, content, "utf-8");
1045
952
  }
1046
953
  function saveUserConfig(config) {
1047
954
  saveConfig(getUserConfigPath(), config);