agents 0.3.10 → 0.4.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 (67) hide show
  1. package/README.md +2 -2
  2. package/dist/{index-N6791tVt.d.ts → agent-DY6QmSI_.d.ts} +3 -25
  3. package/dist/ai-types.js +1 -1
  4. package/dist/client-connection-CGMuV62J.js +472 -0
  5. package/dist/client-connection-CGMuV62J.js.map +1 -0
  6. package/dist/client-storage-Cvy5r9FG.d.ts +355 -0
  7. package/dist/client.d.ts +11 -7
  8. package/dist/client.js +6 -2
  9. package/dist/client.js.map +1 -1
  10. package/dist/email.d.ts +146 -16
  11. package/dist/email.js +222 -2
  12. package/dist/email.js.map +1 -0
  13. package/dist/index.d.ts +142 -41
  14. package/dist/index.js +2326 -6
  15. package/dist/index.js.map +1 -0
  16. package/dist/internal_context.d.ts +33 -6
  17. package/dist/internal_context.js +11 -2
  18. package/dist/internal_context.js.map +1 -0
  19. package/dist/mcp/client.d.ts +516 -2
  20. package/dist/mcp/client.js +662 -3
  21. package/dist/mcp/client.js.map +1 -0
  22. package/dist/mcp/do-oauth-client-provider.d.ts +61 -2
  23. package/dist/mcp/do-oauth-client-provider.js +154 -2
  24. package/dist/mcp/do-oauth-client-provider.js.map +1 -0
  25. package/dist/mcp/index.d.ts +3 -5
  26. package/dist/mcp/index.js +8 -7
  27. package/dist/mcp/index.js.map +1 -1
  28. package/dist/mcp/x402.d.ts +34 -14
  29. package/dist/mcp/x402.js +128 -66
  30. package/dist/mcp/x402.js.map +1 -1
  31. package/dist/{mcp-BwPscEiF.d.ts → mcp-Dw5vDrY8.d.ts} +1 -1
  32. package/dist/observability/index.d.ts +23 -2
  33. package/dist/observability/index.js +25 -6
  34. package/dist/observability/index.js.map +1 -0
  35. package/dist/react.d.ts +10 -10
  36. package/dist/react.js +6 -2
  37. package/dist/react.js.map +1 -1
  38. package/dist/types.d.ts +14 -1
  39. package/dist/types.js +16 -2
  40. package/dist/types.js.map +1 -0
  41. package/dist/utils.js +15 -2
  42. package/dist/utils.js.map +1 -0
  43. package/dist/workflow-types.d.ts +235 -23
  44. package/dist/workflows.d.ts +22 -24
  45. package/dist/workflows.js +2 -5
  46. package/dist/workflows.js.map +1 -1
  47. package/package.json +24 -28
  48. package/dist/client-CtC9E06G.js +0 -1122
  49. package/dist/client-CtC9E06G.js.map +0 -1
  50. package/dist/client-DV1CZKqa.d.ts +0 -969
  51. package/dist/do-oauth-client-provider-BqnOQzjy.d.ts +0 -70
  52. package/dist/do-oauth-client-provider-DDg8QrEA.js +0 -155
  53. package/dist/do-oauth-client-provider-DDg8QrEA.js.map +0 -1
  54. package/dist/email-8ljcpvwV.d.ts +0 -157
  55. package/dist/email-XHsSYsTO.js +0 -223
  56. package/dist/email-XHsSYsTO.js.map +0 -1
  57. package/dist/internal_context-CEu5ji80.d.ts +0 -29
  58. package/dist/internal_context-D9eKFth1.js +0 -8
  59. package/dist/internal_context-D9eKFth1.js.map +0 -1
  60. package/dist/src-i_UcyBYf.js +0 -2147
  61. package/dist/src-i_UcyBYf.js.map +0 -1
  62. package/dist/types-BITaDFf-.js +0 -16
  63. package/dist/types-BITaDFf-.js.map +0 -1
  64. package/dist/types-DSSHBW6w.d.ts +0 -14
  65. package/dist/utils-B49TmLCI.js +0 -16
  66. package/dist/utils-B49TmLCI.js.map +0 -1
  67. package/dist/workflow-types-Z_Oem1FJ.d.ts +0 -260
