import React, { useState } from "react"; import { motion, AnimatePresence } from "framer-motion"; import { Network, Globe, Terminal, Trash2, Play, CheckCircle, Loader2, RefreshCw, FolderOpen, User, FileText, ChevronDown, ChevronUp, Copy } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { api, type MCPServer } from "@/lib/api"; interface MCPServerListProps { /** * List of MCP servers to display */ servers: MCPServer[]; /** * Whether the list is loading */ loading: boolean; /** * Callback when a server is removed */ onServerRemoved: (name: string) => void; /** * Callback to refresh the server list */ onRefresh: () => void; } /** * Component for displaying a list of MCP servers * Shows servers grouped by scope with status indicators */ export const MCPServerList: React.FC = ({ servers, loading, onServerRemoved, onRefresh, }) => { const [removingServer, setRemovingServer] = useState(null); const [testingServer, setTestingServer] = useState(null); const [expandedServers, setExpandedServers] = useState>(new Set()); const [copiedServer, setCopiedServer] = useState(null); // Group servers by scope const serversByScope = servers.reduce((acc, server) => { const scope = server.scope || "local"; if (!acc[scope]) acc[scope] = []; acc[scope].push(server); return acc; }, {} as Record); /** * Toggles expanded state for a server */ const toggleExpanded = (serverName: string) => { setExpandedServers(prev => { const next = new Set(prev); if (next.has(serverName)) { next.delete(serverName); } else { next.add(serverName); } return next; }); }; /** * Copies command to clipboard */ const copyCommand = async (command: string, serverName: string) => { try { await navigator.clipboard.writeText(command); setCopiedServer(serverName); setTimeout(() => setCopiedServer(null), 2000); } catch (error) { console.error("Failed to copy command:", error); } }; /** * Removes a server */ const handleRemoveServer = async (name: string) => { try { setRemovingServer(name); await api.mcpRemove(name); onServerRemoved(name); } catch (error) { console.error("Failed to remove server:", error); } finally { setRemovingServer(null); } }; /** * Tests connection to a server */ const handleTestConnection = async (name: string) => { try { setTestingServer(name); const result = await api.mcpTestConnection(name); // TODO: Show result in a toast or modal console.log("Test result:", result); } catch (error) { console.error("Failed to test connection:", error); } finally { setTestingServer(null); } }; /** * Gets icon for transport type */ const getTransportIcon = (transport: string) => { switch (transport) { case "stdio": return ; case "sse": return ; default: return ; } }; /** * Gets icon for scope */ const getScopeIcon = (scope: string) => { switch (scope) { case "local": return ; case "project": return ; case "user": return ; default: return null; } }; /** * Gets scope display name */ const getScopeDisplayName = (scope: string) => { switch (scope) { case "local": return "Local (Project-specific)"; case "project": return "Project (Shared via .mcp.json)"; case "user": return "User (All projects)"; default: return scope; } }; /** * Renders a single server item */ const renderServerItem = (server: MCPServer) => { const isExpanded = expandedServers.has(server.name); const isCopied = copiedServer === server.name; return (
{getTransportIcon(server.transport)}

{server.name}

{server.status?.running && ( Running )}
{server.command && !isExpanded && (

{server.command}

)} {server.transport === "sse" && server.url && !isExpanded && (

{server.url}

)} {Object.keys(server.env).length > 0 && !isExpanded && (
Environment variables: {Object.keys(server.env).length}
)}
{/* Expanded Details */} {isExpanded && ( {server.command && (

Command

{server.command}

)} {server.args && server.args.length > 0 && (

Arguments

{server.args.map((arg, idx) => (
[{idx}] {arg}
))}
)} {server.transport === "sse" && server.url && (

URL

{server.url}

)} {Object.keys(server.env).length > 0 && (

Environment Variables

{Object.entries(server.env).map(([key, value]) => (
{key} = {value}
))}
)}
)}
); }; if (loading) { return (
); } return (
{/* Header */}

Configured Servers

{servers.length} server{servers.length !== 1 ? "s" : ""} configured

{/* Server List */} {servers.length === 0 ? (

No MCP servers configured

Add a server to get started with Model Context Protocol

) : (
{Object.entries(serversByScope).map(([scope, scopeServers]) => (
{getScopeIcon(scope)} {getScopeDisplayName(scope)} ({scopeServers.length})
{scopeServers.map(renderServerItem)}
))}
)}
); };