agents 0.0.0-293b546 → 0.0.0-295f4dd

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 (64) hide show
  1. package/dist/ai-chat-agent.d.ts +23 -14
  2. package/dist/ai-chat-agent.js +40 -26
  3. package/dist/ai-chat-agent.js.map +1 -1
  4. package/dist/{ai-chat-v5-migration-BSiGZmYU.js → ai-chat-v5-migration-DguhuLKF.js} +1 -1
  5. package/dist/{ai-chat-v5-migration-BSiGZmYU.js.map → ai-chat-v5-migration-DguhuLKF.js.map} +1 -1
  6. package/dist/ai-chat-v5-migration.js +1 -1
  7. package/dist/ai-react.d.ts +10 -9
  8. package/dist/ai-react.js +43 -28
  9. package/dist/ai-react.js.map +1 -1
  10. package/dist/{ai-types-CrMqkwc_.js → ai-types-CwgHzwUb.js} +1 -1
  11. package/dist/{ai-types-CrMqkwc_.js.map → ai-types-CwgHzwUb.js.map} +1 -1
  12. package/dist/{ai-types-81H_-Uxh.d.ts → ai-types-D_hTbf25.d.ts} +1 -1
  13. package/dist/ai-types.d.ts +1 -1
  14. package/dist/ai-types.js +1 -1
  15. package/dist/cli/index.d.ts +1 -0
  16. package/dist/{cli.js → cli/index.js} +7 -6
  17. package/dist/cli/index.js.map +1 -0
  18. package/dist/client-C_M5uPrn.d.ts +834 -0
  19. package/dist/{client-B3SR12TQ.js → client-CcyhkGfN.js} +2 -2
  20. package/dist/{client-B3SR12TQ.js.map → client-CcyhkGfN.js.map} +1 -1
  21. package/dist/{client-BAQA84dr.d.ts → client-ClORm6f0.d.ts} +2 -2
  22. package/dist/{client-C8VrzljV.js → client-QZa2Rq0l.js} +371 -187
  23. package/dist/client-QZa2Rq0l.js.map +1 -0
  24. package/dist/client.d.ts +2 -2
  25. package/dist/client.js +2 -2
  26. package/dist/codemode/ai.js +6 -5
  27. package/dist/codemode/ai.js.map +1 -1
  28. package/dist/context-BkKbAa1R.js +8 -0
  29. package/dist/context-BkKbAa1R.js.map +1 -0
  30. package/dist/context-_sPQqJWv.d.ts +24 -0
  31. package/dist/context.d.ts +6 -0
  32. package/dist/context.js +3 -0
  33. package/dist/{do-oauth-client-provider-C2CHH5x-.d.ts → do-oauth-client-provider-B-ryFIPr.d.ts} +20 -5
  34. package/dist/{do-oauth-client-provider-CwqK5SXm.js → do-oauth-client-provider-B1fVIshX.js} +69 -8
  35. package/dist/do-oauth-client-provider-B1fVIshX.js.map +1 -0
  36. package/dist/{index-BUle9RiP.d.ts → index-CyDpAVHZ.d.ts} +2 -2
  37. package/dist/{index-7kI1zprE.d.ts → index-DUnsVDnf.d.ts} +50 -51
  38. package/dist/index.d.ts +34 -34
  39. package/dist/index.js +6 -5
  40. package/dist/mcp/client.d.ts +4 -4
  41. package/dist/mcp/client.js +2 -2
  42. package/dist/mcp/do-oauth-client-provider.d.ts +1 -1
  43. package/dist/mcp/do-oauth-client-provider.js +1 -1
  44. package/dist/mcp/index.d.ts +8 -7
  45. package/dist/mcp/index.js +37 -14
  46. package/dist/mcp/index.js.map +1 -1
  47. package/dist/{mcp-BwPscEiF.d.ts → mcp-CzbSsLfc.d.ts} +1 -1
  48. package/dist/observability/index.d.ts +2 -2
  49. package/dist/observability/index.js +6 -5
  50. package/dist/{react-9nVfoERh.d.ts → react-DIGWa87z.d.ts} +29 -5
  51. package/dist/react.d.ts +15 -10
  52. package/dist/react.js +10 -3
  53. package/dist/react.js.map +1 -1
  54. package/dist/{serializable-faDkMCai.d.ts → serializable-C4GLimgv.d.ts} +1 -1
  55. package/dist/serializable.d.ts +1 -1
  56. package/dist/{src-xjQt2wBU.js → src-BmbDclOA.js} +26 -25
  57. package/dist/src-BmbDclOA.js.map +1 -0
  58. package/package.json +5 -5
  59. package/dist/cli.d.ts +0 -8
  60. package/dist/cli.js.map +0 -1
  61. package/dist/client-BG2wUgN5.d.ts +0 -1462
  62. package/dist/client-C8VrzljV.js.map +0 -1
  63. package/dist/do-oauth-client-provider-CwqK5SXm.js.map +0 -1
  64. package/dist/src-xjQt2wBU.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import { t as DurableObjectOAuthClientProvider } from "./do-oauth-client-provider-CwqK5SXm.js";
1
+ import { t as DurableObjectOAuthClientProvider } from "./do-oauth-client-provider-B1fVIshX.js";
2
2
  import { nanoid } from "nanoid";
3
3
  import { CfWorkerJsonSchemaValidator } from "@modelcontextprotocol/sdk/validation/cfworker-provider.js";
