Fix mcp_tool_permissions JSON string deserialization in _resolve_team_allowed_mcp_servers

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
yuneng-jiang
2026-03-11 00:23:40 -07:00
parent 860cb17571
commit ff2f96d09e
3 changed files with 43 additions and 4 deletions
@@ -203,9 +203,10 @@ async def _resolve_team_allowed_mcp_servers(
team_object_permission.mcp_access_groups or []
)
)
tool_perm_servers: List[str] = list(
(team_object_permission.mcp_tool_permissions or {}).keys()
)
raw_tool_perms = team_object_permission.mcp_tool_permissions or {}
if isinstance(raw_tool_perms, str):
raw_tool_perms = json.loads(raw_tool_perms)
tool_perm_servers: List[str] = list(raw_tool_perms.keys())
return set(direct_servers + access_group_servers + tool_perm_servers)
@@ -15,6 +15,7 @@ from litellm.proxy._types import LiteLLM_ObjectPermissionTable
from litellm.proxy.management_helpers.object_permission_utils import (
_extract_requested_mcp_access_groups,
_extract_requested_mcp_server_ids,
_resolve_team_allowed_mcp_servers,
_set_object_permission,
validate_key_mcp_servers_against_team,
)
@@ -395,3 +396,40 @@ async def test_validate_team_access_groups_resolve_to_servers(mock_access_groups
team_obj=team_obj,
)
# ---- Tests for _resolve_team_allowed_mcp_servers with JSON string mcp_tool_permissions ----
@pytest.mark.asyncio
@patch(
"litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.MCPRequestHandler._get_mcp_servers_from_access_groups",
new_callable=AsyncMock,
return_value=[],
)
async def test_resolve_team_allowed_mcp_servers_string_tool_permissions(mock_access_groups):
"""mcp_tool_permissions stored as a JSON string (via safe_dumps) should be deserialized correctly."""
mock_perm = MagicMock(spec=LiteLLM_ObjectPermissionTable)
mock_perm.mcp_servers = ["server-1"]
mock_perm.mcp_access_groups = []
mock_perm.mcp_tool_permissions = json.dumps({"server-2": ["tool1"]})
result = await _resolve_team_allowed_mcp_servers(mock_perm)
assert result == {"server-1", "server-2"}
@pytest.mark.asyncio
@patch(
"litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.MCPRequestHandler._get_mcp_servers_from_access_groups",
new_callable=AsyncMock,
return_value=[],
)
async def test_resolve_team_allowed_mcp_servers_dict_tool_permissions(mock_access_groups):
"""mcp_tool_permissions as a dict should work without deserialization."""
mock_perm = MagicMock(spec=LiteLLM_ObjectPermissionTable)
mock_perm.mcp_servers = []
mock_perm.mcp_access_groups = []
mock_perm.mcp_tool_permissions = {"server-a": ["tool1"]}
result = await _resolve_team_allowed_mcp_servers(mock_perm)
assert result == {"server-a"}
@@ -9,7 +9,7 @@ const mcpServersKeys = createQueryKeys("mcpServers");
export const useMCPServers = (teamId?: string | null) => {
const { accessToken } = useAuthorized();
return useQuery<MCPServer[]>({
queryKey: mcpServersKeys.list({ teamId: teamId ?? undefined }),
queryKey: mcpServersKeys.list(teamId ? { filters: { teamId } } : undefined),
queryFn: async () => await fetchMCPServers(accessToken!, teamId),
enabled: !!accessToken,
});