Repository navigation
Conversation
The handler hijacked the reply and wrote the head straight to the raw response, so headers set by hooks (CORS, security headers) were lost and a cross-origin client could neither open the stream nor read Mcp-Session-Id. Copy reply.getHeaders() onto the raw response right after hijacking. The error path called reply.send() after the hijack, which Fastify ignores, so a failure while opening the stream never answered. Write the 500 on the raw response instead, with the same headers, or destroy the stream if its head has already gone out. Fixes #335 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LmhsFjk8W96X2PvmShmDm9
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The legacy
GET /mcpSSE handler callsreply.hijack()and thenraw.writeHead(200). Headers that hooks put on the reply (CORS, security headers) are dropped. A cross-origin browser client can neither open the stream nor readMcp-Session-Id.While writing the test I found a second bug on the same path: the error branch calls
reply.send()after the hijack. Fastify ignores that, so a failure while opening the stream (the session store being down, for example) never got a response, and the client hung until it timed out.Fix
reply.getHeaders()beforehijack()and set them on the raw response. Then set the SSE headers on top, soContent-TypeandCache-Controlstill win. This follows what the 2026-07-28 streams do on feat: implement MCP 2026-07-28 alongside the handshake revisions #170.Content-Typetoapplication/json. If the head has already been sent, the stream is destroyed instead. Errors raised before the hijack still go throughreply.send()as before.onSendhooks still do not run for this response, as with any hijacked reply. Headers have to be set inonRequest/preHandler, which is where CORS plugins set them.Tests
test/sse-hook-headers.test.ts:onRequesthook, next totext/event-streamandMcp-Session-IdsessionStore.createfailing, a real listening server answers 500 with JSON and the hook headers. Before this fix it timed out.Fixes #335
🤖 Generated with Claude Code
https://claude.ai/code/session_01LmhsFjk8W96X2PvmShmDm9