mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-20 14:12:59 +03:00
Merged via merge-train (release/v3.8.50, batch1 2026-08-20) — static gates (typecheck/file-size/complexity/cognitive/changelog) green on the combined tree; test:unit reds observed in the boarded run were verified pre-existing on the pure release tip (unrelated flake), not caused by this PR. Thanks for the contribution!
This commit is contained in:
@@ -284,12 +284,30 @@ export async function handleMcpStreamableHTTP(request: Request): Promise<Respons
|
||||
return protectMcpSseResponse(request, await handleStreamableRequest(request));
|
||||
}
|
||||
|
||||
interface RpcRequest {
|
||||
method?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle SSE requests.
|
||||
* SSE transport is implemented via Streamable HTTP transport with GET for SSE stream
|
||||
* and POST for messages (the Streamable HTTP transport supports both patterns).
|
||||
*/
|
||||
export async function handleMcpSSE(request: Request): Promise<Response> {
|
||||
if (request.method === "POST") {
|
||||
try {
|
||||
const body = await request.clone().json();
|
||||
const isInitialize = Array.isArray(body)
|
||||
? body.some((req: RpcRequest) => req?.method === "initialize")
|
||||
: (body as RpcRequest)?.method === "initialize";
|
||||
|
||||
if (isInitialize) {
|
||||
console.log("[MCP] New client initialize detected, resetting SSE singleton...");
|
||||
closeSseTransport();
|
||||
}
|
||||
} catch (err) {}
|
||||
}
|
||||
const { transport } = ensureSseServer();
|
||||
|
||||
try {
|
||||
|
||||
81
tests/unit/mcp-sse-singleton-reset-10772.test.ts
Normal file
81
tests/unit/mcp-sse-singleton-reset-10772.test.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
const mod = await import("../../open-sse/mcp-server/httpTransport.ts");
|
||||
|
||||
function initializeRequest(id: number): Request {
|
||||
return new Request("http://localhost/api/mcp/sse", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json, text/event-stream",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
jsonrpc: "2.0",
|
||||
method: "initialize",
|
||||
id,
|
||||
params: {
|
||||
protocolVersion: "2025-03-26",
|
||||
capabilities: {},
|
||||
clientInfo: { name: "test-client", version: "1.0.0" },
|
||||
},
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
function batchedInitializeRequest(id: number): Request {
|
||||
return new Request("http://localhost/api/mcp/sse", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json, text/event-stream",
|
||||
},
|
||||
body: JSON.stringify([
|
||||
{
|
||||
jsonrpc: "2.0",
|
||||
method: "initialize",
|
||||
id,
|
||||
params: {
|
||||
protocolVersion: "2025-03-26",
|
||||
capabilities: {},
|
||||
clientInfo: { name: "test-client", version: "1.0.0" },
|
||||
},
|
||||
},
|
||||
]),
|
||||
});
|
||||
}
|
||||
|
||||
// ── Regression #10690 / PR #10772: a second `initialize` POST must reset the
|
||||
// SSE singleton instead of failing with 400 ("already initialized"). ─────────
|
||||
|
||||
test("handleMcpSSE: a second POST initialize after a successful first one does not return 400", async () => {
|
||||
mod.shutdownMcpHttp();
|
||||
|
||||
const firstRes = await mod.handleMcpSSE(initializeRequest(1));
|
||||
assert.notEqual(firstRes.status, 400, "first initialize should not fail");
|
||||
|
||||
const secondRes = await mod.handleMcpSSE(initializeRequest(2));
|
||||
assert.notEqual(
|
||||
secondRes.status,
|
||||
400,
|
||||
"a second client initialize must reset the SSE singleton instead of returning 400"
|
||||
);
|
||||
|
||||
mod.shutdownMcpHttp();
|
||||
});
|
||||
|
||||
test("handleMcpSSE: a batched (array) JSON-RPC body containing initialize also resets the singleton", async () => {
|
||||
mod.shutdownMcpHttp();
|
||||
|
||||
const firstRes = await mod.handleMcpSSE(initializeRequest(1));
|
||||
assert.notEqual(firstRes.status, 400, "first initialize should not fail");
|
||||
|
||||
const batchedRes = await mod.handleMcpSSE(batchedInitializeRequest(2));
|
||||
assert.notEqual(
|
||||
batchedRes.status,
|
||||
400,
|
||||
"a batched initialize entry must also trigger the SSE singleton reset"
|
||||
);
|
||||
|
||||
mod.shutdownMcpHttp();
|
||||
});
|
||||
Reference in New Issue
Block a user