Skip to content

Commit e6b4825

Browse files
author
Muhammad Ali
committed
Add tests
1 parent 74060de commit e6b4825

File tree

1 file changed

+281
-0
lines changed

1 file changed

+281
-0
lines changed

packages/agents/src/tests/mcp/transports/worker-transport.test.ts

Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,287 @@ describe("WorkerTransport", () => {
858858
});
859859
});
860860

861+
describe("Client Capabilities Persistence (Serverless Restart)", () => {
862+
it("should persist initializeParams when client sends capabilities", async () => {
863+
const server = createTestServer();
864+
let storedState: TransportState | undefined;
865+
866+
const mockStorage = {
867+
get: async () => storedState,
868+
set: async (state: TransportState) => {
869+
storedState = state;
870+
}
871+
};
872+
873+
const transport = await setupTransport(server, {
874+
sessionIdGenerator: () => "test-session",
875+
storage: mockStorage,
876+
enableJsonResponse: true
877+
});
878+
879+
const request = new Request("http://example.com/", {
880+
method: "POST",
881+
headers: {
882+
"Content-Type": "application/json",
883+
Accept: "application/json, text/event-stream"
884+
},
885+
body: JSON.stringify({
886+
jsonrpc: "2.0",
887+
id: "1",
888+
method: "initialize",
889+
params: {
890+
capabilities: {
891+
elicitation: { form: {} }
892+
},
893+
clientInfo: { name: "test-client", version: "1.0" },
894+
protocolVersion: "2025-06-18"
895+
}
896+
})
897+
});
898+
899+
const response = await transport.handleRequest(request);
900+
await response.json();
901+
902+
expect(response.status).toBe(200);
903+
expect(storedState).toBeDefined();
904+
expect(storedState?.initializeParams).toBeDefined();
905+
expect(storedState?.initializeParams?.capabilities?.elicitation?.form).toBeDefined();
906+
expect(storedState?.initializeParams?.clientInfo).toEqual({
907+
name: "test-client",
908+
version: "1.0"
909+
});
910+
expect(storedState?.initializeParams?.protocolVersion).toBe("2025-06-18");
911+
});
912+
913+
it("should restore client capabilities on Server instance after restart", async () => {
914+
// Phase 1: Initialize with capabilities
915+
let storedState: TransportState | undefined;
916+
const mockStorage = {
917+
get: async () => storedState,
918+
set: async (state: TransportState) => {
919+
storedState = state;
920+
}
921+
};
922+
923+
const server1 = createTestServer();
924+
const transport1 = await setupTransport(server1, {
925+
sessionIdGenerator: () => "test-session",
926+
storage: mockStorage,
927+
enableJsonResponse: true
928+
});
929+
930+
const initRequest = new Request("http://example.com/", {
931+
method: "POST",
932+
headers: {
933+
"Content-Type": "application/json",
934+
Accept: "application/json, text/event-stream"
935+
},
936+
body: JSON.stringify({
937+
jsonrpc: "2.0",
938+
id: "1",
939+
method: "initialize",
940+
params: {
941+
capabilities: {
942+
elicitation: { form: {} }
943+
},
944+
clientInfo: { name: "test-client", version: "1.0" },
945+
protocolVersion: "2025-06-18"
946+
}
947+
})
948+
});
949+
950+
await transport1.handleRequest(initRequest);
951+
952+
// Verify server1 has capabilities
953+
expect(server1.server.getClientCapabilities()?.elicitation?.form).toBeDefined();
954+
955+
// Phase 2: Simulate serverless restart with NEW instances
956+
const server2 = createTestServer();
957+
const transport2 = await setupTransport(server2, {
958+
sessionIdGenerator: () => "test-session",
959+
storage: mockStorage,
960+
enableJsonResponse: true
961+
});
962+
963+
// Trigger state restoration by making a request
964+
const listRequest = new Request("http://example.com/", {
965+
method: "POST",
966+
headers: {
967+
"Content-Type": "application/json",
968+
Accept: "application/json, text/event-stream",
969+
"mcp-session-id": "test-session"
970+
},
971+
body: JSON.stringify({
972+
jsonrpc: "2.0",
973+
id: "2",
974+
method: "tools/list",
975+
params: {}
976+
})
977+
});
978+
979+
await transport2.handleRequest(listRequest);
980+
981+
// Verify capabilities were restored on server2
982+
expect(transport2.sessionId).toBe("test-session");
983+
expect(server2.server.getClientCapabilities()).toBeDefined();
984+
expect(server2.server.getClientCapabilities()?.elicitation?.form).toBeDefined();
985+
});
986+
987+
it("should restore clientInfo on Server instance after restart", async () => {
988+
let storedState: TransportState | undefined;
989+
const mockStorage = {
990+
get: async () => storedState,
991+
set: async (state: TransportState) => {
992+
storedState = state;
993+
}
994+
};
995+
996+
const server1 = createTestServer();
997+
const transport1 = await setupTransport(server1, {
998+
sessionIdGenerator: () => "test-session",
999+
storage: mockStorage,
1000+
enableJsonResponse: true
1001+
});
1002+
1003+
const initRequest = new Request("http://example.com/", {
1004+
method: "POST",
1005+
headers: {
1006+
"Content-Type": "application/json",
1007+
Accept: "application/json, text/event-stream"
1008+
},
1009+
body: JSON.stringify({
1010+
jsonrpc: "2.0",
1011+
id: "1",
1012+
method: "initialize",
1013+
params: {
1014+
capabilities: {},
1015+
clientInfo: { name: "my-client", version: "2.0" },
1016+
protocolVersion: "2025-06-18"
1017+
}
1018+
})
1019+
});
1020+
1021+
await transport1.handleRequest(initRequest);
1022+
1023+
// Simulate restart
1024+
const server2 = createTestServer();
1025+
const transport2 = await setupTransport(server2, {
1026+
sessionIdGenerator: () => "test-session",
1027+
storage: mockStorage,
1028+
enableJsonResponse: true
1029+
});
1030+
1031+
const listRequest = new Request("http://example.com/", {
1032+
method: "POST",
1033+
headers: {
1034+
"Content-Type": "application/json",
1035+
Accept: "application/json, text/event-stream",
1036+
"mcp-session-id": "test-session"
1037+
},
1038+
body: JSON.stringify({
1039+
jsonrpc: "2.0",
1040+
id: "2",
1041+
method: "tools/list",
1042+
params: {}
1043+
})
1044+
});
1045+
1046+
await transport2.handleRequest(listRequest);
1047+
1048+
// Verify clientInfo was restored
1049+
expect(server2.server.getClientVersion()).toEqual({
1050+
name: "my-client",
1051+
version: "2.0"
1052+
});
1053+
});
1054+
1055+
it("should handle old storage format without initializeParams (backward compatibility)", async () => {
1056+
// Simulate old stored state without initializeParams field
1057+
const oldState: TransportState = {
1058+
sessionId: "old-session",
1059+
initialized: true
1060+
// No initializeParams - simulating old storage format
1061+
};
1062+
1063+
const mockStorage = {
1064+
get: async () => oldState,
1065+
set: async () => {}
1066+
};
1067+
1068+
const server = createTestServer();
1069+
const transport = await setupTransport(server, {
1070+
storage: mockStorage,
1071+
enableJsonResponse: true
1072+
});
1073+
1074+
const request = new Request("http://example.com/", {
1075+
method: "POST",
1076+
headers: {
1077+
"Content-Type": "application/json",
1078+
Accept: "application/json, text/event-stream",
1079+
"mcp-session-id": "old-session"
1080+
},
1081+
body: JSON.stringify({
1082+
jsonrpc: "2.0",
1083+
id: "1",
1084+
method: "tools/list",
1085+
params: {}
1086+
})
1087+
});
1088+
1089+
// Should not throw
1090+
const response = await transport.handleRequest(request);
1091+
expect(response.status).toBe(200);
1092+
1093+
// Session restored but capabilities not available (no initializeParams)
1094+
expect(transport.sessionId).toBe("old-session");
1095+
expect(server.server.getClientCapabilities()).toBeUndefined();
1096+
});
1097+
1098+
it("should persist initializeParams with empty capabilities", async () => {
1099+
const server = createTestServer();
1100+
let storedState: TransportState | undefined;
1101+
1102+
const mockStorage = {
1103+
get: async () => storedState,
1104+
set: async (state: TransportState) => {
1105+
storedState = state;
1106+
}
1107+
};
1108+
1109+
const transport = await setupTransport(server, {
1110+
sessionIdGenerator: () => "test-session",
1111+
storage: mockStorage,
1112+
enableJsonResponse: true
1113+
});
1114+
1115+
const request = new Request("http://example.com/", {
1116+
method: "POST",
1117+
headers: {
1118+
"Content-Type": "application/json",
1119+
Accept: "application/json, text/event-stream"
1120+
},
1121+
body: JSON.stringify({
1122+
jsonrpc: "2.0",
1123+
id: "1",
1124+
method: "initialize",
1125+
params: {
1126+
capabilities: {}, // Empty but present
1127+
clientInfo: { name: "test-client", version: "1.0" },
1128+
protocolVersion: "2025-06-18"
1129+
}
1130+
})
1131+
});
1132+
1133+
const response = await transport.handleRequest(request);
1134+
await response.json();
1135+
1136+
expect(response.status).toBe(200);
1137+
expect(storedState?.initializeParams).toBeDefined();
1138+
expect(storedState?.initializeParams?.capabilities).toEqual({});
1139+
});
1140+
});
1141+
8611142
describe("Session Management", () => {
8621143
it("should use custom sessionIdGenerator", async () => {
8631144
const server = createTestServer();

0 commit comments

Comments
 (0)