4
4
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
@@ -60,6 +60,22 @@ function isTransportNotImplemented(error) {
60
60
 
61
61
  //#endregion
62
62
  //#region src/mcp/client-connection.ts
63
+ /**
64
+ * Connection state machine for MCP client connections.
65
+ *
66
+ * State transitions:
67
+ * - Non-OAuth: init() → CONNECTING → DISCOVERING → READY
68
+ * - OAuth: init() → AUTHENTICATING → (callback) → CONNECTING → DISCOVERING → READY
69
+ * - Any state can transition to FAILED on error
70
+ */
71
+ const MCPConnectionState = {
72
+ AUTHENTICATING: "authenticating",
73
+ CONNECTING: "connecting",
74
+ CONNECTED: "connected",
75
+ DISCOVERING: "discovering",
76
+ READY: "ready",
77
+ FAILED: "failed"
78
+ };
63
79
  var MCPClientConnection = class {
64
80
  constructor(url, info, options = {
65
81
  client: {},
@@ -67,7 +83,7 @@ var MCPClientConnection = class {
67
83
  }) {
68
84
  this.url = url;
69
85
  this.options = options;
70
- this.connectionState = "connecting";
86
+ this.connectionState = MCPConnectionState.CONNECTING;
71
87
  this.tools = [];
72
88
  this.prompts = [];
73
89
  this.resources = [];
@@ -83,36 +99,49 @@ var MCPClientConnection = class {
83
99
  });
84
100
  }
85
101
  /**
86
- * Initialize a client connection
102
+ * Initialize a client connection, if authentication is required, the connection will be in the AUTHENTICATING state
103
+ * Sets connection state based on the result and emits observability events
87
104
  *
88
- * @returns
105
+ * @returns Error message if connection failed, undefined otherwise
89
106
  */
90
107
  async init() {
91
108
  const transportType = this.options.transport.type;
92
109
  if (!transportType) throw new Error("Transport type must be specified");
93
- try {
94
- await this.tryConnect(transportType);
95
- } catch (e) {
96
- if (isUnauthorized(e)) {
97
- this.connectionState = "authenticating";
98
- return;
99
- }
110
+ const res = await this.tryConnect(transportType);
111
+ this.connectionState = res.state;
112
+ if (res.state === MCPConnectionState.CONNECTED && res.transport) {
113
+ this.client.setRequestHandler(ElicitRequestSchema, async (request) => {
114
+ return await this.handleElicitationRequest(request);
115
+ });
116
+ this.lastConnectedTransport = res.transport;
100
117
  this._onObservabilityEvent.fire({
101
118
  type: "mcp:client:connect",
102
- displayMessage: `Connection initialization failed for ${this.url.toString()}`,
119
+ displayMessage: `Connected successfully using ${res.transport} transport for ${this.url.toString()}`,
120
+ payload: {
121
+ url: this.url.toString(),
122
+ transport: res.transport,
123
+ state: this.connectionState
124
+ },
125
+ timestamp: Date.now(),
126
+ id: nanoid()
127
+ });
128
+ return;
129
+ } else if (res.state === MCPConnectionState.FAILED && res.error) {
130
+ const errorMessage = toErrorMessage(res.error);
131
+ this._onObservabilityEvent.fire({
132
+ type: "mcp:client:connect",
133
+ displayMessage: `Failed to connect to ${this.url.toString()}: ${errorMessage}`,
103
134
  payload: {
104
135
  url: this.url.toString(),
105
136
  transport: transportType,
106
137
  state: this.connectionState,
107
- error: toErrorMessage(e)
138
+ error: errorMessage
108
139
  },
109
140
  timestamp: Date.now(),
110
141
  id: nanoid()
111
142
  });
112
- this.connectionState = "failed";
113
- return;
143
+ return errorMessage;
114
144
  }
115
- await this.discoverAndRegister();
116
145
  }
117
146
  /**
118
147
  * Finish OAuth by probing transports based on configured type.
@@ -144,113 +173,189 @@ var MCPClientConnection = class {
144
173
  * Complete OAuth authorization
145
174
  */
146
175
  async completeAuthorization(code) {
147
- if (this.connectionState !== "authenticating") throw new Error("Connection must be in authenticating state to complete authorization");
176
+ if (this.connectionState !== MCPConnectionState.AUTHENTICATING) throw new Error("Connection must be in authenticating state to complete authorization");
148
177
  try {
149
178
  await this.finishAuthProbe(code);
150
- this.connectionState = "connecting";
179
+ this.connectionState = MCPConnectionState.CONNECTING;
151
180
  } catch (error) {
152
- this.connectionState = "failed";
181
+ this.connectionState = MCPConnectionState.FAILED;
153
182
  throw error;
154
183
  }
155
184
  }
156
185
  /**
157
- * Establish connection after successful authorization
186
+ * Discover server capabilities and register tools, resources, prompts, and templates.
187
+ * This method does the work but does not manage connection state - that's handled by discover().
158
188
  */
159
- async establishConnection() {
160
- if (this.connectionState !== "connecting") throw new Error("Connection must be in connecting state to establish connection");
189
+ async discoverAndRegister() {
190
+ this.serverCapabilities = this.client.getServerCapabilities();
191
+ if (!this.serverCapabilities) throw new Error("The MCP Server failed to return server capabilities");
192
+ const operations = [];
193
+ const operationNames = [];
194
+ operations.push(Promise.resolve(this.client.getInstructions()));
195
+ operationNames.push("instructions");
196
+ if (this.serverCapabilities.tools) {
197
+ operations.push(this.registerTools());
198
+ operationNames.push("tools");
199
+ }
200
+ if (this.serverCapabilities.resources) {
201
+ operations.push(this.registerResources());
202
+ operationNames.push("resources");
203
+ }
204
+ if (this.serverCapabilities.prompts) {
205
+ operations.push(this.registerPrompts());
206
+ operationNames.push("prompts");
207
+ }
208
+ if (this.serverCapabilities.resources) {
209
+ operations.push(this.registerResourceTemplates());
210
+ operationNames.push("resource templates");
211
+ }
161
212
  try {
162
- const transportType = this.options.transport.type;
163
- if (!transportType) throw new Error("Transport type must be specified");
164
- await this.tryConnect(transportType);
165
- await this.discoverAndRegister();
213
+ const results = await Promise.all(operations);
214
+ for (let i = 0; i < results.length; i++) {
215
+ const result = results[i];
216
+ switch (operationNames[i]) {
217
+ case "instructions":
218
+ this.instructions = result;
219
+ break;
220
+ case "tools":
221
+ this.tools = result;
222
+ break;
223
+ case "resources":
224
+ this.resources = result;
225
+ break;
226
+ case "prompts":
227
+ this.prompts = result;
228
+ break;
229
+ case "resource templates":
230
+ this.resourceTemplates = result;
231
+ break;
232
+ }
233
+ }
166
234
  } catch (error) {
167
- this.connectionState = "failed";
235
+ this._onObservabilityEvent.fire({
236
+ type: "mcp:client:discover",
237
+ displayMessage: `Failed to discover capabilities for ${this.url.toString()}: ${toErrorMessage(error)}`,
238
+ payload: {
239
+ url: this.url.toString(),
240
+ error: toErrorMessage(error)
241
+ },
242
+ timestamp: Date.now(),
243
+ id: nanoid()
244
+ });
168
245
  throw error;
169
246
  }
170
247
  }
171
248
  /**
172
- * Discover server capabilities and register tools, resources, prompts, and templates
249
+ * Discover server capabilities with timeout and cancellation support.
250
+ * If called while a previous discovery is in-flight, the previous discovery will be aborted.
251
+ *
252
+ * @param options Optional configuration
253
+ * @param options.timeoutMs Timeout in milliseconds (default: 15000)
254
+ * @returns Result indicating success/failure with optional error message
173
255
  */
174
- async discoverAndRegister() {
175
- this.connectionState = "discovering";
176
- this.serverCapabilities = this.client.getServerCapabilities();
177
- if (!this.serverCapabilities) throw new Error("The MCP Server failed to return server capabilities");
178
- const [instructionsResult, toolsResult, resourcesResult, promptsResult, resourceTemplatesResult] = await Promise.allSettled([
179
- this.client.getInstructions(),
180
- this.registerTools(),
181
- this.registerResources(),
182
- this.registerPrompts(),
183
- this.registerResourceTemplates()
184
- ]);
185
- const operations = [
186
- {
187
- name: "instructions",
188
- result: instructionsResult
189
- },
190
- {
191
- name: "tools",
192
- result: toolsResult
193
- },
194
- {
195
- name: "resources",
196
- result: resourcesResult
197
- },
198
- {
199
- name: "prompts",
200
- result: promptsResult
201
- },
202
- {
203
- name: "resource templates",
204
- result: resourceTemplatesResult
205
- }
206
- ];
207
- for (const { name, result } of operations) if (result.status === "rejected") {
208
- const url = this.url.toString();
256
+ async discover(options = {}) {
257
+ const { timeoutMs = 15e3 } = options;
258
+ if (this.connectionState !== MCPConnectionState.CONNECTED && this.connectionState !== MCPConnectionState.READY) {
209
259
  this._onObservabilityEvent.fire({
210
260
  type: "mcp:client:discover",
211
- displayMessage: `Failed to discover ${name} for ${url}`,
261
+ displayMessage: `Discovery skipped for ${this.url.toString()}, state is ${this.connectionState}`,
212
262
  payload: {
213
- url,
214
- capability: name,
215
- error: result.reason
263
+ url: this.url.toString(),
264
+ state: this.connectionState
216
265
  },
217
266
  timestamp: Date.now(),
218
267
  id: nanoid()
219
268
  });
269
+ return {
270
+ success: false,
271
+ error: `Discovery skipped - connection in ${this.connectionState} state`
272
+ };
273
+ }
274
+ if (this._discoveryAbortController) {
275
+ this._discoveryAbortController.abort();
276
+ this._discoveryAbortController = void 0;
277
+ }
278
+ const abortController = new AbortController();
279
+ this._discoveryAbortController = abortController;
280
+ this.connectionState = MCPConnectionState.DISCOVERING;
281
+ let timeoutId;
282
+ try {
283
+ const timeoutPromise = new Promise((_, reject) => {
284
+ timeoutId = setTimeout(() => reject(/* @__PURE__ */ new Error(`Discovery timed out after ${timeoutMs}ms`)), timeoutMs);
285
+ });
286
+ if (abortController.signal.aborted) throw new Error("Discovery was cancelled");
287
+ const abortPromise = new Promise((_, reject) => {
288
+ abortController.signal.addEventListener("abort", () => {
289
+ reject(/* @__PURE__ */ new Error("Discovery was cancelled"));
290
+ });
291
+ });
292
+ await Promise.race([
293
+ this.discoverAndRegister(),
294
+ timeoutPromise,
295
+ abortPromise
296
+ ]);
297
+ if (timeoutId !== void 0) clearTimeout(timeoutId);
298
+ this.connectionState = MCPConnectionState.READY;
299
+ this._onObservabilityEvent.fire({
300
+ type: "mcp:client:discover",
301
+ displayMessage: `Discovery completed for ${this.url.toString()}`,
302
+ payload: { url: this.url.toString() },
303
+ timestamp: Date.now(),
304
+ id: nanoid()
305
+ });
306
+ return { success: true };
307
+ } catch (e) {
308
+ if (timeoutId !== void 0) clearTimeout(timeoutId);
309
+ this.connectionState = MCPConnectionState.CONNECTED;
310
+ return {
311
+ success: false,
312
+ error: e instanceof Error ? e.message : String(e)
313
+ };
314
+ } finally {
315
+ this._discoveryAbortController = void 0;
220
316
  }
221
- this.instructions = instructionsResult.status === "fulfilled" ? instructionsResult.value : void 0;
222
- this.tools = toolsResult.status === "fulfilled" ? toolsResult.value : [];
223
- this.resources = resourcesResult.status === "fulfilled" ? resourcesResult.value : [];
224
- this.prompts = promptsResult.status === "fulfilled" ? promptsResult.value : [];
225
- this.resourceTemplates = resourceTemplatesResult.status === "fulfilled" ? resourceTemplatesResult.value : [];
226
- this.connectionState = "ready";
227
317
  }
228
318
  /**
229
- * Notification handler registration
319
+ * Cancel any in-flight discovery operation.
320
+ * Called when closing the connection.
321
+ */
322
+ cancelDiscovery() {
323
+ if (this._discoveryAbortController) {
324
+ this._discoveryAbortController.abort();
325
+ this._discoveryAbortController = void 0;
326
+ }
327
+ }
328
+ /**
329
+ * Notification handler registration for tools
330
+ * Should only be called if serverCapabilities.tools exists
230
331
  */
231
332
  async registerTools() {
232
- if (!this.serverCapabilities || !this.serverCapabilities.tools) return [];
233
- if (this.serverCapabilities.tools.listChanged) this.client.setNotificationHandler(ToolListChangedNotificationSchema, async (_notification) => {
333
+ if (this.serverCapabilities?.tools?.listChanged) this.client.setNotificationHandler(ToolListChangedNotificationSchema, async (_notification) => {
234
334
  this.tools = await this.fetchTools();
235
335
  });
236
336
  return this.fetchTools();
237
337
  }
338
+ /**
339
+ * Notification handler registration for resources
340
+ * Should only be called if serverCapabilities.resources exists
341
+ */
238
342
  async registerResources() {
239
- if (!this.serverCapabilities || !this.serverCapabilities.resources) return [];
240
- if (this.serverCapabilities.resources.listChanged) this.client.setNotificationHandler(ResourceListChangedNotificationSchema, async (_notification) => {
343
+ if (this.serverCapabilities?.resources?.listChanged) this.client.setNotificationHandler(ResourceListChangedNotificationSchema, async (_notification) => {
241
344
  this.resources = await this.fetchResources();
242
345
  });
243
346
  return this.fetchResources();
244
347
  }
348
+ /**
349
+ * Notification handler registration for prompts
350
+ * Should only be called if serverCapabilities.prompts exists
351
+ */
245
352
  async registerPrompts() {
246
- if (!this.serverCapabilities || !this.serverCapabilities.prompts) return [];
247
- if (this.serverCapabilities.prompts.listChanged) this.client.setNotificationHandler(PromptListChangedNotificationSchema, async (_notification) => {
353
+ if (this.serverCapabilities?.prompts?.listChanged) this.client.setNotificationHandler(PromptListChangedNotificationSchema, async (_notification) => {
248
354
  this.prompts = await this.fetchPrompts();
249
355
  });
250
356
  return this.fetchPrompts();
251
357
  }
252
358
  async registerResourceTemplates() {
253
- if (!this.serverCapabilities || !this.serverCapabilities.resources) return [];
254
359
  return this.fetchResourceTemplates();
255
360
  }
256
361
  async fetchTools() {
@@ -316,44 +421,24 @@ var MCPClientConnection = class {
316
421
  const transport = this.getTransport(currentTransportType);
317
422
  try {
318
423
  await this.client.connect(transport);
319
- this.lastConnectedTransport = currentTransportType;
320
- const url = this.url.toString();
321
- this._onObservabilityEvent.fire({
322
- type: "mcp:client:connect",
323
- displayMessage: `Connected successfully using ${currentTransportType} transport for ${url}`,
324
- payload: {
325
- url,
326
- transport: currentTransportType,
327
- state: this.connectionState
328
- },
329
- timestamp: Date.now(),
330
- id: nanoid()
331
- });
332
- break;
424
+ return {
425
+ state: MCPConnectionState.CONNECTED,
426
+ transport: currentTransportType
427
+ };
333
428
  } catch (e) {
334
429
  const error = e instanceof Error ? e : new Error(String(e));
335
- if (isUnauthorized(error)) throw e;
336
- if (hasFallback && isTransportNotImplemented(error)) {
337
- const url = this.url.toString();
338
- this._onObservabilityEvent.fire({
339
- type: "mcp:client:connect",
340
- displayMessage: `${currentTransportType} transport not available, trying ${transports[transports.indexOf(currentTransportType) + 1]} for ${url}`,
341
- payload: {
342
- url,
343
- transport: currentTransportType,
344
- state: this.connectionState
345
- },
346
- timestamp: Date.now(),
347
- id: nanoid()
348
- });
349
- continue;
350
- }
351
- throw e;
430
+ if (isUnauthorized(error)) return { state: MCPConnectionState.AUTHENTICATING };
431
+ if (isTransportNotImplemented(error) && hasFallback) continue;
432
+ return {
433
+ state: MCPConnectionState.FAILED,
434
+ error
435
+ };
352
436
  }
353
437
  }
354
- this.client.setRequestHandler(ElicitRequestSchema, async (request) => {
355
- return await this.handleElicitationRequest(request);
356
- });
438
+ return {
439
+ state: MCPConnectionState.FAILED,
440
+ error: /* @__PURE__ */ new Error("No transports available")
441
+ };
357
442
  }
358
443
  _capabilityErrorHandler(empty, method) {
359
444
  return (e) => {
@@ -447,12 +532,12 @@ var MCPClientManager = class {
447
532
  for (const server of servers) {
448
533
  const existingConn = this.mcpConnections[server.id];
449
534
  if (existingConn) {
450
- if (existingConn.connectionState === "ready") {
535
+ if (existingConn.connectionState === MCPConnectionState.READY) {
451
536
  console.warn(`[MCPClientManager] Server ${server.id} already has a ready connection. Skipping recreation.`);
452
537
  continue;
453
538
  }
454
- if (existingConn.connectionState === "authenticating" || existingConn.connectionState === "connecting" || existingConn.connectionState === "discovering") continue;
455
- if (existingConn.connectionState === "failed") {
539
+ if (existingConn.connectionState === MCPConnectionState.AUTHENTICATING || existingConn.connectionState === MCPConnectionState.CONNECTING || existingConn.connectionState === MCPConnectionState.DISCOVERING) continue;
540
+ if (existingConn.connectionState === MCPConnectionState.FAILED) {
456
541
  try {
457
542
  await existingConn.client.close();
458
543
  } catch (error) {
@@ -465,7 +550,7 @@ var MCPClientManager = class {
465
550
  }
466
551
  const parsedOptions = server.server_options ? JSON.parse(server.server_options) : null;
467
552
  const authProvider = this.createAuthProvider(server.id, server.callback_url, clientName, server.client_id ?? void 0);
468
- this.createConnection(server.id, server.server_url, {
553
+ const conn = this.createConnection(server.id, server.server_url, {
469
554
  client: parsedOptions?.client ?? {},
470
555
  transport: {
471
556
  ...parsedOptions?.transport ?? {},
@@ -473,13 +558,27 @@ var MCPClientManager = class {
473
558
  authProvider
474
559
  }
475
560
  });
476
- await this.connectToServer(server.id).catch((error) => {
477
- console.error(`Error restoring ${server.id}:`, error);
478
- });
561
+ if (server.auth_url) {
562
+ conn.connectionState = MCPConnectionState.AUTHENTICATING;
563
+ continue;
564
+ }
565
+ this._restoreServer(server.id);
479
566
  }
480
567
  this._isRestored = true;
481
568
  }
482
569
  /**
570
+ * Internal method to restore a single server connection and discovery
571
+ */
572
+ async _restoreServer(serverId) {
573
+ if ((await this.connectToServer(serverId).catch((error) => {
574
+ console.error(`Error connecting to ${serverId}:`, error);
575
+ return null;
576
+ }))?.state === MCPConnectionState.CONNECTED) {
577
+ const discoverResult = await this.discoverIfConnected(serverId);
578
+ if (discoverResult && !discoverResult.success) console.error(`Error discovering ${serverId}:`, discoverResult.error);
579
+ }
580
+ }
581
+ /**
483
582
  * Connect to and register an MCP server
484
583
  *
485
584
  * @deprecated This method is maintained for backward compatibility.
@@ -526,7 +625,7 @@ var MCPClientManager = class {
526
625
  await this.mcpConnections[id].init();
527
626
  if (options.reconnect?.oauthCode) try {
528
627
  await this.mcpConnections[id].completeAuthorization(options.reconnect.oauthCode);
529
- await this.mcpConnections[id].establishConnection();
628
+ await this.mcpConnections[id].init();
530
629
  } catch (error) {
531
630
  this._onObservabilityEvent.fire({
532
631
  type: "mcp:client:connect",
@@ -543,19 +642,22 @@ var MCPClientManager = class {
543
642
  throw error;
544
643
  }
545
644
  const authUrl = options.transport?.authProvider?.authUrl;
546
- if (this.mcpConnections[id].connectionState === "authenticating" && authUrl && options.transport?.authProvider?.redirectUrl) return {
645
+ if (this.mcpConnections[id].connectionState === MCPConnectionState.AUTHENTICATING && authUrl && options.transport?.authProvider?.redirectUrl) return {
547
646
  authUrl,
548
647
  clientId: options.transport?.authProvider?.clientId,
549
648
  id
550
649
  };
650
+ const discoverResult = await this.discoverIfConnected(id);
651
+ if (discoverResult && !discoverResult.success) throw new Error(`Failed to discover server capabilities: ${discoverResult.error}`);
551
652
  return { id };
552
653
  }
553
654
  /**
554
655
  * Create an in-memory connection object and set up observability
555
656
  * Does NOT save to storage - use registerServer() for that
657
+ * @returns The connection object (existing or newly created)
556
658
  */
557
659
  createConnection(id, url, options) {
558
- if (this.mcpConnections[id]) return;
660
+ if (this.mcpConnections[id]) return this.mcpConnections[id];
559
661
  const normalizedTransport = {
560
662
  ...options.transport,
561
663
  type: options.transport?.type ?? "auto"
@@ -577,6 +679,7 @@ var MCPClientManager = class {
577
679
  store.add(this.mcpConnections[id].onObservabilityEvent((event) => {
578
680
  this._onObservabilityEvent.fire(event);
579
681
  }));
682
+ return this.mcpConnections[id];
580
683
  }
581
684
  /**
582
685
  * Register an MCP server connection without connecting
@@ -613,14 +716,13 @@ var MCPClientManager = class {
613
716
  /**
614
717
  * Connect to an already registered MCP server and initialize the connection.
615
718
  *
616
- * For OAuth servers, this returns `{ state: "authenticating", authUrl, clientId? }`
617
- * without establishing the connection. The user must complete the OAuth flow via
618
- * the authUrl, which will trigger a callback handled by `handleCallbackRequest()`.
619
- *
620
- * For non-OAuth servers, this establishes the connection immediately and returns
621
- * `{ state: "ready" }`.
719
+ * For OAuth servers, returns `{ state: "authenticating", authUrl, clientId? }`.
720
+ * The user must complete the OAuth flow via the authUrl, which triggers a
721
+ * callback handled by `handleCallbackRequest()`.
622
722
  *
623
- * Updates storage with auth URL and client ID after connection.
723
+ * For non-OAuth servers, establishes the transport connection and returns
724
+ * `{ state: "connected" }`. Call `discoverIfConnected()` afterwards to
725
+ * discover capabilities and transition to "ready" state.
624
726
  *
625
727
  * @param id Server ID (must be registered first via registerServer())
626
728
  * @returns Connection result with current state and OAuth info (if applicable)
@@ -628,70 +730,99 @@ var MCPClientManager = class {
628
730
  async connectToServer(id) {
629
731
  const conn = this.mcpConnections[id];
630
732
  if (!conn) throw new Error(`Server ${id} is not registered. Call registerServer() first.`);
631
- await conn.init();
632
- const authUrl = conn.options.transport.authProvider?.authUrl;
633
- if (conn.connectionState === "authenticating" && authUrl && conn.options.transport.authProvider?.redirectUrl) {
634
- const clientId = conn.options.transport.authProvider?.clientId;
635
- const serverRow = this.getServersFromStorage().find((s) => s.id === id);
636
- if (serverRow) this.saveServerToStorage({
637
- ...serverRow,
638
- auth_url: authUrl,
639
- client_id: clientId ?? null
640
- });
641
- this._onServerStateChanged.fire();
642
- return {
643
- state: "authenticating",
644
- authUrl,
645
- clientId
733
+ const error = await conn.init();
734
+ this._onServerStateChanged.fire();
735
+ switch (conn.connectionState) {
736
+ case MCPConnectionState.FAILED: return {
737
+ state: conn.connectionState,
738
+ error: error ?? "Unknown connection error"
739
+ };
740
+ case MCPConnectionState.AUTHENTICATING: {
741
+ const authUrl = conn.options.transport.authProvider?.authUrl;
742
+ const redirectUrl = conn.options.transport.authProvider?.redirectUrl;
743
+ if (!authUrl || !redirectUrl) return {
744
+ state: MCPConnectionState.FAILED,
745
+ error: `OAuth configuration incomplete: missing ${!authUrl ? "authUrl" : "redirectUrl"}`
746
+ };
747
+ const clientId = conn.options.transport.authProvider?.clientId;
748
+ const serverRow = this.getServersFromStorage().find((s) => s.id === id);
749
+ if (serverRow) {
750
+ this.saveServerToStorage({
751
+ ...serverRow,
752
+ auth_url: authUrl,
753
+ client_id: clientId ?? null
754
+ });
755
+ this._onServerStateChanged.fire();
756
+ }
757
+ return {
758
+ state: conn.connectionState,
759
+ authUrl,
760
+ clientId
761
+ };
762
+ }
763
+ case MCPConnectionState.CONNECTED: return { state: conn.connectionState };
764
+ default: return {
765
+ state: MCPConnectionState.FAILED,
766
+ error: `Unexpected connection state after init: ${conn.connectionState}`
646
767
  };
647
768
  }
648
- if (conn.connectionState === "ready") this._onServerStateChanged.fire();
649
- return { state: "ready" };
769
+ }
770
+ extractServerIdFromState(state) {
771
+ if (!state) return null;
772
+ const parts = state.split(".");
773
+ return parts.length === 2 ? parts[1] : null;
650
774
  }
651
775
  isCallbackRequest(req) {
652
776
  if (req.method !== "GET") return false;
653
777
  if (!req.url.includes("/callback")) return false;
654
- return this.getServersFromStorage().some((server) => server.callback_url && req.url.startsWith(server.callback_url));
778
+ const state = new URL(req.url).searchParams.get("state");
779
+ const serverId = this.extractServerIdFromState(state);
780
+ if (!serverId) return false;
781
+ return this.getServersFromStorage().some((server) => server.id === serverId);
655
782
  }
656
783
  async handleCallbackRequest(req) {
657
784
  const url = new URL(req.url);
658
- const matchingServer = this.getServersFromStorage().find((server) => {
659
- return server.callback_url && req.url.startsWith(server.callback_url);
660
- });
661
- if (!matchingServer) throw new Error(`No callback URI match found for the request url: ${req.url}. Was the request matched with \`isCallbackRequest()\`?`);
662
- const serverId = matchingServer.id;
663
785
  const code = url.searchParams.get("code");
664
786
  const state = url.searchParams.get("state");
665
787
  const error = url.searchParams.get("error");
666
788
  const errorDescription = url.searchParams.get("error_description");
789
+ if (!state) throw new Error("Unauthorized: no state provided");
790
+ const serverId = this.extractServerIdFromState(state);
791
+ if (!serverId) throw new Error("No serverId found in state parameter. Expected format: {nonce}.{serverId}");
792
+ if (!this.getServersFromStorage().some((server) => server.id === serverId)) throw new Error(`No server found with id "${serverId}". Was the request matched with \`isCallbackRequest()\`?`);
793
+ if (this.mcpConnections[serverId] === void 0) throw new Error(`Could not find serverId: ${serverId}`);
794
+ const conn = this.mcpConnections[serverId];
795
+ if (!conn.options.transport.authProvider) throw new Error("Trying to finalize authentication for a server connection without an authProvider");
796
+ const authProvider = conn.options.transport.authProvider;
797
+ authProvider.serverId = serverId;
798
+ const stateValidation = await authProvider.checkState(state);
799
+ if (!stateValidation.valid) throw new Error(`Invalid state: ${stateValidation.error}`);
667
800
  if (error) return {
668
801
  serverId,
669
802
  authSuccess: false,
670
803
  authError: errorDescription || error
671
804
  };
672
805
  if (!code) throw new Error("Unauthorized: no code provided");
673
- if (!state) throw new Error("Unauthorized: no state provided");
674
- if (this.mcpConnections[serverId] === void 0) throw new Error(`Could not find serverId: ${serverId}`);
675
- if (this.mcpConnections[serverId].connectionState === "ready") return {
676
- serverId,
677
- authSuccess: true
678
- };
679
- if (this.mcpConnections[serverId].connectionState !== "authenticating") throw new Error(`Failed to authenticate: the client is in "${this.mcpConnections[serverId].connectionState}" state, expected "authenticating"`);
680
- const conn = this.mcpConnections[serverId];
681
- if (!conn.options.transport.authProvider) throw new Error("Trying to finalize authentication for a server connection without an authProvider");
682
- const clientId = conn.options.transport.authProvider.clientId || state;
683
- conn.options.transport.authProvider.clientId = clientId;
684
- conn.options.transport.authProvider.serverId = serverId;
806
+ if (this.mcpConnections[serverId].connectionState === MCPConnectionState.READY || this.mcpConnections[serverId].connectionState === MCPConnectionState.CONNECTED) {
807
+ this.clearServerAuthUrl(serverId);
808
+ return {
809
+ serverId,
810
+ authSuccess: true
811
+ };
812
+ }
813
+ if (this.mcpConnections[serverId].connectionState !== MCPConnectionState.AUTHENTICATING) throw new Error(`Failed to authenticate: the client is in "${this.mcpConnections[serverId].connectionState}" state, expected "authenticating"`);
685
814
  try {
815
+ await authProvider.consumeState(state);
686
816
  await conn.completeAuthorization(code);
817
+ await authProvider.deleteCodeVerifier();
687
818
  this.clearServerAuthUrl(serverId);
688
819
  this._onServerStateChanged.fire();
689
820
  return {
690
821
  serverId,
691
822
  authSuccess: true
692
823
  };
693
- } catch (error$1) {
694
- const errorMessage = error$1 instanceof Error ? error$1.message : String(error$1);
824
+ } catch (authError) {
825
+ const errorMessage = authError instanceof Error ? authError.message : String(authError);
695
826
  this._onServerStateChanged.fire();
696
827
  return {
697
828
  serverId,
@@ -701,8 +832,40 @@ var MCPClientManager = class {
701
832
  }
702
833
  }
703
834
  /**
835
+ * Discover server capabilities if connection is in CONNECTED or READY state.
836
+ * Transitions to DISCOVERING then READY (or CONNECTED on error).
837
+ * Can be called to refresh server capabilities (e.g., from a UI refresh button).
838
+ *
839
+ * If called while a previous discovery is in-flight for the same server,
840
+ * the previous discovery will be aborted.
841
+ *
842
+ * @param serverId The server ID to discover
843
+ * @param options Optional configuration
844
+ * @param options.timeoutMs Timeout in milliseconds (default: 30000)
845
+ * @returns Result with current state and optional error, or undefined if connection not found
846
+ */
847
+ async discoverIfConnected(serverId, options = {}) {
848
+ const conn = this.mcpConnections[serverId];
849
+ if (!conn) {
850
+ this._onObservabilityEvent.fire({
851
+ type: "mcp:client:discover",
852
+ displayMessage: `Connection not found for ${serverId}`,
853
+ payload: {},
854
+ timestamp: Date.now(),
855
+ id: nanoid()
856
+ });
857
+ return;
858
+ }
859
+ const result = await conn.discover(options);
860
+ this._onServerStateChanged.fire();
861
+ return {
862
+ ...result,
863
+ state: conn.connectionState
864
+ };
865
+ }
866
+ /**
704
867
  * Establish connection in the background after OAuth completion
705
- * This method is called asynchronously and doesn't block the OAuth callback response
868
+ * This method connects to the server and discovers its capabilities
706
869
  * @param serverId The server ID to establish connection for
707
870
  */
708
871
  async establishConnection(serverId) {
@@ -717,25 +880,34 @@ var MCPClientManager = class {
717
880
  });
718
881
  return;
719
882
  }
720
- try {
721
- await conn.establishConnection();
722
- this._onServerStateChanged.fire();
723
- } catch (error) {
724
- const url = conn.url.toString();
883
+ if (conn.connectionState === MCPConnectionState.DISCOVERING || conn.connectionState === MCPConnectionState.READY) {
725
884
  this._onObservabilityEvent.fire({
726
885
  type: "mcp:client:connect",
727
- displayMessage: `Failed to establish connection to server ${serverId} with url ${url}`,
886
+ displayMessage: `establishConnection skipped for ${serverId}, already in ${conn.connectionState} state`,
728
887
  payload: {
729
- url,
730
- transport: conn.options.transport.type ?? "auto",
731
- state: conn.connectionState,
732
- error: toErrorMessage(error)
888
+ url: conn.url.toString(),
889
+ transport: conn.options.transport.type || "unknown",
890
+ state: conn.connectionState
733
891
  },
734
892
  timestamp: Date.now(),
735
893
  id: nanoid()
736
894
  });
737
- this._onServerStateChanged.fire();
895
+ return;
738
896
  }
897
+ const connectResult = await this.connectToServer(serverId);
898
+ this._onServerStateChanged.fire();
899
+ if (connectResult.state === MCPConnectionState.CONNECTED) await this.discoverIfConnected(serverId);
900
+ this._onObservabilityEvent.fire({
901
+ type: "mcp:client:connect",
902
+ displayMessage: `establishConnection completed for ${serverId}, final state: ${conn.connectionState}`,
903
+ payload: {
904
+ url: conn.url.toString(),
905
+ transport: conn.options.transport.type || "unknown",
906
+ state: conn.connectionState
907
+ },
908
+ timestamp: Date.now(),
909
+ id: nanoid()
910
+ });
739
911
  }
740
912
  /**
741
913
  * Configure OAuth callback handling
@@ -779,7 +951,7 @@ var MCPClientManager = class {
779
951
  */
780
952
  getAITools() {
781
953
  if (!this.jsonSchema) throw new Error("jsonSchema not initialized.");
782
- for (const [id, conn] of Object.entries(this.mcpConnections)) if (conn.connectionState !== "ready" && conn.connectionState !== "authenticating") console.warn(`[getAITools] WARNING: Reading tools from connection ${id} in state "${conn.connectionState}". Tools may not be loaded yet.`);
954
+ for (const [id, conn] of Object.entries(this.mcpConnections)) 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.`);
783
955
  return Object.fromEntries(getNamespacedData(this.mcpConnections, "tools").map((tool) => {
784
956
  return [`tool_${tool.serverId.replace(/-/g, "")}_${tool.name}`, {
785
957
  description: tool.description,
@@ -809,10 +981,18 @@ var MCPClientManager = class {
809
981
  return this.getAITools();
810
982
  }
811
983
  /**
812
- * Closes all connections to MCP servers
984
+ * Closes all active in-memory connections to MCP servers.
985
+ *
986
+ * Note: This only closes the transport connections - it does NOT remove
987
+ * servers from storage. Servers will still be listed and their callback
988
+ * URLs will still match incoming OAuth requests.
989
+ *
990
+ * Use removeServer() instead if you want to fully clean up a server
991
+ * (closes connection AND removes from storage).
813
992
  */
814
993
  async closeAllConnections() {
815
994
  const ids = Object.keys(this.mcpConnections);
995
+ for (const id of ids) this.mcpConnections[id].cancelDiscovery();
816
996
  await Promise.all(ids.map(async (id) => {
817
997
  await this.mcpConnections[id].client.close();
818
998
  }));
@@ -829,6 +1009,7 @@ var MCPClientManager = class {
829
1009
  */
830
1010
  async closeConnection(id) {
831
1011
  if (!this.mcpConnections[id]) throw new Error(`Connection with id "${id}" does not exist.`);
1012
+ this.mcpConnections[id].cancelDiscovery();
832
1013
  await this.mcpConnections[id].client.close();
833
1014
  delete this.mcpConnections[id];
834
1015
  const store = this._connectionDisposables.get(id);
@@ -836,9 +1017,12 @@ var MCPClientManager = class {
836
1017
  this._connectionDisposables.delete(id);
837
1018
  }
838
1019
  /**
839
- * Remove an MCP server from storage
1020
+ * Remove an MCP server - closes connection if active and removes from storage.
840
1021
  */
841
- removeServer(serverId) {
1022
+ async removeServer(serverId) {
1023
+ if (this.mcpConnections[serverId]) try {
1024
+ await this.closeConnection(serverId);
1025
+ } catch (_e) {}
842
1026
  this.removeServerFromStorage(serverId);
843
1027
  this._onServerStateChanged.fire();
844
1028
  }
@@ -917,5 +1101,5 @@ function getNamespacedData(mcpClients, type) {
917
1101
  }
918
1102
 
919
1103
  //#endregion
920
- export { getNamespacedData as n, DisposableStore as r, MCPClientManager as t };
921
- //# sourceMappingURL=client-C8VrzljV.js.map
1104
+ export { DisposableStore as i, getNamespacedData as n, MCPConnectionState as r, MCPClientManager as t };
1105
+ //# sourceMappingURL=client-QZa2Rq0l.js.map