Skip to content

Commit dfbc104

Browse files
authored
Soften MCP SSE rotation max age (#1318)
1 parent 8e49072 commit dfbc104

2 files changed

Lines changed: 20 additions & 10 deletions

File tree

packages/hosts/cloudflare/src/mcp/agents-sse-max-age.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "@effect/vitest"
22
import { MAX_SSE_AGE_MS, McpAgent } from "agents/mcp";
33
import { Effect, Option, Schema } from "effect";
44

5+
import { SESSION_TIMEOUT_MS } from "./session-alarm-policy";
6+
57
const KEEPALIVE_INTERVAL_MS = 25_000;
68
const MAX_PENDING_SSE_BYTES = 8 * 1024 * 1024;
79

@@ -264,6 +266,11 @@ describe("agents SSE max-age rotation", () => {
264266
vi.useRealTimers();
265267
});
266268

269+
it("keeps the default max age well above the session idle timeout", () => {
270+
expect(MAX_SSE_AGE_MS).toBe(30 * 60 * 1000);
271+
expect(MAX_SSE_AGE_MS).toBeGreaterThanOrEqual(6 * SESSION_TIMEOUT_MS);
272+
});
273+
267274
it("closes a healthy draining SSE connection within one keepalive tick after max age", async () => {
268275
const { response, ws } = await openSse();
269276
const drained = drainResponse(response);

patches/agents@0.17.3.patch

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ index c8fad448e8797b89690a99d93490d1363851b225..439da01e789713d217778e8bdebfa1a9
66
xt as MCPClientOAuthCallbackConfig,
77
zt as ElicitResult
88
} from "../agent-tool-types-CNyE1iz_.js";
9-
+declare const MAX_SSE_AGE_MS = 300000;
9+
+declare const MAX_SSE_AGE_MS = 1800000;
1010
export {
1111
type ClearableEventStore,
1212
type CreateMcpHandlerOptions,
@@ -22,11 +22,14 @@ diff --git a/dist/mcp/index.js b/dist/mcp/index.js
2222
index 1edcf0c8c9e67aa211ae515e7672cdf79912101e..0ba4f5bff8e6a968aa18c609952e576cedf13d8f 100644
2323
--- a/dist/mcp/index.js
2424
+++ b/dist/mcp/index.js
25-
@@ -28,13 +28,14 @@ import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/
25+
@@ -28,13 +28,17 @@ import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/
2626
const KEEPALIVE_INTERVAL_MS = 25e3;
2727
/** SSE comment frame the parser drops before any event dispatch. */
2828
const KEEPALIVE_FRAME = ": keepalive\n\n";
29-
+const MAX_SSE_AGE_MS = 300_000;
29+
+// Max age is a stalled-client memory backstop, not session idleness. Keep it
30+
+// well above Executor's 5 minute session idle timeout so active clients rarely
31+
+// rotate.
32+
+const MAX_SSE_AGE_MS = 30 * 60 * 1000;
3033
/**
3134
* Start an SSE keepalive on `writer`. Returns a `clearInterval` handle
3235
* that the stream cleanup must invoke when the stream closes.
@@ -39,7 +42,7 @@ index 1edcf0c8c9e67aa211ae515e7672cdf79912101e..0ba4f5bff8e6a968aa18c609952e576c
3942
}, KEEPALIVE_INTERVAL_MS);
4043
return handle;
4144
}
42-
@@ -180,10 +181,15 @@ const createStreamingHttpHandler = (basePath, namespace, options = {}) => {
45+
@@ -180,10 +184,15 @@ const createStreamingHttpHandler = (basePath, namespace, options = {}) => {
4346
});
4447
return new Response(body, { status: 404 });
4548
}
@@ -59,7 +62,7 @@ index 1edcf0c8c9e67aa211ae515e7672cdf79912101e..0ba4f5bff8e6a968aa18c609952e576c
5962
request.headers.forEach((value, key) => {
6063
existingHeaders[key] = value;
6164
});
62-
@@ -206,45 +212,71 @@ const createStreamingHttpHandler = (basePath, namespace, options = {}) => {
65+
@@ -206,45 +215,71 @@ const createStreamingHttpHandler = (basePath, namespace, options = {}) => {
6366
jsonrpc: "2.0"
6467
});
6568
return new Response(body, { status: 500 });
@@ -157,7 +160,7 @@ index 1edcf0c8c9e67aa211ae515e7672cdf79912101e..0ba4f5bff8e6a968aa18c609952e576c
157160
}
158161
onClose().catch(console.error);
159162
});
160-
@@ -279,10 +311,16 @@ const createStreamingHttpHandler = (basePath, namespace, options = {}) => {
163+
@@ -279,10 +314,16 @@ const createStreamingHttpHandler = (basePath, namespace, options = {}) => {
161164
id: null,
162165
jsonrpc: "2.0"
163166
}), { status: 400 });
@@ -178,7 +181,7 @@ index 1edcf0c8c9e67aa211ae515e7672cdf79912101e..0ba4f5bff8e6a968aa18c609952e576c
178181
props: ctx.props,
179182
jurisdiction: options.jurisdiction
180183
});
181-
@@ -306,27 +344,86 @@ const createStreamingHttpHandler = (basePath, namespace, options = {}) => {
184+
@@ -306,27 +347,86 @@ const createStreamingHttpHandler = (basePath, namespace, options = {}) => {
182185
if (!ws) {
183186
await writer.close();
184187
return new Response("Failed to establish WS to DO", { status: 500 });
@@ -280,7 +283,7 @@ index 1edcf0c8c9e67aa211ae515e7672cdf79912101e..0ba4f5bff8e6a968aa18c609952e576c
280283
return new Response(readable, {
281284
headers: {
282285
"Cache-Control": "no-cache",
283-
@@ -389,10 +486,16 @@ const createLegacySseHandler = (basePath, namespace, options = {}) => {
286+
@@ -389,10 +489,16 @@ const createLegacySseHandler = (basePath, namespace, options = {}) => {
284287
const url = new URL(request.url);
285288
if (request.method === "GET" && basePattern.test(url)) {
286289
const sessionId = url.searchParams.get("sessionId") || namespace.newUniqueId().toString();
@@ -301,7 +304,7 @@ index 1edcf0c8c9e67aa211ae515e7672cdf79912101e..0ba4f5bff8e6a968aa18c609952e576c
301304
endpointUrl.pathname = encodeURI(`${basePath}/message`);
302305
endpointUrl.searchParams.set("sessionId", sessionId);
303306
const endpointMessage = `event: endpoint\ndata: ${endpointUrl.pathname + endpointUrl.search + endpointUrl.hash}\n\n`;
304-
@@ -414,35 +517,94 @@ const createLegacySseHandler = (basePath, namespace, options = {}) => {
307+
@@ -414,35 +520,94 @@ const createLegacySseHandler = (basePath, namespace, options = {}) => {
305308
console.error("Failed to establish WebSocket connection");
306309
await writer.close();
307310
return new Response("Failed to establish WebSocket connection", { status: 500 });
@@ -414,7 +417,7 @@ index 1edcf0c8c9e67aa211ae515e7672cdf79912101e..0ba4f5bff8e6a968aa18c609952e576c
414417
console.error("Error closing SSE connection:", error);
415418
}
416419
}
417-
@@ -1698,6 +1860,6 @@ var McpAgent = class McpAgent extends Agent {
420+
@@ -1698,6 +1863,6 @@ var McpAgent = class McpAgent extends Agent {
418421
};
419422
McpAgent.STREAM_REQS_KEY_PREFIX = "__mcp_stream_reqs__:";
420423
//#endregion

0 commit comments

Comments
 (0)