diff --git a/litellm/__init__.py b/litellm/__init__.py index cf71502b8a9..4c05c90981f 100644 --- a/litellm/__init__.py +++ b/litellm/__init__.py @@ -160,11 +160,11 @@ langfuse_default_tags: Optional[List[str]] = None langsmith_batch_size: Optional[int] = None prometheus_initialize_budget_metrics: Optional[bool] = False require_auth_for_metrics_endpoint: Optional[bool] = False -# When True (default), BYOK MCP servers raise HTTP 503 if the credential store -# (Prisma DB) is unavailable, rather than silently bypassing the BYOK check. -# Set to False to restore pre-v1.x legacy behaviour for stateless deployments -# that do not use a database. -require_byok_credential_store: Optional[bool] = True +# When True, BYOK MCP servers raise HTTP 503 if the credential store (Prisma DB) +# is unavailable, rather than silently bypassing the BYOK check. +# Defaults to False for backwards compatibility with deployments that do not +# configure a database. Set to True to enforce strict credential-store presence. +require_byok_credential_store: Optional[bool] = False argilla_batch_size: Optional[int] = None datadog_use_v1: Optional[bool] = False # if you want to use v1 datadog logged payload. gcs_pub_sub_use_v1: Optional[bool] = ( diff --git a/litellm/proxy/_experimental/mcp_server/server.py b/litellm/proxy/_experimental/mcp_server/server.py index 5fb011f5148..91ac5e60685 100644 --- a/litellm/proxy/_experimental/mcp_server/server.py +++ b/litellm/proxy/_experimental/mcp_server/server.py @@ -1602,6 +1602,10 @@ if MCP_AVAILABLE: }, ) return None + # `get_user_credential` is the centralized data-access helper for the + # LiteLLM_MCPUserCredentials table (defined in mcp_server/db.py). + # The 60-second in-memory cache (_byok_cred_cache) ensures this is + # called at most once per TTL window, keeping the hot path DB-free. raw = await get_user_credential( prisma_client=prisma_client, user_id=user_id, diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_openapi_oauth2_endpoints.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_openapi_oauth2_endpoints.py index 564386502db..3ffed9b225f 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_openapi_oauth2_endpoints.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_openapi_oauth2_endpoints.py @@ -536,13 +536,19 @@ async def test_check_byok_credential_raises_503_when_no_db(): # Ensure no cache hit _byok_cred_cache.pop(("user1", "byok-server"), None) + import litellm + with patch( "litellm.proxy.proxy_server.prisma_client", None, create=True, ): - with pytest.raises(HTTPException) as exc_info: - await _check_byok_credential(mock_server, mock_user) + litellm.require_byok_credential_store = True # type: ignore[attr-defined] + try: + with pytest.raises(HTTPException) as exc_info: + await _check_byok_credential(mock_server, mock_user) + finally: + litellm.require_byok_credential_store = False # type: ignore[attr-defined] assert exc_info.value.status_code == 503 assert "byok_store_unavailable" in str(exc_info.value.detail) @@ -613,13 +619,19 @@ async def test_get_byok_credential_raises_503_when_no_db(): mock_user = MagicMock(spec=UserAPIKeyAuth) mock_user.user_id = "user-db-unavail" + import litellm + with patch( "litellm.proxy.proxy_server.prisma_client", None, create=True, ): - with pytest.raises(HTTPException) as exc_info: - await _get_byok_credential(mock_server, mock_user) + litellm.require_byok_credential_store = True # type: ignore[attr-defined] + try: + with pytest.raises(HTTPException) as exc_info: + await _get_byok_credential(mock_server, mock_user) + finally: + litellm.require_byok_credential_store = False # type: ignore[attr-defined] assert exc_info.value.status_code == 503 assert "byok_store_unavailable" in str(exc_info.value.detail)