fix(mcp): keep resource-only servers available after unknown methods (#115169)

This commit is contained in:
Peter Steinberger
2026-07-28 08:07:32 -04:00
committed by GitHub
parent bdb3918f4d
commit bb634261cb
2 changed files with 69 additions and 5 deletions
+68 -4
View File
@@ -1728,15 +1728,35 @@ process.on("SIGINT", shutdown);`,
}
});
it("keeps resource-only MCP servers available for utility tools", async () => {
it.each([
{
name: "resource-only servers reporting method not found",
capabilities: { resources: { listChanged: true } },
listToolsMethodNotFound: true,
listToolsJsonRpcErrorMessage: undefined,
},
{
name: "resource-only servers reporting unknown method",
capabilities: { resources: { listChanged: true } },
listToolsMethodNotFound: false,
listToolsJsonRpcErrorMessage: "Unknown method",
},
{
name: "prompt-only servers reporting unknown method",
capabilities: { prompts: { listChanged: true } },
listToolsMethodNotFound: false,
listToolsJsonRpcErrorMessage: "Unknown method",
},
])("keeps $name available for utility tools", async (testCase) => {
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "bundle-mcp-resource-only-"));
const serverPath = path.join(tempDir, "resource-only.mjs");
const logPath = path.join(tempDir, "server.log");
await writeListToolsMcpServer({
filePath: serverPath,
logPath,
capabilities: { resources: { listChanged: true } },
listToolsMethodNotFound: true,
capabilities: testCase.capabilities,
listToolsMethodNotFound: testCase.listToolsMethodNotFound,
listToolsJsonRpcErrorMessage: testCase.listToolsJsonRpcErrorMessage,
});
const runtime = await getOrCreateSessionMcpRuntime({
@@ -1762,8 +1782,9 @@ process.on("SIGINT", shutdown);`,
expect(catalog.servers.notes).toMatchObject({
serverName: "notes",
toolCount: 0,
resources: { listChanged: true },
...testCase.capabilities,
});
expect(catalog.diagnostics ?? []).toEqual([]);
await waitForFileText(logPath, "recv initialize", LIST_TOOLS_SERVER_LOG_TIMEOUT_MS);
} finally {
await runtime.dispose();
@@ -1771,6 +1792,49 @@ process.on("SIGINT", shutdown);`,
}
});
it("does not suppress unknown tools/list methods from tools-capable MCP servers", async () => {
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "bundle-mcp-tools-unknown-method-"));
const serverPath = path.join(tempDir, "tools-unknown-method.mjs");
const logPath = path.join(tempDir, "server.log");
await writeListToolsMcpServer({
filePath: serverPath,
logPath,
capabilities: { tools: {}, resources: { listChanged: true } },
listToolsJsonRpcErrorMessage: "Unknown method",
});
const runtime = await getOrCreateSessionMcpRuntime({
sessionId: "session-tools-unknown-method",
sessionKey: "agent:test:session-tools-unknown-method",
workspaceDir: "/workspace",
cfg: {
mcp: {
servers: {
notes: {
command: process.execPath,
args: [serverPath],
},
},
},
},
});
try {
const catalog = await runtime.getCatalog();
expect(catalog.servers).toEqual({});
expect(catalog.tools).toEqual([]);
expect(catalog.diagnostics?.[0]).toMatchObject({
serverName: "notes",
message: expect.stringContaining("Unknown method"),
});
await waitForFileText(logPath, "recv tools/list", LIST_TOOLS_SERVER_LOG_TIMEOUT_MS);
} finally {
await runtime.dispose();
await fs.rm(tempDir, { recursive: true, force: true });
}
});
it("does not pause MCP servers for normal tool error results", async () => {
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "bundle-mcp-error-backoff-"));
const serverPath = path.join(tempDir, "error-backoff.mjs");
+1 -1
View File
@@ -183,7 +183,7 @@ function isMcpMethodNotFoundError(error: unknown): boolean {
return true;
}
const message = String(error);
return message.includes("-32601") || /method not found/i.test(message);
return message.includes("-32601") || /\b(?:method not found|unknown method)\b/i.test(message);
}
async function listAllToolsBestEffort(params: {