/** * ConnectionsPanel: wizard "Connections" section. * * Renders a tool bundle dropdown (virtual-server-first), A2A agent * multi-select, MCP server (gateway) list, and a preview of * "X tools + Y connected agents". * * Additive: this component is imported optionally by ProjectsView.tsx. */ import React, { useMemo, useState } from 'react'; export function ConnectionsPanel(props) { const { catalog, loading, error, toolSource, setToolSource, selectedA2AAgentIds, setSelectedA2AAgentIds, onRefresh, onSync, syncBusy, } = props; const servers = catalog?.servers || []; const tools = catalog?.tools || []; const agents = catalog?.a2a_agents || []; const gateways = catalog?.gateways || []; const [showToolsPreview, setShowToolsPreview] = useState(false); const enabledTools = useMemo(() => tools.filter((t) => t.enabled !== false), [tools]); const serverToolCount = useMemo(() => { if (!toolSource.startsWith('server:')) return 0; const sid = toolSource.replace('server:', ''); const s = servers.find((x) => x.id === sid); return s?.tool_ids?.length || 0; }, [toolSource, servers]); const toolCount = useMemo(() => { if (toolSource === 'none') return 0; if (toolSource === 'all') return enabledTools.length; if (toolSource.startsWith('server:')) return serverToolCount; return 0; }, [toolSource, enabledTools.length, serverToolCount]); /** Tools visible under the current tool bundle selection */ const visibleTools = useMemo(() => { if (toolSource === 'none') return []; if (toolSource === 'all') return enabledTools; if (toolSource.startsWith('server:')) { const sid = toolSource.replace('server:', ''); const s = servers.find((x) => x.id === sid); if (!s) return []; const ids = new Set(s.tool_ids || []); return tools.filter((t) => ids.has(t.id)); } return []; }, [toolSource, enabledTools, servers, tools]); const toggleAgent = (id) => { setSelectedA2AAgentIds(selectedA2AAgentIds.includes(id) ? selectedA2AAgentIds.filter((x) => x !== id) : [...selectedA2AAgentIds, id]); }; return (
Connections
Choose a tool bundle + optional A2A agents.
{onSync && ()}
{loading ?
Loading catalog...
: null} {error ?
Catalog error: {error}
: null} {catalog && catalog.forge && !catalog.forge.healthy ? (
Forge not healthy: {catalog.forge.error || 'unknown error'}
) : null} {/* Tool bundle */}
Tool bundle
This agent can use{' '} {toolCount} tools +{' '} {selectedA2AAgentIds.length}{' '} connected agents.
{/* Enterprise warning for "All enabled tools" */} {toolSource === 'all' && enabledTools.length > 0 && (
Wide scope: "All enabled tools" grants access to every tool in Forge. For tighter control, select a Virtual Server bundle instead.
)} {/* Tools preview (collapsible) */} {toolCount > 0 && (
{showToolsPreview && (
{visibleTools.map((t) => (
{t.name}
))}
)}
)} {catalog?.last_updated ? (
Fetched from MCP Context Forge · Last updated: {new Date(catalog.last_updated).toLocaleTimeString()}
) : null}
{/* A2A agents */}
A2A agents (optional)
{agents.length === 0 ? (
No A2A agents registered in MCP Context Forge yet. {onSync && ()}
) : (
{agents.map((a) => { const checked = selectedA2AAgentIds.includes(a.id); return (); })}
)}
{/* MCP Servers (Gateways) */}
MCP Servers
{gateways.length === 0 ? (
No MCP servers registered yet. {onSync && ()}
) : (
{gateways.map((g) => (
{g.name}
{g.url ? (
{g.url}
) : null}
{g.transport ? ({g.transport}) : null}
))}
)}
); }