Skip to content

Commit cd13e53

Browse files
feat(mcp_server_manager.py): expand allowed/disallowed list
1 parent 2869893 commit cd13e53

File tree

2 files changed

+80
-2
lines changed

2 files changed

+80
-2
lines changed

litellm/proxy/_experimental/mcp_server/mcp_server_manager.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -760,9 +760,15 @@ def check_allowed_or_banned_tools(self, tool_name: str, server: MCPServer) -> bo
760760
Check if the tool is allowed or banned for the given server
761761
"""
762762
if server.allowed_tools:
763-
return tool_name in server.allowed_tools
763+
return (
764+
tool_name in server.allowed_tools
765+
or f"{server.name}-{tool_name}" in server.allowed_tools
766+
)
764767
if server.disallowed_tools:
765-
return tool_name not in server.disallowed_tools
768+
return (
769+
tool_name not in server.disallowed_tools
770+
and f"{server.name}-{tool_name}" not in server.disallowed_tools
771+
)
766772
return True
767773

768774
async def check_tool_permission_for_key_team(

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

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1150,6 +1150,78 @@ async def test_check_tool_permission_for_key_team_allows_all_when_no_restriction
11501150
user_api_key_auth=user_auth,
11511151
)
11521152

1153+
@pytest.mark.asyncio
1154+
async def test_allowed_tools_with_mixed_prefixed_and_unprefixed_names(self):
1155+
"""
1156+
Test that allowed_tools works with both unprefixed and prefixed tool names.
1157+
This tests the scenario where allowed_tools = ["getpetbyid", "my_api_mcp-findpetsbystatus"]
1158+
Both getpetbyid (unprefixed) and findpetsbystatus (called unprefixed but allowed via prefix) should work.
1159+
"""
1160+
manager = MCPServerManager()
1161+
1162+
# Create server with mixed prefixed/unprefixed allowed_tools
1163+
server = MCPServer(
1164+
server_id="my_api_mcp",
1165+
name="my_api_mcp",
1166+
transport=MCPTransport.stdio,
1167+
allowed_tools=["getpetbyid", "my_api_mcp-findpetsbystatus"],
1168+
disallowed_tools=None,
1169+
)
1170+
1171+
# Mock dependencies - set object_permission and object_permission_id to None
1172+
# so permission checks return None (no restrictions)
1173+
user_api_key_auth = MagicMock()
1174+
user_api_key_auth.object_permission = None
1175+
user_api_key_auth.object_permission_id = None
1176+
proxy_logging_obj = MagicMock()
1177+
1178+
# Mock the async methods that pre_call_tool_check calls
1179+
proxy_logging_obj._create_mcp_request_object_from_kwargs = MagicMock(
1180+
return_value={}
1181+
)
1182+
proxy_logging_obj._convert_mcp_to_llm_format = MagicMock(return_value={})
1183+
proxy_logging_obj.pre_call_hook = AsyncMock(return_value={})
1184+
1185+
# Test 1: Call getpetbyid (unprefixed in allowed_tools) - should succeed
1186+
await manager.pre_call_tool_check(
1187+
name="getpetbyid",
1188+
arguments={"petId": "1"},
1189+
server_name_from_prefix="my_api_mcp",
1190+
user_api_key_auth=user_api_key_auth,
1191+
proxy_logging_obj=proxy_logging_obj,
1192+
server=server,
1193+
)
1194+
1195+
# Test 2: Call findpetsbystatus (prefixed in allowed_tools as "my_api_mcp-findpetsbystatus") - should succeed
1196+
await manager.pre_call_tool_check(
1197+
name="findpetsbystatus",
1198+
arguments={"status": "available"},
1199+
server_name_from_prefix="my_api_mcp",
1200+
user_api_key_auth=user_api_key_auth,
1201+
proxy_logging_obj=proxy_logging_obj,
1202+
server=server,
1203+
)
1204+
1205+
# Test 3: Call a tool that's not in allowed_tools - should fail
1206+
with pytest.raises(HTTPException) as exc_info:
1207+
await manager.pre_call_tool_check(
1208+
name="deletepet",
1209+
arguments={"petId": "1"},
1210+
server_name_from_prefix="my_api_mcp",
1211+
user_api_key_auth=user_api_key_auth,
1212+
proxy_logging_obj=proxy_logging_obj,
1213+
server=server,
1214+
)
1215+
1216+
assert exc_info.value.status_code == 403
1217+
assert (
1218+
"Tool deletepet is not allowed for server my_api_mcp"
1219+
in exc_info.value.detail["error"]
1220+
)
1221+
assert (
1222+
"Contact proxy admin to allow this tool" in exc_info.value.detail["error"]
1223+
)
1224+
11531225

11541226
if __name__ == "__main__":
11551227
pytest.main([__file__])

0 commit comments

Comments
 (0)