agents 0.13.3 → 0.14.1

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 (83) hide show
  1. package/README.md +6 -4
  2. package/dist/{agent-tool-types-l98LCbBl.d.ts → agent-tool-types-BAJWu8s4.d.ts} +474 -117
  3. package/dist/agent-tool-types.d.ts +13 -11
  4. package/dist/{agent-tools-Bg5ilERh.d.ts → agent-tools-0R6KEert.d.ts} +2 -2
  5. package/dist/{agent-tools-BAdX1vdI.js → agent-tools-DYrkT-Kx.js} +46 -6
  6. package/dist/agent-tools-DYrkT-Kx.js.map +1 -0
  7. package/dist/agent-tools.d.ts +14 -20
  8. package/dist/agent-tools.js +10 -6
  9. package/dist/agent-tools.js.map +1 -1
  10. package/dist/browser/ai.d.ts +1 -1
  11. package/dist/browser/ai.js +1 -1
  12. package/dist/browser/index.d.ts +1 -1
  13. package/dist/browser/index.js +1 -1
  14. package/dist/browser/tanstack-ai.d.ts +1 -1
  15. package/dist/browser/tanstack-ai.js +1 -1
  16. package/dist/chat/index.d.ts +162 -19
  17. package/dist/chat/index.js +97 -13
  18. package/dist/chat/index.js.map +1 -1
  19. package/dist/chat-sdk/index.d.ts +5 -5
  20. package/dist/chat-sdk/index.js +2 -2
  21. package/dist/chat-sdk/index.js.map +1 -1
  22. package/dist/{classPrivateFieldGet2-Evpt0SEr.js → classPrivateFieldGet2-D_obpP6O.js} +5 -5
  23. package/dist/classPrivateMethodInitSpec-10iTYB7F.js +7 -0
  24. package/dist/{client-D1kFXo80.js → client-FUizKzj2.js} +299 -95
  25. package/dist/client-FUizKzj2.js.map +1 -0
  26. package/dist/client.d.ts +1 -1
  27. package/dist/{compaction-helpers-B-pG5J22.d.ts → compaction-helpers-BEUILPss.d.ts} +59 -33
  28. package/dist/{compaction-helpers-fJyf8j4m.js → compaction-helpers-iiKMr2TQ.js} +22 -3
  29. package/dist/compaction-helpers-iiKMr2TQ.js.map +1 -0
  30. package/dist/{do-oauth-client-provider-4OKQU9rT.d.ts → do-oauth-client-provider-D4ZwyBDu.d.ts} +21 -1
  31. package/dist/{email-J0GGS3sa.d.ts → email-CL27preh.d.ts} +1 -1
  32. package/dist/email.d.ts +2 -2
  33. package/dist/experimental/memory/session/index.d.ts +30 -25
  34. package/dist/experimental/memory/session/index.js +7 -2
  35. package/dist/experimental/memory/session/index.js.map +1 -1
  36. package/dist/experimental/memory/utils/index.d.ts +12 -10
  37. package/dist/experimental/memory/utils/index.js +2 -2
  38. package/dist/{index-DKey3P4s.d.ts → index-RJ4OxMOe.d.ts} +270 -1
  39. package/dist/index.d.ts +74 -67
  40. package/dist/index.js +485 -64
  41. package/dist/index.js.map +1 -1
  42. package/dist/{internal_context-BZrMS0B5.d.ts → internal_context-Dg4Cgjcu.d.ts} +1 -1
  43. package/dist/internal_context.d.ts +1 -1
  44. package/dist/mcp/client.d.ts +17 -13
  45. package/dist/mcp/client.js +2 -2
  46. package/dist/mcp/do-oauth-client-provider.d.ts +1 -1
  47. package/dist/mcp/do-oauth-client-provider.js +143 -17
  48. package/dist/mcp/do-oauth-client-provider.js.map +1 -1
  49. package/dist/mcp/index.d.ts +35 -27
  50. package/dist/mcp/index.js +402 -69
  51. package/dist/mcp/index.js.map +1 -1
  52. package/dist/observability/index.d.ts +1 -1
  53. package/dist/observability/index.js +15 -1
  54. package/dist/observability/index.js.map +1 -1
  55. package/dist/react.d.ts +3 -3
  56. package/dist/react.js +1 -1
  57. package/dist/{retries-BVdRl5ZE.d.ts → retries-CF_HKSlJ.d.ts} +1 -1
  58. package/dist/retries.d.ts +1 -1
  59. package/dist/serializable.d.ts +1 -1
  60. package/dist/{shared-Cvj92byG.d.ts → shared-4CAYLCTO.d.ts} +1 -1
  61. package/dist/{shared-CiKaIK4h.js → shared-BIpUk4G5.js} +3 -7
  62. package/dist/{shared-CiKaIK4h.js.map → shared-BIpUk4G5.js.map} +1 -1
  63. package/dist/skills/index.d.ts +236 -0
  64. package/dist/skills/index.js +1326 -0
  65. package/dist/skills/index.js.map +1 -0
  66. package/dist/sub-routing.d.ts +6 -6
  67. package/dist/{tool-output-truncation-CH-khbZ3.js → tool-output-truncation-CNnnGZQ3.js} +1 -1
  68. package/dist/{tool-output-truncation-CH-khbZ3.js.map → tool-output-truncation-CNnnGZQ3.js.map} +1 -1
  69. package/dist/{types-_JjKmv-l.d.ts → types-6Zo2zfoO.d.ts} +1 -1
  70. package/dist/types.d.ts +1 -1
  71. package/dist/vite.d.ts +1 -1
  72. package/dist/vite.js +248 -2
  73. package/dist/vite.js.map +1 -1
  74. package/dist/{workflow-types-Dkzg4hAx.d.ts → workflow-types-SrZK_o9p.d.ts} +1 -1
  75. package/dist/workflow-types.d.ts +1 -1
  76. package/dist/workflows.d.ts +13 -3
  77. package/dist/workflows.js +10 -1
  78. package/dist/workflows.js.map +1 -1
  79. package/package.json +31 -13
  80. package/skills-module.d.ts +22 -0
  81. package/dist/agent-tools-BAdX1vdI.js.map +0 -1
  82. package/dist/client-D1kFXo80.js.map +0 -1
  83. package/dist/compaction-helpers-fJyf8j4m.js.map +0 -1
