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