Skip to content

Commit 46181b8

Browse files
committed
Fix: Return empty Array for list methods when no handlers registered (#929)
1 parent 856d9ec commit 46181b8

File tree

2 files changed

+187
-15
lines changed

2 files changed

+187
-15
lines changed

src/client/index.test.ts

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import {
1717
ElicitRequestSchema,
1818
ListRootsRequestSchema,
1919
ErrorCode,
20+
ListPromptsRequestSchema,
21+
ListResourceTemplatesRequestSchema,
2022
} from "../types.js";
2123
import { Transport } from "../shared/transport.js";
2224
import { Server } from "../server/index.js";
@@ -1301,3 +1303,137 @@ describe('outputSchema validation', () => {
13011303

13021304

13031305
});
1306+
1307+
test("should return empty arrays for all list methods when server has capabilities but no handlers", async () => {
1308+
const server = new Server(
1309+
{
1310+
name: "test server",
1311+
version: "1.0",
1312+
},
1313+
{
1314+
capabilities: {
1315+
prompts: {},
1316+
resources: {},
1317+
tools: {},
1318+
},
1319+
},
1320+
);
1321+
1322+
server.setRequestHandler(InitializeRequestSchema, (_request) => ({
1323+
protocolVersion: LATEST_PROTOCOL_VERSION,
1324+
capabilities: {
1325+
prompts: {},
1326+
resources: {},
1327+
tools: {},
1328+
},
1329+
serverInfo: {
1330+
name: "test",
1331+
version: "1.0",
1332+
},
1333+
}));
1334+
1335+
const [clientTransport, serverTransport] =
1336+
InMemoryTransport.createLinkedPair();
1337+
1338+
const client = new Client(
1339+
{
1340+
name: "test client",
1341+
version: "1.0",
1342+
},
1343+
{
1344+
capabilities: {
1345+
sampling: {},
1346+
},
1347+
},
1348+
);
1349+
1350+
await Promise.all([
1351+
client.connect(clientTransport),
1352+
server.connect(serverTransport),
1353+
]);
1354+
1355+
const promptsResult = await client.listPrompts();
1356+
const resourcesResult = await client.listResources();
1357+
const resourceTemplatesResult = await client.listResourceTemplates();
1358+
const toolsResult = await client.listTools();
1359+
1360+
expect(promptsResult.prompts).toEqual([]);
1361+
expect(resourcesResult.resources).toEqual([]);
1362+
expect(resourceTemplatesResult.resourceTemplates).toEqual([]);
1363+
expect(toolsResult.tools).toEqual([]);
1364+
});
1365+
1366+
test("should handle empty collections consistently across all capabilities", async () => {
1367+
const server = new Server(
1368+
{
1369+
name: "test server",
1370+
version: "1.0",
1371+
},
1372+
{
1373+
capabilities: {
1374+
prompts: {},
1375+
resources: {},
1376+
tools: {},
1377+
},
1378+
},
1379+
);
1380+
1381+
server.setRequestHandler(InitializeRequestSchema, (_request) => ({
1382+
protocolVersion: LATEST_PROTOCOL_VERSION,
1383+
capabilities: {
1384+
prompts: {},
1385+
resources: {},
1386+
tools: {},
1387+
},
1388+
serverInfo: {
1389+
name: "test",
1390+
version: "1.0",
1391+
},
1392+
}));
1393+
1394+
server.setRequestHandler(ListPromptsRequestSchema, () => ({
1395+
prompts: [],
1396+
}));
1397+
1398+
server.setRequestHandler(ListResourcesRequestSchema, () => ({
1399+
resources: [],
1400+
}));
1401+
1402+
server.setRequestHandler(ListResourceTemplatesRequestSchema, () => ({
1403+
resourceTemplates: [],
1404+
}));
1405+
1406+
server.setRequestHandler(ListToolsRequestSchema, () => ({
1407+
tools: [],
1408+
}));
1409+
1410+
const [clientTransport, serverTransport] =
1411+
InMemoryTransport.createLinkedPair();
1412+
1413+
const client = new Client(
1414+
{
1415+
name: "test client",
1416+
version: "1.0",
1417+
},
1418+
{
1419+
capabilities: {
1420+
sampling: {},
1421+
},
1422+
},
1423+
);
1424+
1425+
await Promise.all([
1426+
client.connect(clientTransport),
1427+
server.connect(serverTransport),
1428+
]);
1429+
1430+
const promptsResult = await client.listPrompts();
1431+
const resourcesResult = await client.listResources();
1432+
const resourceTemplatesResult = await client.listResourceTemplates();
1433+
const toolsResult = await client.listTools();
1434+
1435+
expect(promptsResult.prompts).toEqual([]);
1436+
expect(resourcesResult.resources).toEqual([]);
1437+
expect(resourceTemplatesResult.resourceTemplates).toEqual([]);
1438+
expect(toolsResult.tools).toEqual([]);
1439+
});

src/shared/protocol.ts

Lines changed: 51 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -366,29 +366,65 @@ export abstract class Protocol<
366366
);
367367
}
368368

369+
/**
370+
* Default handler for requests when no specific handler is registered.
371+
* Automatically returns empty arrays for standard MCP list methods.
372+
*/
373+
private _defaultFallbackRequestHandler( request: JSONRPCRequest, transport: Transport ): SendResultT | null {
374+
const method = request.method;
375+
const listMethodResponses: Record<string, string> = {
376+
"prompts/list": "prompts",
377+
"resources/list": "resources",
378+
"resources/templates/list": "resourceTemplates",
379+
"tools/list": "tools",
380+
};
381+
const responseKey = listMethodResponses[method];
382+
const result = responseKey ? { [responseKey]: [] } as SendResultT : null;
383+
if (result !== null) {
384+
transport
385+
.send({
386+
result: result,
387+
jsonrpc: "2.0",
388+
id: request.id,
389+
})
390+
.catch((error) =>
391+
this._onerror(
392+
new Error(`Failed to send default list response: ${error}`),
393+
),
394+
);
395+
return result;
396+
}
397+
398+
transport
399+
.send({
400+
jsonrpc: "2.0",
401+
id: request.id,
402+
error: {
403+
code: ErrorCode.MethodNotFound,
404+
message: "Method not found",
405+
},
406+
})
407+
.catch((error) =>
408+
this._onerror(
409+
new Error(`Failed to send an error response: ${error}`),
410+
),
411+
);
412+
return null;
413+
}
414+
369415
private _onrequest(request: JSONRPCRequest, extra?: MessageExtraInfo): void {
370416
const handler =
371417
this._requestHandlers.get(request.method) ?? this.fallbackRequestHandler;
372418

373419
// Capture the current transport at request time to ensure responses go to the correct client
374420
const capturedTransport = this._transport;
375421

422+
if(capturedTransport === undefined) {
423+
throw new Error("Error: transport not found.")
424+
}
376425
if (handler === undefined) {
377-
capturedTransport
378-
?.send({
379-
jsonrpc: "2.0",
380-
id: request.id,
381-
error: {
382-
code: ErrorCode.MethodNotFound,
383-
message: "Method not found",
384-
},
385-
})
386-
.catch((error) =>
387-
this._onerror(
388-
new Error(`Failed to send an error response: ${error}`),
389-
),
390-
);
391-
return;
426+
this._defaultFallbackRequestHandler(request, capturedTransport);
427+
return;
392428
}
393429

394430
const abortController = new AbortController();

0 commit comments

Comments
 (0)