agents 0.7.4 → 0.7.6
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.
- package/dist/ai-chat-agent.js +3 -4
- package/dist/ai-chat-agent.js.map +1 -1
- package/dist/ai-chat-v5-migration.js +3 -4
- package/dist/ai-chat-v5-migration.js.map +1 -1
- package/dist/ai-react.js +3 -4
- package/dist/ai-react.js.map +1 -1
- package/dist/ai-types.js +1 -2
- package/dist/ai-types.js.map +1 -1
- package/dist/cli/index.js +2 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/client-K8Z-u76l.js +1468 -0
- package/dist/client-K8Z-u76l.js.map +1 -0
- package/dist/client.js +1 -2
- package/dist/client.js.map +1 -1
- package/dist/codemode/ai.js +2 -2
- package/dist/do-oauth-client-provider-C2jurFjW.d.ts +78 -0
- package/dist/email-U_MG7UET.d.ts +157 -0
- package/dist/email.d.ts +16 -146
- package/dist/email.js +2 -2
- package/dist/email.js.map +1 -1
- package/dist/experimental/forever.d.ts +26 -71
- package/dist/experimental/forever.js +2 -3
- package/dist/experimental/forever.js.map +1 -1
- package/dist/experimental/memory/session/index.js +3 -12
- package/dist/experimental/memory/session/index.js.map +1 -1
- package/dist/experimental/workspace.d.ts +273 -0
- package/dist/experimental/workspace.js +1265 -0
- package/dist/experimental/workspace.js.map +1 -0
- package/dist/index-BS_jL8MI.d.ts +492 -0
- package/dist/index-WBy5hmm3.d.ts +2840 -0
- package/dist/index.d.ts +49 -1320
- package/dist/index.js +281 -138
- package/dist/index.js.map +1 -1
- package/dist/internal_context-DgcmHqS1.d.ts +37 -0
- package/dist/internal_context.d.ts +5 -32
- package/dist/internal_context.js +1 -2
- package/dist/internal_context.js.map +1 -1
- package/dist/mcp/client.d.ts +2 -575
- package/dist/mcp/client.js +1 -847
- package/dist/mcp/do-oauth-client-provider.d.ts +2 -61
- package/dist/mcp/do-oauth-client-provider.js +1 -2
- package/dist/mcp/do-oauth-client-provider.js.map +1 -1
- package/dist/mcp/index.d.ts +2 -95
- package/dist/mcp/index.js +60 -57
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/x402.js +1 -2
- package/dist/mcp/x402.js.map +1 -1
- package/dist/observability/index.d.ts +2 -93
- package/dist/observability/index.js +4 -3
- package/dist/observability/index.js.map +1 -1
- package/dist/react.d.ts +1 -2
- package/dist/react.js +1 -2
- package/dist/react.js.map +1 -1
- package/dist/retries-DXMQGhG3.d.ts +79 -0
- package/dist/retries.d.ts +7 -72
- package/dist/retries.js +1 -1
- package/dist/retries.js.map +1 -1
- package/dist/schedule.js +1 -2
- package/dist/schedule.js.map +1 -1
- package/dist/serializable.js +1 -1
- package/dist/types-BB1plA51.d.ts +15 -0
- package/dist/types.d.ts +1 -14
- package/dist/types.js +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils.js +1 -1
- package/dist/workflow-types-CZNXKj_D.d.ts +260 -0
- package/dist/workflow-types.d.ts +23 -235
- package/dist/workflow-types.js +1 -1
- package/dist/workflow-types.js.map +1 -1
- package/dist/workflows.d.ts +22 -23
- package/dist/workflows.js +5 -6
- package/dist/workflows.js.map +1 -1
- package/package.json +25 -13
- package/dist/agent-eZnMHidZ.d.ts +0 -273
- package/dist/client-connection-D3Wcd6Q6.js +0 -603
- package/dist/client-connection-D3Wcd6Q6.js.map +0 -1
- package/dist/client-storage-BPjfP_is.d.ts +0 -604
- package/dist/experimental/sub-agent.d.ts +0 -205
- package/dist/experimental/sub-agent.js +0 -191
- package/dist/experimental/sub-agent.js.map +0 -1
- package/dist/mcp/client.js.map +0 -1
package/dist/mcp/client.js
CHANGED
|
@@ -1,848 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as RPC_DO_PREFIX, c as Emitter, n as MCPConnectionState, o as toErrorMessage, s as DisposableStore, t as MCPClientConnection } from "../client-connection-D3Wcd6Q6.js";
|
|
3
|
-
import { DurableObjectOAuthClientProvider } from "./do-oauth-client-provider.js";
|
|
4
|
-
import { nanoid } from "nanoid";
|
|
5
|
-
import { CfWorkerJsonSchemaValidator } from "@modelcontextprotocol/sdk/validation/cfworker-provider.js";
|
|
6
|
-
|
|
7
|
-
//#region src/mcp/client.ts
|
|
8
|
-
const defaultClientOptions = { jsonSchemaValidator: new CfWorkerJsonSchemaValidator() };
|
|
9
|
-
/**
|
|
10
|
-
* Blocked hostname patterns for SSRF protection.
|
|
11
|
-
* Prevents MCP client from connecting to internal/private network addresses.
|
|
12
|
-
*/
|
|
13
|
-
const BLOCKED_HOSTNAMES = new Set([
|
|
14
|
-
"localhost",
|
|
15
|
-
"0.0.0.0",
|
|
16
|
-
"[::1]",
|
|
17
|
-
"[::]",
|
|
18
|
-
"metadata.google.internal"
|
|
19
|
-
]);
|
|
20
|
-
/**
|
|
21
|
-
* Check whether a hostname looks like a private/internal IP address.
|
|
22
|
-
* Blocks RFC 1918, link-local, loopback, and cloud metadata endpoints.
|
|
23
|
-
*/
|
|
24
|
-
function isBlockedUrl(url) {
|
|
25
|
-
let parsed;
|
|
26
|
-
try {
|
|
27
|
-
parsed = new URL(url);
|
|
28
|
-
} catch {
|
|
29
|
-
return true;
|
|
30
|
-
}
|
|
31
|
-
const hostname = parsed.hostname;
|
|
32
|
-
if (BLOCKED_HOSTNAMES.has(hostname)) return true;
|
|
33
|
-
const ipv4Parts = hostname.split(".");
|
|
34
|
-
if (ipv4Parts.length === 4 && ipv4Parts.every((p) => /^\d{1,3}$/.test(p))) {
|
|
35
|
-
const [a, b] = ipv4Parts.map(Number);
|
|
36
|
-
if (a === 10) return true;
|
|
37
|
-
if (a === 172 && b >= 16 && b <= 31) return true;
|
|
38
|
-
if (a === 192 && b === 168) return true;
|
|
39
|
-
if (a === 127) return true;
|
|
40
|
-
if (a === 169 && b === 254) return true;
|
|
41
|
-
if (a === 0) return true;
|
|
42
|
-
}
|
|
43
|
-
if (hostname.startsWith("[") && hostname.endsWith("]")) {
|
|
44
|
-
const addr = hostname.slice(1, -1).toLowerCase();
|
|
45
|
-
if (addr.startsWith("fc") || addr.startsWith("fd")) return true;
|
|
46
|
-
if (addr.startsWith("fe80")) return true;
|
|
47
|
-
}
|
|
48
|
-
return false;
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Utility class that aggregates multiple MCP clients into one
|
|
52
|
-
*/
|
|
53
|
-
var MCPClientManager = class {
|
|
54
|
-
/**
|
|
55
|
-
* @param _name Name of the MCP client
|
|
56
|
-
* @param _version Version of the MCP Client
|
|
57
|
-
* @param options Storage adapter for persisting MCP server state
|
|
58
|
-
*/
|
|
59
|
-
constructor(_name, _version, options) {
|
|
60
|
-
this._name = _name;
|
|
61
|
-
this._version = _version;
|
|
62
|
-
this.mcpConnections = {};
|
|
63
|
-
this._didWarnAboutUnstableGetAITools = false;
|
|
64
|
-
this._connectionDisposables = /* @__PURE__ */ new Map();
|
|
65
|
-
this._isRestored = false;
|
|
66
|
-
this._pendingConnections = /* @__PURE__ */ new Map();
|
|
67
|
-
this._onObservabilityEvent = new Emitter();
|
|
68
|
-
this.onObservabilityEvent = this._onObservabilityEvent.event;
|
|
69
|
-
this._onServerStateChanged = new Emitter();
|
|
70
|
-
this.onServerStateChanged = this._onServerStateChanged.event;
|
|
71
|
-
if (!options.storage) throw new Error("MCPClientManager requires a valid DurableObjectStorage instance");
|
|
72
|
-
this._storage = options.storage;
|
|
73
|
-
this._createAuthProviderFn = options.createAuthProvider;
|
|
74
|
-
}
|
|
75
|
-
sql(query, ...bindings) {
|
|
76
|
-
return [...this._storage.sql.exec(query, ...bindings)];
|
|
77
|
-
}
|
|
78
|
-
saveServerToStorage(server) {
|
|
79
|
-
this.sql(`INSERT OR REPLACE INTO cf_agents_mcp_servers (
|
|
80
|
-
id, name, server_url, client_id, auth_url, callback_url, server_options
|
|
81
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?)`, server.id, server.name, server.server_url, server.client_id ?? null, server.auth_url ?? null, server.callback_url, server.server_options ?? null);
|
|
82
|
-
}
|
|
83
|
-
removeServerFromStorage(serverId) {
|
|
84
|
-
this.sql("DELETE FROM cf_agents_mcp_servers WHERE id = ?", serverId);
|
|
85
|
-
}
|
|
86
|
-
getServersFromStorage() {
|
|
87
|
-
return this.sql("SELECT id, name, server_url, client_id, auth_url, callback_url, server_options FROM cf_agents_mcp_servers");
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* Get the retry options for a server from stored server_options
|
|
91
|
-
*/
|
|
92
|
-
getServerRetryOptions(serverId) {
|
|
93
|
-
const rows = this.sql("SELECT server_options FROM cf_agents_mcp_servers WHERE id = ?", serverId);
|
|
94
|
-
if (!rows.length || !rows[0].server_options) return void 0;
|
|
95
|
-
return JSON.parse(rows[0].server_options).retry;
|
|
96
|
-
}
|
|
97
|
-
clearServerAuthUrl(serverId) {
|
|
98
|
-
this.sql("UPDATE cf_agents_mcp_servers SET auth_url = NULL WHERE id = ?", serverId);
|
|
99
|
-
}
|
|
100
|
-
failConnection(serverId, error) {
|
|
101
|
-
this.clearServerAuthUrl(serverId);
|
|
102
|
-
if (this.mcpConnections[serverId]) {
|
|
103
|
-
this.mcpConnections[serverId].connectionState = MCPConnectionState.FAILED;
|
|
104
|
-
this.mcpConnections[serverId].connectionError = error;
|
|
105
|
-
}
|
|
106
|
-
this._onServerStateChanged.fire();
|
|
107
|
-
return {
|
|
108
|
-
serverId,
|
|
109
|
-
authSuccess: false,
|
|
110
|
-
authError: error
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* Create an auth provider for a server
|
|
115
|
-
* @internal
|
|
116
|
-
*/
|
|
117
|
-
createAuthProvider(serverId, callbackUrl, clientName, clientId) {
|
|
118
|
-
if (!this._storage) throw new Error("Cannot create auth provider: storage is not initialized");
|
|
119
|
-
const authProvider = new DurableObjectOAuthClientProvider(this._storage, clientName, callbackUrl);
|
|
120
|
-
authProvider.serverId = serverId;
|
|
121
|
-
if (clientId) authProvider.clientId = clientId;
|
|
122
|
-
return authProvider;
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* Get saved RPC servers from storage (servers with rpc:// URLs).
|
|
126
|
-
* These are restored separately by the Agent class since they need env bindings.
|
|
127
|
-
*/
|
|
128
|
-
getRpcServersFromStorage() {
|
|
129
|
-
return this.getServersFromStorage().filter((s) => s.server_url.startsWith(RPC_DO_PREFIX));
|
|
130
|
-
}
|
|
131
|
-
/**
|
|
132
|
-
* Save an RPC server to storage for hibernation recovery.
|
|
133
|
-
* The bindingName is stored in server_options so the Agent can look up
|
|
134
|
-
* the namespace from env during restore.
|
|
135
|
-
*/
|
|
136
|
-
saveRpcServerToStorage(id, name, normalizedName, bindingName, props) {
|
|
137
|
-
this.saveServerToStorage({
|
|
138
|
-
id,
|
|
139
|
-
name,
|
|
140
|
-
server_url: `${RPC_DO_PREFIX}${normalizedName}`,
|
|
141
|
-
client_id: null,
|
|
142
|
-
auth_url: null,
|
|
143
|
-
callback_url: "",
|
|
144
|
-
server_options: JSON.stringify({
|
|
145
|
-
bindingName,
|
|
146
|
-
props
|
|
147
|
-
})
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* Restore MCP server connections from storage
|
|
152
|
-
* This method is called on Agent initialization to restore previously connected servers.
|
|
153
|
-
* RPC servers (rpc:// URLs) are skipped here -- they are restored by the Agent class
|
|
154
|
-
* which has access to env bindings.
|
|
155
|
-
*
|
|
156
|
-
* @param clientName Name to use for OAuth client (typically the agent instance name)
|
|
157
|
-
*/
|
|
158
|
-
async restoreConnectionsFromStorage(clientName) {
|
|
159
|
-
if (this._isRestored) return;
|
|
160
|
-
const servers = this.getServersFromStorage();
|
|
161
|
-
if (!servers || servers.length === 0) {
|
|
162
|
-
this._isRestored = true;
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
for (const server of servers) {
|
|
166
|
-
if (server.server_url.startsWith(RPC_DO_PREFIX)) continue;
|
|
167
|
-
const existingConn = this.mcpConnections[server.id];
|
|
168
|
-
if (existingConn) {
|
|
169
|
-
if (existingConn.connectionState === MCPConnectionState.READY) {
|
|
170
|
-
console.warn(`[MCPClientManager] Server ${server.id} already has a ready connection. Skipping recreation.`);
|
|
171
|
-
continue;
|
|
172
|
-
}
|
|
173
|
-
if (existingConn.connectionState === MCPConnectionState.AUTHENTICATING || existingConn.connectionState === MCPConnectionState.CONNECTING || existingConn.connectionState === MCPConnectionState.DISCOVERING) continue;
|
|
174
|
-
if (existingConn.connectionState === MCPConnectionState.FAILED) {
|
|
175
|
-
try {
|
|
176
|
-
await existingConn.client.close();
|
|
177
|
-
} catch (error) {
|
|
178
|
-
console.warn(`[MCPClientManager] Error closing failed connection ${server.id}:`, error);
|
|
179
|
-
}
|
|
180
|
-
delete this.mcpConnections[server.id];
|
|
181
|
-
this._connectionDisposables.get(server.id)?.dispose();
|
|
182
|
-
this._connectionDisposables.delete(server.id);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
const parsedOptions = server.server_options ? JSON.parse(server.server_options) : null;
|
|
186
|
-
let authProvider;
|
|
187
|
-
if (server.callback_url) {
|
|
188
|
-
authProvider = this._createAuthProviderFn ? this._createAuthProviderFn(server.callback_url) : this.createAuthProvider(server.id, server.callback_url, clientName, server.client_id ?? void 0);
|
|
189
|
-
authProvider.serverId = server.id;
|
|
190
|
-
if (server.client_id) authProvider.clientId = server.client_id;
|
|
191
|
-
}
|
|
192
|
-
const conn = this.createConnection(server.id, server.server_url, {
|
|
193
|
-
client: parsedOptions?.client ?? {},
|
|
194
|
-
transport: {
|
|
195
|
-
...parsedOptions?.transport ?? {},
|
|
196
|
-
type: parsedOptions?.transport?.type ?? "auto",
|
|
197
|
-
authProvider
|
|
198
|
-
}
|
|
199
|
-
});
|
|
200
|
-
if (server.auth_url) {
|
|
201
|
-
conn.connectionState = MCPConnectionState.AUTHENTICATING;
|
|
202
|
-
continue;
|
|
203
|
-
}
|
|
204
|
-
this._trackConnection(server.id, this._restoreServer(server.id, parsedOptions?.retry));
|
|
205
|
-
}
|
|
206
|
-
this._isRestored = true;
|
|
207
|
-
}
|
|
208
|
-
/**
|
|
209
|
-
* Track a pending connection promise for a server.
|
|
210
|
-
* The promise is removed from the map when it settles.
|
|
211
|
-
*/
|
|
212
|
-
_trackConnection(serverId, promise) {
|
|
213
|
-
const tracked = promise.finally(() => {
|
|
214
|
-
if (this._pendingConnections.get(serverId) === tracked) this._pendingConnections.delete(serverId);
|
|
215
|
-
});
|
|
216
|
-
this._pendingConnections.set(serverId, tracked);
|
|
217
|
-
}
|
|
218
|
-
/**
|
|
219
|
-
* Wait for all in-flight connection and discovery operations to settle.
|
|
220
|
-
* This is useful when you need MCP tools to be available before proceeding,
|
|
221
|
-
* e.g. before calling getAITools() after the agent wakes from hibernation.
|
|
222
|
-
*
|
|
223
|
-
* Returns once every pending connection has either connected and discovered,
|
|
224
|
-
* failed, or timed out. Never rejects.
|
|
225
|
-
*
|
|
226
|
-
* @param options.timeout - Maximum time in milliseconds to wait.
|
|
227
|
-
* `0` returns immediately without waiting.
|
|
228
|
-
* `undefined` (default) waits indefinitely.
|
|
229
|
-
*/
|
|
230
|
-
async waitForConnections(options) {
|
|
231
|
-
if (this._pendingConnections.size === 0) return;
|
|
232
|
-
if (options?.timeout != null && options.timeout <= 0) return;
|
|
233
|
-
const settled = Promise.allSettled(this._pendingConnections.values());
|
|
234
|
-
if (options?.timeout != null && options.timeout > 0) {
|
|
235
|
-
let timerId;
|
|
236
|
-
const timer = new Promise((resolve) => {
|
|
237
|
-
timerId = setTimeout(resolve, options.timeout);
|
|
238
|
-
});
|
|
239
|
-
await Promise.race([settled, timer]);
|
|
240
|
-
clearTimeout(timerId);
|
|
241
|
-
} else await settled;
|
|
242
|
-
}
|
|
243
|
-
/**
|
|
244
|
-
* Internal method to restore a single server connection and discovery
|
|
245
|
-
*/
|
|
246
|
-
async _restoreServer(serverId, retry) {
|
|
247
|
-
const maxAttempts = retry?.maxAttempts ?? 3;
|
|
248
|
-
if ((await tryN(maxAttempts, async () => this.connectToServer(serverId), {
|
|
249
|
-
baseDelayMs: retry?.baseDelayMs ?? 500,
|
|
250
|
-
maxDelayMs: retry?.maxDelayMs ?? 5e3
|
|
251
|
-
}).catch((error) => {
|
|
252
|
-
console.error(`Error connecting to ${serverId} after ${maxAttempts} attempts:`, error);
|
|
253
|
-
return null;
|
|
254
|
-
}))?.state === MCPConnectionState.CONNECTED) {
|
|
255
|
-
const discoverResult = await this.discoverIfConnected(serverId);
|
|
256
|
-
if (discoverResult && !discoverResult.success) console.error(`Error discovering ${serverId}:`, discoverResult.error);
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
/**
|
|
260
|
-
* Connect to and register an MCP server
|
|
261
|
-
*
|
|
262
|
-
* @deprecated This method is maintained for backward compatibility.
|
|
263
|
-
* For new code, use registerServer() and connectToServer() separately.
|
|
264
|
-
*
|
|
265
|
-
* @param url Server URL
|
|
266
|
-
* @param options Connection options
|
|
267
|
-
* @returns Object with server ID, auth URL (if OAuth), and client ID (if OAuth)
|
|
268
|
-
*/
|
|
269
|
-
async connect(url, options = {}) {
|
|
270
|
-
/**
|
|
271
|
-
* We need to delay loading ai sdk, because putting it in module scope is
|
|
272
|
-
* causing issues with startup time.
|
|
273
|
-
* The only place it's used is in getAITools, which only matters after
|
|
274
|
-
* .connect() is called on at least one server.
|
|
275
|
-
* So it's safe to delay loading it until .connect() is called.
|
|
276
|
-
*/
|
|
277
|
-
await this.ensureJsonSchema();
|
|
278
|
-
const id = options.reconnect?.id ?? nanoid(8);
|
|
279
|
-
if (options.transport?.authProvider) {
|
|
280
|
-
options.transport.authProvider.serverId = id;
|
|
281
|
-
if (options.reconnect?.oauthClientId) options.transport.authProvider.clientId = options.reconnect?.oauthClientId;
|
|
282
|
-
}
|
|
283
|
-
if (isBlockedUrl(url)) throw new Error(`Blocked URL: ${url} — MCP client connections to private/internal addresses are not allowed`);
|
|
284
|
-
if (!options.reconnect?.oauthCode || !this.mcpConnections[id]) {
|
|
285
|
-
const normalizedTransport = {
|
|
286
|
-
...options.transport,
|
|
287
|
-
type: options.transport?.type ?? "auto"
|
|
288
|
-
};
|
|
289
|
-
this.mcpConnections[id] = new MCPClientConnection(new URL(url), {
|
|
290
|
-
name: this._name,
|
|
291
|
-
version: this._version
|
|
292
|
-
}, {
|
|
293
|
-
client: options.client ?? {},
|
|
294
|
-
transport: normalizedTransport
|
|
295
|
-
});
|
|
296
|
-
const store = new DisposableStore();
|
|
297
|
-
const existing = this._connectionDisposables.get(id);
|
|
298
|
-
if (existing) existing.dispose();
|
|
299
|
-
this._connectionDisposables.set(id, store);
|
|
300
|
-
store.add(this.mcpConnections[id].onObservabilityEvent((event) => {
|
|
301
|
-
this._onObservabilityEvent.fire(event);
|
|
302
|
-
}));
|
|
303
|
-
}
|
|
304
|
-
await this.mcpConnections[id].init();
|
|
305
|
-
if (options.reconnect?.oauthCode) try {
|
|
306
|
-
await this.mcpConnections[id].completeAuthorization(options.reconnect.oauthCode);
|
|
307
|
-
await this.mcpConnections[id].init();
|
|
308
|
-
} catch (error) {
|
|
309
|
-
this._onObservabilityEvent.fire({
|
|
310
|
-
type: "mcp:client:connect",
|
|
311
|
-
payload: {
|
|
312
|
-
url,
|
|
313
|
-
transport: options.transport?.type ?? "auto",
|
|
314
|
-
state: this.mcpConnections[id].connectionState,
|
|
315
|
-
error: toErrorMessage(error)
|
|
316
|
-
},
|
|
317
|
-
timestamp: Date.now()
|
|
318
|
-
});
|
|
319
|
-
throw error;
|
|
320
|
-
}
|
|
321
|
-
const authUrl = options.transport?.authProvider?.authUrl;
|
|
322
|
-
if (this.mcpConnections[id].connectionState === MCPConnectionState.AUTHENTICATING && authUrl && options.transport?.authProvider?.redirectUrl) return {
|
|
323
|
-
authUrl,
|
|
324
|
-
clientId: options.transport?.authProvider?.clientId,
|
|
325
|
-
id
|
|
326
|
-
};
|
|
327
|
-
const discoverResult = await this.discoverIfConnected(id);
|
|
328
|
-
if (discoverResult && !discoverResult.success) throw new Error(`Failed to discover server capabilities: ${discoverResult.error}`);
|
|
329
|
-
return { id };
|
|
330
|
-
}
|
|
331
|
-
/**
|
|
332
|
-
* Create an in-memory connection object and set up observability
|
|
333
|
-
* Does NOT save to storage - use registerServer() for that
|
|
334
|
-
* @returns The connection object (existing or newly created)
|
|
335
|
-
*/
|
|
336
|
-
createConnection(id, url, options) {
|
|
337
|
-
if (this.mcpConnections[id]) return this.mcpConnections[id];
|
|
338
|
-
const normalizedTransport = {
|
|
339
|
-
...options.transport,
|
|
340
|
-
type: options.transport?.type ?? "auto"
|
|
341
|
-
};
|
|
342
|
-
this.mcpConnections[id] = new MCPClientConnection(new URL(url), {
|
|
343
|
-
name: this._name,
|
|
344
|
-
version: this._version
|
|
345
|
-
}, {
|
|
346
|
-
client: {
|
|
347
|
-
...defaultClientOptions,
|
|
348
|
-
...options.client
|
|
349
|
-
},
|
|
350
|
-
transport: normalizedTransport
|
|
351
|
-
});
|
|
352
|
-
const store = new DisposableStore();
|
|
353
|
-
const existing = this._connectionDisposables.get(id);
|
|
354
|
-
if (existing) existing.dispose();
|
|
355
|
-
this._connectionDisposables.set(id, store);
|
|
356
|
-
store.add(this.mcpConnections[id].onObservabilityEvent((event) => {
|
|
357
|
-
this._onObservabilityEvent.fire(event);
|
|
358
|
-
}));
|
|
359
|
-
return this.mcpConnections[id];
|
|
360
|
-
}
|
|
361
|
-
/**
|
|
362
|
-
* Register an MCP server connection without connecting
|
|
363
|
-
* Creates the connection object, sets up observability, and saves to storage
|
|
364
|
-
*
|
|
365
|
-
* @param id Server ID
|
|
366
|
-
* @param options Registration options including URL, name, callback URL, and connection config
|
|
367
|
-
* @returns Server ID
|
|
368
|
-
*/
|
|
369
|
-
async registerServer(id, options) {
|
|
370
|
-
if (isBlockedUrl(options.url)) throw new Error(`Blocked URL: ${options.url} — MCP client connections to private/internal addresses are not allowed`);
|
|
371
|
-
this.createConnection(id, options.url, {
|
|
372
|
-
client: options.client,
|
|
373
|
-
transport: {
|
|
374
|
-
...options.transport,
|
|
375
|
-
type: options.transport?.type ?? "auto"
|
|
376
|
-
}
|
|
377
|
-
});
|
|
378
|
-
const { authProvider: _, ...transportWithoutAuth } = options.transport ?? {};
|
|
379
|
-
this.saveServerToStorage({
|
|
380
|
-
id,
|
|
381
|
-
name: options.name,
|
|
382
|
-
server_url: options.url,
|
|
383
|
-
callback_url: options.callbackUrl ?? "",
|
|
384
|
-
client_id: options.clientId ?? null,
|
|
385
|
-
auth_url: options.authUrl ?? null,
|
|
386
|
-
server_options: JSON.stringify({
|
|
387
|
-
client: options.client,
|
|
388
|
-
transport: transportWithoutAuth,
|
|
389
|
-
retry: options.retry
|
|
390
|
-
})
|
|
391
|
-
});
|
|
392
|
-
this._onServerStateChanged.fire();
|
|
393
|
-
return id;
|
|
394
|
-
}
|
|
395
|
-
/**
|
|
396
|
-
* Connect to an already registered MCP server and initialize the connection.
|
|
397
|
-
*
|
|
398
|
-
* For OAuth servers, returns `{ state: "authenticating", authUrl, clientId? }`.
|
|
399
|
-
* The user must complete the OAuth flow via the authUrl, which triggers a
|
|
400
|
-
* callback handled by `handleCallbackRequest()`.
|
|
401
|
-
*
|
|
402
|
-
* For non-OAuth servers, establishes the transport connection and returns
|
|
403
|
-
* `{ state: "connected" }`. Call `discoverIfConnected()` afterwards to
|
|
404
|
-
* discover capabilities and transition to "ready" state.
|
|
405
|
-
*
|
|
406
|
-
* @param id Server ID (must be registered first via registerServer())
|
|
407
|
-
* @returns Connection result with current state and OAuth info (if applicable)
|
|
408
|
-
*/
|
|
409
|
-
async connectToServer(id) {
|
|
410
|
-
const conn = this.mcpConnections[id];
|
|
411
|
-
if (!conn) throw new Error(`Server ${id} is not registered. Call registerServer() first.`);
|
|
412
|
-
const error = await conn.init();
|
|
413
|
-
this._onServerStateChanged.fire();
|
|
414
|
-
switch (conn.connectionState) {
|
|
415
|
-
case MCPConnectionState.FAILED: return {
|
|
416
|
-
state: conn.connectionState,
|
|
417
|
-
error: error ?? "Unknown connection error"
|
|
418
|
-
};
|
|
419
|
-
case MCPConnectionState.AUTHENTICATING: {
|
|
420
|
-
const authUrl = conn.options.transport.authProvider?.authUrl;
|
|
421
|
-
const redirectUrl = conn.options.transport.authProvider?.redirectUrl;
|
|
422
|
-
if (!authUrl || !redirectUrl) return {
|
|
423
|
-
state: MCPConnectionState.FAILED,
|
|
424
|
-
error: `OAuth configuration incomplete: missing ${!authUrl ? "authUrl" : "redirectUrl"}`
|
|
425
|
-
};
|
|
426
|
-
const clientId = conn.options.transport.authProvider?.clientId;
|
|
427
|
-
const serverRow = this.getServersFromStorage().find((s) => s.id === id);
|
|
428
|
-
if (serverRow) {
|
|
429
|
-
this.saveServerToStorage({
|
|
430
|
-
...serverRow,
|
|
431
|
-
auth_url: authUrl,
|
|
432
|
-
client_id: clientId ?? null
|
|
433
|
-
});
|
|
434
|
-
this._onServerStateChanged.fire();
|
|
435
|
-
}
|
|
436
|
-
this._onObservabilityEvent.fire({
|
|
437
|
-
type: "mcp:client:authorize",
|
|
438
|
-
payload: {
|
|
439
|
-
serverId: id,
|
|
440
|
-
authUrl,
|
|
441
|
-
clientId
|
|
442
|
-
},
|
|
443
|
-
timestamp: Date.now()
|
|
444
|
-
});
|
|
445
|
-
return {
|
|
446
|
-
state: conn.connectionState,
|
|
447
|
-
authUrl,
|
|
448
|
-
clientId
|
|
449
|
-
};
|
|
450
|
-
}
|
|
451
|
-
case MCPConnectionState.CONNECTED: return { state: conn.connectionState };
|
|
452
|
-
default: return {
|
|
453
|
-
state: MCPConnectionState.FAILED,
|
|
454
|
-
error: `Unexpected connection state after init: ${conn.connectionState}`
|
|
455
|
-
};
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
extractServerIdFromState(state) {
|
|
459
|
-
if (!state) return null;
|
|
460
|
-
const parts = state.split(".");
|
|
461
|
-
return parts.length === 2 ? parts[1] : null;
|
|
462
|
-
}
|
|
463
|
-
isCallbackRequest(req) {
|
|
464
|
-
if (req.method !== "GET") return false;
|
|
465
|
-
const url = new URL(req.url);
|
|
466
|
-
const state = url.searchParams.get("state");
|
|
467
|
-
const serverId = this.extractServerIdFromState(state);
|
|
468
|
-
if (!serverId) return false;
|
|
469
|
-
return this.getServersFromStorage().some((server) => {
|
|
470
|
-
if (server.id !== serverId) return false;
|
|
471
|
-
try {
|
|
472
|
-
const storedUrl = new URL(server.callback_url);
|
|
473
|
-
return storedUrl.origin === url.origin && storedUrl.pathname === url.pathname;
|
|
474
|
-
} catch {
|
|
475
|
-
return false;
|
|
476
|
-
}
|
|
477
|
-
});
|
|
478
|
-
}
|
|
479
|
-
validateCallbackRequest(req) {
|
|
480
|
-
const url = new URL(req.url);
|
|
481
|
-
const code = url.searchParams.get("code");
|
|
482
|
-
const state = url.searchParams.get("state");
|
|
483
|
-
const error = url.searchParams.get("error");
|
|
484
|
-
const errorDescription = url.searchParams.get("error_description");
|
|
485
|
-
if (!state) return {
|
|
486
|
-
valid: false,
|
|
487
|
-
error: "Unauthorized: no state provided"
|
|
488
|
-
};
|
|
489
|
-
const serverId = this.extractServerIdFromState(state);
|
|
490
|
-
if (!serverId) return {
|
|
491
|
-
valid: false,
|
|
492
|
-
error: "No serverId found in state parameter. Expected format: {nonce}.{serverId}"
|
|
493
|
-
};
|
|
494
|
-
if (error) return {
|
|
495
|
-
serverId,
|
|
496
|
-
valid: false,
|
|
497
|
-
error: errorDescription || error
|
|
498
|
-
};
|
|
499
|
-
if (!code) return {
|
|
500
|
-
serverId,
|
|
501
|
-
valid: false,
|
|
502
|
-
error: "Unauthorized: no code provided"
|
|
503
|
-
};
|
|
504
|
-
if (!this.getServersFromStorage().some((server) => server.id === serverId)) return {
|
|
505
|
-
serverId,
|
|
506
|
-
valid: false,
|
|
507
|
-
error: `No server found with id "${serverId}". Was the request matched with \`isCallbackRequest()\`?`
|
|
508
|
-
};
|
|
509
|
-
if (this.mcpConnections[serverId] === void 0) return {
|
|
510
|
-
serverId,
|
|
511
|
-
valid: false,
|
|
512
|
-
error: `No connection found for serverId "${serverId}".`
|
|
513
|
-
};
|
|
514
|
-
return {
|
|
515
|
-
valid: true,
|
|
516
|
-
serverId,
|
|
517
|
-
code,
|
|
518
|
-
state
|
|
519
|
-
};
|
|
520
|
-
}
|
|
521
|
-
async handleCallbackRequest(req) {
|
|
522
|
-
const validation = this.validateCallbackRequest(req);
|
|
523
|
-
if (!validation.valid) {
|
|
524
|
-
if (validation.serverId && this.mcpConnections[validation.serverId]) return this.failConnection(validation.serverId, validation.error);
|
|
525
|
-
return {
|
|
526
|
-
serverId: validation.serverId,
|
|
527
|
-
authSuccess: false,
|
|
528
|
-
authError: validation.error
|
|
529
|
-
};
|
|
530
|
-
}
|
|
531
|
-
const { serverId, code, state } = validation;
|
|
532
|
-
const conn = this.mcpConnections[serverId];
|
|
533
|
-
try {
|
|
534
|
-
if (!conn.options.transport.authProvider) throw new Error("Trying to finalize authentication for a server connection without an authProvider");
|
|
535
|
-
const authProvider = conn.options.transport.authProvider;
|
|
536
|
-
authProvider.serverId = serverId;
|
|
537
|
-
const stateValidation = await authProvider.checkState(state);
|
|
538
|
-
if (!stateValidation.valid) throw new Error(stateValidation.error || "Invalid state");
|
|
539
|
-
if (conn.connectionState === MCPConnectionState.READY || conn.connectionState === MCPConnectionState.CONNECTED) {
|
|
540
|
-
this.clearServerAuthUrl(serverId);
|
|
541
|
-
return {
|
|
542
|
-
serverId,
|
|
543
|
-
authSuccess: true
|
|
544
|
-
};
|
|
545
|
-
}
|
|
546
|
-
if (conn.connectionState !== MCPConnectionState.AUTHENTICATING) throw new Error(`Failed to authenticate: the client is in "${conn.connectionState}" state, expected "authenticating"`);
|
|
547
|
-
await authProvider.consumeState(state);
|
|
548
|
-
await conn.completeAuthorization(code);
|
|
549
|
-
await authProvider.deleteCodeVerifier();
|
|
550
|
-
this.clearServerAuthUrl(serverId);
|
|
551
|
-
conn.connectionError = null;
|
|
552
|
-
this._onServerStateChanged.fire();
|
|
553
|
-
return {
|
|
554
|
-
serverId,
|
|
555
|
-
authSuccess: true
|
|
556
|
-
};
|
|
557
|
-
} catch (err) {
|
|
558
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
559
|
-
return this.failConnection(serverId, message);
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
/**
|
|
563
|
-
* Discover server capabilities if connection is in CONNECTED or READY state.
|
|
564
|
-
* Transitions to DISCOVERING then READY (or CONNECTED on error).
|
|
565
|
-
* Can be called to refresh server capabilities (e.g., from a UI refresh button).
|
|
566
|
-
*
|
|
567
|
-
* If called while a previous discovery is in-flight for the same server,
|
|
568
|
-
* the previous discovery will be aborted.
|
|
569
|
-
*
|
|
570
|
-
* @param serverId The server ID to discover
|
|
571
|
-
* @param options Optional configuration
|
|
572
|
-
* @param options.timeoutMs Timeout in milliseconds (default: 30000)
|
|
573
|
-
* @returns Result with current state and optional error, or undefined if connection not found
|
|
574
|
-
*/
|
|
575
|
-
async discoverIfConnected(serverId, options = {}) {
|
|
576
|
-
const conn = this.mcpConnections[serverId];
|
|
577
|
-
if (!conn) {
|
|
578
|
-
this._onObservabilityEvent.fire({
|
|
579
|
-
type: "mcp:client:discover",
|
|
580
|
-
payload: {},
|
|
581
|
-
timestamp: Date.now()
|
|
582
|
-
});
|
|
583
|
-
return;
|
|
584
|
-
}
|
|
585
|
-
const result = await conn.discover(options);
|
|
586
|
-
this._onServerStateChanged.fire();
|
|
587
|
-
return {
|
|
588
|
-
...result,
|
|
589
|
-
state: conn.connectionState
|
|
590
|
-
};
|
|
591
|
-
}
|
|
592
|
-
/**
|
|
593
|
-
* Establish connection in the background after OAuth completion.
|
|
594
|
-
* This method connects to the server and discovers its capabilities.
|
|
595
|
-
* The connection is automatically tracked so that `waitForConnections()`
|
|
596
|
-
* will include it.
|
|
597
|
-
* @param serverId The server ID to establish connection for
|
|
598
|
-
*/
|
|
599
|
-
async establishConnection(serverId) {
|
|
600
|
-
const promise = this._doEstablishConnection(serverId);
|
|
601
|
-
this._trackConnection(serverId, promise);
|
|
602
|
-
return promise;
|
|
603
|
-
}
|
|
604
|
-
async _doEstablishConnection(serverId) {
|
|
605
|
-
const conn = this.mcpConnections[serverId];
|
|
606
|
-
if (!conn) {
|
|
607
|
-
this._onObservabilityEvent.fire({
|
|
608
|
-
type: "mcp:client:preconnect",
|
|
609
|
-
payload: { serverId },
|
|
610
|
-
timestamp: Date.now()
|
|
611
|
-
});
|
|
612
|
-
return;
|
|
613
|
-
}
|
|
614
|
-
if (conn.connectionState === MCPConnectionState.DISCOVERING || conn.connectionState === MCPConnectionState.READY) {
|
|
615
|
-
this._onObservabilityEvent.fire({
|
|
616
|
-
type: "mcp:client:connect",
|
|
617
|
-
payload: {
|
|
618
|
-
url: conn.url.toString(),
|
|
619
|
-
transport: conn.options.transport.type || "unknown",
|
|
620
|
-
state: conn.connectionState
|
|
621
|
-
},
|
|
622
|
-
timestamp: Date.now()
|
|
623
|
-
});
|
|
624
|
-
return;
|
|
625
|
-
}
|
|
626
|
-
const retry = this.getServerRetryOptions(serverId);
|
|
627
|
-
const connectResult = await tryN(retry?.maxAttempts ?? 3, async () => this.connectToServer(serverId), {
|
|
628
|
-
baseDelayMs: retry?.baseDelayMs ?? 500,
|
|
629
|
-
maxDelayMs: retry?.maxDelayMs ?? 5e3
|
|
630
|
-
});
|
|
631
|
-
this._onServerStateChanged.fire();
|
|
632
|
-
if (connectResult.state === MCPConnectionState.CONNECTED) await this.discoverIfConnected(serverId);
|
|
633
|
-
this._onObservabilityEvent.fire({
|
|
634
|
-
type: "mcp:client:connect",
|
|
635
|
-
payload: {
|
|
636
|
-
url: conn.url.toString(),
|
|
637
|
-
transport: conn.options.transport.type || "unknown",
|
|
638
|
-
state: conn.connectionState
|
|
639
|
-
},
|
|
640
|
-
timestamp: Date.now()
|
|
641
|
-
});
|
|
642
|
-
}
|
|
643
|
-
/**
|
|
644
|
-
* Configure OAuth callback handling
|
|
645
|
-
* @param config OAuth callback configuration
|
|
646
|
-
*/
|
|
647
|
-
configureOAuthCallback(config) {
|
|
648
|
-
this._oauthCallbackConfig = config;
|
|
649
|
-
}
|
|
650
|
-
/**
|
|
651
|
-
* Get the current OAuth callback configuration
|
|
652
|
-
* @returns The current OAuth callback configuration
|
|
653
|
-
*/
|
|
654
|
-
getOAuthCallbackConfig() {
|
|
655
|
-
return this._oauthCallbackConfig;
|
|
656
|
-
}
|
|
657
|
-
/**
|
|
658
|
-
* @returns namespaced list of tools
|
|
659
|
-
*/
|
|
660
|
-
listTools() {
|
|
661
|
-
return getNamespacedData(this.mcpConnections, "tools");
|
|
662
|
-
}
|
|
663
|
-
/**
|
|
664
|
-
* Lazy-loads the jsonSchema function from the AI SDK.
|
|
665
|
-
*
|
|
666
|
-
* This defers importing the "ai" package until it's actually needed, which helps reduce
|
|
667
|
-
* initial bundle size and startup time. The jsonSchema function is required for converting
|
|
668
|
-
* MCP tools into AI SDK tool definitions via getAITools().
|
|
669
|
-
*
|
|
670
|
-
* @internal This method is for internal use only. It's automatically called before operations
|
|
671
|
-
* that need jsonSchema (like getAITools() or OAuth flows). External consumers should not need
|
|
672
|
-
* to call this directly.
|
|
673
|
-
*/
|
|
674
|
-
async ensureJsonSchema() {
|
|
675
|
-
if (!this.jsonSchema) {
|
|
676
|
-
const { jsonSchema } = await import("ai");
|
|
677
|
-
this.jsonSchema = jsonSchema;
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
/**
|
|
681
|
-
* @returns a set of tools that you can use with the AI SDK
|
|
682
|
-
*/
|
|
683
|
-
getAITools() {
|
|
684
|
-
if (!this.jsonSchema) throw new Error("jsonSchema not initialized.");
|
|
685
|
-
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.`);
|
|
686
|
-
const entries = [];
|
|
687
|
-
for (const tool of getNamespacedData(this.mcpConnections, "tools")) try {
|
|
688
|
-
const toolKey = `tool_${tool.serverId.replace(/-/g, "")}_${tool.name}`;
|
|
689
|
-
entries.push([toolKey, {
|
|
690
|
-
description: tool.description,
|
|
691
|
-
execute: async (args) => {
|
|
692
|
-
const result = await this.callTool({
|
|
693
|
-
arguments: args,
|
|
694
|
-
name: tool.name,
|
|
695
|
-
serverId: tool.serverId
|
|
696
|
-
});
|
|
697
|
-
if (result.isError) {
|
|
698
|
-
const textContent = result.content?.[0];
|
|
699
|
-
const message = textContent?.type === "text" && textContent.text ? textContent.text : "Tool call failed";
|
|
700
|
-
throw new Error(message);
|
|
701
|
-
}
|
|
702
|
-
return result;
|
|
703
|
-
},
|
|
704
|
-
inputSchema: tool.inputSchema ? this.jsonSchema(tool.inputSchema) : this.jsonSchema({ type: "object" }),
|
|
705
|
-
outputSchema: tool.outputSchema ? this.jsonSchema(tool.outputSchema) : void 0
|
|
706
|
-
}]);
|
|
707
|
-
} catch (e) {
|
|
708
|
-
console.warn(`[getAITools] Skipping tool "${tool.name}" from "${tool.serverId}": ${e}`);
|
|
709
|
-
}
|
|
710
|
-
return Object.fromEntries(entries);
|
|
711
|
-
}
|
|
712
|
-
/**
|
|
713
|
-
* @deprecated this has been renamed to getAITools(), and unstable_getAITools will be removed in the next major version
|
|
714
|
-
* @returns a set of tools that you can use with the AI SDK
|
|
715
|
-
*/
|
|
716
|
-
unstable_getAITools() {
|
|
717
|
-
if (!this._didWarnAboutUnstableGetAITools) {
|
|
718
|
-
this._didWarnAboutUnstableGetAITools = true;
|
|
719
|
-
console.warn("unstable_getAITools is deprecated, use getAITools instead. unstable_getAITools will be removed in the next major version.");
|
|
720
|
-
}
|
|
721
|
-
return this.getAITools();
|
|
722
|
-
}
|
|
723
|
-
/**
|
|
724
|
-
* Closes all active in-memory connections to MCP servers.
|
|
725
|
-
*
|
|
726
|
-
* Note: This only closes the transport connections - it does NOT remove
|
|
727
|
-
* servers from storage. Servers will still be listed and their callback
|
|
728
|
-
* URLs will still match incoming OAuth requests.
|
|
729
|
-
*
|
|
730
|
-
* Use removeServer() instead if you want to fully clean up a server
|
|
731
|
-
* (closes connection AND removes from storage).
|
|
732
|
-
*/
|
|
733
|
-
async closeAllConnections() {
|
|
734
|
-
const ids = Object.keys(this.mcpConnections);
|
|
735
|
-
this._pendingConnections.clear();
|
|
736
|
-
for (const id of ids) this.mcpConnections[id].cancelDiscovery();
|
|
737
|
-
await Promise.all(ids.map(async (id) => {
|
|
738
|
-
await this.mcpConnections[id].client.close();
|
|
739
|
-
}));
|
|
740
|
-
for (const id of ids) {
|
|
741
|
-
const store = this._connectionDisposables.get(id);
|
|
742
|
-
if (store) store.dispose();
|
|
743
|
-
this._connectionDisposables.delete(id);
|
|
744
|
-
delete this.mcpConnections[id];
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
/**
|
|
748
|
-
* Closes a connection to an MCP server
|
|
749
|
-
* @param id The id of the connection to close
|
|
750
|
-
*/
|
|
751
|
-
async closeConnection(id) {
|
|
752
|
-
if (!this.mcpConnections[id]) throw new Error(`Connection with id "${id}" does not exist.`);
|
|
753
|
-
this.mcpConnections[id].cancelDiscovery();
|
|
754
|
-
this._pendingConnections.delete(id);
|
|
755
|
-
await this.mcpConnections[id].client.close();
|
|
756
|
-
delete this.mcpConnections[id];
|
|
757
|
-
const store = this._connectionDisposables.get(id);
|
|
758
|
-
if (store) store.dispose();
|
|
759
|
-
this._connectionDisposables.delete(id);
|
|
760
|
-
}
|
|
761
|
-
/**
|
|
762
|
-
* Remove an MCP server - closes connection if active and removes from storage.
|
|
763
|
-
*/
|
|
764
|
-
async removeServer(serverId) {
|
|
765
|
-
if (this.mcpConnections[serverId]) try {
|
|
766
|
-
await this.closeConnection(serverId);
|
|
767
|
-
} catch (_e) {}
|
|
768
|
-
this.removeServerFromStorage(serverId);
|
|
769
|
-
this._onServerStateChanged.fire();
|
|
770
|
-
}
|
|
771
|
-
/**
|
|
772
|
-
* List all MCP servers from storage
|
|
773
|
-
*/
|
|
774
|
-
listServers() {
|
|
775
|
-
return this.getServersFromStorage();
|
|
776
|
-
}
|
|
777
|
-
/**
|
|
778
|
-
* Dispose the manager and all resources.
|
|
779
|
-
*/
|
|
780
|
-
async dispose() {
|
|
781
|
-
try {
|
|
782
|
-
await this.closeAllConnections();
|
|
783
|
-
} finally {
|
|
784
|
-
this._onServerStateChanged.dispose();
|
|
785
|
-
this._onObservabilityEvent.dispose();
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
/**
|
|
789
|
-
* @returns namespaced list of prompts
|
|
790
|
-
*/
|
|
791
|
-
listPrompts() {
|
|
792
|
-
return getNamespacedData(this.mcpConnections, "prompts");
|
|
793
|
-
}
|
|
794
|
-
/**
|
|
795
|
-
* @returns namespaced list of tools
|
|
796
|
-
*/
|
|
797
|
-
listResources() {
|
|
798
|
-
return getNamespacedData(this.mcpConnections, "resources");
|
|
799
|
-
}
|
|
800
|
-
/**
|
|
801
|
-
* @returns namespaced list of resource templates
|
|
802
|
-
*/
|
|
803
|
-
listResourceTemplates() {
|
|
804
|
-
return getNamespacedData(this.mcpConnections, "resourceTemplates");
|
|
805
|
-
}
|
|
806
|
-
/**
|
|
807
|
-
* Namespaced version of callTool
|
|
808
|
-
*/
|
|
809
|
-
async callTool(params, resultSchema, options) {
|
|
810
|
-
const { serverId, ...mcpParams } = params;
|
|
811
|
-
const unqualifiedName = mcpParams.name.replace(`${serverId}.`, "");
|
|
812
|
-
return this.mcpConnections[serverId].client.callTool({
|
|
813
|
-
...mcpParams,
|
|
814
|
-
name: unqualifiedName
|
|
815
|
-
}, resultSchema, options);
|
|
816
|
-
}
|
|
817
|
-
/**
|
|
818
|
-
* Namespaced version of readResource
|
|
819
|
-
*/
|
|
820
|
-
readResource(params, options) {
|
|
821
|
-
return this.mcpConnections[params.serverId].client.readResource(params, options);
|
|
822
|
-
}
|
|
823
|
-
/**
|
|
824
|
-
* Namespaced version of getPrompt
|
|
825
|
-
*/
|
|
826
|
-
getPrompt(params, options) {
|
|
827
|
-
return this.mcpConnections[params.serverId].client.getPrompt(params, options);
|
|
828
|
-
}
|
|
829
|
-
};
|
|
830
|
-
function getNamespacedData(mcpClients, type) {
|
|
831
|
-
return Object.entries(mcpClients).map(([name, conn]) => {
|
|
832
|
-
return {
|
|
833
|
-
data: conn[type],
|
|
834
|
-
name
|
|
835
|
-
};
|
|
836
|
-
}).flatMap(({ name: serverId, data }) => {
|
|
837
|
-
return data.map((item) => {
|
|
838
|
-
return {
|
|
839
|
-
...item,
|
|
840
|
-
serverId
|
|
841
|
-
};
|
|
842
|
-
});
|
|
843
|
-
});
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
//#endregion
|
|
1
|
+
import { n as getNamespacedData, t as MCPClientManager } from "../client-K8Z-u76l.js";
|
|
847
2
|
export { MCPClientManager, getNamespacedData };
|
|
848
|
-
//# sourceMappingURL=client.js.map
|