mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-05 02:41:56 +00:00
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:
parent
c10ff563ea
commit
002c3ad9da
2 changed files with 33 additions and 0 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue