agents 0.20.0 → 0.20.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.
@@ -18,7 +18,7 @@ import {
18
18
  wt as MCPClientManagerOptions,
19
19
  xt as MCPClientElicitationHandler,
20
20
  yt as MCPAITool
21
- } from "../agent-tool-types-Btk9ETS-.js";
21
+ } from "../agent-tool-types-BC-WFlsz.js";
22
22
  export {
23
23
  type ElicitRequest,
24
24
  type ElicitResult,
@@ -33,7 +33,7 @@ import {
33
33
  tn as MCPStorageApi,
34
34
  xt as MCPClientElicitationHandler,
35
35
  zt as RPCClientTransportOptions
36
- } from "../agent-tool-types-Btk9ETS-.js";
36
+ } from "../agent-tool-types-BC-WFlsz.js";
37
37
  import {
38
38
  a as McpAuthContext,
39
39
  n as StatelessMcpHandler,
package/dist/mcp/index.js CHANGED
@@ -1,13 +1,38 @@
1
1
  import "../types.js";
2
2
  import { c as RPC_DO_PREFIX, i as normalizeServerId, n as MCP_SERVER_ID_MAX_LENGTH, o as RPCClientTransport, s as RPCServerTransport } from "../client-zqKcsyFa.js";
3
3
  import { Agent, getAgentByName, getCurrentAgent } from "../index.js";
4
- import { a as KEEPALIVE_INTERVAL_MS, i as KEEPALIVE_FRAME, n as getMcpAuthContext, o as startKeepalive, r as runWithAuthContext, t as createStatelessMcpHandler } from "../handler-stateless-8hQN_kC3.js";
4
+ import { n as getMcpAuthContext, r as runWithAuthContext, t as createStatelessMcpHandler } from "../handler-stateless-CIkKPETH.js";
5
5
  import { SSEClientTransport, StreamableHTTPClientTransport } from "@modelcontextprotocol/client";
6
6
  import { ElicitRequestSchema, InitializeRequestSchema, JSONRPCMessageSchema, SUPPORTED_PROTOCOL_VERSIONS, isInitializeRequest, isJSONRPCErrorResponse, isJSONRPCNotification, isJSONRPCRequest, isJSONRPCResultResponse } from "@modelcontextprotocol/sdk/types.js";
7
7
  import { McpServer, Server } from "@modelcontextprotocol/server";
8
8
  import { McpServer as McpServer$1 } from "@modelcontextprotocol/sdk/server/mcp.js";
9
9
  import { Server as Server$1 } from "@modelcontextprotocol/sdk/server/index.js";
10
10
  import { WebStandardStreamableHTTPServerTransport as WebStandardStreamableHTTPServerTransport$1 } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
11
+ //#region src/mcp/sse-keepalive.ts
12
+ /**
13
+ * SSE keepalive for the retained McpAgent WebSocket-to-SSE bridge.
14
+ *
15
+ * SDK-backed HTTP transports own their keepalive timers. This helper remains
16
+ * only for the custom bridge, where long-running POST tool calls can otherwise
17
+ * sit silent until Cloudflare's edge closes the idle stream.
18
+ *
19
+ * See cloudflare/agents#1583.
20
+ */
21
+ /** Interval between bridge SSE keepalive comment frames, in ms. */
22
+ const KEEPALIVE_INTERVAL_MS = 25e3;
23
+ /** SSE comment frame the parser drops before any event dispatch. */
24
+ const KEEPALIVE_FRAME = ": keepalive\n\n";
25
+ /**
26
+ * Start an SSE keepalive on `writer`. Returns a `clearInterval` handle
27
+ * that the stream cleanup must invoke when the stream closes.
28
+ */
29
+ function startKeepalive(writer, encoder) {
30
+ const handle = setInterval(() => {
31
+ writer.write(encoder.encode(KEEPALIVE_FRAME)).catch(() => clearInterval(handle));
32
+ }, KEEPALIVE_INTERVAL_MS);
33
+ return handle;
34
+ }
35
+ //#endregion
11
36
  //#region src/mcp/utils.ts
12
37
  /**
13
38
  * Since we use WebSockets to bridge the client to the
@@ -1010,13 +1035,9 @@ var StreamableHTTPEdgeClientTransport = class extends StreamableHTTPClientTransp
1010
1035
  * survive DO hibernation / eviction. On the first request after a cold
1011
1036
  * start, the saved initialize params are replayed through the `Server`
1012
1037
  * so client capabilities are re-established.
1013
- * 3. **SSE keepalive** — SSE responses are wrapped in a TransformStream that
1014
- * injects a `: keepalive\n\n` comment frame every 25s so the Cloudflare
1015
- * edge ~5min idle-stream watchdog doesn't kill long-running tool calls.
1016
- * Disabled on the standalone GET stream when an `eventStore` is
1017
- * configured — clients recover idle drops via `Last-Event-ID` instead.
1018
- * POST response streams always keepalive (no resumption path during a
1019
- * mid-flight tool call). See cloudflare/agents#1583.
1038
+ * 3. **SSE keepalive** — delegated to the SDK transport, which writes SSE
1039
+ * comment frames and owns timer cleanup. Configure the cadence with the
1040
+ * inherited `keepAliveMs` option.
1020
1041
  *
1021
1042
  * Stateless handlers do not import this module. Everything else (session
1022
1043
  * validation, SSE streaming, protocol-version
@@ -1041,7 +1062,6 @@ var WorkerTransport = class extends WebStandardStreamableHTTPServerTransport$1 {
1041
1062
  });
1042
1063
  this._stateRestored = false;
1043
1064
  this._bridgeInstalled = false;
1044
- this._keepaliveCleanups = /* @__PURE__ */ new Map();
1045
1065
  this._closedRequestIds = /* @__PURE__ */ new Set();
1046
1066
  this._corsOptions = corsOptions;
1047
1067
  this._storage = storage;
@@ -1057,17 +1077,15 @@ var WorkerTransport = class extends WebStandardStreamableHTTPServerTransport$1 {
1057
1077
  /**
1058
1078
  * Top-level request entry point. Handles CORS preflight, restores any
1059
1079
  * persisted state on first invocation, then delegates to the SDK transport
1060
- * and finally appends CORS headers + keepalive to whatever response comes
1061
- * back.
1080
+ * and finally appends CORS headers to whatever response comes back.
1062
1081
  */
1063
1082
  async handleRequest(request, options) {
1064
1083
  if (request.method === "OPTIONS") return new Response(null, { headers: this.getCorsHeaders({ forPreflight: true }) });
1065
1084
  await this.restoreState();
1066
1085
  this.installOnSessionInitializedBridge();
1067
1086
  await this.captureInitializeParams(request, options);
1068
- const requestIdForKeepalive = request.method === "GET" ? "_standalone" : this._pendingRequestId;
1069
1087
  const response = await super.handleRequest(request, options);
1070
- return this.withCorsHeaders(this.withKeepalive(this.normalizeAllowHeader(response), requestIdForKeepalive));
1088
+ return this.withCorsHeaders(this.normalizeAllowHeader(response));
1071
1089
  }
1072
1090
  /**
1073
1091
  * The SDK's 405 responses advertise `Allow: GET, POST, DELETE` because
@@ -1087,19 +1105,10 @@ var WorkerTransport = class extends WebStandardStreamableHTTPServerTransport$1 {
1087
1105
  });
1088
1106
  }
1089
1107
  closeSSEStream(requestId) {
1090
- this._keepaliveCleanups.get(requestId)?.();
1091
- this._keepaliveCleanups.delete(requestId);
1092
1108
  this._closedRequestIds.add(requestId);
1093
1109
  super.closeSSEStream(requestId);
1094
1110
  }
1095
- closeStandaloneSSEStream() {
1096
- this._keepaliveCleanups.get("_standalone")?.();
1097
- this._keepaliveCleanups.delete("_standalone");
1098
- super.closeStandaloneSSEStream();
1099
- }
1100
1111
  async close() {
1101
- for (const cleanup of Array.from(this._keepaliveCleanups.values())) cleanup();
1102
- this._keepaliveCleanups.clear();
1103
1112
  this._closedRequestIds.clear();
1104
1113
  await super.close();
1105
1114
  }
@@ -1129,77 +1138,6 @@ var WorkerTransport = class extends WebStandardStreamableHTTPServerTransport$1 {
1129
1138
  if (requestId !== void 0 && this._closedRequestIds.has(requestId)) return;
1130
1139
  await super.send(message, options);
1131
1140
  }
1132
- /**
1133
- * If the response is an SSE stream, tee the body through a TransformStream
1134
- * that injects a `: keepalive\n\n` comment frame every 25s. The interval
1135
- * is cleared when the wrapped stream closes — which happens both when the
1136
- * SDK ends the underlying stream naturally and when `closeSSEStream` is
1137
- * called.
1138
- *
1139
- * Keepalive policy:
1140
- * - POST response streams (`key` is a request id): always keepalive.
1141
- * In-progress tool calls have no recovery path — if the stream drops
1142
- * mid-execution the result is lost — so we keep it under the
1143
- * Cloudflare edge ~5min idle watchdog.
1144
- * - Standalone GET stream (`key === "_standalone"`): keepalive only
1145
- * when no `eventStore` is configured. When resumability is enabled,
1146
- * clients reconnect with `Last-Event-ID` after an idle drop, so we
1147
- * skip the keepalive and let the DO hibernate.
1148
- *
1149
- * Uses the shared `sse-keepalive` constants so both this wrapper and
1150
- * `McpAgent.serve()` write identical frames at the same cadence.
1151
- * See cloudflare/agents#1583.
1152
- */
1153
- withKeepalive(response, key) {
1154
- if (!(response.headers.get("Content-Type") ?? "").includes("text/event-stream") || !response.body) return response;
1155
- if (key === "_standalone" && this.eventStoreConfigured()) return response;
1156
- const encoder = new TextEncoder();
1157
- let intervalId;
1158
- let controllerRef;
1159
- const clear = () => {
1160
- if (intervalId !== void 0) {
1161
- clearInterval(intervalId);
1162
- intervalId = void 0;
1163
- }
1164
- if (key !== void 0) this._keepaliveCleanups.delete(key);
1165
- };
1166
- const transform = new TransformStream({
1167
- start: (controller) => {
1168
- controllerRef = controller;
1169
- intervalId = setInterval(() => {
1170
- try {
1171
- controllerRef?.enqueue(encoder.encode(KEEPALIVE_FRAME));
1172
- } catch {
1173
- clear();
1174
- }
1175
- }, KEEPALIVE_INTERVAL_MS);
1176
- if (key !== void 0) this._keepaliveCleanups.set(key, clear);
1177
- },
1178
- transform(chunk, controller) {
1179
- controller.enqueue(chunk);
1180
- },
1181
- flush() {
1182
- clear();
1183
- },
1184
- cancel() {
1185
- clear();
1186
- }
1187
- });
1188
- const piped = response.body.pipeThrough(transform);
1189
- return new Response(piped, {
1190
- status: response.status,
1191
- statusText: response.statusText,
1192
- headers: response.headers
1193
- });
1194
- }
1195
- /**
1196
- * Does the SDK transport have an `eventStore`? Reaches into the SDK's
1197
- * private field because the option isn't surfaced on the public API —
1198
- * we only need a yes/no for keepalive policy.
1199
- */
1200
- eventStoreConfigured() {
1201
- return this._eventStore !== void 0;
1202
- }
1203
1141
  getCorsHeaders({ forPreflight } = {}) {
1204
1142
  const merged = {
1205
1143
  ...DEFAULT_CORS_OPTIONS,
@@ -1235,19 +1173,15 @@ var WorkerTransport = class extends WebStandardStreamableHTTPServerTransport$1 {
1235
1173
  this._bridgeInstalled = true;
1236
1174
  }
1237
1175
  async captureInitializeParams(request, handleOptions) {
1238
- this._pendingRequestId = void 0;
1239
1176
  if (request.method !== "POST") return;
1240
1177
  try {
1241
1178
  const parsed = handleOptions?.parsedBody ?? await request.clone().json();
1242
- const messages = Array.isArray(parsed) ? parsed : [parsed];
1243
- const init = messages.find((m) => typeof m === "object" && m !== null && isInitializeRequest(m));
1179
+ const init = (Array.isArray(parsed) ? parsed : [parsed]).find((m) => typeof m === "object" && m !== null && isInitializeRequest(m));
1244
1180
  if (init && isInitializeRequest(init)) this._capturedInitializeParams = {
1245
1181
  capabilities: init.params.capabilities,
1246
1182
  clientInfo: init.params.clientInfo,
1247
1183
  protocolVersion: init.params.protocolVersion
1248
1184
  };
1249
- const firstRequest = messages.find((m) => typeof m === "object" && m !== null && "id" in m && "method" in m);
1250
- if (firstRequest) this._pendingRequestId = firstRequest.id;
1251
1185
  } catch {}
1252
1186
  }
1253
1187
  async restoreState() {