@@ -7,7 +7,7 @@ import { z } from "zod";
7
7
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
8
8
  import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
9
9
  import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
10
- import { ElicitRequestSchema, JSONRPCMessageSchema, PromptListChangedNotificationSchema, ResourceListChangedNotificationSchema, ToolListChangedNotificationSchema } from "@modelcontextprotocol/sdk/types.js";
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) {
13
13
  return { dispose: fn };
@@ -121,8 +121,8 @@ var RPCClientTransport = class {
121
121
  var RPCServerTransport = class {
122
122
  constructor(options) {
123
123
  this._started = false;
124
- this._pendingResponse = null;
125
- this._responseResolver = null;
124
+ this._pendingRequests = /* @__PURE__ */ new Map();
125
+ this._pendingContinuations = [];
126
126
  this._timeout = options?.timeout ?? 6e4;
127
127
  }
128
128
  setProtocolVersion(version) {
@@ -138,109 +138,146 @@ var RPCServerTransport = class {
138
138
  async close() {
139
139
  this._started = false;
140
140
  this.onclose?.();
141
- if (this._responseResolver) {
142
- this._responseResolver();
143
- this._responseResolver = null;
141
+ const error = /* @__PURE__ */ new Error("Transport closed");
142
+ for (const pending of this._pendingRequests.values()) {
143
+ clearTimeout(pending.timeoutId);
144
+ pending.reject(error);
144
145
  }
146
+ this._pendingRequests.clear();
147
+ for (const pending of this._pendingContinuations) {
148
+ clearTimeout(pending.timeoutId);
149
+ pending.reject(error);
150
+ }
151
+ this._pendingContinuations = [];
152
+ }
153
+ _makeTimeout(onTimeout) {
154
+ return setTimeout(onTimeout, this._timeout);
155
+ }
156
+ _appendPending(pending, message) {
157
+ pending.messages.push(message);
158
+ }
159
+ _completePending(pending, message) {
160
+ pending.messages.push(message);
161
+ clearTimeout(pending.timeoutId);
162
+ const messages = pending.messages;
163
+ queueMicrotask(() => {
164
+ pending.resolve(messages.length === 1 ? messages[0] : messages);
165
+ });
166
+ }
167
+ _completeRequest(key, message) {
168
+ const pending = this._pendingRequests.get(key);
169
+ if (!pending) return false;
170
+ this._pendingRequests.delete(key);
171
+ this._completePending(pending, message);
172
+ return true;
173
+ }
174
+ _appendRequest(key, message) {
175
+ const pending = this._pendingRequests.get(key);
176
+ if (!pending) return false;
177
+ this._appendPending(pending, message);
178
+ return true;
145
179
  }
146
- async send(message, _options) {
180
+ _completeContinuation(message) {
181
+ const pending = this._pendingContinuations.shift();
182
+ if (!pending) return false;
183
+ this._completePending(pending, message);
184
+ return true;
185
+ }
186
+ _appendContinuation(message) {
187
+ const pending = this._pendingContinuations[0];
188
+ if (!pending) return false;
189
+ this._appendPending(pending, message);
190
+ return true;
191
+ }
192
+ async send(message, options) {
147
193
  if (!this._started) throw new Error("Transport not started");
148
- if (!this._pendingResponse) this._pendingResponse = message;
149
- else if (Array.isArray(this._pendingResponse)) this._pendingResponse.push(message);
150
- else this._pendingResponse = [this._pendingResponse, message];
151
- if (this._responseResolver) {
152
- const resolver = this._responseResolver;
153
- queueMicrotask(() => resolver());
194
+ if (isJSONRPCResultResponse(message) || isJSONRPCErrorResponse(message)) {
195
+ const id = message.id;
196
+ if (id === void 0) {
197
+ this.onerror?.(/* @__PURE__ */ new Error(`RPC response missing id: ${JSON.stringify(message)}`));
198
+ return;
199
+ }
200
+ if (this._completeRequest(id.toString(), message)) return;
201
+ if (this._completeContinuation(message)) return;
202
+ this.onerror?.(/* @__PURE__ */ new Error(`No pending RPC request found for response: ${JSON.stringify(message)}`));
203
+ return;
204
+ }
205
+ const relatedRequestId = options?.relatedRequestId?.toString();
206
+ const expectsResponse = "id" in message;
207
+ if (relatedRequestId) {
208
+ if (expectsResponse) {
209
+ if (this._completeRequest(relatedRequestId, message)) return;
210
+ } else if (this._appendRequest(relatedRequestId, message)) return;
154
211
  }
212
+ if (expectsResponse) {
213
+ if (this._completeContinuation(message)) return;
214
+ } else if (this._appendContinuation(message)) return;
215
+ this.onerror?.(/* @__PURE__ */ new Error(`No pending RPC request found for message: ${JSON.stringify(message)}`));
155
216
  }
156
217
  /**
157
218
  * @internal Called by McpAgent.handleMcpMessage() — not for external use.
158
219
  *
159
- * Wait for the next send() call and return whatever it produces.
220
+ * Wait for the next unmatched send() call that expects a client response or
221
+ * completes a resumed tool call.
160
222
  *
161
- * Used after resolving an elicitation response: the tool handler is still
162
- * running and will eventually call send() with either another elicitation
163
- * request or the final tool result. This method captures that send() using
164
- * the same _responseResolver / _pendingResponse / timeout mechanism as
165
- * handle().
223
+ * Used after resolving an elicitation response: the original tool call has
224
+ * already returned the elicitation request to the RPC client, and the resumed
225
+ * tool handler will eventually send the final tool result. That final response
226
+ * has the original tool request id, so there is no active handle() waiter left
227
+ * for id-based routing; this continuation waiter receives it instead.
166
228
  */
167
229
  async _awaitPendingResponse() {
168
230
  if (!this._started) throw new Error("Transport not started");
169
- this._pendingResponse = null;
170
- let timeoutId = null;
171
- const responsePromise = new Promise((resolve, reject) => {
172
- timeoutId = setTimeout(() => {
173
- this._responseResolver = null;
174
- reject(/* @__PURE__ */ new Error(`Request timeout: No response received within ${this._timeout}ms`));
175
- }, this._timeout);
176
- this._responseResolver = () => {
177
- if (timeoutId) {
178
- clearTimeout(timeoutId);
179
- timeoutId = null;
180
- }
181
- this._responseResolver = null;
182
- resolve();
231
+ return await new Promise((resolve, reject) => {
232
+ const pending = {
233
+ messages: [],
234
+ resolve,
235
+ reject,
236
+ timeoutId: this._makeTimeout(() => {
237
+ const index = this._pendingContinuations.indexOf(pending);
238
+ if (index !== -1) this._pendingContinuations.splice(index, 1);
239
+ reject(/* @__PURE__ */ new Error(`Request timeout: No response received within ${this._timeout}ms`));
240
+ })
183
241
  };
242
+ this._pendingContinuations.push(pending);
184
243
  });
185
- try {
186
- await responsePromise;
187
- } catch (error) {
188
- this._pendingResponse = null;
189
- this._responseResolver = null;
190
- throw error;
191
- }
192
- const response = this._pendingResponse;
193
- this._pendingResponse = null;
194
- return response ?? void 0;
195
244
  }
196
245
  async handle(message) {
197
246
  if (!this._started) throw new Error("Transport not started");
198
247
  if (Array.isArray(message)) {
199
248
  validateBatch(message);
200
- const responses = [];
201
- for (const msg of message) {
202
- const response = await this.handle(msg);
203
- if (response !== void 0) if (Array.isArray(response)) responses.push(...response);
204
- else responses.push(response);
205
- }
206
- return responses.length === 0 ? void 0 : responses;
249
+ const flattened = (await Promise.all(message.map((msg) => this.handle(msg)))).flatMap((response) => {
250
+ if (response === void 0) return [];
251
+ return Array.isArray(response) ? response : [response];
252
+ });
253
+ return flattened.length === 0 ? void 0 : flattened;
207
254
  }
208
255
  try {
209
256
  JSONRPCMessageSchema.parse(message);
210
257
  } catch {
211
258
  return makeInvalidRequestError(typeof message === "object" && message !== null && "id" in message ? message.id : null);
212
259
  }
213
- this._pendingResponse = null;
214
260
  if (!("id" in message)) {
215
261
  this.onmessage?.(message);
216
262
  return;
217
263
  }
218
- let timeoutId = null;
264
+ const id = message.id?.toString();
265
+ if (!id) return makeInvalidRequestError(message.id);
266
+ if (this._pendingRequests.has(id)) throw new Error(`Duplicate pending RPC request id: ${id}`);
219
267
  const responsePromise = new Promise((resolve, reject) => {
220
- timeoutId = setTimeout(() => {
221
- this._responseResolver = null;
222
- reject(/* @__PURE__ */ new Error(`Request timeout: No response received within ${this._timeout}ms`));
223
- }, this._timeout);
224
- this._responseResolver = () => {
225
- if (timeoutId) {
226
- clearTimeout(timeoutId);
227
- timeoutId = null;
228
- }
229
- this._responseResolver = null;
230
- resolve();
268
+ const pending = {
269
+ messages: [],
270
+ resolve,
271
+ reject,
272
+ timeoutId: this._makeTimeout(() => {
273
+ this._pendingRequests.delete(id);
274
+ reject(/* @__PURE__ */ new Error(`Request timeout: No response received within ${this._timeout}ms`));
275
+ })
231
276
  };
277
+ this._pendingRequests.set(id, pending);
232
278
  });
233
279
  this.onmessage?.(message);
234
- try {
235
- await responsePromise;
236
- } catch (error) {
237
- this._pendingResponse = null;
238
- this._responseResolver = null;
239
- throw error;
240
- }
241
- const response = this._pendingResponse;
242
- this._pendingResponse = null;
243
- return response ?? void 0;
280
+ return await responsePromise;
244
281
  }
245
282
  };
246
283
  //#endregion
@@ -364,11 +401,12 @@ var MCPClientConnection = class {
364
401
  /**
365
402
  * Complete OAuth authorization
366
403
  */
367
- async completeAuthorization(code) {
368
- if (this.connectionState !== MCPConnectionState.AUTHENTICATING) throw new Error("Connection must be in authenticating state to complete authorization");
404
+ async completeAuthorization(code, options = {}) {
405
+ const expectedState = options.alreadyAccepted ? MCPConnectionState.CONNECTING : MCPConnectionState.AUTHENTICATING;
406
+ if (this.connectionState !== expectedState) throw new Error(`Connection must be in ${expectedState} state to complete authorization`);
407
+ if (!options.alreadyAccepted) this.connectionState = MCPConnectionState.CONNECTING;
369
408
  try {
370
409
  await this.finishAuthProbe(code);
371
- this.connectionState = MCPConnectionState.CONNECTING;
372
410
  } catch (error) {
373
411
  this.connectionState = MCPConnectionState.FAILED;
374
412
  throw error;
@@ -711,6 +749,37 @@ var MCPClientConnection = class {
711
749
  //#endregion
712
750
  //#region src/mcp/client.ts
713
751
  const defaultClientOptions = { jsonSchemaValidator: new CfWorkerJsonSchemaValidator() };
752
+ /** Maximum length of a normalized MCP server id. */
753
+ const MCP_SERVER_ID_MAX_LENGTH = 64;
754
+ /**
755
+ * Normalize a caller-supplied MCP server id into a stable, storage- and
756
+ * tool-name-safe form.
757
+ *
758
+ * The id is surfaced in several places where the character set matters:
759
+ * - as the primary key in the `cf_agents_mcp_servers` SQLite table
760
+ * - embedded in AI SDK tool names as `` `tool_${id.replace(/-/g, "")}_${tool}` ``
761
+ * (tool names must match `/^[A-Za-z0-9_]+$/`)
762
+ * - as a key on the `mcpConnections` map and OAuth provider storage
763
+ *
764
+ * Rules:
765
+ * 1. Lowercase.
766
+ * 2. Replace any run of disallowed characters with a single `-`.
767
+ * 3. Collapse repeated `-` and trim leading/trailing `-`/`_`.
768
+ * 4. Prefix with `id-` if the result is empty or doesn't start with a letter.
769
+ * 5. Truncate to {@link MCP_SERVER_ID_MAX_LENGTH} characters.
770
+ *
771
+ * @example
772
+ * normalizeServerId("my-supplied-id"); // "my-supplied-id"
773
+ * normalizeServerId("GitHub MCP!"); // "github-mcp"
774
+ * normalizeServerId("42-things"); // "id-42-things"
775
+ */
776
+ function normalizeServerId(input) {
777
+ if (typeof input !== "string") throw new TypeError(`normalizeServerId: expected string, got ${typeof input}`);
778
+ let id = input.toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/-+/g, "-").replace(/^[-_]+|[-_]+$/g, "");
779
+ if (id.length === 0 || !/^[a-z]/.test(id)) id = `id-${id}`.replace(/-+$/g, "");
780
+ if (id.length > 64) id = id.slice(0, 64).replace(/-+$/g, "");
781
+ return id;
782
+ }
714
783
  /**
715
784
  * Blocked hostname patterns for SSRF protection.
716
785
  * Prevents MCP client from connecting to internal/private network addresses
@@ -838,6 +907,69 @@ var MCPClientManager = class {
838
907
  removeServerFromStorage(serverId) {
839
908
  this.sql("DELETE FROM cf_agents_mcp_servers WHERE id = ?", serverId);
840
909
  }
910
+ /**
911
+ * Rename a server's id, in-place, across every place the id is used as a
912
+ * key. Used to JIT-migrate servers that were originally registered under an
913
+ * auto-generated nanoid to a caller-supplied stable id (see
914
+ * `Agent.addMcpServer`'s `{ id }` option).
915
+ *
916
+ * Migrates:
917
+ * - the `cf_agents_mcp_servers` row (primary key)
918
+ * - the in-memory `mcpConnections` map key
919
+ * - the connection disposables map key
920
+ * - the attached `authProvider.serverId`, if any
921
+ * - OAuth-related storage keys under `/{clientName}/{oldId}/...`
922
+ *
923
+ * Safe to call when no OAuth keys exist (RPC / bearer-token HTTP servers).
924
+ * If `oldId === newId` this is a no-op. If a row already exists under
925
+ * `newId`, throws — the caller is expected to have verified uniqueness.
926
+ *
927
+ * @internal Exposed for `Agent.addMcpServer` JIT-migration.
928
+ */
929
+ async migrateServerId(oldId, newId, clientName) {
930
+ if (oldId === newId) return;
931
+ if (this.sql("SELECT id FROM cf_agents_mcp_servers WHERE id = ?", oldId).length === 0) {
932
+ this._renameInMemoryConnection(oldId, newId);
933
+ return;
934
+ }
935
+ if (this.sql("SELECT id FROM cf_agents_mcp_servers WHERE id = ?", newId).length > 0) throw new Error(`Cannot migrate MCP server id "${oldId}" → "${newId}": new id is already in use.`);
936
+ this.sql("UPDATE cf_agents_mcp_servers SET id = ? WHERE id = ?", newId, oldId);
937
+ const oldPrefix = `/${clientName}/${oldId}/`;
938
+ const newPrefix = `/${clientName}/${newId}/`;
939
+ try {
940
+ const keys = await this._storage.list({ prefix: oldPrefix });
941
+ if (keys.size > 0) {
942
+ const writes = {};
943
+ const deletes = [];
944
+ for (const [oldKey, value] of keys) {
945
+ const newKey = newPrefix + oldKey.slice(oldPrefix.length);
946
+ writes[newKey] = value;
947
+ deletes.push(oldKey);
948
+ }
949
+ await this._storage.put(writes);
950
+ await this._storage.delete(deletes);
951
+ }
952
+ } catch (error) {
953
+ console.warn(`[MCPClientManager] OAuth key migration ${oldPrefix} → ${newPrefix} failed:`, error);
954
+ }
955
+ this._renameInMemoryConnection(oldId, newId);
956
+ this._onServerStateChanged.fire();
957
+ }
958
+ _renameInMemoryConnection(oldId, newId) {
959
+ if (oldId === newId) return;
960
+ const conn = this.mcpConnections[oldId];
961
+ if (conn) {
962
+ this.mcpConnections[newId] = conn;
963
+ delete this.mcpConnections[oldId];
964
+ const authProvider = conn.options.transport.authProvider;
965
+ if (authProvider) authProvider.serverId = newId;
966
+ }
967
+ const disposables = this._connectionDisposables.get(oldId);
968
+ if (disposables) {
969
+ this._connectionDisposables.set(newId, disposables);
970
+ this._connectionDisposables.delete(oldId);
971
+ }
972
+ }
841
973
  getServersFromStorage() {
842
974
  return this.sql("SELECT id, name, server_url, client_id, auth_url, callback_url, server_options FROM cf_agents_mcp_servers");
843
975
  }
@@ -900,6 +1032,54 @@ var MCPClientManager = class {
900
1032
  authError: error
901
1033
  };
902
1034
  }
1035
+ isAuthAcceptedConnection(conn) {
1036
+ return conn.connectionState === MCPConnectionState.READY || conn.connectionState === MCPConnectionState.CONNECTED || conn.connectionState === MCPConnectionState.CONNECTING || conn.connectionState === MCPConnectionState.DISCOVERING;
1037
+ }
1038
+ oauthCallbackSuccess(serverId, conn) {
1039
+ this.clearServerAuthUrl(serverId);
1040
+ conn.connectionError = null;
1041
+ return {
1042
+ serverId,
1043
+ authSuccess: true
1044
+ };
1045
+ }
1046
+ async runWithCodeVerifierState(authProvider, state, callback) {
1047
+ if (authProvider.runWithCodeVerifierState) return authProvider.runWithCodeVerifierState(state, callback);
1048
+ return callback();
1049
+ }
1050
+ async consumeStaleOAuthState(serverId, authProvider, state) {
1051
+ try {
1052
+ const stateValidation = await authProvider.checkState(state);
1053
+ if (!stateValidation.valid) {
1054
+ console.warn(`[MCPClientManager] Ignoring stale OAuth callback with invalid state for server "${serverId}": ${stateValidation.error ?? "Invalid state"}`);
1055
+ return;
1056
+ }
1057
+ await authProvider.consumeState(state);
1058
+ } catch (cleanupError) {
1059
+ console.warn(`[MCPClientManager] Failed to clean up stale OAuth callback state for server "${serverId}":`, cleanupError);
1060
+ }
1061
+ }
1062
+ async completeAuthorizationAndCleanupVerifier(serverId, conn, authProvider, state, code) {
1063
+ await this.runWithCodeVerifierState(authProvider, state, async () => {
1064
+ let completeError;
1065
+ let cleanupError;
1066
+ try {
1067
+ await conn.completeAuthorization(code, { alreadyAccepted: true });
1068
+ } catch (error) {
1069
+ completeError = error;
1070
+ }
1071
+ try {
1072
+ await authProvider.deleteCodeVerifier();
1073
+ } catch (deleteError) {
1074
+ cleanupError = deleteError;
1075
+ }
1076
+ if (completeError) {
1077
+ if (cleanupError) console.warn(`[MCPClientManager] Failed to clean up OAuth code verifier for server "${serverId}":`, cleanupError);
1078
+ throw completeError;
1079
+ }
1080
+ if (cleanupError) throw cleanupError;
1081
+ });
1082
+ }
903
1083
  /**
904
1084
  * Create an auth provider for a server
905
1085
  * @internal
@@ -1082,7 +1262,19 @@ var MCPClientManager = class {
1082
1262
  }
1083
1263
  await this.mcpConnections[id].init();
1084
1264
  if (options.reconnect?.oauthCode) try {
1085
- await this.mcpConnections[id].completeAuthorization(options.reconnect.oauthCode);
1265
+ const authProvider = this.mcpConnections[id].options.transport.authProvider;
1266
+ let completeError;
1267
+ try {
1268
+ await this.mcpConnections[id].completeAuthorization(options.reconnect.oauthCode);
1269
+ } catch (error) {
1270
+ completeError = error;
1271
+ }
1272
+ try {
1273
+ await authProvider?.deleteCodeVerifier();
1274
+ } catch (cleanupError) {
1275
+ console.warn(`[MCPClientManager] Failed to clean up OAuth code verifier for server "${id}":`, cleanupError);
1276
+ }
1277
+ if (completeError) throw completeError;
1086
1278
  await this.mcpConnections[id].init();
1087
1279
  } catch (error) {
1088
1280
  this._onObservabilityEvent.fire({
@@ -1273,11 +1465,13 @@ var MCPClientManager = class {
1273
1465
  };
1274
1466
  if (error) return {
1275
1467
  serverId,
1468
+ state,
1276
1469
  valid: false,
1277
1470
  error: errorDescription || error
1278
1471
  };
1279
1472
  if (!code) return {
1280
1473
  serverId,
1474
+ state,
1281
1475
  valid: false,
1282
1476
  error: "Unauthorized: no code provided"
1283
1477
  };
@@ -1301,7 +1495,18 @@ var MCPClientManager = class {
1301
1495
  async handleCallbackRequest(req) {
1302
1496
  const validation = this.validateCallbackRequest(req);
1303
1497
  if (!validation.valid) {
1304
- if (validation.serverId && this.mcpConnections[validation.serverId]) return this.failConnection(validation.serverId, validation.error);
1498
+ const conn = validation.serverId ? this.mcpConnections[validation.serverId] : void 0;
1499
+ if (validation.serverId && conn) {
1500
+ if (this.isAuthAcceptedConnection(conn)) {
1501
+ const authProvider = conn.options.transport.authProvider;
1502
+ if (validation.state && authProvider) {
1503
+ authProvider.serverId = validation.serverId;
1504
+ await this.consumeStaleOAuthState(validation.serverId, authProvider, validation.state);
1505
+ }
1506
+ return this.oauthCallbackSuccess(validation.serverId, conn);
1507
+ }
1508
+ return this.failConnection(validation.serverId, validation.error);
1509
+ }
1305
1510
  return {
1306
1511
  serverId: validation.serverId,
1307
1512
  authSuccess: false,
@@ -1315,26 +1520,25 @@ var MCPClientManager = class {
1315
1520
  const authProvider = conn.options.transport.authProvider;
1316
1521
  authProvider.serverId = serverId;
1317
1522
  const stateValidation = await authProvider.checkState(state);
1318
- if (!stateValidation.valid) throw new Error(stateValidation.error || "Invalid state");
1319
- if (conn.connectionState === MCPConnectionState.READY || conn.connectionState === MCPConnectionState.CONNECTED) {
1320
- this.clearServerAuthUrl(serverId);
1321
- return {
1322
- serverId,
1323
- authSuccess: true
1324
- };
1523
+ if (!stateValidation.valid) {
1524
+ if (this.isAuthAcceptedConnection(conn)) {
1525
+ await this.consumeStaleOAuthState(serverId, authProvider, state);
1526
+ return this.oauthCallbackSuccess(serverId, conn);
1527
+ }
1528
+ throw new Error(stateValidation.error || "Invalid state");
1529
+ }
1530
+ if (this.isAuthAcceptedConnection(conn)) {
1531
+ await this.consumeStaleOAuthState(serverId, authProvider, state);
1532
+ return this.oauthCallbackSuccess(serverId, conn);
1325
1533
  }
1326
1534
  if (conn.connectionState !== MCPConnectionState.AUTHENTICATING) throw new Error(`Failed to authenticate: the client is in "${conn.connectionState}" state, expected "authenticating"`);
1535
+ conn.connectionState = MCPConnectionState.CONNECTING;
1327
1536
  await authProvider.consumeState(state);
1328
- await conn.completeAuthorization(code);
1537
+ await this.completeAuthorizationAndCleanupVerifier(serverId, conn, authProvider, state, code);
1329
1538
  this.updateStoredSessionId(serverId, conn.sessionId);
1330
- await authProvider.deleteCodeVerifier();
1331
- this.clearServerAuthUrl(serverId);
1332
- conn.connectionError = null;
1539
+ const result = this.oauthCallbackSuccess(serverId, conn);
1333
1540
  this._onServerStateChanged.fire();
1334
- return {
1335
- serverId,
1336
- authSuccess: true
1337
- };
1541
+ return result;
1338
1542
  } catch (err) {
1339
1543
  const message = err instanceof Error ? err.message : String(err);
1340
1544
  return this.failConnection(serverId, message);
@@ -1623,6 +1827,6 @@ function getNamespacedData(mcpClients, type) {
1623
1827
  });
1624
1828
  }
1625
1829
  //#endregion
1626
- export { RPCServerTransport as a, RPCClientTransport as i, getNamespacedData as n, RPC_DO_PREFIX as o, MCPConnectionState as r, DisposableStore as s, MCPClientManager as t };
1830
+ 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 };
1627
1831
 
1628
- //# sourceMappingURL=client-D1kFXo80.js.map
1832
+ //# sourceMappingURL=client-FUizKzj2.js.map