agents 0.17.3 → 0.18.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 (47) hide show
  1. package/dist/{agent-tool-types-CNyE1iz_.d.ts → agent-tool-types-BNUGGBzQ.d.ts} +191 -61
  2. package/dist/agent-tool-types.d.ts +1 -1
  3. package/dist/{agent-tools-BeOheFBK.d.ts → agent-tools-BFbzVLFc.d.ts} +2 -2
  4. package/dist/agent-tools.d.ts +1 -1
  5. package/dist/chat/index.d.ts +29 -9
  6. package/dist/chat/index.js +31 -41
  7. package/dist/chat/index.js.map +1 -1
  8. package/dist/chat/react.d.ts +25 -1
  9. package/dist/chat/react.js +249 -93
  10. package/dist/chat/react.js.map +1 -1
  11. package/dist/chat-sdk/index.d.ts +1 -1
  12. package/dist/{client-BZ-B3NhC.js → client-CcjiFpTf.js} +352 -81
  13. package/dist/client-CcjiFpTf.js.map +1 -0
  14. package/dist/client.d.ts +1 -1
  15. package/dist/cloudflare-BldFV0Pa.js +117 -0
  16. package/dist/cloudflare-BldFV0Pa.js.map +1 -0
  17. package/dist/index.d.ts +13 -11
  18. package/dist/index.js +153 -48
  19. package/dist/index.js.map +1 -1
  20. package/dist/mcp/client.d.ts +18 -14
  21. package/dist/mcp/client.js +1 -1
  22. package/dist/mcp/index.d.ts +38 -30
  23. package/dist/mcp/index.js +1 -1
  24. package/dist/mcp/index.js.map +1 -1
  25. package/dist/observability/ai/index.d.ts +155 -0
  26. package/dist/observability/ai/index.js +1845 -0
  27. package/dist/observability/ai/index.js.map +1 -0
  28. package/dist/react.d.ts +1 -1
  29. package/dist/{retries-CvHJwSuh.d.ts → retries-CAvxtG9d.d.ts} +16 -7
  30. package/dist/retries.d.ts +8 -6
  31. package/dist/retries.js +20 -2
  32. package/dist/retries.js.map +1 -1
  33. package/dist/serializable.d.ts +1 -1
  34. package/dist/sub-routing.d.ts +6 -6
  35. package/dist/vite.d.ts +4 -4
  36. package/dist/vite.js +4 -2
  37. package/dist/vite.js.map +1 -1
  38. package/dist/{wire-types-nflOzNuU.js → wire-types-CU9rLoeS.js} +33 -2
  39. package/dist/wire-types-CU9rLoeS.js.map +1 -0
  40. package/dist/workflows.d.ts +1 -1
  41. package/docs/agent-class.md +9 -1
  42. package/docs/configuration.md +20 -0
  43. package/docs/mcp-client.md +52 -4
  44. package/docs/observability.md +282 -0
  45. package/package.json +7 -2
  46. package/dist/client-BZ-B3NhC.js.map +0 -1
  47. package/dist/wire-types-nflOzNuU.js.map +0 -1
@@ -2,11 +2,11 @@ import { tryN } from "./retries.js";
2
2
  import { DurableObjectOAuthClientProvider } from "./mcp/do-oauth-client-provider.js";
3
3
  import { nanoid } from "nanoid";
4
4
  import { getServerByName } from "partyserver";
5
- import { CfWorkerJsonSchemaValidator } from "@modelcontextprotocol/sdk/validation/cfworker-provider.js";
6
5
  import { z } from "zod";
7
6
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
7
+ import { CfWorkerJsonSchemaValidator } from "@modelcontextprotocol/sdk/validation/cfworker-provider.js";
8
8
  import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
9
- import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
9
+ import { StreamableHTTPClientTransport, StreamableHTTPError } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
10
10
  import { ElicitRequestSchema, JSONRPCMessageSchema, PromptListChangedNotificationSchema, ResourceListChangedNotificationSchema, ToolListChangedNotificationSchema, isJSONRPCErrorResponse, isJSONRPCResultResponse } from "@modelcontextprotocol/sdk/types.js";
11
11
  //#region src/core/events.ts
12
12
  function toDisposable(fn) {
@@ -282,6 +282,7 @@ var RPCServerTransport = class {
282
282
  };
283
283
  //#endregion
284
284
  //#region src/mcp/client-connection.ts
285
+ const defaultClientOptions = { jsonSchemaValidator: new CfWorkerJsonSchemaValidator() };
285
286
  /**
286
287
  * Connection state machine for MCP client connections.
287
288
  *
@@ -304,12 +305,21 @@ const MCPConnectionState = {
304
305
  /** Connection failed at some point */
305
306
  FAILED: "failed"
306
307
  };
