agents 0.0.0-cf3b3d7 → 0.0.0-d08612f
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/README.md +5 -3
- package/dist/ai-chat-agent.d.ts +95 -13
- package/dist/ai-chat-agent.js +354 -74
- package/dist/ai-chat-agent.js.map +1 -1
- package/dist/{ai-chat-v5-migration-DBHGW4Hv.js → ai-chat-v5-migration-DguhuLKF.js} +1 -1
- package/dist/{ai-chat-v5-migration-DBHGW4Hv.js.map → ai-chat-v5-migration-DguhuLKF.js.map} +1 -1
- package/dist/ai-chat-v5-migration.js +1 -1
- package/dist/ai-react.d.ts +15 -9
- package/dist/ai-react.js +171 -26
- package/dist/ai-react.js.map +1 -1
- package/dist/{ai-types-B3aQaFv3.js → ai-types-CwgHzwUb.js} +5 -1
- package/dist/ai-types-CwgHzwUb.js.map +1 -0
- package/dist/{ai-types-D5YoPrBZ.d.ts → ai-types-D_hTbf25.d.ts} +15 -7
- package/dist/ai-types.d.ts +1 -1
- package/dist/ai-types.js +1 -1
- package/dist/cli/index.d.ts +1 -0
- package/dist/{cli.js → cli/index.js} +7 -6
- package/dist/cli/index.js.map +1 -0
- package/dist/{client-BfiZ3HQd.js → client-CcyhkGfN.js} +2 -2
- package/dist/{client-BfiZ3HQd.js.map → client-CcyhkGfN.js.map} +1 -1
- package/dist/{client-CbWe9FBd.d.ts → client-ClORm6f0.d.ts} +2 -2
- package/dist/client-DfIOsabL.d.ts +834 -0
- package/dist/{client-DpkZyXgJ.js → client-QZa2Rq0l.js} +398 -194
- package/dist/client-QZa2Rq0l.js.map +1 -0
- package/dist/client.d.ts +2 -2
- package/dist/client.js +2 -2
- package/dist/codemode/ai.js +6 -5
- package/dist/codemode/ai.js.map +1 -1
- package/dist/context-BkKbAa1R.js +8 -0
- package/dist/context-BkKbAa1R.js.map +1 -0
- package/dist/context-_sPQqJWv.d.ts +24 -0
- package/dist/context.d.ts +6 -0
- package/dist/context.js +3 -0
- package/dist/do-oauth-client-provider-B-ryFIPr.d.ts +70 -0
- package/dist/{do-oauth-client-provider-D2P1lSft.js → do-oauth-client-provider-B1fVIshX.js} +71 -9
- package/dist/do-oauth-client-provider-B1fVIshX.js.map +1 -0
- package/dist/{index-DhJCaDWd.d.ts → index-CyDpAVHZ.d.ts} +2 -2
- package/dist/{index-DCRAdW9R.d.ts → index-DPJ32qQn.d.ts} +60 -55
- package/dist/index.d.ts +34 -34
- package/dist/index.js +6 -5
- package/dist/mcp/client.d.ts +4 -4
- package/dist/mcp/client.js +2 -2
- package/dist/mcp/do-oauth-client-provider.d.ts +1 -1
- package/dist/mcp/do-oauth-client-provider.js +1 -1
- package/dist/mcp/index.d.ts +10 -9
- package/dist/mcp/index.js +19 -11
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/x402.js +10 -6
- package/dist/mcp/x402.js.map +1 -1
- package/dist/{mcp-Dw5vDrY8.d.ts → mcp-CzbSsLfc.d.ts} +1 -1
- package/dist/observability/index.d.ts +2 -2
- package/dist/observability/index.js +6 -5
- package/dist/{react-DM_FD53F.d.ts → react-DTzwSLAh.d.ts} +29 -5
- package/dist/react.d.ts +15 -10
- package/dist/react.js +11 -4
- package/dist/react.js.map +1 -1
- package/dist/{serializable-CymX8ovI.d.ts → serializable-C4GLimgv.d.ts} +1 -1
- package/dist/serializable.d.ts +1 -1
- package/dist/{src-Dk8lwxHf.js → src-BmbDclOA.js} +38 -100
- package/dist/src-BmbDclOA.js.map +1 -0
- package/package.json +8 -8
- package/dist/ai-types-B3aQaFv3.js.map +0 -1
- package/dist/cli.d.ts +0 -8
- package/dist/cli.js.map +0 -1
- package/dist/client-BaCHMay9.d.ts +0 -5427
- package/dist/client-DpkZyXgJ.js.map +0 -1
- package/dist/do-oauth-client-provider-CnbnngL2.d.ts +0 -134
- package/dist/do-oauth-client-provider-D2P1lSft.js.map +0 -1
- package/dist/src-Dk8lwxHf.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as DurableObjectOAuthClientProvider } from "./do-oauth-client-provider-
|
|
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 =
|
|
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
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
this.
|
|
98
|
-
|
|
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;
|
|
117
|
+
this._onObservabilityEvent.fire({
|
|
118
|
+
type: "mcp:client:connect",
|
|
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);
|
|
100
131
|
this._onObservabilityEvent.fire({
|
|
101
132
|
type: "mcp:client:connect",
|
|
102
|
-
displayMessage: `
|
|
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:
|
|
138
|
+
error: errorMessage
|
|
108
139
|
},
|
|
109
140
|
timestamp: Date.now(),
|
|
110
141
|
id: nanoid()
|
|
111
142
|
});
|
|
112
|
-
|
|
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 !==
|
|
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 =
|
|
179
|
+
this.connectionState = MCPConnectionState.CONNECTING;
|
|
151
180
|
} catch (error) {
|
|
152
|
-
this.connectionState =
|
|
181
|
+
this.connectionState = MCPConnectionState.FAILED;
|
|
153
182
|
throw error;
|
|
154
183
|
}
|
|
155
184
|
}
|
|
156
185
|
/**
|
|
157
|
-
*
|
|
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
|
|
160
|
-
|
|
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
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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.
|
|
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
|
|
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
|
|
175
|
-
|
|
176
|
-
this.
|
|
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: `
|
|
261
|
+
displayMessage: `Discovery skipped for ${this.url.toString()}, state is ${this.connectionState}`,
|
|
212
262
|
payload: {
|
|
213
|
-
url,
|
|
214
|
-
|
|
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
|
-
*
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
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))
|
|
336
|
-
if (
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
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
|
-
|
|
355
|
-
|
|
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) => {
|
|
@@ -400,13 +485,32 @@ var MCPClientManager = class {
|
|
|
400
485
|
this.onObservabilityEvent = this._onObservabilityEvent.event;
|
|
401
486
|
this._onServerStateChanged = new Emitter();
|
|
402
487
|
this.onServerStateChanged = this._onServerStateChanged.event;
|
|
488
|
+
if (!options.storage) throw new Error("MCPClientManager requires a valid DurableObjectStorage instance");
|
|
403
489
|
this._storage = options.storage;
|
|
404
490
|
}
|
|
491
|
+
sql(query, ...bindings) {
|
|
492
|
+
return [...this._storage.sql.exec(query, ...bindings)];
|
|
493
|
+
}
|
|
494
|
+
saveServerToStorage(server) {
|
|
495
|
+
this.sql(`INSERT OR REPLACE INTO cf_agents_mcp_servers (
|
|
496
|
+
id, name, server_url, client_id, auth_url, callback_url, server_options
|
|
497
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?)`, server.id, server.name, server.server_url, server.client_id ?? null, server.auth_url ?? null, server.callback_url, server.server_options ?? null);
|
|
498
|
+
}
|
|
499
|
+
removeServerFromStorage(serverId) {
|
|
500
|
+
this.sql("DELETE FROM cf_agents_mcp_servers WHERE id = ?", serverId);
|
|
501
|
+
}
|
|
502
|
+
getServersFromStorage() {
|
|
503
|
+
return this.sql("SELECT id, name, server_url, client_id, auth_url, callback_url, server_options FROM cf_agents_mcp_servers");
|
|
504
|
+
}
|
|
505
|
+
clearServerAuthUrl(serverId) {
|
|
506
|
+
this.sql("UPDATE cf_agents_mcp_servers SET auth_url = NULL WHERE id = ?", serverId);
|
|
507
|
+
}
|
|
405
508
|
/**
|
|
406
509
|
* Create an auth provider for a server
|
|
407
510
|
* @internal
|
|
408
511
|
*/
|
|
409
512
|
createAuthProvider(serverId, callbackUrl, clientName, clientId) {
|
|
513
|
+
if (!this._storage) throw new Error("Cannot create auth provider: storage is not initialized");
|
|
410
514
|
const authProvider = new DurableObjectOAuthClientProvider(this._storage, clientName, callbackUrl);
|
|
411
515
|
authProvider.serverId = serverId;
|
|
412
516
|
if (clientId) authProvider.clientId = clientId;
|
|
@@ -420,7 +524,7 @@ var MCPClientManager = class {
|
|
|
420
524
|
*/
|
|
421
525
|
async restoreConnectionsFromStorage(clientName) {
|
|
422
526
|
if (this._isRestored) return;
|
|
423
|
-
const servers =
|
|
527
|
+
const servers = this.getServersFromStorage();
|
|
424
528
|
if (!servers || servers.length === 0) {
|
|
425
529
|
this._isRestored = true;
|
|
426
530
|
return;
|
|
@@ -428,12 +532,12 @@ var MCPClientManager = class {
|
|
|
428
532
|
for (const server of servers) {
|
|
429
533
|
const existingConn = this.mcpConnections[server.id];
|
|
430
534
|
if (existingConn) {
|
|
431
|
-
if (existingConn.connectionState ===
|
|
535
|
+
if (existingConn.connectionState === MCPConnectionState.READY) {
|
|
432
536
|
console.warn(`[MCPClientManager] Server ${server.id} already has a ready connection. Skipping recreation.`);
|
|
433
537
|
continue;
|
|
434
538
|
}
|
|
435
|
-
if (existingConn.connectionState ===
|
|
436
|
-
if (existingConn.connectionState ===
|
|
539
|
+
if (existingConn.connectionState === MCPConnectionState.AUTHENTICATING || existingConn.connectionState === MCPConnectionState.CONNECTING || existingConn.connectionState === MCPConnectionState.DISCOVERING) continue;
|
|
540
|
+
if (existingConn.connectionState === MCPConnectionState.FAILED) {
|
|
437
541
|
try {
|
|
438
542
|
await existingConn.client.close();
|
|
439
543
|
} catch (error) {
|
|
@@ -446,7 +550,7 @@ var MCPClientManager = class {
|
|
|
446
550
|
}
|
|
447
551
|
const parsedOptions = server.server_options ? JSON.parse(server.server_options) : null;
|
|
448
552
|
const authProvider = this.createAuthProvider(server.id, server.callback_url, clientName, server.client_id ?? void 0);
|
|
449
|
-
this.createConnection(server.id, server.server_url, {
|
|
553
|
+
const conn = this.createConnection(server.id, server.server_url, {
|
|
450
554
|
client: parsedOptions?.client ?? {},
|
|
451
555
|
transport: {
|
|
452
556
|
...parsedOptions?.transport ?? {},
|
|
@@ -454,13 +558,27 @@ var MCPClientManager = class {
|
|
|
454
558
|
authProvider
|
|
455
559
|
}
|
|
456
560
|
});
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
561
|
+
if (server.auth_url) {
|
|
562
|
+
conn.connectionState = MCPConnectionState.AUTHENTICATING;
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
565
|
+
this._restoreServer(server.id);
|
|
460
566
|
}
|
|
461
567
|
this._isRestored = true;
|
|
462
568
|
}
|
|
463
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
|
+
/**
|
|
464
582
|
* Connect to and register an MCP server
|
|
465
583
|
*
|
|
466
584
|
* @deprecated This method is maintained for backward compatibility.
|
|
@@ -507,7 +625,7 @@ var MCPClientManager = class {
|
|
|
507
625
|
await this.mcpConnections[id].init();
|
|
508
626
|
if (options.reconnect?.oauthCode) try {
|
|
509
627
|
await this.mcpConnections[id].completeAuthorization(options.reconnect.oauthCode);
|
|
510
|
-
await this.mcpConnections[id].
|
|
628
|
+
await this.mcpConnections[id].init();
|
|
511
629
|
} catch (error) {
|
|
512
630
|
this._onObservabilityEvent.fire({
|
|
513
631
|
type: "mcp:client:connect",
|
|
@@ -524,19 +642,22 @@ var MCPClientManager = class {
|
|
|
524
642
|
throw error;
|
|
525
643
|
}
|
|
526
644
|
const authUrl = options.transport?.authProvider?.authUrl;
|
|
527
|
-
if (this.mcpConnections[id].connectionState ===
|
|
645
|
+
if (this.mcpConnections[id].connectionState === MCPConnectionState.AUTHENTICATING && authUrl && options.transport?.authProvider?.redirectUrl) return {
|
|
528
646
|
authUrl,
|
|
529
647
|
clientId: options.transport?.authProvider?.clientId,
|
|
530
648
|
id
|
|
531
649
|
};
|
|
650
|
+
const discoverResult = await this.discoverIfConnected(id);
|
|
651
|
+
if (discoverResult && !discoverResult.success) throw new Error(`Failed to discover server capabilities: ${discoverResult.error}`);
|
|
532
652
|
return { id };
|
|
533
653
|
}
|
|
534
654
|
/**
|
|
535
655
|
* Create an in-memory connection object and set up observability
|
|
536
656
|
* Does NOT save to storage - use registerServer() for that
|
|
657
|
+
* @returns The connection object (existing or newly created)
|
|
537
658
|
*/
|
|
538
659
|
createConnection(id, url, options) {
|
|
539
|
-
if (this.mcpConnections[id]) return;
|
|
660
|
+
if (this.mcpConnections[id]) return this.mcpConnections[id];
|
|
540
661
|
const normalizedTransport = {
|
|
541
662
|
...options.transport,
|
|
542
663
|
type: options.transport?.type ?? "auto"
|
|
@@ -558,6 +679,7 @@ var MCPClientManager = class {
|
|
|
558
679
|
store.add(this.mcpConnections[id].onObservabilityEvent((event) => {
|
|
559
680
|
this._onObservabilityEvent.fire(event);
|
|
560
681
|
}));
|
|
682
|
+
return this.mcpConnections[id];
|
|
561
683
|
}
|
|
562
684
|
/**
|
|
563
685
|
* Register an MCP server connection without connecting
|
|
@@ -575,7 +697,8 @@ var MCPClientManager = class {
|
|
|
575
697
|
type: options.transport?.type ?? "auto"
|
|
576
698
|
}
|
|
577
699
|
});
|
|
578
|
-
|
|
700
|
+
const { authProvider: _, ...transportWithoutAuth } = options.transport ?? {};
|
|
701
|
+
this.saveServerToStorage({
|
|
579
702
|
id,
|
|
580
703
|
name: options.name,
|
|
581
704
|
server_url: options.url,
|
|
@@ -584,7 +707,7 @@ var MCPClientManager = class {
|
|
|
584
707
|
auth_url: options.authUrl ?? null,
|
|
585
708
|
server_options: JSON.stringify({
|
|
586
709
|
client: options.client,
|
|
587
|
-
transport:
|
|
710
|
+
transport: transportWithoutAuth
|
|
588
711
|
})
|
|
589
712
|
});
|
|
590
713
|
this._onServerStateChanged.fire();
|
|
@@ -593,14 +716,13 @@ var MCPClientManager = class {
|
|
|
593
716
|
/**
|
|
594
717
|
* Connect to an already registered MCP server and initialize the connection.
|
|
595
718
|
*
|
|
596
|
-
* For OAuth servers,
|
|
597
|
-
*
|
|
598
|
-
*
|
|
599
|
-
*
|
|
600
|
-
* For non-OAuth servers, this establishes the connection immediately and returns
|
|
601
|
-
* `{ 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()`.
|
|
602
722
|
*
|
|
603
|
-
*
|
|
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.
|
|
604
726
|
*
|
|
605
727
|
* @param id Server ID (must be registered first via registerServer())
|
|
606
728
|
* @returns Connection result with current state and OAuth info (if applicable)
|
|
@@ -608,70 +730,99 @@ var MCPClientManager = class {
|
|
|
608
730
|
async connectToServer(id) {
|
|
609
731
|
const conn = this.mcpConnections[id];
|
|
610
732
|
if (!conn) throw new Error(`Server ${id} is not registered. Call registerServer() first.`);
|
|
611
|
-
await conn.init();
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
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}`
|
|
626
767
|
};
|
|
627
768
|
}
|
|
628
|
-
if (conn.connectionState === "ready") this._onServerStateChanged.fire();
|
|
629
|
-
return { state: "ready" };
|
|
630
769
|
}
|
|
631
|
-
|
|
770
|
+
extractServerIdFromState(state) {
|
|
771
|
+
if (!state) return null;
|
|
772
|
+
const parts = state.split(".");
|
|
773
|
+
return parts.length === 2 ? parts[1] : null;
|
|
774
|
+
}
|
|
775
|
+
isCallbackRequest(req) {
|
|
632
776
|
if (req.method !== "GET") return false;
|
|
633
777
|
if (!req.url.includes("/callback")) return false;
|
|
634
|
-
|
|
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);
|
|
635
782
|
}
|
|
636
783
|
async handleCallbackRequest(req) {
|
|
637
784
|
const url = new URL(req.url);
|
|
638
|
-
const matchingServer = (await this._storage.listServers()).find((server) => {
|
|
639
|
-
return server.callback_url && req.url.startsWith(server.callback_url);
|
|
640
|
-
});
|
|
641
|
-
if (!matchingServer) throw new Error(`No callback URI match found for the request url: ${req.url}. Was the request matched with \`isCallbackRequest()\`?`);
|
|
642
|
-
const serverId = matchingServer.id;
|
|
643
785
|
const code = url.searchParams.get("code");
|
|
644
786
|
const state = url.searchParams.get("state");
|
|
645
787
|
const error = url.searchParams.get("error");
|
|
646
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}`);
|
|
647
800
|
if (error) return {
|
|
648
801
|
serverId,
|
|
649
802
|
authSuccess: false,
|
|
650
803
|
authError: errorDescription || error
|
|
651
804
|
};
|
|
652
805
|
if (!code) throw new Error("Unauthorized: no code provided");
|
|
653
|
-
if (
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
if (!conn.options.transport.authProvider) throw new Error("Trying to finalize authentication for a server connection without an authProvider");
|
|
662
|
-
const clientId = conn.options.transport.authProvider.clientId || state;
|
|
663
|
-
conn.options.transport.authProvider.clientId = clientId;
|
|
664
|
-
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"`);
|
|
665
814
|
try {
|
|
815
|
+
await authProvider.consumeState(state);
|
|
666
816
|
await conn.completeAuthorization(code);
|
|
667
|
-
await
|
|
817
|
+
await authProvider.deleteCodeVerifier();
|
|
818
|
+
this.clearServerAuthUrl(serverId);
|
|
668
819
|
this._onServerStateChanged.fire();
|
|
669
820
|
return {
|
|
670
821
|
serverId,
|
|
671
822
|
authSuccess: true
|
|
672
823
|
};
|
|
673
|
-
} catch (
|
|
674
|
-
const errorMessage =
|
|
824
|
+
} catch (authError) {
|
|
825
|
+
const errorMessage = authError instanceof Error ? authError.message : String(authError);
|
|
675
826
|
this._onServerStateChanged.fire();
|
|
676
827
|
return {
|
|
677
828
|
serverId,
|
|
@@ -681,8 +832,40 @@ var MCPClientManager = class {
|
|
|
681
832
|
}
|
|
682
833
|
}
|
|
683
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
|
+
/**
|
|
684
867
|
* Establish connection in the background after OAuth completion
|
|
685
|
-
* This method
|
|
868
|
+
* This method connects to the server and discovers its capabilities
|
|
686
869
|
* @param serverId The server ID to establish connection for
|
|
687
870
|
*/
|
|
688
871
|
async establishConnection(serverId) {
|
|
@@ -697,25 +880,34 @@ var MCPClientManager = class {
|
|
|
697
880
|
});
|
|
698
881
|
return;
|
|
699
882
|
}
|
|
700
|
-
|
|
701
|
-
await conn.establishConnection();
|
|
702
|
-
this._onServerStateChanged.fire();
|
|
703
|
-
} catch (error) {
|
|
704
|
-
const url = conn.url.toString();
|
|
883
|
+
if (conn.connectionState === MCPConnectionState.DISCOVERING || conn.connectionState === MCPConnectionState.READY) {
|
|
705
884
|
this._onObservabilityEvent.fire({
|
|
706
885
|
type: "mcp:client:connect",
|
|
707
|
-
displayMessage: `
|
|
886
|
+
displayMessage: `establishConnection skipped for ${serverId}, already in ${conn.connectionState} state`,
|
|
708
887
|
payload: {
|
|
709
|
-
url,
|
|
710
|
-
transport: conn.options.transport.type
|
|
711
|
-
state: conn.connectionState
|
|
712
|
-
error: toErrorMessage(error)
|
|
888
|
+
url: conn.url.toString(),
|
|
889
|
+
transport: conn.options.transport.type || "unknown",
|
|
890
|
+
state: conn.connectionState
|
|
713
891
|
},
|
|
714
892
|
timestamp: Date.now(),
|
|
715
893
|
id: nanoid()
|
|
716
894
|
});
|
|
717
|
-
|
|
895
|
+
return;
|
|
718
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
|
+
});
|
|
719
911
|
}
|
|
720
912
|
/**
|
|
721
913
|
* Configure OAuth callback handling
|
|
@@ -759,7 +951,7 @@ var MCPClientManager = class {
|
|
|
759
951
|
*/
|
|
760
952
|
getAITools() {
|
|
761
953
|
if (!this.jsonSchema) throw new Error("jsonSchema not initialized.");
|
|
762
|
-
for (const [id, conn] of Object.entries(this.mcpConnections)) if (conn.connectionState !==
|
|
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.`);
|
|
763
955
|
return Object.fromEntries(getNamespacedData(this.mcpConnections, "tools").map((tool) => {
|
|
764
956
|
return [`tool_${tool.serverId.replace(/-/g, "")}_${tool.name}`, {
|
|
765
957
|
description: tool.description,
|
|
@@ -789,10 +981,18 @@ var MCPClientManager = class {
|
|
|
789
981
|
return this.getAITools();
|
|
790
982
|
}
|
|
791
983
|
/**
|
|
792
|
-
* 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).
|
|
793
992
|
*/
|
|
794
993
|
async closeAllConnections() {
|
|
795
994
|
const ids = Object.keys(this.mcpConnections);
|
|
995
|
+
for (const id of ids) this.mcpConnections[id].cancelDiscovery();
|
|
796
996
|
await Promise.all(ids.map(async (id) => {
|
|
797
997
|
await this.mcpConnections[id].client.close();
|
|
798
998
|
}));
|
|
@@ -809,6 +1009,7 @@ var MCPClientManager = class {
|
|
|
809
1009
|
*/
|
|
810
1010
|
async closeConnection(id) {
|
|
811
1011
|
if (!this.mcpConnections[id]) throw new Error(`Connection with id "${id}" does not exist.`);
|
|
1012
|
+
this.mcpConnections[id].cancelDiscovery();
|
|
812
1013
|
await this.mcpConnections[id].client.close();
|
|
813
1014
|
delete this.mcpConnections[id];
|
|
814
1015
|
const store = this._connectionDisposables.get(id);
|
|
@@ -816,17 +1017,20 @@ var MCPClientManager = class {
|
|
|
816
1017
|
this._connectionDisposables.delete(id);
|
|
817
1018
|
}
|
|
818
1019
|
/**
|
|
819
|
-
* Remove an MCP server from storage
|
|
1020
|
+
* Remove an MCP server - closes connection if active and removes from storage.
|
|
820
1021
|
*/
|
|
821
1022
|
async removeServer(serverId) {
|
|
822
|
-
|
|
1023
|
+
if (this.mcpConnections[serverId]) try {
|
|
1024
|
+
await this.closeConnection(serverId);
|
|
1025
|
+
} catch (_e) {}
|
|
1026
|
+
this.removeServerFromStorage(serverId);
|
|
823
1027
|
this._onServerStateChanged.fire();
|
|
824
1028
|
}
|
|
825
1029
|
/**
|
|
826
1030
|
* List all MCP servers from storage
|
|
827
1031
|
*/
|
|
828
|
-
|
|
829
|
-
return
|
|
1032
|
+
listServers() {
|
|
1033
|
+
return this.getServersFromStorage();
|
|
830
1034
|
}
|
|
831
1035
|
/**
|
|
832
1036
|
* Dispose the manager and all resources.
|
|
@@ -897,5 +1101,5 @@ function getNamespacedData(mcpClients, type) {
|
|
|
897
1101
|
}
|
|
898
1102
|
|
|
899
1103
|
//#endregion
|
|
900
|
-
export { getNamespacedData as n,
|
|
901
|
-
//# sourceMappingURL=client-
|
|
1104
|
+
export { DisposableStore as i, getNamespacedData as n, MCPConnectionState as r, MCPClientManager as t };
|
|
1105
|
+
//# sourceMappingURL=client-QZa2Rq0l.js.map
|