{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "elements-mcp-server-panel",
  "type": "registry:component",
  "title": "Server panel",
  "description": "Which servers are connected, what each one brought, and which is still waiting on you.",
  "registryDependencies": [
    "https://r.assistant-ui.com/elements-surfaces.json"
  ],
  "dependencies": [
    "lucide-react"
  ],
  "files": [
    {
      "type": "registry:component",
      "path": "components/assistant-ui/elements/mcp-server-panel.tsx",
      "sourcePath": "packages/ui/src/components/react/assistant-ui/elements/mcp-server-panel.tsx",
      "content": "\"use client\";\n\nimport type { ComponentProps } from \"react\";\nimport { ChevronRightIcon, PlugIcon } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\nimport { field, mono, paper } from \"./surfaces\";\n\nexport type McpServerStatus =\n  | \"connected\"\n  | \"connecting\"\n  | \"needs-auth\"\n  | \"failed\";\n\nexport interface McpServer {\n  id: string;\n  name: string;\n  transport: string;\n  status: McpServerStatus;\n  tools: readonly string[];\n}\n\nconst DOT: Record<McpServerStatus, string> = {\n  connected: \"bg-emerald-500\",\n  connecting: \"bg-foreground/30 animate-pulse\",\n  \"needs-auth\": \"bg-amber-500\",\n  failed: \"bg-red-500\",\n};\n\nconst LABEL: Record<McpServerStatus, string> = {\n  connected: \"connected\",\n  connecting: \"connecting\",\n  \"needs-auth\": \"needs auth\",\n  failed: \"failed\",\n};\n\nexport function McpServerPanel({\n  servers,\n  expandedId,\n  onToggle,\n  onAuthorize,\n  className,\n  ...props\n}: Omit<\n  ComponentProps<\"div\">,\n  \"children\" | \"servers\" | \"expandedId\" | \"onToggle\" | \"onAuthorize\"\n> & {\n  servers: readonly McpServer[];\n  expandedId?: string;\n  onToggle?: (id: string) => void;\n  onAuthorize?: (id: string) => void;\n}) {\n  const connected = servers.filter(\n    (server) => server.status === \"connected\",\n  ).length;\n\n  return (\n    <div\n      data-slot=\"mcp-server-panel\"\n      className={cn(\n        paper,\n        \"flex w-full max-w-sm flex-col gap-1 rounded-2xl p-3\",\n        className,\n      )}\n\n      {...props}\n    >\n      <div className=\"flex items-baseline justify-between px-1 pb-1\">\n        <span className=\"text-[13.5px] font-medium\">Servers</span>\n        <span className={cn(mono, \"text-foreground/35 tabular-nums\")}>\n          {connected} of {servers.length} connected\n        </span>\n      </div>\n\n      {servers.map((server) => {\n        const expanded = expandedId === server.id;\n        return (\n          <div key={server.id} className=\"flex flex-col\">\n            <button\n              type=\"button\"\n              aria-expanded={expanded}\n              onClick={() => onToggle?.(server.id)}\n              className=\"hover:bg-foreground/[0.04] flex items-center gap-2.5 rounded-xl px-1.5 py-2 text-start transition-colors\"\n            >\n              <ChevronRightIcon\n                className={cn(\n                  \"text-foreground/25 size-3 shrink-0 transition-transform duration-200 motion-reduce:transition-none\",\n                  expanded && \"rotate-90\",\n                )}\n              />\n              <PlugIcon className=\"text-foreground/35 size-3.5 shrink-0\" />\n              <span className=\"min-w-0 flex-1 truncate text-[13.5px]\">\n                {server.name}\n              </span>\n              <span className={cn(mono, \"text-foreground/30 shrink-0\")}>\n                {server.tools.length} tools\n              </span>\n              <span\n                aria-hidden\n                className={cn(\n                  \"size-1.5 shrink-0 rounded-full\",\n                  DOT[server.status],\n                )}\n              />\n              <span className=\"sr-only\">{LABEL[server.status]}</span>\n            </button>\n\n            {expanded && (\n              <div className=\"fade-in slide-in-from-top-1 animate-in flex flex-col gap-1.5 px-1.5 ps-8 pb-2 duration-200\">\n                <div className=\"flex items-center gap-2\">\n                  <span className={cn(mono, \"text-foreground/30\")}>\n                    {server.transport}\n                  </span>\n                  <span className={cn(mono, \"text-foreground/30\")}>\n                    · {LABEL[server.status]}\n                  </span>\n                  {server.status === \"needs-auth\" && onAuthorize && (\n                    <button\n                      type=\"button\"\n                      onClick={() => onAuthorize?.(server.id)}\n                      className=\"ms-auto rounded-full bg-amber-500/15 px-2 py-0.5 text-[11px] font-medium text-amber-700 transition-[background-color,scale] duration-150 hover:bg-amber-500/25 active:scale-[0.96] dark:text-amber-300\"\n                    >\n                      Authorize\n                    </button>\n                  )}\n                </div>\n                <div className=\"flex flex-wrap gap-1\">\n                  {server.tools.map((tool) => (\n                    <span\n                      key={tool}\n                      className={cn(\n                        field,\n                        mono,\n                        \"text-foreground/55 rounded-md px-1.5 py-0.5\",\n                      )}\n                    >\n                      {tool}\n                    </span>\n                  ))}\n                </div>\n              </div>\n            )}\n          </div>\n        );\n      })}\n    </div>\n  );\n}\n"
    }
  ]
}