Skip to content

Commit 2869893

Browse files
feat(mcp_server_manager.py): ensure list tools for openapi servers works as expected
1 parent 1c317a3 commit 2869893

File tree

5 files changed

+84
-38
lines changed

5 files changed

+84
-38
lines changed

litellm/proxy/_experimental/mcp_server/mcp_server_manager.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -324,13 +324,6 @@ def _register_openapi_tools(self, spec_path: str, server: MCPServer, base_url: s
324324
)
325325
base_tool_name = operation_id.replace(" ", "_").lower()
326326

327-
# Check if tool is allowed for this server
328-
if not self.check_allowed_or_banned_tools(base_tool_name, server):
329-
verbose_logger.debug(
330-
f"Skipping tool {base_tool_name} - not in allowed_tools for server {server.name}"
331-
)
332-
continue
333-
334327
# Add server prefix to tool name
335328
prefixed_tool_name = add_server_prefix_to_tool_name(
336329
base_tool_name, server_prefix

litellm/proxy/_experimental/mcp_server/server.py

Lines changed: 6 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -501,13 +501,13 @@ async def _get_tools_from_mcp_servers(
501501
)
502502

503503
filtered_tools = filter_tools_by_allowed_tools(tools, server)
504-
504+
505505
filtered_tools = await filter_tools_by_key_team_permissions(
506506
tools=filtered_tools,
507507
server_id=server_id,
508508
user_api_key_auth=user_api_key_auth,
509509
)
510-
510+
511511
all_tools.extend(filtered_tools)
512512

513513
verbose_logger.debug(
@@ -522,6 +522,7 @@ async def _get_tools_from_mcp_servers(
522522
verbose_logger.info(
523523
f"Successfully fetched {len(all_tools)} tools total from all MCP servers"
524524
)
525+
525526
return all_tools
526527

527528
async def filter_tools_by_key_team_permissions(
@@ -531,7 +532,7 @@ async def filter_tools_by_key_team_permissions(
531532
) -> List[MCPTool]:
532533
"""
533534
Filter tools based on key/team mcp_tool_permissions.
534-
535+
535536
Note: Tool names in the DB are stored without server prefixes,
536537
but tool names from MCP servers are prefixed. We need to strip
537538
the prefix before comparing.
@@ -553,7 +554,7 @@ async def filter_tools_by_key_team_permissions(
553554
else:
554555
# No restrictions, return all tools
555556
filtered_tools = tools
556-
557+
557558
return filtered_tools
558559

559560
async def _list_mcp_tools(
@@ -598,30 +599,7 @@ async def _list_mcp_tools(
598599
)
599600
# Continue with empty managed tools list instead of failing completely
600601

601-
# Get tools from local registry
602-
local_tools = []
603-
try:
604-
local_tools_raw = global_mcp_tool_registry.list_tools()
605-
606-
# Convert local tools to MCPTool format
607-
for tool in local_tools_raw:
608-
# Convert from litellm.types.mcp_server.tool_registry.MCPTool to mcp.types.Tool
609-
mcp_tool = MCPTool(
610-
name=tool.name,
611-
description=tool.description,
612-
inputSchema=tool.input_schema,
613-
)
614-
local_tools.append(mcp_tool)
615-
except Exception as e:
616-
verbose_logger.exception(
617-
f"Error getting tools from local registry: {str(e)}"
618-
)
619-
# Continue with empty local tools list instead of failing completely
620-
621-
# Combine all tools
622-
all_tools = managed_tools + local_tools
623-
624-
return all_tools
602+
return managed_tools
625603

626604
@client
627605
async def call_mcp_tool(

litellm/proxy/_new_secret_config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ mcp_servers:
2121
url: "http://0.0.0.0:8090"
2222
spec_path: "/Users/krrishdholakia/Documents/temp_py_folder/example_openapi.json"
2323
auth_type: none
24+
allowed_tools: ["getpetbyid", "my_api_mcp-findpetsbystatus"]
2425

2526

2627
litellm_settings:

litellm/types/mcp_server/mcp_server_manager.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ class MCPServer(BaseModel):
1616
alias: Optional[str] = None
1717
server_name: Optional[str] = None
1818
url: Optional[str] = None
19-
spec_path: Optional[str] = None
2019
transport: MCPTransportType
2120
spec_path: Optional[str] = None
2221
auth_type: Optional[MCPAuthType] = None

tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server.py

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1001,7 +1001,7 @@ async def mock_get_tools_from_server(
10011001
async def test_list_tools_strips_prefix_when_matching_permissions():
10021002
"""
10031003
Test that tool permission filtering correctly strips prefixes from tool names.
1004-
1004+
10051005
Tools from MCP servers are prefixed (e.g., "GITMCP-fetch_litellm_documentation"),
10061006
but allowed tools in DB are stored without prefix (e.g., "fetch_litellm_documentation").
10071007
The filtering should strip the prefix before comparing.
@@ -1056,7 +1056,9 @@ async def mock_get_tools_from_server(
10561056
tool1.inputSchema = {}
10571057

10581058
tool2 = MagicMock()
1059-
tool2.name = "GITMCP-search_litellm_documentation" # Prefixed, not in allowed list
1059+
tool2.name = (
1060+
"GITMCP-search_litellm_documentation" # Prefixed, not in allowed list
1061+
)
10601062
tool2.description = "Search docs"
10611063
tool2.inputSchema = {}
10621064

@@ -1093,3 +1095,76 @@ async def mock_get_tools_from_server(
10931095
"GITMCP-fetch_litellm_documentation",
10941096
"GITMCP-search_litellm_code",
10951097
]
1098+
1099+
1100+
def test_filter_tools_by_allowed_tools():
1101+
"""Test that filter_tools_by_allowed_tools filters tools correctly"""
1102+
from mcp.types import Tool
1103+
1104+
from litellm.proxy._experimental.mcp_server.server import (
1105+
filter_tools_by_allowed_tools,
1106+
)
1107+
from litellm.types.mcp import MCPTransport
1108+
from litellm.types.mcp_server.mcp_server_manager import MCPServer
1109+
1110+
mcp_server = MCPServer(
1111+
server_id="my_api_mcp",
1112+
name="my_api_mcp",
1113+
alias="my_api_mcp",
1114+
transport=MCPTransport.http,
1115+
allowed_tools=["getpetbyid", "my_api_mcp-findpetsbystatus"],
1116+
disallowed_tools=None,
1117+
)
1118+
tools_to_return = [
1119+
Tool(
1120+
name="my_api_mcp-getpetbyid",
1121+
title=None,
1122+
description="Find pet by ID",
1123+
inputSchema={
1124+
"type": "object",
1125+
"properties": {"petId": {"type": "integer", "description": ""}},
1126+
"required": ["petId"],
1127+
},
1128+
outputSchema=None,
1129+
annotations=None,
1130+
),
1131+
Tool(
1132+
name="my_api_mcp-findpetsbystatus",
1133+
title=None,
1134+
description="Finds Pets by status",
1135+
inputSchema={
1136+
"type": "object",
1137+
"properties": {"status": {"type": "string", "description": ""}},
1138+
"required": ["status"],
1139+
},
1140+
outputSchema=None,
1141+
annotations=None,
1142+
),
1143+
Tool(
1144+
name="my_api_mcp-addpet",
1145+
title=None,
1146+
description="Add a new pet to the store",
1147+
inputSchema={
1148+
"type": "object",
1149+
"properties": {
1150+
"body": {
1151+
"type": "object",
1152+
"description": "Request body",
1153+
"properties": {
1154+
"name": {"type": "string"},
1155+
"status": {"type": "string"},
1156+
},
1157+
}
1158+
},
1159+
"required": ["body"],
1160+
},
1161+
outputSchema=None,
1162+
annotations=None,
1163+
),
1164+
]
1165+
1166+
filtered_tools = filter_tools_by_allowed_tools(tools_to_return, mcp_server)
1167+
1168+
assert len(filtered_tools) == 2
1169+
assert filtered_tools[0].name == "my_api_mcp-getpetbyid"
1170+
assert filtered_tools[1].name == "my_api_mcp-findpetsbystatus"

0 commit comments

Comments
 (0)