fix(mcp): fail fast for unknown tools when server mapping exists

Server-name fallback in call_tool must not open an upstream session when
the tool is absent from a populated mapping. Update the HTTP transport test
to register a known tool before asserting not-found behavior.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Sameer Kankute 2026-05-19 23:04:17 +05:30
parent c10ff563ea
commit 002c3ad9da
No known key found for this signature in database
2 changed files with 33 additions and 0 deletions

View file

@ -2855,6 +2855,7 @@ class MCPServerManager:
# resolution only when the tool->server mapping isn't populated (e.g.
# OAuth2 servers skipped during init).
mcp_server: Optional[MCPServer] = None
resolved_by_server_name_only = False
prefixed_tool_name = add_server_prefix_to_name(name, server_name)
mcp_server = self._get_mcp_server_from_tool_name(prefixed_tool_name)
if mcp_server is None:
@ -2863,6 +2864,7 @@ class MCPServerManager:
server_name
):
mcp_server = candidate
resolved_by_server_name_only = True
break
if mcp_server is None:
# Last resort: lookup by unprefixed tool name. Only accept the
@ -2878,6 +2880,17 @@ class MCPServerManager:
if mcp_server is None:
raise ValueError(f"Tool {name} not found")
# Server-name fallback is for mapping-not-yet-populated paths (e.g. REST).
# If this server already has tools in the mapping, unknown names should fail
# fast instead of opening a real upstream session.
if resolved_by_server_name_only:
tool_known = (
name in self.tool_name_to_mcp_server_name_mapping
or prefixed_tool_name in self.tool_name_to_mcp_server_name_mapping
)
if not tool_known and self._mapping_has_tools_for_server(mcp_server):
raise ValueError(f"Tool {name} not found")
#########################################################
# Pre MCP Tool Call Hook
# Allow validation and modification of tool calls before execution
@ -3031,6 +3044,21 @@ class MCPServerManager:
self.tool_name_to_mcp_server_name_mapping[original_name] = server.name
self.tool_name_to_mcp_server_name_mapping[tool.name] = server.name
def _mapping_has_tools_for_server(self, server: MCPServer) -> bool:
"""Return True if the tool mapping lists any tool for this server."""
server_identifiers = {
normalize_server_name(server.name),
}
if server.server_name:
server_identifiers.add(normalize_server_name(server.server_name))
if server.alias:
server_identifiers.add(normalize_server_name(server.alias))
for mapped_server_name in self.tool_name_to_mcp_server_name_mapping.values():
if normalize_server_name(mapped_server_name) in server_identifiers:
return True
return False
def _get_mcp_server_from_tool_name(self, tool_name: str) -> Optional[MCPServer]:
"""
Get the MCP Server from the tool name (handles both prefixed and non-prefixed names)

View file

@ -382,6 +382,11 @@ async def test_mcp_http_transport_tool_not_found():
}
)
# Mapping populated for this server but not for the requested tool
test_manager.tool_name_to_mcp_server_name_mapping["gmail_send_email"] = (
"test_http_server"
)
# Try to call a tool that doesn't exist in mapping
with pytest.raises(ValueError, match="Tool nonexistent_tool not found"):
await test_manager.call_tool(