Skip to content

Commit 81a5558

Browse files
committed
fix(server): leave SSE connection policy to adapters
Remove hop-by-hop Connection headers from Web Standard Streamable HTTP responses so concrete HTTP adapters can advertise their actual connection lifetime.
1 parent 1e1392e commit 81a5558

3 files changed

Lines changed: 11 additions & 6 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@modelcontextprotocol/server': patch
3+
---
4+
5+
Leave hop-by-hop connection policy for Streamable HTTP SSE responses to the HTTP adapter.

packages/server/src/server/streamableHttp.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -480,8 +480,7 @@ export class WebStandardStreamableHTTPServerTransport implements Transport {
480480

481481
const headers: Record<string, string> = {
482482
'Content-Type': 'text/event-stream',
483-
'Cache-Control': 'no-cache, no-transform',
484-
Connection: 'keep-alive'
483+
'Cache-Control': 'no-cache, no-transform'
485484
};
486485

487486
// After initialization, always include the session ID if we have one
@@ -536,8 +535,7 @@ export class WebStandardStreamableHTTPServerTransport implements Transport {
536535

537536
const headers: Record<string, string> = {
538537
'Content-Type': 'text/event-stream',
539-
'Cache-Control': 'no-cache, no-transform',
540-
Connection: 'keep-alive'
538+
'Cache-Control': 'no-cache, no-transform'
541539
};
542540

543541
if (this.sessionId !== undefined) {
@@ -837,8 +835,7 @@ export class WebStandardStreamableHTTPServerTransport implements Transport {
837835

838836
const headers: Record<string, string> = {
839837
'Content-Type': 'text/event-stream',
840-
'Cache-Control': 'no-cache',
841-
Connection: 'keep-alive'
838+
'Cache-Control': 'no-cache'
842839
};
843840

844841
// After initialization, always include the session ID if we have one

packages/server/test/server/streamableHttp.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ describe('Zod v4', () => {
166166
expect(response.status).toBe(200);
167167
expect(response.headers.get('content-type')).toBe('text/event-stream');
168168
expect(response.headers.get('mcp-session-id')).toBeDefined();
169+
expect(response.headers.get('connection'), 'connection policy belongs to the HTTP adapter').toBeNull();
169170
});
170171

171172
it('should reject second initialization request', async () => {
@@ -358,6 +359,7 @@ describe('Zod v4', () => {
358359
expect(response.status).toBe(200);
359360
expect(response.headers.get('content-type')).toBe('text/event-stream');
360361
expect(response.headers.get('mcp-session-id')).toBe(sessionId);
362+
expect(response.headers.get('connection'), 'connection policy belongs to the HTTP adapter').toBeNull();
361363
});
362364

363365
it('should reject GET without Accept: text/event-stream', async () => {
@@ -857,6 +859,7 @@ describe('Zod v4', () => {
857859
createRequest('GET', undefined, { sessionId, extraHeaders: { 'Last-Event-ID': primingId! } })
858860
);
859861
expect(reconnect.status).toBe(200);
862+
expect(reconnect.headers.get('connection'), 'connection policy belongs to the HTTP adapter').toBeNull();
860863
release();
861864
const replayed = await readSSEEvent(reconnect);
862865
expect(replayed).toContain('notifications/progress');

0 commit comments

Comments
 (0)