diff --git a/litellm/__init__.py b/litellm/__init__.py index d75f47567a5..cf71502b8a9 100644 --- a/litellm/__init__.py +++ b/litellm/__init__.py @@ -160,6 +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 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 9f3b8196244..5fb011f5148 100644 --- a/litellm/proxy/_experimental/mcp_server/server.py +++ b/litellm/proxy/_experimental/mcp_server/server.py @@ -28,6 +28,7 @@ from starlette.requests import Request as StarletteRequest from starlette.responses import JSONResponse from starlette.types import Receive, Scope, Send +import litellm from litellm._logging import verbose_logger from litellm.constants import MAXIMUM_TRACEBACK_LINES_TO_LOG from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj @@ -1586,16 +1587,21 @@ if MCP_AVAILABLE: if prisma_client is None: # Without a database we cannot fetch the per-user credential. - # Return a 503 (infrastructure problem) so callers can distinguish - # "no credential" (401) from "credential store unavailable" (503). - raise HTTPException( - status_code=503, - detail={ - "error": "byok_store_unavailable", - "server_id": mcp_server.server_id, - "message": "Credential store is not available; cannot fetch BYOK credential.", - }, - ) + # When require_byok_credential_store is True (default) we raise 503 + # so callers can distinguish "no credential" (401) from + # "credential store unavailable" (503). + # Set litellm.require_byok_credential_store = False to restore the + # legacy silent-bypass behaviour for stateless deployments. + if litellm.require_byok_credential_store: + raise HTTPException( + status_code=503, + detail={ + "error": "byok_store_unavailable", + "server_id": mcp_server.server_id, + "message": "Credential store is not available; cannot fetch BYOK credential.", + }, + ) + return None raw = await get_user_credential( prisma_client=prisma_client, user_id=user_id, @@ -1666,17 +1672,20 @@ if MCP_AVAILABLE: if prisma_client is None: # Without a database we cannot verify whether the user has a stored - # credential, so we must deny access rather than silently bypass the - # BYOK check. A 503 signals a configuration/infrastructure problem, - # not an auth failure, so clients can distinguish the two cases. - raise HTTPException( - status_code=503, - detail={ - "error": "byok_store_unavailable", - "server_id": mcp_server.server_id, - "message": "Credential store is not available; cannot verify BYOK access.", - }, - ) + # credential. When require_byok_credential_store is True (default) + # we raise 503 to distinguish infra failures from missing credentials. + # Set litellm.require_byok_credential_store = False to restore the + # legacy silent-bypass behaviour for stateless deployments. + if litellm.require_byok_credential_store: + raise HTTPException( + status_code=503, + detail={ + "error": "byok_store_unavailable", + "server_id": mcp_server.server_id, + "message": "Credential store is not available; cannot verify BYOK access.", + }, + ) + return raw_credential = await get_user_credential( prisma_client=prisma_client,