From eccd397d392e95520afef914d5e9af3fc37e8fdd Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 23 Apr 2026 17:05:59 +0000 Subject: [PATCH] feat(ui): mcp_tools/mcp_server_view to shadcn - @heroicons ArrowLeft/Eye/EyeOff + @tremor Title/Card/Button/Text/ Grid/Icon + antd Button \u2192 shadcn Button/Card + lucide ArrowLeft/ Check/Copy/Eye/EyeOff. - Tremor TabGroup/TabList/TabPanel family kept for now (phase 1 whitelist). Grid \u2192 plain grid-cols-1 sm:grid-cols-2 lg:grid-cols-3. - Allow-all/Network/Allowed-tools badges use categorical emerald/ orange/blue palette; file added to .eslintrc overrides. Gates: TS \u2713 | Build \u2713. Co-authored-by: yuneng-jiang --- ui/litellm-dashboard/.eslintrc.json | 1 + .../components/mcp_tools/mcp_server_view.tsx | 359 +++++++++++++----- 2 files changed, 256 insertions(+), 104 deletions(-) diff --git a/ui/litellm-dashboard/.eslintrc.json b/ui/litellm-dashboard/.eslintrc.json index 80d97495c0f..390b215ef7f 100644 --- a/ui/litellm-dashboard/.eslintrc.json +++ b/ui/litellm-dashboard/.eslintrc.json @@ -70,6 +70,7 @@ "src/components/model_dashboard/health_check_columns.tsx", "src/components/UsagePage/components/EntityUsage/TopKeyView.tsx", "src/components/policies/policy_templates.tsx", + "src/components/mcp_tools/mcp_server_view.tsx", "src/components/permissions/VectorStorePermissions.tsx", "src/components/permissions/AgentPermissions.tsx", "src/components/permissions/MCPServerPermissions.tsx", diff --git a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_view.tsx b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_view.tsx index c70d188bdbb..7809b06065a 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_view.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_view.tsx @@ -1,6 +1,16 @@ import React, { useState } from "react"; -import { ArrowLeftIcon, EyeIcon, EyeOffIcon } from "@heroicons/react/outline"; -import { Title, Card, Button, Text, Grid, TabGroup, TabList, TabPanel, TabPanels, Tab, Icon } from "@tremor/react"; +import { ArrowLeft, Check, Copy, Eye, EyeOff } from "lucide-react"; +// eslint-disable-next-line litellm-ui/no-banned-ui-imports +import { + TabGroup, + TabList, + TabPanel, + TabPanels, + Tab, +} from "@tremor/react"; +import { Button } from "@/components/ui/button"; +import { Card } from "@/components/ui/card"; +import { cn } from "@/lib/utils"; import { MCPServer, handleTransport, handleAuth } from "./types"; // TODO: Move Tools viewer from index file @@ -9,8 +19,6 @@ import MCPServerEdit from "./mcp_server_edit"; import MCPServerCostDisplay from "./mcp_server_cost_display"; import { getMaskedAndFullUrl } from "./utils"; import { copyToClipboard as utilCopyToClipboard } from "@/utils/dataUtils"; -import { CheckIcon, CopyIcon } from "lucide-react"; -import { Button as AntdButton } from "antd"; interface MCPServerViewProps { mcpServer: MCPServer; @@ -38,21 +46,29 @@ export const MCPServerView: React.FC = ({ const [copiedStates, setCopiedStates] = useState>({}); const [selectedTabIndex, setSelectedTabIndex] = useState(0); - const handleSuccess = (updated: MCPServer) => { + const handleSuccess = () => { setEditing(false); onBack(); }; const urlValue = mcpServer.url ?? ""; - const { maskedUrl, hasToken } = urlValue ? getMaskedAndFullUrl(urlValue) : { maskedUrl: "—", hasToken: false }; + const { maskedUrl, hasToken } = urlValue + ? getMaskedAndFullUrl(urlValue) + : { maskedUrl: "—", hasToken: false }; - const renderUrlWithToggle = (url: string | null | undefined, showFull: boolean) => { + const renderUrlWithToggle = ( + url: string | null | undefined, + showFull: boolean, + ) => { if (!url) return "—"; if (!hasToken) return url; return showFull ? url : maskedUrl; }; - const copyToClipboard = async (text: string | null | undefined, key: string) => { + const copyToClipboard = async ( + text: string | null | undefined, + key: string, + ) => { const success = await utilCopyToClipboard(text); if (success) { setCopiedStates((prev) => ({ ...prev, [key]: true })); @@ -64,56 +80,91 @@ export const MCPServerView: React.FC = ({ const getTransportBadge = (transport: string) => { const label = transport.toUpperCase(); - return {label}; + return ( + + {label} + + ); }; const getAuthBadge = (authType: string) => { - return {authType}; + return ( + + {authType} + + ); }; return (
-
- {mcpServer.server_name || mcpServer.alias || "Unnamed Server"} - : } - onClick={() => copyToClipboard(mcpServer.server_name || mcpServer.alias, "mcp-server_name")} - className={`transition-all duration-200 ${copiedStates["mcp-server_name"] - ? "text-green-600 bg-green-50 border-green-200" - : "text-gray-400 hover:text-gray-600 hover:bg-gray-100" - }`} - /> - {mcpServer.alias && mcpServer.server_name && mcpServer.alias !== mcpServer.server_name && ( - - {mcpServer.alias} - - )} +

