import { PlusCircle, Search, SearchX, Trash, Unplug } from 'lucide-react'; import { useState } from 'react'; import type { MCPClient, MCPClientStatus } from '@/api'; import { DeleteDialog } from '@/components/dialog/DeleteDialog.tsx'; import { CreateMCPDialog } from '@/components/dialog/MCPDialog.tsx'; import { PanelEmpty } from '@/components/panel/PanelEmpty'; import { Button } from '@/components/ui/button'; import { InputGroup, InputGroupAddon, InputGroupInput } from '@/components/ui/input-group'; import { Item, ItemActions, ItemContent, ItemDescription, ItemTitle } from '@/components/ui/item'; import { Kbd, KbdGroup } from '@/components/ui/kbd'; import { useTranslation } from '@/i18n/useI18n.ts'; interface McpPanelProps { /** The MCP servers equipped in the workspace. */ mcps: MCPClientStatus[]; /** Whether the MCP list is still loading. */ loading?: boolean; /** * Add one or more MCP servers to the workspace. * * @param mcps - The MCP client configs to add. */ onAdd: (mcps: MCPClient[]) => Promise; /** * Remove an MCP server by name. * * @param name - The MCP server name to remove. */ onRemove: (name: string) => Promise; } /** * Pure content body for the MCP dock panel: a search box, the list of * equipped MCP servers, and an "Add MCP" action. Holds only local UI * state (search text, delete confirmation target); all data arrives * via props so it owns no data fetching. * * Renders without its own header/border — the surrounding `Panel` * chrome (from `PanelDock`) provides those. * * @param mcps - The MCP servers to list. * @param loading - Whether the list is loading. * @param onAdd - Add-MCP callback. * @param onRemove - Remove-MCP callback. * @returns The MCP panel body. */ export function McpPanel({ mcps, loading = false, onAdd, onRemove }: McpPanelProps) { const { t } = useTranslation(); const [search, setSearch] = useState(''); const [deleteOpen, setDeleteOpen] = useState(false); const [deleteTarget, setDeleteTarget] = useState(null); const filtered = search ? mcps.filter((m) => m.name.toLowerCase().includes(search.toLowerCase())) : mcps; return (
{t('panel.mcp.description')} setSearch(e.target.value)} /> {loading ? (

{t('panel.loading')}

) : filtered.length === 0 ? ( ) : (
{filtered.map((mcp) => ( {mcp.name} {mcp.mcp_config.type === 'stdio_mcp' ? 'STDIO' : 'HTTP'} {t('panel.mcp.tools', { count: mcp.tools.length })} ))}
)} { if (deleteTarget) await onRemove(deleteTarget); }} />
); }