From af64a0e18cccc536cc3c35061ff1d0955cc102c8 Mon Sep 17 00:00:00 2001 From: ihrpr Date: Mon, 7 Apr 2025 16:03:31 +0100 Subject: [PATCH] remove custom headers --- src/server/mcp.test.ts | 11 +-- src/server/streamableHttp.test.ts | 135 +----------------------------- src/server/streamableHttp.ts | 25 ++---- 3 files changed, 11 insertions(+), 160 deletions(-) diff --git a/src/server/mcp.test.ts b/src/server/mcp.test.ts index 0c136e29..f33c669f 100644 --- a/src/server/mcp.test.ts +++ b/src/server/mcp.test.ts @@ -379,7 +379,7 @@ describe("tool()", () => { expect(receivedSessionId).toBe("test-session-123"); }); - test("should provide sendNotification withing tool call", async () => { + test("should provide sendNotification within tool call", async () => { const mcpServer = new McpServer( { name: "test server", @@ -401,12 +401,10 @@ describe("tool()", () => { ); let receivedLogMessage: string | undefined; - - const loggingMessage = "hello here is log message 1" + const loggingMessage = "hello here is log message 1"; client.setNotificationHandler(LoggingMessageNotificationSchema, (notification) => { receivedLogMessage = notification.params.data as string; - }); mcpServer.tool("test-tool", async ({ sendNotification }) => { @@ -422,15 +420,10 @@ describe("tool()", () => { }); const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); - // Set a test sessionId on the server transport - serverTransport.sessionId = "test-session-123"; - - await Promise.all([ client.connect(clientTransport), mcpServer.server.connect(serverTransport), ]); - await client.request( { method: "tools/call", diff --git a/src/server/streamableHttp.test.ts b/src/server/streamableHttp.test.ts index 129b499e..6c5c8705 100644 --- a/src/server/streamableHttp.test.ts +++ b/src/server/streamableHttp.test.ts @@ -99,7 +99,7 @@ describe("StreamableHTTPServerTransport", () => { await transport.handleRequest(req, mockResponse); - expect(mockResponse.writeHead).toHaveBeenCalledWith(404, {}); + expect(mockResponse.writeHead).toHaveBeenCalledWith(404); // check if the error response is a valid JSON-RPC error format expect(mockResponse.end).toHaveBeenCalledWith(expect.stringContaining('"jsonrpc":"2.0"')); expect(mockResponse.end).toHaveBeenCalledWith(expect.stringContaining('"error"')); @@ -116,7 +116,7 @@ describe("StreamableHTTPServerTransport", () => { await transport.handleRequest(req, mockResponse); - expect(mockResponse.writeHead).toHaveBeenCalledWith(400, {}); + expect(mockResponse.writeHead).toHaveBeenCalledWith(400); expect(mockResponse.end).toHaveBeenCalledWith(expect.stringContaining('"jsonrpc":"2.0"')); expect(mockResponse.end).toHaveBeenCalledWith(expect.stringContaining('"message":"Bad Request: Mcp-Session-Id header is required"')); }); @@ -842,135 +842,4 @@ describe("StreamableHTTPServerTransport", () => { expect(onMessageMock).not.toHaveBeenCalledWith(requestBodyMessage); }); }); - - describe("Custom Headers", () => { - const customHeaders = { - "X-Custom-Header": "custom-value", - "X-API-Version": "1.0", - "Access-Control-Allow-Origin": "*" - }; - - let transportWithHeaders: StreamableHTTPServerTransport; - let mockResponse: jest.Mocked; - - beforeEach(() => { - transportWithHeaders = new StreamableHTTPServerTransport({ sessionId: randomUUID(), customHeaders }); - mockResponse = createMockResponse(); - }); - - it("should include custom headers in SSE response", async () => { - const req = createMockRequest({ - method: "POST", - headers: { - "content-type": "application/json", - accept: "application/json, text/event-stream", - "mcp-session-id": transportWithHeaders.sessionId - }, - body: JSON.stringify({ - jsonrpc: "2.0", - method: "test", - params: {}, - id: "test-headers-id" - }) - }); - - await transportWithHeaders.handleRequest(req, mockResponse); - - expect(mockResponse.writeHead).toHaveBeenCalledWith( - 200, - expect.objectContaining({ - ...customHeaders, - "Content-Type": "text/event-stream", - "Cache-Control": "no-cache", - "Connection": "keep-alive", - "mcp-session-id": transportWithHeaders.sessionId - }) - ); - }); - - it("should include custom headers in error responses", async () => { - const req = createMockRequest({ - method: "GET", - headers: { - accept: "application/json, text/event-stream", - "mcp-session-id": "invalid-session-id" - }, - }); - - await transportWithHeaders.handleRequest(req, mockResponse); - - expect(mockResponse.writeHead).toHaveBeenCalledWith( - 404, - expect.objectContaining(customHeaders) - ); - }); - - it("should not override essential headers with custom headers", async () => { - const transportWithConflictingHeaders = new StreamableHTTPServerTransport({ - sessionId: randomUUID(), - customHeaders: { - "Content-Type": "text/plain", - "X-Custom-Header": "custom-value" - } - }); - - const req = createMockRequest({ - method: "POST", - headers: { - "content-type": "application/json", - accept: "application/json, text/event-stream", - "mcp-session-id": transportWithConflictingHeaders.sessionId - }, - body: JSON.stringify({ - jsonrpc: "2.0", - method: "test", - params: {}, - id: "test-conflict-id" - }) - }); - - await transportWithConflictingHeaders.handleRequest(req, mockResponse); - - expect(mockResponse.writeHead).toHaveBeenCalledWith( - 200, - expect.objectContaining({ - "Content-Type": "text/event-stream", - "X-Custom-Header": "custom-value" - }) - ); - }); - - it("should work with empty custom headers", async () => { - const transportWithoutHeaders = new StreamableHTTPServerTransport({ - sessionId: randomUUID(), - }); - - const req = createMockRequest({ - method: "POST", - headers: { - "content-type": "application/json", - accept: "application/json, text/event-stream", - "mcp-session-id": transportWithoutHeaders.sessionId - }, - body: JSON.stringify({ - jsonrpc: "2.0", - method: "test", - params: {}, - id: "test-empty-headers-id" - }) - }); - - await transportWithoutHeaders.handleRequest(req, mockResponse); - - expect(mockResponse.writeHead).toHaveBeenCalledWith( - 200, - expect.objectContaining({ - "Content-Type": "text/event-stream", - "Cache-Control": "no-cache", - "Connection": "keep-alive", - "mcp-session-id": transportWithoutHeaders.sessionId - }) - ); - }); - }); }); \ No newline at end of file diff --git a/src/server/streamableHttp.ts b/src/server/streamableHttp.ts index 984b2c19..d7f44160 100644 --- a/src/server/streamableHttp.ts +++ b/src/server/streamableHttp.ts @@ -13,15 +13,11 @@ export interface StreamableHTTPServerTransportOptions { /** * The session ID SHOULD be globally unique and cryptographically secure (e.g., a securely generated UUID, a JWT, or a cryptographic hash) * - * When sessionId is not set, the transport will be in stateless mode. + * When there is no sessionId, the transport will not perform session management. */ sessionId: string | undefined; - /** - * Custom headers to be included in all responses - * These headers will be added to both SSE and regular HTTP responses - */ - customHeaders?: Record; + } @@ -63,7 +59,6 @@ export class StreamableHTTPServerTransport implements Transport { // when sessionId is not set (undefined), it means the transport is in stateless mode private _sessionId: string | undefined; private _started: boolean = false; - private _customHeaders: Record; private _sseResponseMapping: Map = new Map(); onclose?: () => void; @@ -72,7 +67,6 @@ export class StreamableHTTPServerTransport implements Transport { constructor(options: StreamableHTTPServerTransportOptions) { this._sessionId = options.sessionId; - this._customHeaders = options.customHeaders || {}; } /** @@ -97,7 +91,7 @@ export class StreamableHTTPServerTransport implements Transport { } else if (req.method === "DELETE") { await this.handleDeleteRequest(req, res); } else { - res.writeHead(405, this._customHeaders).end(JSON.stringify({ + res.writeHead(405).end(JSON.stringify({ jsonrpc: "2.0", error: { code: -32000, @@ -120,7 +114,6 @@ export class StreamableHTTPServerTransport implements Transport { } res.writeHead(405, { - ...this._customHeaders, "Allow": "POST, DELETE" }).end(JSON.stringify({ jsonrpc: "2.0", @@ -193,9 +186,7 @@ export class StreamableHTTPServerTransport implements Transport { msg => 'method' in msg && msg.method === 'initialize' ); if (isInitializationRequest) { - const headers: Record = { - ...this._customHeaders - }; + const headers: Record = {}; if (this._sessionId !== undefined) { headers["mcp-session-id"] = this._sessionId; @@ -232,14 +223,12 @@ export class StreamableHTTPServerTransport implements Transport { } } else if (hasRequests) { const headers: Record = { - ...this._customHeaders, "Content-Type": "text/event-stream", "Cache-Control": "no-cache", Connection: "keep-alive", }; - // For initialization requests, always include the session ID if we have one - // even if we're in stateless mode + // After initialization, always include the session ID if we have one if (this._sessionId !== undefined) { headers["mcp-session-id"] = this._sessionId; } @@ -296,7 +285,7 @@ export class StreamableHTTPServerTransport implements Transport { if (!sessionId) { // Non-initialization requests without a session ID should return 400 Bad Request - res.writeHead(400, this._customHeaders).end(JSON.stringify({ + res.writeHead(400).end(JSON.stringify({ jsonrpc: "2.0", error: { code: -32000, @@ -307,7 +296,7 @@ export class StreamableHTTPServerTransport implements Transport { return false; } else if ((Array.isArray(sessionId) ? sessionId[0] : sessionId) !== this._sessionId) { // Reject requests with invalid session ID with 404 Not Found - res.writeHead(404, this._customHeaders).end(JSON.stringify({ + res.writeHead(404).end(JSON.stringify({ jsonrpc: "2.0", error: { code: -32001,