Skip to content

Commit 3194bbd

Browse files
committed
trim to the openSession restart-retry backstop
Per review: the concurrency half is superseded by #1907 (already on main, stricter). Rebased onto current main and kept only the retry loop on the documented 503 restart envelope.
1 parent 9c67fd5 commit 3194bbd

1 file changed

Lines changed: 33 additions & 12 deletions

File tree

e2e/cloud/mcp-session-cap-eviction.test.ts

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -73,21 +73,42 @@ const openSession = async (
7373
label: string,
7474
recordSession: (sessionId: string) => void,
7575
): Promise<string> => {
76-
const initialized = await postJson(mcpUrl, bearer, {
77-
jsonrpc: "2.0" as const,
78-
id: "initialize",
79-
method: "initialize",
80-
params: {
81-
protocolVersion: PROTOCOL_VERSION,
82-
capabilities: {},
83-
clientInfo: { name: `executor-e2e-cap-eviction-${label}`, version: "0.0.1" },
84-
},
85-
});
86-
const sessionId = initialized.headers.get("mcp-session-id");
87-
if (!sessionId) {
76+
// The platform can reset a session Durable Object while its initialize
77+
// is in flight, and the server answers that with the documented restart
78+
// envelope (503, -32001, "MCP session is restarting, please retry") — the
79+
// same contract a streamable-http client follows: same request, after the
80+
// advertised delay. Treat it as transient here instead of failing the
81+
// scenario on a retryable platform blip.
82+
const RESTART_ATTEMPTS = 8;
83+
const RESTART_DELAY_MS = 250;
84+
let minted: { readonly response: Response; readonly sessionId: string } | undefined;
85+
for (let attempt = 0; attempt < RESTART_ATTEMPTS; attempt += 1) {
86+
const response = await postJson(mcpUrl, bearer, {
87+
jsonrpc: "2.0" as const,
88+
id: "initialize",
89+
method: "initialize",
90+
params: {
91+
protocolVersion: PROTOCOL_VERSION,
92+
capabilities: {},
93+
clientInfo: { name: `executor-e2e-cap-eviction-${label}`, version: "0.0.1" },
94+
},
95+
});
96+
const candidate = response.headers.get("mcp-session-id");
97+
if (candidate !== null && candidate.length > 0) {
98+
minted = { response, sessionId: candidate };
99+
break;
100+
}
101+
const body = await response.text().catch(() => "");
102+
const isRestart = response.status === 503 && body.includes("MCP session is restarting");
103+
if (!isRestart) break;
104+
if (attempt === RESTART_ATTEMPTS - 1) break;
105+
await new Promise((resolve) => setTimeout(resolve, RESTART_DELAY_MS));
106+
}
107+
if (!minted) {
88108
// oxlint-disable-next-line executor/no-error-constructor -- boundary: e2e setup precondition.
89109
throw new Error(`openSession (${label}): no mcp-session-id header`);
90110
}
111+
const { response: initialized, sessionId } = minted;
91112
// Recorded the moment the id exists — BEFORE the body read and status
92113
// assertion below, either of which can throw with the session already live
93114
// on the server. The cleanup finalizer needs the id on every one of those

0 commit comments

Comments
 (0)