fix: skip DB team lookup for UI session token team_id in MCP auth

When the LiteLLM dashboard uses a UI session token, the team_id is set
to 'litellm-dashboard' which is a placeholder, not a real team in the
DB. Previously, _get_team_object_permission and
_get_mcp_access_groups_for_team would attempt to look up this team,
causing a 404 exception that was caught and logged as a warning.

This resulted in:
1. Noisy 'Failed to get allowed MCP servers for team' warnings
2. Team permission check returning empty results, causing MCP servers
   without allow_all_keys=True to be filtered out for UI session users

Fix: Add an early return for UI_SESSION_TOKEN_TEAM_ID in both
_get_team_object_permission and _get_mcp_access_groups_for_team,
returning None/[] gracefully without a DB lookup.

Co-authored-by: Ishaan Jaff <ishaan-jaff@users.noreply.github.com>
This commit is contained in:
Cursor Agent 2026-02-28 03:07:02 +00:00
parent 8b50703f74
commit ae07c9686a
2 changed files with 115 additions and 0 deletions

View file

@ -462,6 +462,7 @@ class MCPRequestHandler:
Note: object_permission is automatically populated when the team is fetched via
get_team_object() in litellm/proxy/auth/auth_checks.py
"""
from litellm.constants import UI_SESSION_TOKEN_TEAM_ID
from litellm.proxy.auth.auth_checks import get_team_object
from litellm.proxy.proxy_server import (
prisma_client,
@ -475,6 +476,9 @@ class MCPRequestHandler:
if not user_api_key_auth or not user_api_key_auth.team_id or not prisma_client:
return None
if user_api_key_auth.team_id == UI_SESSION_TOKEN_TEAM_ID:
return None
# Get the team object (which has object_permission already loaded)
team_obj: Optional[LiteLLM_TeamTable] = await get_team_object(
team_id=user_api_key_auth.team_id,
@ -1021,6 +1025,7 @@ class MCPRequestHandler:
"""
Get MCP access groups for the team
"""
from litellm.constants import UI_SESSION_TOKEN_TEAM_ID
from litellm.proxy.auth.auth_checks import get_team_object
from litellm.proxy.proxy_server import (
prisma_client,
@ -1034,6 +1039,9 @@ class MCPRequestHandler:
if user_api_key_auth.team_id is None:
return []
if user_api_key_auth.team_id == UI_SESSION_TOKEN_TEAM_ID:
return []
if prisma_client is None:
verbose_logger.debug("prisma_client is None")
return []

View file

@ -1738,3 +1738,110 @@ class TestAgentMCPPermissions:
user_api_key_auth=user_api_key_auth,
)
assert sorted(result) == ["tool_a", "tool_b"]
@pytest.mark.asyncio
class TestUISessionTokenTeamHandling:
"""Test that UI session tokens with team_id='litellm-dashboard' are handled
gracefully without attempting DB lookups for the non-existent team."""
async def test_get_team_object_permission_returns_none_for_ui_session_team(self):
"""_get_team_object_permission should return None immediately for
UI_SESSION_TOKEN_TEAM_ID without calling get_team_object."""
from litellm.constants import UI_SESSION_TOKEN_TEAM_ID
mock_user_auth = UserAPIKeyAuth(
api_key="test-key",
user_id="test-user",
team_id=UI_SESSION_TOKEN_TEAM_ID,
)
mock_prisma = MagicMock()
with patch("litellm.proxy.proxy_server.prisma_client", mock_prisma):
with patch(
"litellm.proxy.auth.auth_checks.get_team_object"
) as mock_get_team:
result = await MCPRequestHandler._get_team_object_permission(
mock_user_auth
)
assert result is None
mock_get_team.assert_not_called()
async def test_get_allowed_mcp_servers_for_team_returns_empty_for_ui_session_team(
self,
):
"""_get_allowed_mcp_servers_for_team should return [] immediately for
UI_SESSION_TOKEN_TEAM_ID without calling get_team_object."""
from litellm.constants import UI_SESSION_TOKEN_TEAM_ID
mock_user_auth = UserAPIKeyAuth(
api_key="test-key",
user_id="test-user",
team_id=UI_SESSION_TOKEN_TEAM_ID,
)
with patch.object(
MCPRequestHandler, "_get_team_object_permission"
) as mock_get_team_perm:
mock_get_team_perm.return_value = None
result = await MCPRequestHandler._get_allowed_mcp_servers_for_team(
mock_user_auth
)
assert result == []
async def test_get_mcp_access_groups_for_team_returns_empty_for_ui_session_team(
self,
):
"""_get_mcp_access_groups_for_team should return [] immediately for
UI_SESSION_TOKEN_TEAM_ID without calling get_team_object."""
from litellm.constants import UI_SESSION_TOKEN_TEAM_ID
mock_user_auth = UserAPIKeyAuth(
api_key="test-key",
user_id="test-user",
team_id=UI_SESSION_TOKEN_TEAM_ID,
)
mock_prisma = MagicMock()
with patch("litellm.proxy.proxy_server.prisma_client", mock_prisma):
with patch(
"litellm.proxy.auth.auth_checks.get_team_object"
) as mock_get_team:
result = await MCPRequestHandler._get_mcp_access_groups_for_team(
mock_user_auth
)
assert result == []
mock_get_team.assert_not_called()
async def test_regular_team_id_still_calls_get_team_object(self):
"""Verify that a regular team_id still triggers get_team_object."""
from litellm.proxy._types import LiteLLM_TeamTable
mock_user_auth = UserAPIKeyAuth(
api_key="test-key",
user_id="test-user",
team_id="real-team-123",
)
mock_team_obj = LiteLLM_TeamTable(
team_id="real-team-123",
object_permission=None,
)
mock_prisma = MagicMock()
with patch("litellm.proxy.proxy_server.prisma_client", mock_prisma):
with patch(
"litellm.proxy.auth.auth_checks.get_team_object"
) as mock_get_team:
mock_get_team.return_value = mock_team_obj
result = await MCPRequestHandler._get_team_object_permission(
mock_user_auth
)
assert result is None
mock_get_team.assert_called_once()