diff --git a/litellm/proxy/_experimental/mcp_server/db.py b/litellm/proxy/_experimental/mcp_server/db.py index 357925d2ba3..93580b54305 100644 --- a/litellm/proxy/_experimental/mcp_server/db.py +++ b/litellm/proxy/_experimental/mcp_server/db.py @@ -514,6 +514,7 @@ async def get_mcp_submissions( rows = await prisma_client.db.litellm_mcpservertable.find_many( where={"submitted_at": {"not": None}}, order={"submitted_at": "desc"}, + take=500, # safety cap; paginate if needed in a future iteration ) items = [LiteLLM_MCPServerTable(**r.model_dump()) for r in rows] diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index fdc9238ee8a..36790e9feae 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -1124,10 +1124,17 @@ class NewMCPServerRequest(LiteLLMPydanticObjectBase): byok_description: List[str] = Field(default_factory=list) byok_api_key_help_url: Optional[str] = None source_url: Optional[str] = None - # BYOM submission fields (set by endpoint, not by caller) - approval_status: Optional[str] = None - submitted_by: Optional[str] = None - submitted_at: Optional[datetime] = 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( + None, description="Server-managed: set by the endpoint; caller values are overridden." + ) + submitted_by: Optional[str] = Field( + None, description="Server-managed: set by the endpoint; caller values are overridden." + ) + submitted_at: Optional[datetime] = Field( + None, description="Server-managed: set by the endpoint; caller values are overridden." + ) @model_validator(mode="before") @classmethod diff --git a/litellm/proxy/management_endpoints/mcp_management_endpoints.py b/litellm/proxy/management_endpoints/mcp_management_endpoints.py index 2ba8ae2ec62..14d9e6d0b20 100644 --- a/litellm/proxy/management_endpoints/mcp_management_endpoints.py +++ b/litellm/proxy/management_endpoints/mcp_management_endpoints.py @@ -195,7 +195,10 @@ if MCP_AVAILABLE: def _field_present(field_name: str) -> bool: value = getattr(payload, field_name, None) - if not value: + if value is None: + return False + # Treat empty string and empty list as absent (mirrors UI compliance check) + if isinstance(value, (str, list)) and not value: return False if field_name == "auth_type" and value == _AUTH_TYPE_SENTINEL: return False diff --git a/ui/litellm-dashboard/src/components/mcp_tools/MCPSubmissionsTab.tsx b/ui/litellm-dashboard/src/components/mcp_tools/MCPSubmissionsTab.tsx index 19a413e65fb..4ce7423f1b5 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/MCPSubmissionsTab.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/MCPSubmissionsTab.tsx @@ -542,11 +542,12 @@ export function MCPSubmissionsTab({ accessToken }: MCPSubmissionsTabProps) { if (!accessToken) return; try { await approveMCPServer(accessToken, serverId); - setConfirmAction(null); await fetchData(); NotificationsManager.success(`MCP server "${serverName}" approved`); } catch { NotificationsManager.fromBackend("Failed to approve MCP server"); + } finally { + setConfirmAction(null); } } @@ -554,11 +555,12 @@ export function MCPSubmissionsTab({ accessToken }: MCPSubmissionsTabProps) { if (!accessToken) return; try { await rejectMCPServer(accessToken, serverId, reviewNotes); - setConfirmAction(null); await fetchData(); NotificationsManager.success(`MCP server "${serverName}" rejected`); } catch { NotificationsManager.fromBackend("Failed to reject MCP server"); + } finally { + setConfirmAction(null); } }