// frontend/components/AdminTabs/mcp/ServerCard.jsx
// One installed MCP server. Collapsed shows summary + actions; expanded
// reveals the per-tool list with risk badges and individual toggles.
import React, { useState } from "react";
import ToolRow from "./ToolRow.jsx";
const RISK_PALETTE = {
low: { bg: "#0f3a26", border: "#1f5a3e", text: "#86efac" },
medium: { bg: "#3a2e0f", border: "#5a4a1f", text: "#fcd34d" },
high: { bg: "#3a0f0f", border: "#5a1f1f", text: "#fca5a5" },
};
export default function ServerCard({
server,
onEnable,
onDisable,
onUninstall,
onTest,
onToggleTool,
onForget,
}) {
const [expanded, setExpanded] = useState(false);
const [testResult, setTestResult] = useState(null);
const [testing, setTesting] = useState(false);
const handleTest = async () => {
setTesting(true);
try {
const result = await onTest();
setTestResult(result);
} finally {
setTesting(false);
}
};
const statusDot = server.enabled ? "#10b981" : "#6b7280";
const statusLabel = server.enabled ? "Enabled" : "Disabled";
// Risk roll-up shown next to the tool count.
const riskCounts = server.tools?.reduce(
(acc, t) => ({ ...acc, [t.risk]: (acc[t.risk] || 0) + 1 }),
{}
) || {};
return (
{server.id}
{statusLabel}
{!server.is_known && (
custom
)}
{server.orphan && (
orphan
)}
{server.source === "forge-sync" && !server.orphan && (
via sync
)}
{server.description || "—"}
{server.endpoint || "—"}
{(server.tags || []).map((t) => (
{t}
))}
{server.enabled ? (
Disable
) : (
Enable
)}
{testing ? "Testing…" : "Test"}
{onForget ? (
Forget
) : (
Uninstall
)}
{server.tool_count} tool{server.tool_count === 1 ? "" : "s"}
{Object.entries(riskCounts).map(([risk, count]) => (
{count} {risk}
))}
setExpanded((v) => !v)}
style={{
padding: "4px 8px",
background: "transparent",
color: "#93c5fd",
border: "none",
cursor: "pointer",
fontSize: "12px",
}}
>
{expanded ? "Hide tools ▴" : "Show tools ▾"}
{testResult && (
{testResult.ok
? "Healthy. Inspector confirmed the server is reachable and advertises its expected tools."
: `Failed: ${testResult.reason || testResult.error || "unknown error"}`}
)}
{expanded && (
{server.tools?.length ? (
server.tools.map((t) => (
onToggleTool(t.name, enabled)}
/>
))
) : (
No tools advertised by this server.
)}
)}
);
}
function Btn({ children, variant = "default", ...props }) {
const palettes = {
default: { bg: "#252634", color: "#e0e0e7", border: "#3a3b4a" },
primary: { bg: "#3B82F6", color: "#fff", border: "#3B82F6" },
ghost: { bg: "transparent", color: "#cdd0d8", border: "#3a3b4a" },
danger: { bg: "transparent", color: "#fca5a5", border: "#5a1f1f" },
};
const p = palettes[variant];
return (
{children}
);
}