mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-04 06:12:10 +03:00
fix(mcp): keep POST SSE responses uncompressed (#8303)
Co-authored-by: Ravi Tharuma <RaviTharuma@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -166,6 +166,26 @@ function errorResponse(message: string, code: number, status = 400): Response {
|
||||
);
|
||||
}
|
||||
|
||||
export function protectMcpSseResponse(request: Request, response: Response): Response {
|
||||
if (
|
||||
request.method !== "POST" ||
|
||||
!response.headers.get("content-type")?.toLowerCase().includes("text/event-stream")
|
||||
) {
|
||||
return response;
|
||||
}
|
||||
|
||||
const headers = new Headers(response.headers);
|
||||
const cacheControl = headers.get("cache-control");
|
||||
if (!/(?:^|,)\s*no-transform(?:\s*(?:,|$))/i.test(cacheControl ?? "")) {
|
||||
headers.set("cache-control", [cacheControl, "no-transform"].filter(Boolean).join(", "));
|
||||
}
|
||||
return new Response(response.body, {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
headers,
|
||||
});
|
||||
}
|
||||
|
||||
function withSessionHeader(response: Response, sessionId: string): Response {
|
||||
if (response.headers.get("mcp-session-id")) {
|
||||
return response;
|
||||
@@ -261,7 +281,7 @@ async function handleStreamableRequest(request: Request): Promise<Response> {
|
||||
* Used by the Next.js route at /api/mcp/stream.
|
||||
*/
|
||||
export async function handleMcpStreamableHTTP(request: Request): Promise<Response> {
|
||||
return handleStreamableRequest(request);
|
||||
return protectMcpSseResponse(request, await handleStreamableRequest(request));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -273,7 +293,10 @@ export async function handleMcpSSE(request: Request): Promise<Response> {
|
||||
const { transport } = ensureSseServer();
|
||||
|
||||
try {
|
||||
return await withMcpHttpAuthContext(request, () => handleRequestWithAuthInfo(transport, request));
|
||||
const response = await withMcpHttpAuthContext(request, () =>
|
||||
handleRequestWithAuthInfo(transport, request)
|
||||
);
|
||||
return protectMcpSseResponse(request, response);
|
||||
} catch (err) {
|
||||
console.error("[MCP] SSE error:", err);
|
||||
return new Response(JSON.stringify({ error: "MCP SSE transport error" }), {
|
||||
|
||||
59
tests/unit/mcp-sse-response-headers-8277.test.ts
Normal file
59
tests/unit/mcp-sse-response-headers-8277.test.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
const { protectMcpSseResponse } = await import("../../open-sse/mcp-server/httpTransport.ts");
|
||||
|
||||
test("POST JSON-RPC SSE response adds no-transform", () => {
|
||||
const response = protectMcpSseResponse(
|
||||
new Request("http://localhost/api/mcp/stream", {
|
||||
method: "POST",
|
||||
headers: { "Accept-Encoding": "gzip" },
|
||||
}),
|
||||
new Response("event: message\ndata: {}\n\n", {
|
||||
headers: {
|
||||
"Content-Type": "Text/Event-Stream; Charset=UTF-8",
|
||||
"Cache-Control": 'private="Authorization"',
|
||||
Vary: "Origin",
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
assert.equal(response.headers.get("content-encoding"), null);
|
||||
assert.equal(response.headers.get("cache-control"), 'private="Authorization", no-transform');
|
||||
assert.equal(response.headers.get("vary"), "Origin");
|
||||
});
|
||||
|
||||
test("SDK-protected GET SSE response remains unchanged", () => {
|
||||
const response = new Response("event: ping\ndata: {}\n\n", {
|
||||
headers: {
|
||||
"Content-Type": "text/event-stream",
|
||||
"Cache-Control": "no-cache, no-transform",
|
||||
"X-Accel-Buffering": "no",
|
||||
},
|
||||
});
|
||||
|
||||
assert.strictEqual(
|
||||
protectMcpSseResponse(new Request("http://localhost/api/mcp/stream"), response),
|
||||
response
|
||||
);
|
||||
});
|
||||
|
||||
test("non-SSE response remains unchanged", () => {
|
||||
const response = new Response("{}", {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Content-Encoding": "gzip",
|
||||
Vary: "Accept-Encoding",
|
||||
},
|
||||
});
|
||||
|
||||
assert.strictEqual(
|
||||
protectMcpSseResponse(
|
||||
new Request("http://localhost/api/mcp/stream", { method: "POST" }),
|
||||
response
|
||||
),
|
||||
response
|
||||
);
|
||||
assert.equal(response.headers.get("content-encoding"), "gzip");
|
||||
assert.equal(response.headers.get("vary"), "Accept-Encoding");
|
||||
});
|
||||
Reference in New Issue
Block a user