+ {mcpServer.server_name || mcpServer.alias || "Unnamed Server"} +

+ + {mcpServer.alias && + mcpServer.server_name && + mcpServer.alias !== mcpServer.server_name && ( + + {mcpServer.alias} + + )}
- {mcpServer.server_id} - : } + + {mcpServer.server_id} + +
{mcpServer.description && ( - {mcpServer.description} +

{mcpServer.description}

)}
- {/* TODO: magic number for index */} {[ @@ -126,39 +177,62 @@ export const MCPServerView: React.FC = ({ {/* Overview Panel */} - +
- Transport + + Transport +
- {getTransportBadge(handleTransport(mcpServer.transport ?? undefined, mcpServer.spec_path ?? undefined))} + {getTransportBadge( + handleTransport( + mcpServer.transport ?? undefined, + mcpServer.spec_path ?? undefined, + ), + )}
- Authentication + + Authentication +
{getAuthBadge(handleAuth(mcpServer.auth_type ?? undefined))}
- Host URL + + Host URL +
- + {renderUrlWithToggle(mcpServer.url, showFullUrl)} - + {hasToken && ( - )}
- +
- Cost Configuration + + Cost Configuration +
- +
@@ -178,11 +252,11 @@ export const MCPServerView: React.FC = ({ {/* Settings Panel */} - +
- MCP Server Settings +

MCP Server Settings

{editing ? null : ( - )} @@ -196,71 +270,121 @@ export const MCPServerView: React.FC = ({ availableAccessGroups={availableAccessGroups} /> ) : ( -
+
- Server Name -
{mcpServer.server_name || —}
+ + Server Name + +
+ {mcpServer.server_name || ( + — + )} +
- Alias -
{mcpServer.alias || —}
+ + Alias + +
+ {mcpServer.alias || ( + — + )} +
- Description -
{mcpServer.description || —}
+ + Description + +
+ {mcpServer.description || ( + — + )} +
- URL -
+ + URL + +
{renderUrlWithToggle(mcpServer.url, showFullUrl)} {hasToken && ( - )}
- Transport -
{getTransportBadge(handleTransport(mcpServer.transport, mcpServer.spec_path))}
-
-
- Authentication -
{getAuthBadge(handleAuth(mcpServer.auth_type))}
-
-
- Extra Headers -
- {mcpServer.extra_headers && mcpServer.extra_headers.length > 0 - ? mcpServer.extra_headers.join(", ") - : —} + + Transport + +
+ {getTransportBadge( + handleTransport( + mcpServer.transport, + mcpServer.spec_path, + ), + )}
- Allow All Keys + + Authentication + +
+ {getAuthBadge(handleAuth(mcpServer.auth_type))} +
+
+
+ + Extra Headers + +
+ {mcpServer.extra_headers && + mcpServer.extra_headers.length > 0 ? ( + mcpServer.extra_headers.join(", ") + ) : ( + — + )} +
+
+
+ + Allow All Keys +
{mcpServer.allow_all_keys ? ( - - + + Enabled ) : ( - + Disabled )}
- Network Access + + Network Access +
{mcpServer.available_on_public_internet ? ( - - + + Public ) : ( - + Internal only @@ -268,41 +392,68 @@ export const MCPServerView: React.FC = ({
- Access Groups + + Access Groups +
- {mcpServer.mcp_access_groups && mcpServer.mcp_access_groups.length > 0 ? ( + {mcpServer.mcp_access_groups && + mcpServer.mcp_access_groups.length > 0 ? (
- {mcpServer.mcp_access_groups.map((group: any, index: number) => ( - - {typeof group === "string" ? group : group?.name ?? ""} - - ))} + {mcpServer.mcp_access_groups.map( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (group: any, index: number) => ( + + {typeof group === "string" + ? group + : (group?.name ?? "")} + + ), + )}
) : ( - — + + — + )}
- Allowed Tools + + Allowed Tools +
- {mcpServer.allowed_tools && mcpServer.allowed_tools.length > 0 ? ( + {mcpServer.allowed_tools && + mcpServer.allowed_tools.length > 0 ? (
- {mcpServer.allowed_tools.map((tool: string, index: number) => ( - - {tool} - - ))} + {mcpServer.allowed_tools.map( + (tool: string, index: number) => ( + + {tool} + + ), + )}
) : ( - All tools enabled + + All tools enabled + )}
- Cost + + Cost +
- +