import React, { useState, useEffect } from 'react'; import { format } from 'date-fns'; import { FiServer, FiDatabase, FiGithub, FiHardDrive, FiSettings, FiTrash2, FiEdit, FiRefreshCw, FiWifi, FiWifiOff, FiActivity, FiTool, FiZap, } from 'react-icons/fi'; import { Card } from '../ui/Card'; import { Button } from '../ui/Button'; import { StatusIndicator } from '../ui/StatusIndicator'; import Typography from '../ui/Typography'; import { Switch } from '../ui/switch'; import { cn } from '../../lib/utils'; import { MCPServerCardProps, MCPServerType } from '../../types/mcp'; import { useMCPStore } from '../../stores/mcpStore'; import { createMCPError } from '../../utils/mcpErrors'; import { MCPToolsModal } from './MCPToolsModal'; const serverTypeIcons: Record = { filesystem: , github: , postgres: , sqlite: , custom: , }; const statusColors = { connected: 'online', disconnected: 'offline', connecting: 'connecting', handshaking: 'connecting', ready: 'online', error: 'error', } as const; const statusIcons = { connected: , disconnected: , connecting: , handshaking: , ready: , error: , } as const; /** * Individual MCP server card component with controls * @param props - Server card props including server config and handlers * @returns Server card with status, controls, and configuration display */ export const MCPServerCard: React.FC = ({ server, onToggle, onEdit, onDelete, onTest, }) => { const [isRefreshing, setIsRefreshing] = useState(false); const [isToolsModalOpen, setIsToolsModalOpen] = useState(false); const handleTest = async () => { setIsRefreshing(true); try { await onTest(server.id); } catch (error) { } finally { setIsRefreshing(false); } }; const formatLastConnected = (date: Date | undefined) => { if (!date) return 'Never'; try { return format(new Date(date), 'PPpp'); } catch { return new Date(date).toLocaleString(); } }; const getConfigSummary = () => { switch (server.type) { case 'filesystem': if (server.config.type === 'filesystem') { return server.config.rootPath || 'No path configured'; } return 'No path configured'; case 'github': if (server.config.type === 'github') { return server.config.owner && server.config.repo ? `${server.config.owner}/${server.config.repo}` : 'No repository configured'; } return 'No repository configured'; case 'postgres': if (server.config.type === 'postgres') { return server.config.host && server.config.database ? `${server.config.host}/${server.config.database}` : 'No database configured'; } return 'No database configured'; case 'sqlite': if (server.config.type === 'sqlite') { return server.config.path || 'No database path configured'; } return 'No database path configured'; case 'custom': if (server.config.type === 'custom') { const command = server.config.command || 'No command configured'; const args = server.config.args; if (args && args.length > 0) { return `${command} ${args.join(' ')}`; } return command; } return 'No command configured'; default: return 'Configuration not set'; } }; const isConnected = server.status === 'connected' || server.status === 'ready'; return (
{server.name}
{server.status === 'handshaking' ? 'Handshaking' : server.status === 'ready' ? 'Ready' : server.status.charAt(0).toUpperCase() + server.status.slice(1)}
• {server.type}
{(server.status === 'connected' || server.status === 'ready') && ( )}
{server.description && (
{server.description}
)}
Configuration {getConfigSummary()}
{server.tools && server.tools.length > 0 ? (
{server.tools.length} Available Tools
{server.tools.slice(0, 4).map((tool, index) => ( {tool.name} ))} {server.tools.length > 4 && ( +{server.tools.length - 4} more )}
) : server.status === 'connected' || server.status === 'ready' ? (
Tools loading...
) : null} {server.errorMessage && (
{server.errorMessage}
)} {server.status === 'connecting' && (
Establishing connection...
)} {server.status === 'handshaking' && (
Performing handshake...
)}
{formatLastConnected(server.lastConnected)}
onToggle(server.id, checked)} /> {server.enabled ? 'Enabled' : 'Disabled'}
setIsToolsModalOpen(false)} serverName={server.name} tools={server.tools || []} />
); };