Skip to content

Commit 789ceef

Browse files
committed
capture sse responses for streamable http transport
1 parent 693fdc3 commit 789ceef

File tree

2 files changed

+89
-3
lines changed

2 files changed

+89
-3
lines changed

src/server/streamableHttp.test.ts

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { StreamableHTTPServerTransport } from "./streamableHttp.js";
33
import { JSONRPCMessage } from "../types.js";
44
import { Readable } from "node:stream";
55
import { randomUUID } from "node:crypto";
6+
import { McpServer } from "./mcp.js";
67
// Mock IncomingMessage
78
function createMockRequest(options: {
89
method: string;
@@ -1221,4 +1222,89 @@ describe("StreamableHTTPServerTransport", () => {
12211222
expect(onMessageMock).not.toHaveBeenCalledWith(requestBodyMessage);
12221223
});
12231224
});
1224-
});
1225+
1226+
describe("Connect to a MCP Server", () => {
1227+
it("should connect to a MCP Server", async () => {
1228+
const mcpServer = new McpServer({
1229+
name: "test-server",
1230+
version: "1.0",
1231+
});
1232+
1233+
mcpServer.tool("test-tool", async () => ({
1234+
content: [
1235+
{
1236+
type: "text",
1237+
text: "Hello, world!",
1238+
},
1239+
],
1240+
}));
1241+
1242+
await mcpServer.connect(transport);
1243+
1244+
const initializeMessage: JSONRPCMessage = {
1245+
jsonrpc: "2.0",
1246+
method: "initialize",
1247+
params: {
1248+
clientInfo: { name: "test-client", version: "1.0" },
1249+
protocolVersion: "2025-03-26"
1250+
},
1251+
id: "init-1",
1252+
};
1253+
1254+
const initializeReq = createMockRequest({
1255+
method: "POST",
1256+
headers: {
1257+
"content-type": "application/json",
1258+
"accept": "application/json, text/event-stream",
1259+
},
1260+
body: JSON.stringify(initializeMessage),
1261+
});
1262+
1263+
await transport.handleRequest(initializeReq, mockResponse);
1264+
1265+
expect(mockResponse.writeHead).toHaveBeenCalledWith(200, {"mcp-session-id": transport.sessionId});
1266+
1267+
mockResponse.end.mockClear();
1268+
mockResponse.writeHead.mockClear();
1269+
mockResponse.write.mockClear();
1270+
1271+
const listToolsMessage: JSONRPCMessage = {
1272+
jsonrpc: "2.0",
1273+
method: "tools/list",
1274+
id: "list-tools",
1275+
};
1276+
1277+
const listToolsReq = createMockRequest({
1278+
method: "POST",
1279+
headers: {
1280+
"content-type": "application/json",
1281+
"accept": "application/json, text/event-stream",
1282+
"mcp-session-id": transport.sessionId,
1283+
},
1284+
body: JSON.stringify(listToolsMessage),
1285+
});
1286+
1287+
await transport.handleRequest(listToolsReq, mockResponse);
1288+
1289+
expect(mockResponse.writeHead).toHaveBeenCalledWith(200, {
1290+
"Cache-Control": "no-cache",
1291+
"Connection": "keep-alive",
1292+
"Content-Type": "text/event-stream",
1293+
"mcp-session-id": transport.sessionId
1294+
});
1295+
1296+
await new Promise(resolve => setTimeout(resolve, 100));
1297+
1298+
const writeCalls = mockResponse.write.mock.calls
1299+
1300+
expect(writeCalls.length).toBeGreaterThan(0);
1301+
1302+
const lastWriteCall = writeCalls[writeCalls.length - 1]
1303+
expect(lastWriteCall.length).toBeGreaterThan(0);
1304+
1305+
const lastWriteCallData = lastWriteCall[0].toString()
1306+
expect(lastWriteCallData).toContain("event: message\ndata: ");
1307+
expect(lastWriteCallData).toContain('"name":"test-tool"');
1308+
});
1309+
});
1310+
});

src/shared/protocol.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ export abstract class Protocol<
369369
result,
370370
jsonrpc: "2.0",
371371
id: request.id,
372-
});
372+
}, { relatedRequestId: request.id });
373373
},
374374
(error) => {
375375
if (abortController.signal.aborted) {
@@ -385,7 +385,7 @@ export abstract class Protocol<
385385
: ErrorCode.InternalError,
386386
message: error.message ?? "Internal error",
387387
},
388-
});
388+
}, { relatedRequestId: request.id });
389389
},
390390
)
391391
.catch((error) =>

0 commit comments

Comments
 (0)