refactor(mcp): unify REST tools-list BYOK lookup onto the shared cached resolver

_get_user_byok_auth_header hand-rolled its own uncached DB read, duplicating
_get_byok_credential (used by the tool-call and protocol tools-list paths) and
bypassing the per-user credential cache. Delegate to it so all three BYOK paths
share one resolver and the 60s cache, keeping a thin try/except so a lookup
error still degrades to listing without the key rather than aborting the
request.
This commit is contained in:
Tin Chi Lo 2026-06-24 14:05:45 -07:00
parent 4d9fabe11d
commit 9f859053a6
2 changed files with 31 additions and 24 deletions

View file

@ -80,6 +80,7 @@ if MCP_AVAILABLE:
from litellm.proxy._experimental.mcp_server.server import (
ListMCPToolsRestAPIResponseObject,
MCPServer,
_get_byok_credential,
_tool_name_matches,
execute_mcp_tool,
filter_tools_by_allowed_tools,
@ -180,34 +181,20 @@ if MCP_AVAILABLE:
server,
user_api_key_dict: UserAPIKeyAuth,
) -> Optional[str]:
"""
For BYOK servers, return the user's stored per-user key as the raw
credential string so the REST tools-list path injects it the same way
the MCP protocol tool-call path does in ``execute_mcp_tool``. The
auth_type formatting (Bearer / x-api-key / ...) is applied downstream by
the MCP client. Returns None for non-BYOK servers or when no credential
is stored.
"""
if not getattr(server, "is_byok", False):
return None
user_id = getattr(user_api_key_dict, "user_id", None)
server_id = getattr(server, "server_id", None)
if not user_id or not server_id:
return None
"""Resolve the caller's stored per-user BYOK key via the shared cached
``_get_byok_credential`` so all three BYOK paths (REST tools-list,
protocol tools-list, tool-call) use one resolver. Swallows lookup errors
so a credential failure degrades to listing without the key instead of
aborting the request. Returns None for non-BYOK servers or when no
credential is stored."""
try:
from litellm.proxy._experimental.mcp_server.db import get_user_credential
from litellm.proxy.utils import get_prisma_client_or_throw
prisma_client = get_prisma_client_or_throw(
"Database not connected. Connect a database to use BYOK MCP tools."
)
return await get_user_credential(prisma_client, user_id, server_id)
return await _get_byok_credential(server, user_api_key_dict)
except Exception as e:
verbose_logger.warning(
f"_get_user_byok_auth_header: failed to retrieve credential for "
f"user={user_id} server={server_id}: {e}"
f"_get_user_byok_auth_header: BYOK credential lookup failed for "
f"server={getattr(server, 'server_id', None)}: {e}"
)
return None
return None
async def _prefetch_user_oauth_creds(
user_api_key_dict: UserAPIKeyAuth,

View file

@ -444,6 +444,17 @@ class TestTestToolsList:
class TestListToolsRestAPI:
pytestmark = pytest.mark.asyncio
@pytest.fixture(autouse=True)
def _clear_byok_cache(self):
"""The REST BYOK helper now delegates to the shared, cached
``_get_byok_credential``; clear that cache around each test so a stored
entry from one test cannot leak into another."""
from litellm.proxy._experimental.mcp_server.server import _byok_cred_cache
_byok_cred_cache.clear()
yield
_byok_cred_cache.clear()
async def test_rejects_disallowed_server(self, monkeypatch):
async def fake_contexts(user_api_key_auth):
return [user_api_key_auth]
@ -623,6 +634,9 @@ class TestListToolsRestAPI:
monkeypatch.setattr(
mcp_db, "get_user_credential", fake_get_user_credential, raising=False
)
monkeypatch.setattr(
"litellm.proxy.proxy_server.prisma_client", object(), raising=False
)
request = _build_request(path="/mcp-rest/tools/list", method="GET")
result = await rest_endpoints.list_tool_rest_api(
@ -867,6 +881,9 @@ class TestListToolsRestAPI:
monkeypatch.setattr(
mcp_db, "get_user_credential", fake_get_user_credential, raising=False
)
monkeypatch.setattr(
"litellm.proxy.proxy_server.prisma_client", object(), raising=False
)
request = _build_request(path="/mcp-rest/tools/list", method="GET")
result = await rest_endpoints.list_tool_rest_api(
@ -952,6 +969,9 @@ class TestListToolsRestAPI:
monkeypatch.setattr(
mcp_db, "get_user_credential", fake_get_user_credential, raising=False
)
monkeypatch.setattr(
"litellm.proxy.proxy_server.prisma_client", object(), raising=False
)
request = _build_request(path="/mcp-rest/tools/list", method="GET")
result = await rest_endpoints.list_tool_rest_api(