From 4d9072bf3e0592bc55ff8fef3efda840d5bdb5f6 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 23 Apr 2026 14:00:23 +0000 Subject: [PATCH] feat(ui): chat/MCPConnectPicker to shadcn - antd Switch/Spin \u2192 shadcn Switch + lucide Loader2 (replacing the Switch's loading prop with a separate inline spinner since shadcn Switch has no loading slot). - Inline style attributes replaced with Tailwind utilities + semantic tokens (text-muted-foreground / text-foreground). Gates: TS \u2713 | Build \u2713. Co-authored-by: yuneng-jiang --- .../src/components/chat/MCPConnectPicker.tsx | 96 +++++++------------ 1 file changed, 34 insertions(+), 62 deletions(-) diff --git a/ui/litellm-dashboard/src/components/chat/MCPConnectPicker.tsx b/ui/litellm-dashboard/src/components/chat/MCPConnectPicker.tsx index 6ef3aecc469..63df17d4307 100644 --- a/ui/litellm-dashboard/src/components/chat/MCPConnectPicker.tsx +++ b/ui/litellm-dashboard/src/components/chat/MCPConnectPicker.tsx @@ -1,5 +1,6 @@ import React, { useEffect, useState } from "react"; -import { Switch, Spin } from "antd"; +import { Switch } from "@/components/ui/switch"; +import { Loader2 } from "lucide-react"; import MessageManager from "@/components/molecules/message_manager"; import { fetchMCPServers, listMCPTools } from "../networking"; import { MCPServer } from "../mcp_tools/types"; @@ -10,10 +11,13 @@ interface Props { onChange: (servers: string[]) => void; } -const MCPConnectPicker: React.FC = ({ accessToken, selectedServers, onChange }) => { +const MCPConnectPicker: React.FC = ({ + accessToken, + selectedServers, + onChange, +}) => { const [servers, setServers] = useState([]); const [loadingServers, setLoadingServers] = useState(true); - // Track which individual servers are being toggled on (verifying tools) const [togglingOn, setTogglingOn] = useState>(new Set()); useEffect(() => { @@ -24,8 +28,9 @@ const MCPConnectPicker: React.FC = ({ accessToken, selectedServers, onCha try { const data = await fetchMCPServers(accessToken); if (cancelled) return; - // API returns { data: MCPServer[] } or MCPServer[] - const list: MCPServer[] = Array.isArray(data) ? data : (data?.data ?? []); + const list: MCPServer[] = Array.isArray(data) + ? data + : (data?.data ?? []); setServers(list); } catch { if (!cancelled) { @@ -47,29 +52,24 @@ const MCPConnectPicker: React.FC = ({ accessToken, selectedServers, onCha const handleToggle = async (serverName: string, checked: boolean) => { if (!checked) { - // Toggle OFF — remove immediately, no tool fetch needed onChange(selectedServers.filter((s) => s !== serverName)); return; } - // Toggle ON — verify tools are reachable first setTogglingOn((prev) => new Set(prev).add(serverName)); try { const result = await listMCPTools(accessToken, serverName); - // listMCPTools never throws; it returns { tools, error, message } on failure if (result?.error) { MessageManager.warning( - `Could not load tools for ${serverName} — it will be excluded from this message.` + `Could not load tools for ${serverName} — it will be excluded from this message.`, ); - // Do not add to selectedServers return; } onChange([...selectedServers, serverName]); } catch { MessageManager.warning( - `Could not load tools for ${serverName} — it will be excluded from this message.` + `Could not load tools for ${serverName} — it will be excluded from this message.`, ); - // Do not add to selectedServers } finally { setTogglingOn((prev) => { const next = new Set(prev); @@ -80,20 +80,13 @@ const MCPConnectPicker: React.FC = ({ accessToken, selectedServers, onCha }; return ( -
+
{loadingServers ? ( -
- +
+
) : servers.length === 0 ? ( -
+
No MCP servers configured
) : ( @@ -105,60 +98,39 @@ const MCPConnectPicker: React.FC = ({ accessToken, selectedServers, onCha return (
{server.mcp_info?.logo_url && ( + // eslint-disable-next-line @next/next/no-img-element {`${name} { + (e.target as HTMLImageElement).style.display = "none"; }} - onError={(e) => { (e.target as HTMLImageElement).style.display = "none"; }} /> )} -
-
+
+
{name}
{server.description && ( -
+
{server.description}
)}
- handleToggle(name, checked)} - /> +
+ {isTogglingOn && ( + + )} + handleToggle(name, checked)} + disabled={isTogglingOn} + /> +
); })