From 30339632300693873ec3dde7ab53db89020d0a3a Mon Sep 17 00:00:00 2001 From: Ishaan Jaffer Date: Tue, 10 Mar 2026 14:05:30 -0700 Subject: [PATCH] fix(mcp): use enum type for approval_status; true count query; block view-only admin from /register --- litellm/proxy/_experimental/mcp_server/db.py | 8 +++++--- litellm/proxy/_types.py | 6 +++--- .../management_endpoints/mcp_management_endpoints.py | 5 ++++- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/db.py b/litellm/proxy/_experimental/mcp_server/db.py index 93580b54305..f54ddd3159c 100644 --- a/litellm/proxy/_experimental/mcp_server/db.py +++ b/litellm/proxy/_experimental/mcp_server/db.py @@ -511,10 +511,12 @@ async def get_mcp_submissions( along with a summary count breakdown by approval_status. Mirrors get_guardrail_submissions() from guardrail_endpoints.py. """ + _where = {"submitted_at": {"not": None}} + total_count = await prisma_client.db.litellm_mcpservertable.count(where=_where) rows = await prisma_client.db.litellm_mcpservertable.find_many( - where={"submitted_at": {"not": None}}, + where=_where, order={"submitted_at": "desc"}, - take=500, # safety cap; paginate if needed in a future iteration + take=500, # page 1 cap; paginate if needed in a future iteration ) items = [LiteLLM_MCPServerTable(**r.model_dump()) for r in rows] @@ -523,7 +525,7 @@ async def get_mcp_submissions( rejected = sum(1 for i in items if i.approval_status == MCPApprovalStatus.rejected) return MCPSubmissionsSummary( - total=len(items), + total=total_count, # true total even when items is capped at 500 pending_review=pending, active=active, rejected=rejected, diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index 36790e9feae..070b16e7fc4 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -1126,7 +1126,7 @@ class NewMCPServerRequest(LiteLLMPydanticObjectBase): source_url: Optional[str] = None # BYOM submission fields — set by the endpoint, not by the caller. # Any caller-provided values are silently overridden before persistence. - approval_status: Optional[str] = Field( + approval_status: Optional[MCPApprovalStatus] = Field( None, description="Server-managed: set by the endpoint; caller values are overridden." ) submitted_by: Optional[str] = Field( @@ -1260,8 +1260,8 @@ class LiteLLM_MCPServerTable(LiteLLMPydanticObjectBase): has_user_credential: Optional[bool] = None source_url: Optional[str] = None # BYOM submission fields - approval_status: Optional[str] = Field( - default="active", + approval_status: Optional[MCPApprovalStatus] = Field( + default=MCPApprovalStatus.active, description="Approval status: 'pending_review', 'active', 'rejected'", ) submitted_by: Optional[str] = None diff --git a/litellm/proxy/management_endpoints/mcp_management_endpoints.py b/litellm/proxy/management_endpoints/mcp_management_endpoints.py index 14d9e6d0b20..38867f2cec6 100644 --- a/litellm/proxy/management_endpoints/mcp_management_endpoints.py +++ b/litellm/proxy/management_endpoints/mcp_management_endpoints.py @@ -765,7 +765,10 @@ if MCP_AVAILABLE: Creates the server with approval_status=pending_review. Requires a team-scoped API key. """ - if user_api_key_dict.user_role == LitellmUserRoles.PROXY_ADMIN: + if user_api_key_dict.user_role in ( + LitellmUserRoles.PROXY_ADMIN, + LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY, + ): raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail={