Merge branch 'main' into request-propagation-in-tools

This commit is contained in:
Konstantin Konstantinov 2025-06-14 01:10:58 +03:00 • committed by GitHub
commit e714050642
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -185,6 +185,8 @@ async function sendPostRequest(baseUrl: URL, message: JSONRPCMessage | JSONRPCMe
if (sessionId) {
headers["mcp-session-id"] = sessionId;
// After initialization, include the protocol version header
headers["mcp-protocol-version"] = "2025-03-26";
}
return fetch(baseUrl, {
@ -279,7 +281,7 @@ describe("StreamableHTTPServerTransport", () => {
expectErrorResponse(errorData, -32600, /Only one initialization request is allowed/);
});
it("should pandle post requests via sse response correctly", async () => {
it("should handle post requests via sse response correctly", async () => {
sessionId = await initializeServer();
const response = await sendPostRequest(baseUrl, TEST_MESSAGES.toolsList, sessionId);
@ -441,6 +443,7 @@ describe("StreamableHTTPServerTransport", () => {
headers: {
Accept: "text/event-stream",
"mcp-session-id": sessionId,
"mcp-protocol-version": "2025-03-26",
},
});
@ -482,6 +485,7 @@ describe("StreamableHTTPServerTransport", () => {
headers: {
Accept: "text/event-stream",
"mcp-session-id": sessionId,
"mcp-protocol-version": "2025-03-26",
},
});
@ -513,6 +517,7 @@ describe("StreamableHTTPServerTransport", () => {
headers: {
Accept: "text/event-stream",
"mcp-session-id": sessionId,
"mcp-protocol-version": "2025-03-26",
},
});
@ -524,6 +529,7 @@ describe("StreamableHTTPServerTransport", () => {
headers: {
Accept: "text/event-stream",
"mcp-session-id": sessionId,
"mcp-protocol-version": "2025-03-26",
},
});
@ -542,6 +548,7 @@ describe("StreamableHTTPServerTransport", () => {
headers: {
Accept: "application/json",
"mcp-session-id": sessionId,
"mcp-protocol-version": "2025-03-26",
},
});
@ -735,6 +742,7 @@ describe("StreamableHTTPServerTransport", () => {
headers: {
Accept: "text/event-stream",
"mcp-session-id": sessionId,
"mcp-protocol-version": "2025-03-26",
},
});
@ -770,7 +778,10 @@ describe("StreamableHTTPServerTransport", () => {
// Now DELETE the session
const deleteResponse = await fetch(tempUrl, {
method: "DELETE",
headers: { "mcp-session-id": tempSessionId || "" },
headers: {
"mcp-session-id": tempSessionId || "",
"mcp-protocol-version": "2025-03-26",
},
});
expect(deleteResponse.status).toBe(200);
@ -786,13 +797,124 @@ describe("StreamableHTTPServerTransport", () => {
// Try to delete with invalid session ID
const response = await fetch(baseUrl, {
method: "DELETE",
headers: { "mcp-session-id": "invalid-session-id" },
headers: {
"mcp-session-id": "invalid-session-id",
"mcp-protocol-version": "2025-03-26",
},
});
expect(response.status).toBe(404);
const errorData = await response.json();
expectErrorResponse(errorData, -32001, /Session not found/);
});
describe("protocol version header validation", () => {
it("should accept requests with matching protocol version", async () => {
sessionId = await initializeServer();
// Send request with matching protocol version
const response = await sendPostRequest(baseUrl, TEST_MESSAGES.toolsList, sessionId);
expect(response.status).toBe(200);
});
it("should accept requests without protocol version header", async () => {
sessionId = await initializeServer();
// Send request without protocol version header
const response = await fetch(baseUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json, text/event-stream",
"mcp-session-id": sessionId,
// No mcp-protocol-version header
},
body: JSON.stringify(TEST_MESSAGES.toolsList),
});
expect(response.status).toBe(200);
});
it("should reject requests with unsupported protocol version", async () => {
sessionId = await initializeServer();
// Send request with unsupported protocol version
const response = await fetch(baseUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json, text/event-stream",
"mcp-session-id": sessionId,
"mcp-protocol-version": "1999-01-01", // Unsupported version
},
body: JSON.stringify(TEST_MESSAGES.toolsList),
});
expect(response.status).toBe(400);
const errorData = await response.json();
expectErrorResponse(errorData, -32000, /Bad Request: Unsupported protocol version \(supported versions: .+\)/);
});
it("should accept when protocol version differs from negotiated version", async () => {
sessionId = await initializeServer();
// Spy on console.warn to verify warning is logged
const warnSpy = jest.spyOn(console, 'warn').mockImplementation();
// Send request with different but supported protocol version
const response = await fetch(baseUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json, text/event-stream",
"mcp-session-id": sessionId,
"mcp-protocol-version": "2024-11-05", // Different but supported version
},
body: JSON.stringify(TEST_MESSAGES.toolsList),
});
// Request should still succeed
expect(response.status).toBe(200);
warnSpy.mockRestore();
});
it("should handle protocol version validation for GET requests", async () => {
sessionId = await initializeServer();
// GET request with unsupported protocol version
const response = await fetch(baseUrl, {
method: "GET",
headers: {
Accept: "text/event-stream",
"mcp-session-id": sessionId,
"mcp-protocol-version": "invalid-version",
},
});
expect(response.status).toBe(400);
const errorData = await response.json();
expectErrorResponse(errorData, -32000, /Bad Request: Unsupported protocol version \(supported versions: .+\)/);
});
it("should handle protocol version validation for DELETE requests", async () => {
sessionId = await initializeServer();
// DELETE request with unsupported protocol version
const response = await fetch(baseUrl, {
method: "DELETE",
headers: {
"mcp-session-id": sessionId,
"mcp-protocol-version": "invalid-version",
},
});
expect(response.status).toBe(400);
const errorData = await response.json();
expectErrorResponse(errorData, -32000, /Bad Request: Unsupported protocol version \(supported versions: .+\)/);
});
});
});
describe("StreamableHTTPServerTransport with AuthInfo", () => {
@ -1185,6 +1307,7 @@ describe("StreamableHTTPServerTransport with resumability", () => {
headers: {
Accept: "text/event-stream",
"mcp-session-id": sessionId,
"mcp-protocol-version": "2025-03-26",
},
});
@ -1261,6 +1384,7 @@ describe("StreamableHTTPServerTransport with resumability", () => {
headers: {
Accept: "text/event-stream",
"mcp-session-id": sessionId,
"mcp-protocol-version": "2025-03-26",
"last-event-id": firstEventId
},
});
@ -1347,14 +1471,20 @@ describe("StreamableHTTPServerTransport in stateless mode", () => {
// Open first SSE stream
const stream1 = await fetch(baseUrl, {
method: "GET",
headers: { Accept: "text/event-stream" },
headers: {
Accept: "text/event-stream",
"mcp-protocol-version": "2025-03-26"
},
});
expect(stream1.status).toBe(200);
// Open second SSE stream - should still be rejected, stateless mode still only allows one
const stream2 = await fetch(baseUrl, {
method: "GET",
headers: { Accept: "text/event-stream" },
headers: {
Accept: "text/event-stream",
"mcp-protocol-version": "2025-03-26"
},
});
expect(stream2.status).toBe(409); // Conflict - only one stream allowed
});