History

Soften MCP SSE rotation max age (#1318)

Rhys Sullivan committed 4 months ago

Commit dfbc1041 parent 8e490722 files · +20 −10Browse files

Unified · Split

2 files
  1. packages/hosts/cloudflare/src/mcp/agents-sse-max-age.test.ts +7 −0
  2. patches/agents@0.17.3.patch +13 −10
packages/hosts/cloudflare/src/mcp/agents-sse-max-age.test.ts+7−0
@@ -2,6 +2,8 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "@effect/vitest" · 1 unmodified line
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", () => { · 256 unmodified lines
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+13−10
@@ -6,7 +6,7 @@ index c8fad448e8797b89690a99d93490d1363851b225..439da01e789713d217778e8bdebfa1a9 · 5 unmodified lines
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 · 9 unmodified lines
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 · 6 unmodified lines
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 · 13 unmodified lines
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 · 91 unmodified lines
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 · 14 unmodified lines
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 · 95 unmodified lines
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 · 14 unmodified lines
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 · 106 unmodified lines
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