@@ -1,4 +1,663 @@
1
- import { n as getNamespacedData, t as MCPClientManager } from "../client-CtC9E06G.js";
2
- import "../do-oauth-client-provider-DDg8QrEA.js";
1
+ import { a as Emitter, i as DisposableStore, n as MCPConnectionState, r as toErrorMessage, t as MCPClientConnection } from "../client-connection-CGMuV62J.js";
2
+ import { DurableObjectOAuthClientProvider } from "./do-oauth-client-provider.js";
3
+ import { nanoid } from "nanoid";
4
+ import escapeHtml from "escape-html";
5
+ import { CfWorkerJsonSchemaValidator } from "@modelcontextprotocol/sdk/validation/cfworker-provider.js";
3
6
 
4
- export { MCPClientManager, getNamespacedData };
7
+ //#region src/mcp/client.ts
8
+ const defaultClientOptions = { jsonSchemaValidator: new CfWorkerJsonSchemaValidator() };
9
+ /**
10
+ * Utility class that aggregates multiple MCP clients into one
11
+ */
12
+ var MCPClientManager = class {
13
+ /**
14
+ * @param _name Name of the MCP client
15
+ * @param _version Version of the MCP Client
16
+ * @param options Storage adapter for persisting MCP server state
17
+ */
18
+ constructor(_name, _version, options) {
19
+ this._name = _name;
20
+ this._version = _version;
21
+ this.mcpConnections = {};
22
+ this._didWarnAboutUnstableGetAITools = false;
23
+ this._connectionDisposables = /* @__PURE__ */ new Map();
24
+ this._isRestored = false;
25
+ this._onObservabilityEvent = new Emitter();
26
+ this.onObservabilityEvent = this._onObservabilityEvent.event;
27
+ this._onServerStateChanged = new Emitter();
28
+ this.onServerStateChanged = this._onServerStateChanged.event;
29
+ if (!options.storage) throw new Error("MCPClientManager requires a valid DurableObjectStorage instance");
30
+ this._storage = options.storage;
31
+ }
32
+ sql(query, ...bindings) {
33
+ return [...this._storage.sql.exec(query, ...bindings)];
34
+ }
35
+ saveServerToStorage(server) {
36
+ this.sql(`INSERT OR REPLACE INTO cf_agents_mcp_servers (
37
+ id, name, server_url, client_id, auth_url, callback_url, server_options
38
+ ) VALUES (?, ?, ?, ?, ?, ?, ?)`, server.id, server.name, server.server_url, server.client_id ?? null, server.auth_url ?? null, server.callback_url, server.server_options ?? null);
39
+ }
40
+ removeServerFromStorage(serverId) {
41
+ this.sql("DELETE FROM cf_agents_mcp_servers WHERE id = ?", serverId);
42
+ }
43
+ getServersFromStorage() {
44
+ return this.sql("SELECT id, name, server_url, client_id, auth_url, callback_url, server_options FROM cf_agents_mcp_servers");
45
+ }
46
+ clearServerAuthUrl(serverId) {
47
+ this.sql("UPDATE cf_agents_mcp_servers SET auth_url = NULL WHERE id = ?", serverId);
48
+ }
49
+ failConnection(serverId, error) {
50
+ this.clearServerAuthUrl(serverId);
51
+ if (this.mcpConnections[serverId]) {
52
+ this.mcpConnections[serverId].connectionState = MCPConnectionState.FAILED;
53
+ this.mcpConnections[serverId].connectionError = error;
54
+ }
55
+ this._onServerStateChanged.fire();
56
+ return {
57
+ serverId,
58
+ authSuccess: false,
59
+ authError: error
60
+ };
61
+ }
62
+ /**
63
+ * Create an auth provider for a server
64
+ * @internal
65
+ */
66
+ createAuthProvider(serverId, callbackUrl, clientName, clientId) {
67
+ if (!this._storage) throw new Error("Cannot create auth provider: storage is not initialized");
68
+ const authProvider = new DurableObjectOAuthClientProvider(this._storage, clientName, callbackUrl);
69
+ authProvider.serverId = serverId;
70
+ if (clientId) authProvider.clientId = clientId;
71
+ return authProvider;
72
+ }
73
+ /**
74
+ * Restore MCP server connections from storage
75
+ * This method is called on Agent initialization to restore previously connected servers
76
+ *
77
+ * @param clientName Name to use for OAuth client (typically the agent instance name)
78
+ */
79
+ async restoreConnectionsFromStorage(clientName) {
80
+ if (this._isRestored) return;
81
+ const servers = this.getServersFromStorage();
82
+ if (!servers || servers.length === 0) {
83
+ this._isRestored = true;
84
+ return;
85
+ }
86
+ for (const server of servers) {
87
+ const existingConn = this.mcpConnections[server.id];
88
+ if (existingConn) {
89
+ if (existingConn.connectionState === MCPConnectionState.READY) {
90
+ console.warn(`[MCPClientManager] Server ${server.id} already has a ready connection. Skipping recreation.`);
91
+ continue;
92
+ }
93
+ if (existingConn.connectionState === MCPConnectionState.AUTHENTICATING || existingConn.connectionState === MCPConnectionState.CONNECTING || existingConn.connectionState === MCPConnectionState.DISCOVERING) continue;
94
+ if (existingConn.connectionState === MCPConnectionState.FAILED) {
95
+ try {
96
+ await existingConn.client.close();
97
+ } catch (error) {
98
+ console.warn(`[MCPClientManager] Error closing failed connection ${server.id}:`, error);
99
+ }
100
+ delete this.mcpConnections[server.id];
101
+ this._connectionDisposables.get(server.id)?.dispose();
102
+ this._connectionDisposables.delete(server.id);
103
+ }
104
+ }
105
+ const parsedOptions = server.server_options ? JSON.parse(server.server_options) : null;
106
+ const authProvider = this.createAuthProvider(server.id, server.callback_url, clientName, server.client_id ?? void 0);
107
+ const conn = this.createConnection(server.id, server.server_url, {
108
+ client: parsedOptions?.client ?? {},
109
+ transport: {
110
+ ...parsedOptions?.transport ?? {},
111
+ type: parsedOptions?.transport?.type ?? "auto",
112
+ authProvider
113
+ }
114
+ });
115
+ if (server.auth_url) {
116
+ conn.connectionState = MCPConnectionState.AUTHENTICATING;
117
+ continue;
118
+ }
119
+ this._restoreServer(server.id);
120
+ }
121
+ this._isRestored = true;
122
+ }
123
+ /**
124
+ * Internal method to restore a single server connection and discovery
125
+ */
126
+ async _restoreServer(serverId) {
127
+ if ((await this.connectToServer(serverId).catch((error) => {
128
+ console.error(`Error connecting to ${serverId}:`, error);
129
+ return null;
130
+ }))?.state === MCPConnectionState.CONNECTED) {
131
+ const discoverResult = await this.discoverIfConnected(serverId);
132
+ if (discoverResult && !discoverResult.success) console.error(`Error discovering ${serverId}:`, discoverResult.error);
133
+ }
134
+ }
135
+ /**
136
+ * Connect to and register an MCP server
137
+ *
138
+ * @deprecated This method is maintained for backward compatibility.
139
+ * For new code, use registerServer() and connectToServer() separately.
140
+ *
141
+ * @param url Server URL
142
+ * @param options Connection options
143
+ * @returns Object with server ID, auth URL (if OAuth), and client ID (if OAuth)
144
+ */
145
+ async connect(url, options = {}) {
146
+ /**
147
+ * We need to delay loading ai sdk, because putting it in module scope is
148
+ * causing issues with startup time.
149
+ * The only place it's used is in getAITools, which only matters after
150
+ * .connect() is called on at least one server.
151
+ * So it's safe to delay loading it until .connect() is called.
152
+ */
153
+ await this.ensureJsonSchema();
154
+ const id = options.reconnect?.id ?? nanoid(8);
155
+ if (options.transport?.authProvider) {
156
+ options.transport.authProvider.serverId = id;
157
+ if (options.reconnect?.oauthClientId) options.transport.authProvider.clientId = options.reconnect?.oauthClientId;
158
+ }
159
+ if (!options.reconnect?.oauthCode || !this.mcpConnections[id]) {
160
+ const normalizedTransport = {
161
+ ...options.transport,
162
+ type: options.transport?.type ?? "auto"
163
+ };
164
+ this.mcpConnections[id] = new MCPClientConnection(new URL(url), {
165
+ name: this._name,
166
+ version: this._version
167
+ }, {
168
+ client: options.client ?? {},
169
+ transport: normalizedTransport
170
+ });
171
+ const store = new DisposableStore();
172
+ const existing = this._connectionDisposables.get(id);
173
+ if (existing) existing.dispose();
174
+ this._connectionDisposables.set(id, store);
175
+ store.add(this.mcpConnections[id].onObservabilityEvent((event) => {
176
+ this._onObservabilityEvent.fire(event);
177
+ }));
178
+ }
179
+ await this.mcpConnections[id].init();
180
+ if (options.reconnect?.oauthCode) try {
181
+ await this.mcpConnections[id].completeAuthorization(options.reconnect.oauthCode);
182
+ await this.mcpConnections[id].init();
183
+ } catch (error) {
184
+ this._onObservabilityEvent.fire({
185
+ type: "mcp:client:connect",
186
+ displayMessage: `Failed to complete OAuth reconnection for ${id} for ${url}`,
187
+ payload: {
188
+ url,
189
+ transport: options.transport?.type ?? "auto",
190
+ state: this.mcpConnections[id].connectionState,
191
+ error: toErrorMessage(error)
192
+ },
193
+ timestamp: Date.now(),
194
+ id
195
+ });
196
+ throw error;
197
+ }
198
+ const authUrl = options.transport?.authProvider?.authUrl;
199
+ if (this.mcpConnections[id].connectionState === MCPConnectionState.AUTHENTICATING && authUrl && options.transport?.authProvider?.redirectUrl) return {
200
+ authUrl,
201
+ clientId: options.transport?.authProvider?.clientId,
202
+ id
203
+ };
204
+ const discoverResult = await this.discoverIfConnected(id);
205
+ if (discoverResult && !discoverResult.success) throw new Error(`Failed to discover server capabilities: ${discoverResult.error}`);
206
+ return { id };
207
+ }
208
+ /**
209
+ * Create an in-memory connection object and set up observability
210
+ * Does NOT save to storage - use registerServer() for that
211
+ * @returns The connection object (existing or newly created)
212
+ */
213
+ createConnection(id, url, options) {
214
+ if (this.mcpConnections[id]) return this.mcpConnections[id];
215
+ const normalizedTransport = {
216
+ ...options.transport,
217
+ type: options.transport?.type ?? "auto"
218
+ };
219
+ this.mcpConnections[id] = new MCPClientConnection(new URL(url), {
220
+ name: this._name,
221
+ version: this._version
222
+ }, {
223
+ client: {
224
+ ...defaultClientOptions,
225
+ ...options.client
226
+ },
227
+ transport: normalizedTransport
228
+ });
229
+ const store = new DisposableStore();
230
+ const existing = this._connectionDisposables.get(id);
231
+ if (existing) existing.dispose();
232
+ this._connectionDisposables.set(id, store);
233
+ store.add(this.mcpConnections[id].onObservabilityEvent((event) => {
234
+ this._onObservabilityEvent.fire(event);
235
+ }));
236
+ return this.mcpConnections[id];
237
+ }
238
+ /**
239
+ * Register an MCP server connection without connecting
240
+ * Creates the connection object, sets up observability, and saves to storage
241
+ *
242
+ * @param id Server ID
243
+ * @param options Registration options including URL, name, callback URL, and connection config
244
+ * @returns Server ID
245
+ */
246
+ async registerServer(id, options) {
247
+ this.createConnection(id, options.url, {
248
+ client: options.client,
249
+ transport: {
250
+ ...options.transport,
251
+ type: options.transport?.type ?? "auto"
252
+ }
253
+ });
254
+ const { authProvider: _, ...transportWithoutAuth } = options.transport ?? {};
255
+ this.saveServerToStorage({
256
+ id,
257
+ name: options.name,
258
+ server_url: options.url,
259
+ callback_url: options.callbackUrl,
260
+ client_id: options.clientId ?? null,
261
+ auth_url: options.authUrl ?? null,
262
+ server_options: JSON.stringify({
263
+ client: options.client,
264
+ transport: transportWithoutAuth
265
+ })
266
+ });
267
+ this._onServerStateChanged.fire();
268
+ return id;
269
+ }
270
+ /**
271
+ * Connect to an already registered MCP server and initialize the connection.
272
+ *
273
+ * For OAuth servers, returns `{ state: "authenticating", authUrl, clientId? }`.
274
+ * The user must complete the OAuth flow via the authUrl, which triggers a
275
+ * callback handled by `handleCallbackRequest()`.
276
+ *
277
+ * For non-OAuth servers, establishes the transport connection and returns
278
+ * `{ state: "connected" }`. Call `discoverIfConnected()` afterwards to
279
+ * discover capabilities and transition to "ready" state.
280
+ *
281
+ * @param id Server ID (must be registered first via registerServer())
282
+ * @returns Connection result with current state and OAuth info (if applicable)
283
+ */
284
+ async connectToServer(id) {
285
+ const conn = this.mcpConnections[id];
286
+ if (!conn) throw new Error(`Server ${id} is not registered. Call registerServer() first.`);
287
+ const error = await conn.init();
288
+ this._onServerStateChanged.fire();
289
+ switch (conn.connectionState) {
290
+ case MCPConnectionState.FAILED: return {
291
+ state: conn.connectionState,
292
+ error: error ?? "Unknown connection error"
293
+ };
294
+ case MCPConnectionState.AUTHENTICATING: {
295
+ const authUrl = conn.options.transport.authProvider?.authUrl;
296
+ const redirectUrl = conn.options.transport.authProvider?.redirectUrl;
297
+ if (!authUrl || !redirectUrl) return {
298
+ state: MCPConnectionState.FAILED,
299
+ error: `OAuth configuration incomplete: missing ${!authUrl ? "authUrl" : "redirectUrl"}`
300
+ };
301
+ const clientId = conn.options.transport.authProvider?.clientId;
302
+ const serverRow = this.getServersFromStorage().find((s) => s.id === id);
303
+ if (serverRow) {
304
+ this.saveServerToStorage({
305
+ ...serverRow,
306
+ auth_url: authUrl,
307
+ client_id: clientId ?? null
308
+ });
309
+ this._onServerStateChanged.fire();
310
+ }
311
+ return {
312
+ state: conn.connectionState,
313
+ authUrl,
314
+ clientId
315
+ };
316
+ }
317
+ case MCPConnectionState.CONNECTED: return { state: conn.connectionState };
318
+ default: return {
319
+ state: MCPConnectionState.FAILED,
320
+ error: `Unexpected connection state after init: ${conn.connectionState}`
321
+ };
322
+ }
323
+ }
324
+ extractServerIdFromState(state) {
325
+ if (!state) return null;
326
+ const parts = state.split(".");
327
+ return parts.length === 2 ? parts[1] : null;
328
+ }
329
+ isCallbackRequest(req) {
330
+ if (req.method !== "GET") return false;
331
+ const url = new URL(req.url);
332
+ const state = url.searchParams.get("state");
333
+ const serverId = this.extractServerIdFromState(state);
334
+ if (!serverId) return false;
335
+ return this.getServersFromStorage().some((server) => {
336
+ if (server.id !== serverId) return false;
337
+ try {
338
+ const storedUrl = new URL(server.callback_url);
339
+ return storedUrl.origin === url.origin && storedUrl.pathname === url.pathname;
340
+ } catch {
341
+ return false;
342
+ }
343
+ });
344
+ }
345
+ async handleCallbackRequest(req) {
346
+ const url = new URL(req.url);
347
+ const code = url.searchParams.get("code");
348
+ const state = url.searchParams.get("state");
349
+ const error = url.searchParams.get("error");
350
+ const errorDescription = url.searchParams.get("error_description");
351
+ if (!state) throw new Error("Unauthorized: no state provided");
352
+ const serverId = this.extractServerIdFromState(state);
353
+ if (!serverId) throw new Error("No serverId found in state parameter. Expected format: {nonce}.{serverId}");
354
+ if (!this.getServersFromStorage().some((server) => server.id === serverId)) throw new Error(`No server found with id "${serverId}". Was the request matched with \`isCallbackRequest()\`?`);
355
+ if (this.mcpConnections[serverId] === void 0) throw new Error(`Could not find serverId: ${serverId}`);
356
+ const conn = this.mcpConnections[serverId];
357
+ try {
358
+ if (!conn.options.transport.authProvider) throw new Error("Trying to finalize authentication for a server connection without an authProvider");
359
+ const authProvider = conn.options.transport.authProvider;
360
+ authProvider.serverId = serverId;
361
+ const stateValidation = await authProvider.checkState(state);
362
+ if (!stateValidation.valid) throw new Error(stateValidation.error || "Invalid state");
363
+ if (error) throw new Error(escapeHtml(errorDescription || error));
364
+ if (!code) throw new Error("Unauthorized: no code provided");
365
+ if (conn.connectionState === MCPConnectionState.READY || conn.connectionState === MCPConnectionState.CONNECTED) {
366
+ this.clearServerAuthUrl(serverId);
367
+ return {
368
+ serverId,
369
+ authSuccess: true
370
+ };
371
+ }
372
+ if (conn.connectionState !== MCPConnectionState.AUTHENTICATING) throw new Error(`Failed to authenticate: the client is in "${conn.connectionState}" state, expected "authenticating"`);
373
+ await authProvider.consumeState(state);
374
+ await conn.completeAuthorization(code);
375
+ await authProvider.deleteCodeVerifier();
376
+ this.clearServerAuthUrl(serverId);
377
+ conn.connectionError = null;
378
+ this._onServerStateChanged.fire();
379
+ return {
380
+ serverId,
381
+ authSuccess: true
382
+ };
383
+ } catch (err) {
384
+ const message = err instanceof Error ? err.message : String(err);
385
+ return this.failConnection(serverId, message);
386
+ }
387
+ }
388
+ /**
389
+ * Discover server capabilities if connection is in CONNECTED or READY state.
390
+ * Transitions to DISCOVERING then READY (or CONNECTED on error).
391
+ * Can be called to refresh server capabilities (e.g., from a UI refresh button).
392
+ *
393
+ * If called while a previous discovery is in-flight for the same server,
394
+ * the previous discovery will be aborted.
395
+ *
396
+ * @param serverId The server ID to discover
397
+ * @param options Optional configuration
398
+ * @param options.timeoutMs Timeout in milliseconds (default: 30000)
399
+ * @returns Result with current state and optional error, or undefined if connection not found
400
+ */
401
+ async discoverIfConnected(serverId, options = {}) {
402
+ const conn = this.mcpConnections[serverId];
403
+ if (!conn) {
404
+ this._onObservabilityEvent.fire({
405
+ type: "mcp:client:discover",
406
+ displayMessage: `Connection not found for ${serverId}`,
407
+ payload: {},
408
+ timestamp: Date.now(),
409
+ id: nanoid()
410
+ });
411
+ return;
412
+ }
413
+ const result = await conn.discover(options);
414
+ this._onServerStateChanged.fire();
415
+ return {
416
+ ...result,
417
+ state: conn.connectionState
418
+ };
419
+ }
420
+ /**
421
+ * Establish connection in the background after OAuth completion
422
+ * This method connects to the server and discovers its capabilities
423
+ * @param serverId The server ID to establish connection for
424
+ */
425
+ async establishConnection(serverId) {
426
+ const conn = this.mcpConnections[serverId];
427
+ if (!conn) {
428
+ this._onObservabilityEvent.fire({
429
+ type: "mcp:client:preconnect",
430
+ displayMessage: `Connection not found for serverId: ${serverId}`,
431
+ payload: { serverId },
432
+ timestamp: Date.now(),
433
+ id: nanoid()
434
+ });
435
+ return;
436
+ }
437
+ if (conn.connectionState === MCPConnectionState.DISCOVERING || conn.connectionState === MCPConnectionState.READY) {
438
+ this._onObservabilityEvent.fire({
439
+ type: "mcp:client:connect",
440
+ displayMessage: `establishConnection skipped for ${serverId}, already in ${conn.connectionState} state`,
441
+ payload: {
442
+ url: conn.url.toString(),
443
+ transport: conn.options.transport.type || "unknown",
444
+ state: conn.connectionState
445
+ },
446
+ timestamp: Date.now(),
447
+ id: nanoid()
448
+ });
449
+ return;
450
+ }
451
+ const connectResult = await this.connectToServer(serverId);
452
+ this._onServerStateChanged.fire();
453
+ if (connectResult.state === MCPConnectionState.CONNECTED) await this.discoverIfConnected(serverId);
454
+ this._onObservabilityEvent.fire({
455
+ type: "mcp:client:connect",
456
+ displayMessage: `establishConnection completed for ${serverId}, final state: ${conn.connectionState}`,
457
+ payload: {
458
+ url: conn.url.toString(),
459
+ transport: conn.options.transport.type || "unknown",
460
+ state: conn.connectionState
461
+ },
462
+ timestamp: Date.now(),
463
+ id: nanoid()
464
+ });
465
+ }
466
+ /**
467
+ * Configure OAuth callback handling
468
+ * @param config OAuth callback configuration
469
+ */
470
+ configureOAuthCallback(config) {
471
+ this._oauthCallbackConfig = config;
472
+ }
473
+ /**
474
+ * Get the current OAuth callback configuration
475
+ * @returns The current OAuth callback configuration
476
+ */
477
+ getOAuthCallbackConfig() {
478
+ return this._oauthCallbackConfig;
479
+ }
480
+ /**
481
+ * @returns namespaced list of tools
482
+ */
483
+ listTools() {
484
+ return getNamespacedData(this.mcpConnections, "tools");
485
+ }
486
+ /**
487
+ * Lazy-loads the jsonSchema function from the AI SDK.
488
+ *
489
+ * This defers importing the "ai" package until it's actually needed, which helps reduce
490
+ * initial bundle size and startup time. The jsonSchema function is required for converting
491
+ * MCP tools into AI SDK tool definitions via getAITools().
492
+ *
493
+ * @internal This method is for internal use only. It's automatically called before operations
494
+ * that need jsonSchema (like getAITools() or OAuth flows). External consumers should not need
495
+ * to call this directly.
496
+ */
497
+ async ensureJsonSchema() {
498
+ if (!this.jsonSchema) {
499
+ const { jsonSchema } = await import("ai");
500
+ this.jsonSchema = jsonSchema;
501
+ }
502
+ }
503
+ /**
504
+ * @returns a set of tools that you can use with the AI SDK
505
+ */
506
+ getAITools() {
507
+ if (!this.jsonSchema) throw new Error("jsonSchema not initialized.");
508
+ 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.`);
509
+ return Object.fromEntries(getNamespacedData(this.mcpConnections, "tools").map((tool) => {
510
+ return [`tool_${tool.serverId.replace(/-/g, "")}_${tool.name}`, {
511
+ description: tool.description,
512
+ execute: async (args) => {
513
+ const result = await this.callTool({
514
+ arguments: args,
515
+ name: tool.name,
516
+ serverId: tool.serverId
517
+ });
518
+ if (result.isError) {
519
+ const textContent = result.content?.[0];
520
+ const message = textContent?.type === "text" && textContent.text ? textContent.text : "Tool call failed";
521
+ throw new Error(message);
522
+ }
523
+ return result;
524
+ },
525
+ inputSchema: this.jsonSchema(tool.inputSchema),
526
+ outputSchema: tool.outputSchema ? this.jsonSchema(tool.outputSchema) : void 0
527
+ }];
528
+ }));
529
+ }
530
+ /**
531
+ * @deprecated this has been renamed to getAITools(), and unstable_getAITools will be removed in the next major version
532
+ * @returns a set of tools that you can use with the AI SDK
533
+ */
534
+ unstable_getAITools() {
535
+ if (!this._didWarnAboutUnstableGetAITools) {
536
+ this._didWarnAboutUnstableGetAITools = true;
537
+ console.warn("unstable_getAITools is deprecated, use getAITools instead. unstable_getAITools will be removed in the next major version.");
538
+ }
539
+ return this.getAITools();
540
+ }
541
+ /**
542
+ * Closes all active in-memory connections to MCP servers.
543
+ *
544
+ * Note: This only closes the transport connections - it does NOT remove
545
+ * servers from storage. Servers will still be listed and their callback
546
+ * URLs will still match incoming OAuth requests.
547
+ *
548
+ * Use removeServer() instead if you want to fully clean up a server
549
+ * (closes connection AND removes from storage).
550
+ */
551
+ async closeAllConnections() {
552
+ const ids = Object.keys(this.mcpConnections);
553
+ for (const id of ids) this.mcpConnections[id].cancelDiscovery();
554
+ await Promise.all(ids.map(async (id) => {
555
+ await this.mcpConnections[id].client.close();
556
+ }));
557
+ for (const id of ids) {
558
+ const store = this._connectionDisposables.get(id);
559
+ if (store) store.dispose();
560
+ this._connectionDisposables.delete(id);
561
+ delete this.mcpConnections[id];
562
+ }
563
+ }
564
+ /**
565
+ * Closes a connection to an MCP server
566
+ * @param id The id of the connection to close
567
+ */
568
+ async closeConnection(id) {
569
+ if (!this.mcpConnections[id]) throw new Error(`Connection with id "${id}" does not exist.`);
570
+ this.mcpConnections[id].cancelDiscovery();
571
+ await this.mcpConnections[id].client.close();
572
+ delete this.mcpConnections[id];
573
+ const store = this._connectionDisposables.get(id);
574
+ if (store) store.dispose();
575
+ this._connectionDisposables.delete(id);
576
+ }
577
+ /**
578
+ * Remove an MCP server - closes connection if active and removes from storage.
579
+ */
580
+ async removeServer(serverId) {
581
+ if (this.mcpConnections[serverId]) try {
582
+ await this.closeConnection(serverId);
583
+ } catch (_e) {}
584
+ this.removeServerFromStorage(serverId);
585
+ this._onServerStateChanged.fire();
586
+ }
587
+ /**
588
+ * List all MCP servers from storage
589
+ */
590
+ listServers() {
591
+ return this.getServersFromStorage();
592
+ }
593
+ /**
594
+ * Dispose the manager and all resources.
595
+ */
596
+ async dispose() {
597
+ try {
598
+ await this.closeAllConnections();
599
+ } finally {
600
+ this._onServerStateChanged.dispose();
601
+ this._onObservabilityEvent.dispose();
602
+ }
603
+ }
604
+ /**
605
+ * @returns namespaced list of prompts
606
+ */
607
+ listPrompts() {
608
+ return getNamespacedData(this.mcpConnections, "prompts");
609
+ }
610
+ /**
611
+ * @returns namespaced list of tools
612
+ */
613
+ listResources() {
614
+ return getNamespacedData(this.mcpConnections, "resources");
615
+ }
616
+ /**
617
+ * @returns namespaced list of resource templates
618
+ */
619
+ listResourceTemplates() {
620
+ return getNamespacedData(this.mcpConnections, "resourceTemplates");
621
+ }
622
+ /**
623
+ * Namespaced version of callTool
624
+ */
625
+ async callTool(params, resultSchema, options) {
626
+ const unqualifiedName = params.name.replace(`${params.serverId}.`, "");
627
+ return this.mcpConnections[params.serverId].client.callTool({
628
+ ...params,
629
+ name: unqualifiedName
630
+ }, resultSchema, options);
631
+ }
632
+ /**
633
+ * Namespaced version of readResource
634
+ */
635
+ readResource(params, options) {
636
+ return this.mcpConnections[params.serverId].client.readResource(params, options);
637
+ }
638
+ /**
639
+ * Namespaced version of getPrompt
640
+ */
641
+ getPrompt(params, options) {
642
+ return this.mcpConnections[params.serverId].client.getPrompt(params, options);
643
+ }
644
+ };
645
+ function getNamespacedData(mcpClients, type) {
646
+ return Object.entries(mcpClients).map(([name, conn]) => {
647
+ return {
648
+ data: conn[type],
649
+ name
650
+ };
651
+ }).flatMap(({ name: serverId, data }) => {
652
+ return data.map((item) => {
653
+ return {
654
+ ...item,
655
+ serverId
656
+ };
657
+ });
658
+ });
659
+ }
660
+
661
+ //#endregion
662
+ export { MCPClientManager, getNamespacedData };
663
+ //# sourceMappingURL=client.js.map