308
+ /** Derive the elicitation capability to advertise from the handler keys. */
309
+ function elicitationCapabilitiesFromHandlers(handlers) {
310
+ if (!handlers) return void 0;
311
+ const elicitation = {};
312
+ if (handlers.form) elicitation.form = {};
313
+ if (handlers.url) elicitation.url = {};
314
+ return elicitation.form || elicitation.url ? elicitation : void 0;
315
+ }
307
316
  var MCPClientConnection = class {
308
- constructor(url, info, options = {
317
+ constructor(url, _info, options = {
309
318
  client: {},
310
319
  transport: {}
311
320
  }) {
312
321
  this.url = url;
322
+ this._info = _info;
313
323
  this.options = options;
314
324
  this.connectionState = MCPConnectionState.CONNECTING;
315
325
  this.connectionError = null;
@@ -320,14 +330,44 @@ var MCPClientConnection = class {
320
330
  this._probingCapabilities = false;
321
331
  this._onObservabilityEvent = new Emitter();
322
332
  this.onObservabilityEvent = this._onObservabilityEvent.event;
333
+ this._elicitationEnabled = false;
334
+ this.options = {
335
+ ...options,
336
+ client: {
337
+ ...defaultClientOptions,
338
+ ...options.client
339
+ }
340
+ };
341
+ this.client = this.createClient();
342
+ }
343
+ createClient() {
344
+ const seed = this.options.capabilitySeed;
345
+ const elicitation = this.options.client?.capabilities?.elicitation ?? elicitationCapabilitiesFromHandlers(this.options.elicitationHandlers) ?? seed?.elicitation;
346
+ this._elicitationEnabled = elicitation !== void 0;
323
347
  const clientOptions = {
324
- ...options.client,
348
+ ...this.options.client,
325
349
  capabilities: {
326
- ...options.client?.capabilities,
327
- elicitation: {}
350
+ ...seed,
351
+ ...this.options.client?.capabilities,
352
+ ...elicitation ? { elicitation } : {}
328
353
  }
329
354
  };
330
- this.client = new Client(info, clientOptions);
355
+ return new Client(this._info, clientOptions);
356
+ }
357
+ /**
358
+ * Configure the handler used for server-initiated elicitation requests.
359
+ *
360
+ * If the connection has not been initialized yet, rebuild the SDK client so
361
+ * handler-driven elicitation capabilities are reflected in the initial
362
+ * handshake. A rebuild (rather than `Client.registerCapabilities`) is
363
+ * required because SDK capability registration is merge-only — it cannot
364
+ * un-advertise a mode when handlers are cleared before connecting. Active
365
+ * connections keep their negotiated capabilities until they reconnect.
366
+ */
367
+ configureElicitationHandlers(handlers) {
368
+ this.options.elicitationHandlers = handlers;
369
+ this.options.capabilitySeed = void 0;
370
+ if (!this.client.transport) this.client = this.createClient();
331
371
  }
332
372
  /**
333
373
  * Initialize a client connection, if authentication is required, the connection will be in the AUTHENTICATING state
@@ -343,11 +383,12 @@ var MCPClientConnection = class {
343
383
  try {
344
384
  await this.client.close();
345
385
  } catch {}
386
+ this.client = this.createClient();
346
387
  }
347
388
  const res = await this.tryConnect(transportType);
348
389
  this.connectionState = res.state;
349
390
  if (res.state === MCPConnectionState.CONNECTED && res.transport) {
350
- this.client.setRequestHandler(ElicitRequestSchema, async (request) => {
391
+ if (this._elicitationEnabled) this.client.setRequestHandler(ElicitRequestSchema, async (request) => {
351
392
  return await this.handleElicitationRequest(request);
352
393
  });
353
394
  this.lastConnectedTransport = res.transport;
@@ -509,6 +550,7 @@ var MCPClientConnection = class {
509
550
  });
510
551
  return {
511
552
  success: false,
553
+ reason: "error",
512
554
  error: `Discovery skipped - connection in ${this.connectionState} state`
513
555
  };
514
556
  }
@@ -546,9 +588,11 @@ var MCPClientConnection = class {
546
588
  } catch (e) {
547
589
  if (timeoutId !== void 0) clearTimeout(timeoutId);
548
590
  this.connectionState = MCPConnectionState.CONNECTED;
591
+ const error = e instanceof Error ? e.message : String(e);
549
592
  return {
550
593
  success: false,
551
- error: e instanceof Error ? e.message : String(e)
594
+ reason: this._probingCapabilities && e instanceof StreamableHTTPError && e.code === 404 ? "stale-session" : "error",
595
+ error
552
596
  };
553
597
  } finally {
554
598
  this._discoveryAbortController = void 0;
@@ -634,11 +678,19 @@ var MCPClientConnection = class {
634
678
  return templatesAgg;
635
679
  }
636
680
  /**
637
- * Handle elicitation request from server
638
- * Automatically uses the Agent's built-in elicitation handling if available
681
+ * Handle elicitation request from server.
682
+ *
683
+ * Delegates to the `elicitationHandlers` connection option when provided.
684
+ *
685
+ * @deprecated Overriding or instance-patching this method directly is
686
+ * deprecated — pass the `elicitationHandlers` connection option instead.
639
687
  */
640
- async handleElicitationRequest(_request) {
641
- throw new Error("Elicitation handler must be implemented for your platform. Override handleElicitationRequest method.");
688
+ async handleElicitationRequest(request) {
689
+ const mode = request.params.mode === "url" ? "url" : "form";
690
+ const handler = this.options.elicitationHandlers?.[mode];
691
+ if (handler) return handler(request);
692
+ if (this.options.elicitationHandlers) throw new Error(`No MCP ${mode}-mode elicitation handler configured for this connection.`);
693
+ throw new Error("Elicitation handler must be implemented for your platform. Provide the MCPClientConnection elicitationHandlers option, or register handlers through the MCP client manager before connecting.");
642
694
  }
643
695
  isResumedStreamableHttpSession() {
644
696
  return this._transport instanceof StreamableHTTPClientTransport && typeof this._transport.sessionId === "string";
@@ -646,6 +698,10 @@ var MCPClientConnection = class {
646
698
  get sessionId() {
647
699
  if (this._transport instanceof StreamableHTTPClientTransport) return this._transport.sessionId;
648
700
  }
701
+ /** @internal Clear a restored session before reconnecting. */
702
+ clearResumedSession() {
703
+ if ("sessionId" in this.options.transport) delete this.options.transport.sessionId;
704
+ }
649
705
  getTransportName(transport) {
650
706
  if (transport instanceof StreamableHTTPClientTransport) return "streamable-http";
651
707
  if (transport instanceof SSEClientTransport) return "sse";
@@ -764,7 +820,6 @@ var MCPClientConnection = class {
764
820
  };
765
821
  //#endregion
766
822
  //#region src/mcp/client.ts
767
- const defaultClientOptions = { jsonSchemaValidator: new CfWorkerJsonSchemaValidator() };
768
823
  /** Maximum length of a normalized MCP server id. */
769
824
  const MCP_SERVER_ID_MAX_LENGTH = 64;
770
825
  /**
@@ -887,6 +942,13 @@ function isBlockedUrl(url) {
887
942
  }
888
943
  return false;
889
944
  }
945
+ /** Converts a resolved connection failure into a retry signal. */
946
+ var ConnectRetryError = class extends Error {
947
+ constructor(result) {
948
+ super("MCP connection attempt failed");
949
+ this.result = result;
950
+ }
951
+ };
890
952
  /**
891
953
  * Utility class that aggregates multiple MCP clients into one
892
954
  */
@@ -900,6 +962,7 @@ var MCPClientManager = class {
900
962
  this._name = _name;
901
963
  this._version = _version;
902
964
  this.mcpConnections = {};
965
+ this._aiToolSchemas = /* @__PURE__ */ new WeakMap();
903
966
  this._didWarnAboutUnstableGetAITools = false;
904
967
  this._connectionDisposables = /* @__PURE__ */ new Map();
905
968
  this._isRestored = false;
@@ -912,6 +975,21 @@ var MCPClientManager = class {
912
975
  this._storage = options.storage;
913
976
  this._createAuthProviderFn = options.createAuthProvider;
914
977
  }
978
+ /**
979
+ * Scope the manager-level elicitation handler to a single connection.
980
+ * Returns undefined when no handler is configured so the connection keeps
981
+ * its default throwing behavior.
982
+ */
983
+ scopedElicitationHandlers(serverId) {
984
+ const handlers = this._elicitationHandlers;
985
+ if (!handlers) return void 0;
986
+ const form = handlers.form;
987
+ const url = handlers.url;
988
+ return {
989
+ form: form ? (request) => form(request, serverId) : void 0,
990
+ url: url ? (request) => url(request, serverId) : void 0
991
+ };
992
+ }
915
993
  sql(query, ...bindings) {
916
994
  return [...this._storage.sql.exec(query, ...bindings)];
917
995
  }
@@ -944,6 +1022,11 @@ var MCPClientManager = class {
944
1022
  */
945
1023
  async migrateServerId(oldId, newId, clientName) {
946
1024
  if (oldId === newId) return;
1025
+ while (true) {
1026
+ const pending = this._pendingConnections.get(oldId);
1027
+ if (!pending) break;
1028
+ await pending.catch(() => {});
1029
+ }
947
1030
  if (this.sql("SELECT id FROM cf_agents_mcp_servers WHERE id = ?", oldId).length === 0) {
948
1031
  this._renameInMemoryConnection(oldId, newId);
949
1032
  return;
@@ -979,6 +1062,8 @@ var MCPClientManager = class {
979
1062
  delete this.mcpConnections[oldId];
980
1063
  const authProvider = conn.options.transport.authProvider;
981
1064
  if (authProvider) authProvider.serverId = newId;
1065
+ const scoped = this.scopedElicitationHandlers(newId);
1066
+ if (scoped) conn.configureElicitationHandlers(scoped);
982
1067
  }
983
1068
  const disposables = this._connectionDisposables.get(oldId);
984
1069
  if (disposables) {
@@ -1007,12 +1092,37 @@ var MCPClientManager = class {
1007
1092
  }));
1008
1093
  }
1009
1094
  /**
1010
- * Get the retry options for a server from stored server_options
1095
+ * Get the parsed server_options for a stored server, if any.
1011
1096
  */
1012
- getServerRetryOptions(serverId) {
1097
+ getStoredServerOptions(serverId) {
1013
1098
  const rows = this.sql("SELECT server_options FROM cf_agents_mcp_servers WHERE id = ?", serverId);
1014
1099
  if (!rows.length || !rows[0].server_options) return void 0;
1015
- return JSON.parse(rows[0].server_options).retry;
1100
+ return JSON.parse(rows[0].server_options);
1101
+ }
1102
+ /**
1103
+ * Clear the capabilities persisted on a stored server row. Called once a
1104
+ * seeded connection's handshake completes (see `createConnection`): the
1105
+ * stamp is valid for one successful restore — sessions that configure
1106
+ * handlers re-stamp every row, so a deploy that stops configuring them
1107
+ * stops advertising stale modes after its first connected wake instead of
1108
+ * forever, while wakes that never handshake don't burn the stamp.
1109
+ */
1110
+ clearStoredCapabilities(serverId) {
1111
+ const row = this.sql("SELECT id, name, server_url, client_id, auth_url, callback_url, server_options FROM cf_agents_mcp_servers WHERE id = ?", serverId)[0];
1112
+ if (!row?.server_options) return;
1113
+ const options = JSON.parse(row.server_options);
1114
+ if (!options.capabilities) return;
1115
+ options.capabilities = void 0;
1116
+ this.saveServerToStorage({
1117
+ ...row,
1118
+ server_options: JSON.stringify(options)
1119
+ });
1120
+ }
1121
+ /**
1122
+ * Get the retry options for a server from stored server_options
1123
+ */
1124
+ getServerRetryOptions(serverId) {
1125
+ return this.getStoredServerOptions(serverId)?.retry;
1016
1126
  }
1017
1127
  clearServerAuthUrl(serverId) {
1018
1128
  this.sql("UPDATE cf_agents_mcp_servers SET auth_url = NULL WHERE id = ?", serverId);
@@ -1063,6 +1173,22 @@ var MCPClientManager = class {
1063
1173
  if (authProvider.runWithCodeVerifierState) return authProvider.runWithCodeVerifierState(state, callback);
1064
1174
  return callback();
1065
1175
  }
1176
+ async hasRedeemableOAuthState(serverId, authProvider, state) {
1177
+ authProvider.serverId = serverId;
1178
+ try {
1179
+ return (await authProvider.checkState(state)).valid;
1180
+ } catch {
1181
+ return false;
1182
+ }
1183
+ }
1184
+ ignoreUnverifiedCallback(serverId, error) {
1185
+ console.warn(`[MCPClientManager] Ignoring OAuth callback with unverified state for server "${serverId}": ${error}`);
1186
+ return {
1187
+ serverId,
1188
+ authSuccess: false,
1189
+ authError: error
1190
+ };
1191
+ }
1066
1192
  async consumeStaleOAuthState(serverId, authProvider, state) {
1067
1193
  try {
1068
1194
  const stateValidation = await authProvider.checkState(state);
@@ -1129,7 +1255,8 @@ var MCPClientManager = class {
1129
1255
  callback_url: "",
1130
1256
  server_options: JSON.stringify({
1131
1257
  bindingName,
1132
- props
1258
+ props,
1259
+ capabilities: this.advertisedHandlerCapabilities()
1133
1260
  })
1134
1261
  });
1135
1262
  }
@@ -1166,6 +1293,7 @@ var MCPClientManager = class {
1166
1293
  }
1167
1294
  }
1168
1295
  const parsedOptions = server.server_options ? JSON.parse(server.server_options) : null;
1296
+ if (parsedOptions?.client) delete parsedOptions.client.jsonSchemaValidator;
1169
1297
  let authProvider;
1170
1298
  if (server.callback_url) {
1171
1299
  authProvider = this._createAuthProviderFn ? this._createAuthProviderFn(server.callback_url) : this.createAuthProvider(server.id, server.callback_url, clientName, server.client_id ?? void 0);
@@ -1223,16 +1351,30 @@ var MCPClientManager = class {
1223
1351
  clearTimeout(timerId);
1224
1352
  } else await settled;
1225
1353
  }
1354
+ async _connectWithRetry(serverId, retry) {
1355
+ const maxAttempts = retry?.maxAttempts ?? 3;
1356
+ const baseDelayMs = retry?.baseDelayMs ?? 500;
1357
+ const maxDelayMs = retry?.maxDelayMs ?? 5e3;
1358
+ try {
1359
+ return await tryN(maxAttempts, async () => {
1360
+ const result = await this.connectToServer(serverId);
1361
+ if (result.state === MCPConnectionState.FAILED) throw new ConnectRetryError(result);
1362
+ return result;
1363
+ }, {
1364
+ baseDelayMs,
1365
+ maxDelayMs
1366
+ });
1367
+ } catch (error) {
1368
+ if (error instanceof ConnectRetryError) return error.result;
1369
+ throw error;
1370
+ }
1371
+ }
1226
1372
  /**
1227
1373
  * Internal method to restore a single server connection and discovery
1228
1374
  */
1229
1375
  async _restoreServer(serverId, retry) {
1230
- const maxAttempts = retry?.maxAttempts ?? 3;
1231
- if ((await tryN(maxAttempts, async () => this.connectToServer(serverId), {
1232
- baseDelayMs: retry?.baseDelayMs ?? 500,
1233
- maxDelayMs: retry?.maxDelayMs ?? 5e3
1234
- }).catch((error) => {
1235
- console.error(`Error connecting to ${serverId} after ${maxAttempts} attempts:`, error);
1376
+ if ((await this._connectWithRetry(serverId, retry).catch((error) => {
1377
+ console.error(`Error connecting to ${serverId}:`, error);
1236
1378
  return null;
1237
1379
  }))?.state === MCPConnectionState.CONNECTED) {
1238
1380
  const discoverResult = await this.discoverIfConnected(serverId);
@@ -1257,24 +1399,16 @@ var MCPClientManager = class {
1257
1399
  }
1258
1400
  if (isBlockedUrl(url)) throw new Error(`Blocked URL: ${url} — MCP client connections to private/internal addresses are not allowed`);
1259
1401
  if (!options.reconnect?.oauthCode || !this.mcpConnections[id]) {
1260
- const normalizedTransport = {
1261
- ...options.transport,
1262
- type: options.transport?.type ?? "auto"
1263
- };
1264
- this.mcpConnections[id] = new MCPClientConnection(new URL(url), {
1265
- name: this._name,
1266
- version: this._version
1267
- }, {
1268
- client: options.client ?? {},
1269
- transport: normalizedTransport
1402
+ const replaced = this.mcpConnections[id];
1403
+ if (replaced) {
1404
+ delete this.mcpConnections[id];
1405
+ await replaced.close().catch(() => {});
1406
+ this.updateStoredSessionId(id, void 0);
1407
+ }
1408
+ this.createConnection(id, url, {
1409
+ client: options.client,
1410
+ transport: options.transport ?? {}
1270
1411
  });
1271
- const store = new DisposableStore();
1272
- const existing = this._connectionDisposables.get(id);
1273
- if (existing) existing.dispose();
1274
- this._connectionDisposables.set(id, store);
1275
- store.add(this.mcpConnections[id].onObservabilityEvent((event) => {
1276
- this._onObservabilityEvent.fire(event);
1277
- }));
1278
1412
  }
1279
1413
  await this.mcpConnections[id].init();
1280
1414
  if (options.reconnect?.oauthCode) try {
@@ -1326,15 +1460,15 @@ var MCPClientManager = class {
1326
1460
  ...options.transport,
1327
1461
  type: options.transport?.type ?? "auto"
1328
1462
  };
1463
+ const capabilitySeed = this.getStoredServerOptions(id)?.capabilities;
1329
1464
  this.mcpConnections[id] = new MCPClientConnection(new URL(url), {
1330
1465
  name: this._name,
1331
1466
  version: this._version
1332
1467
  }, {
1333
- client: {
1334
- ...defaultClientOptions,
1335
- ...options.client
1336
- },
1337
- transport: normalizedTransport
1468
+ client: options.client ?? {},
1469
+ transport: normalizedTransport,
1470
+ elicitationHandlers: this.scopedElicitationHandlers(id),
1471
+ capabilitySeed
1338
1472
  });
1339
1473
  const store = new DisposableStore();
1340
1474
  const existing = this._connectionDisposables.get(id);
@@ -1343,6 +1477,17 @@ var MCPClientManager = class {
1343
1477
  store.add(this.mcpConnections[id].onObservabilityEvent((event) => {
1344
1478
  this._onObservabilityEvent.fire(event);
1345
1479
  }));
1480
+ if (capabilitySeed) {
1481
+ const conn = this.mcpConnections[id];
1482
+ const seedClear = conn.onObservabilityEvent((event) => {
1483
+ if (event.type !== "mcp:client:connect" || event.payload.state !== MCPConnectionState.CONNECTED) return;
1484
+ seedClear.dispose();
1485
+ if (this._elicitationHandlers || !conn.options.capabilitySeed) return;
1486
+ const currentId = Object.keys(this.mcpConnections).find((key) => this.mcpConnections[key] === conn);
1487
+ if (currentId) this.clearStoredCapabilities(currentId);
1488
+ });
1489
+ store.add(seedClear);
1490
+ }
1346
1491
  return this.mcpConnections[id];
1347
1492
  }
1348
1493
  /**
@@ -1363,6 +1508,7 @@ var MCPClientManager = class {
1363
1508
  }
1364
1509
  });
1365
1510
  const { authProvider: _, ...transportWithoutAuth } = options.transport ?? {};
1511
+ const { jsonSchemaValidator: _validator, ...serializableClient } = options.client ?? {};
1366
1512
  this.saveServerToStorage({
1367
1513
  id,
1368
1514
  name: options.name,
@@ -1371,9 +1517,10 @@ var MCPClientManager = class {
1371
1517
  client_id: options.clientId ?? null,
1372
1518
  auth_url: options.authUrl ?? null,
1373
1519
  server_options: JSON.stringify({
1374
- client: options.client,
1520
+ client: serializableClient,
1375
1521
  transport: transportWithoutAuth,
1376
- retry: options.retry
1522
+ retry: options.retry,
1523
+ capabilities: this.advertisedHandlerCapabilities()
1377
1524
  })
1378
1525
  });
1379
1526
  this._onServerStateChanged.fire();
@@ -1521,6 +1668,8 @@ var MCPClientManager = class {
1521
1668
  }
1522
1669
  return this.oauthCallbackSuccess(validation.serverId, conn);
1523
1670
  }
1671
+ const authProvider = conn.options.transport.authProvider;
1672
+ if (validation.state && authProvider && !await this.hasRedeemableOAuthState(validation.serverId, authProvider, validation.state)) return this.ignoreUnverifiedCallback(validation.serverId, validation.error);
1524
1673
  return this.failConnection(validation.serverId, validation.error);
1525
1674
  }
1526
1675
  return {
@@ -1541,13 +1690,13 @@ var MCPClientManager = class {
1541
1690
  await this.consumeStaleOAuthState(serverId, authProvider, state);
1542
1691
  return this.oauthCallbackSuccess(serverId, conn);
1543
1692
  }
1544
- throw new Error(stateValidation.error || "Invalid state");
1693
+ return this.ignoreUnverifiedCallback(serverId, stateValidation.error || "Invalid state");
1545
1694
  }
1546
1695
  if (this.isAuthAcceptedConnection(conn)) {
1547
1696
  await this.consumeStaleOAuthState(serverId, authProvider, state);
1548
1697
  return this.oauthCallbackSuccess(serverId, conn);
1549
1698
  }
1550
- if (conn.connectionState !== MCPConnectionState.AUTHENTICATING) throw new Error(`Failed to authenticate: the client is in "${conn.connectionState}" state, expected "authenticating"`);
1699
+ if (conn.connectionState !== MCPConnectionState.AUTHENTICATING && conn.connectionState !== MCPConnectionState.FAILED) throw new Error(`Failed to authenticate from "${conn.connectionState}" state`);
1551
1700
  conn.connectionState = MCPConnectionState.CONNECTING;
1552
1701
  await authProvider.consumeState(state);
1553
1702
  await this.completeAuthorizationAndCleanupVerifier(serverId, conn, authProvider, state, code);
@@ -1584,11 +1733,41 @@ var MCPClientManager = class {
1584
1733
  return;
1585
1734
  }
1586
1735
  const result = await conn.discover(options);
1736
+ if (!result.success && result.reason === "stale-session") return this._recoverStaleSession(conn, serverId, options);
1587
1737
  this._onServerStateChanged.fire();
1588
- return {
1589
- ...result,
1738
+ return this._toDiscoverResult(conn, result);
1739
+ }
1740
+ _toDiscoverResult(conn, result) {
1741
+ return result.success ? {
1742
+ success: true,
1743
+ state: conn.connectionState
1744
+ } : {
1745
+ success: false,
1746
+ error: result.error,
1747
+ state: conn.connectionState
1748
+ };
1749
+ }
1750
+ async _recoverStaleSession(conn, serverId, options) {
1751
+ conn.clearResumedSession();
1752
+ this.updateStoredSessionId(serverId, void 0);
1753
+ let connectResult;
1754
+ try {
1755
+ connectResult = await this.connectToServer(serverId);
1756
+ } catch (error) {
1757
+ return {
1758
+ success: false,
1759
+ error: toErrorMessage(error),
1760
+ state: conn.connectionState
1761
+ };
1762
+ }
1763
+ if (connectResult.state !== MCPConnectionState.CONNECTED) return {
1764
+ success: false,
1765
+ error: connectResult.state === MCPConnectionState.FAILED ? connectResult.error : `Connection in ${connectResult.state} state after session re-initialization`,
1590
1766
  state: conn.connectionState
1591
1767
  };
1768
+ const result = await conn.discover(options);
1769
+ this._onServerStateChanged.fire();
1770
+ return this._toDiscoverResult(conn, result);
1592
1771
  }
1593
1772
  /**
1594
1773
  * Establish connection in the background after OAuth completion.
@@ -1625,10 +1804,7 @@ var MCPClientManager = class {
1625
1804
  return;
1626
1805
  }
1627
1806
  const retry = this.getServerRetryOptions(serverId);
1628
- const connectResult = await tryN(retry?.maxAttempts ?? 3, async () => this.connectToServer(serverId), {
1629
- baseDelayMs: retry?.baseDelayMs ?? 500,
1630
- maxDelayMs: retry?.maxDelayMs ?? 5e3
1631
- });
1807
+ const connectResult = await this._connectWithRetry(serverId, retry);
1632
1808
  this._onServerStateChanged.fire();
1633
1809
  if (connectResult.state === MCPConnectionState.CONNECTED) await this.discoverIfConnected(serverId);
1634
1810
  this._onObservabilityEvent.fire({
@@ -1649,6 +1825,51 @@ var MCPClientManager = class {
1649
1825
  this._oauthCallbackConfig = config;
1650
1826
  }
1651
1827
  /**
1828
+ * Configure handling for server-initiated `elicitation/create` requests.
1829
+ *
1830
+ * The handler is held in memory only and applied to every MCP connection
1831
+ * created or restored by this manager. Call this before registering
1832
+ * connections when you want the initial MCP handshake to advertise
1833
+ * handler-driven form- and url-mode elicitation. Existing active connections
1834
+ * keep their negotiated capabilities until they reconnect.
1835
+ *
1836
+ * The advertised modes are persisted with each stored server, so
1837
+ * connections restored after hibernation re-advertise them at the handshake
1838
+ * even when this is called later in the wake-up (e.g. from onStart) — the
1839
+ * handlers attach to the live connections as soon as this runs.
1840
+ *
1841
+ * Pass undefined to clear the handler.
1842
+ *
1843
+ * @param handlers Elicitation handlers keyed by mode, each scoped with the server id that sent the request
1844
+ */
1845
+ configureElicitationHandlers(handlers) {
1846
+ this._elicitationHandlers = handlers && (handlers.form || handlers.url) ? handlers : void 0;
1847
+ this.persistAdvertisedCapabilities();
1848
+ for (const [id, connection] of Object.entries(this.mcpConnections)) connection.configureElicitationHandlers(this.scopedElicitationHandlers(id));
1849
+ }
1850
+ /** Client capabilities advertised from the currently configured handlers. */
1851
+ advertisedHandlerCapabilities() {
1852
+ const elicitation = elicitationCapabilitiesFromHandlers(this._elicitationHandlers);
1853
+ return elicitation ? { elicitation } : void 0;
1854
+ }
1855
+ /**
1856
+ * Record the handler-derived capabilities on every stored server row so a
1857
+ * restore after hibernation re-advertises them before the handlers
1858
+ * themselves are reconfigured.
1859
+ */
1860
+ persistAdvertisedCapabilities() {
1861
+ const capabilities = this.advertisedHandlerCapabilities();
1862
+ for (const server of this.getServersFromStorage()) {
1863
+ const options = server.server_options ? JSON.parse(server.server_options) : {};
1864
+ if (JSON.stringify(options.capabilities) === JSON.stringify(capabilities)) continue;
1865
+ options.capabilities = capabilities;
1866
+ this.saveServerToStorage({
1867
+ ...server,
1868
+ server_options: JSON.stringify(options)
1869
+ });
1870
+ }
1871
+ }
1872
+ /**
1652
1873
  * Get the current OAuth callback configuration
1653
1874
  * @returns The current OAuth callback configuration
1654
1875
  */
@@ -1663,37 +1884,87 @@ var MCPClientManager = class {
1663
1884
  return getNamespacedData(this.filterConnections(filter), "tools");
1664
1885
  }
1665
1886
  /**
1887
+ * Convert connected MCP tools for the AI SDK. Converted schemas are reused
1888
+ * while a live connection retains the same catalog array and schema-source
1889
+ * identities; tool records and execute closures are rebuilt on every call.
1890
+ *
1666
1891
  * @param filter - Optional filter to scope results to specific servers
1667
1892
  * @returns a set of tools that you can use with the AI SDK
1668
1893
  */
1669
1894
  getAITools(filter) {
1670
1895
  const connections = this.filterConnections(filter);
1671
- for (const [id, conn] of Object.entries(connections)) if (conn.connectionState !== MCPConnectionState.READY && conn.connectionState !== MCPConnectionState.AUTHENTICATING) console.warn(`[getAITools] WARNING: Reading tools from connection ${id} in state "${conn.connectionState}". Tools may not be loaded yet.`);
1672
1896
  const entries = [];
1673
- for (const tool of getNamespacedData(connections, "tools")) try {
1674
- const toolKey = `tool_${tool.serverId.replace(/-/g, "")}_${tool.name}`;
1675
- const title = tool.title ?? tool.annotations?.title;
1676
- entries.push([toolKey, {
1677
- description: tool.description,
1678
- title,
1679
- execute: async (args) => {
1680
- const result = await this.callTool({
1681
- arguments: args,
1682
- name: tool.name,
1683
- serverId: tool.serverId
1684
- });
1685
- if (result.isError) {
1686
- const textContent = result.content?.[0];
1687
- const message = textContent?.type === "text" && textContent.text ? textContent.text : "Tool call failed";
1688
- throw new Error(message);
1897
+ for (const [serverId, conn] of Object.entries(connections)) {
1898
+ if (conn.connectionState !== MCPConnectionState.READY && conn.connectionState !== MCPConnectionState.AUTHENTICATING) console.warn(`[getAITools] WARNING: Reading tools from connection ${serverId} in state "${conn.connectionState}". Tools may not be loaded yet.`);
1899
+ const catalog = conn.tools;
1900
+ let cache = this._aiToolSchemas.get(conn);
1901
+ if (!cache || cache.catalog !== catalog) {
1902
+ cache = {
1903
+ catalog,
1904
+ converted: []
1905
+ };
1906
+ this._aiToolSchemas.set(conn, cache);
1907
+ }
1908
+ for (const [index, tool] of catalog.entries()) {
1909
+ const toolName = tool.name;
1910
+ try {
1911
+ const sourceInputSchema = tool.inputSchema;
1912
+ const sourceOutputSchema = tool.outputSchema;
1913
+ let slot = cache.converted[index];
1914
+ if (!slot || slot.tool !== tool || slot.inputSchema !== sourceInputSchema || slot.outputSchema !== sourceOutputSchema) {
1915
+ try {
1916
+ slot = {
1917
+ status: "converted",
1918
+ tool,
1919
+ inputSchema: sourceInputSchema,
1920
+ outputSchema: sourceOutputSchema,
1921
+ converted: {
1922
+ inputSchema: sourceInputSchema ? z.fromJSONSchema(sourceInputSchema) : z.fromJSONSchema({ type: "object" }),
1923
+ outputSchema: sourceOutputSchema ? z.fromJSONSchema(sourceOutputSchema) : void 0
1924
+ }
1925
+ };
1926
+ } catch (error) {
1927
+ const errorText = String(error);
1928
+ cache.converted[index] = {
1929
+ status: "failed",
1930
+ tool,
1931
+ inputSchema: sourceInputSchema,
1932
+ outputSchema: sourceOutputSchema,
1933
+ error: errorText
1934
+ };
1935
+ console.warn(`[getAITools] Skipping tool "${toolName}" from "${serverId}": ${errorText}`);
1936
+ continue;
1937
+ }
1938
+ cache.converted[index] = slot;
1689
1939
  }
1690
- return result;
1691
- },
1692
- inputSchema: tool.inputSchema ? z.fromJSONSchema(tool.inputSchema) : z.fromJSONSchema({ type: "object" }),
1693
- outputSchema: tool.outputSchema ? z.fromJSONSchema(tool.outputSchema) : void 0
1694
- }]);
1695
- } catch (e) {
1696
- console.warn(`[getAITools] Skipping tool "${tool.name}" from "${tool.serverId}": ${e}`);
1940
+ if (slot.status === "failed") continue;
1941
+ const toolKey = `tool_${serverId.replace(/-/g, "")}_${toolName}`;
1942
+ const title = tool.title ?? tool.annotations?.title;
1943
+ const description = tool.description;
1944
+ entries.push([toolKey, {
1945
+ description,
1946
+ title,
1947
+ execute: async (args) => {
1948
+ const result = await this.callTool({
1949
+ arguments: args,
1950
+ name: toolName,
1951
+ serverId
1952
+ });
1953
+ if (result.isError) {
1954
+ const textContent = result.content?.[0];
1955
+ const message = textContent?.type === "text" && textContent.text ? textContent.text : "Tool call failed";
1956
+ throw new Error(message);
1957
+ }
1958
+ return result;
1959
+ },
1960
+ inputSchema: slot.converted.inputSchema,
1961
+ outputSchema: slot.converted.outputSchema
1962
+ }]);
1963
+ } catch (error) {
1964
+ console.warn(`[getAITools] Skipping tool "${toolName}" from "${serverId}": ${error}`);
1965
+ }
1966
+ }
1967
+ cache.converted.length = catalog.length;
1697
1968
  }
1698
1969
  return Object.fromEntries(entries);
1699
1970
  }
@@ -1845,4 +2116,4 @@ function getNamespacedData(mcpClients, type) {
1845
2116
  //#endregion
1846
2117
  export { MCPConnectionState as a, RPC_DO_PREFIX as c, normalizeServerId as i, DisposableStore as l, MCP_SERVER_ID_MAX_LENGTH as n, RPCClientTransport as o, getNamespacedData as r, RPCServerTransport as s, MCPClientManager as t };
1847
2118
 
1848
- //# sourceMappingURL=client-BZ-B3NhC.js.map
2119
+ //# sourceMappingURL=client-CcjiFpTf.js.map