{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "mcp-config",
  "type": "registry:component",
  "title": "MCP Config Dialog",
  "description": "Dialog listing MCP connectors and custom servers, with OAuth and bearer auth controls.",
  "registryDependencies": [
    "https://r.assistant-ui.com/badge.json",
    "button",
    "dialog",
    "label",
    "separator",
    "input"
  ],
  "dependencies": [
    "@assistant-ui/react-mcp",
    "@assistant-ui/store",
    "lucide-react"
  ],
  "files": [
    {
      "type": "registry:component",
      "path": "components/assistant-ui/elements/mcp-config.aui.tsx",
      "sourcePath": "packages/ui/src/components/react/assistant-ui/elements/mcp-config.aui.radix.tsx",
      "content": "\"use client\";\n\nimport { type FC, type ReactNode, useState } from \"react\";\nimport { useAuiState } from \"@assistant-ui/store\";\nimport {\n  McpAddFormPrimitive,\n  McpManagerPrimitive,\n  McpServerPrimitive,\n  type MCPConnectionState,\n} from \"@assistant-ui/react-mcp\";\nimport {\n  Loader2Icon,\n  PlugIcon,\n  PlugZapIcon,\n  PlusIcon,\n  ServerIcon,\n  ShieldAlertIcon,\n  Trash2Icon,\n  XIcon,\n} from \"lucide-react\";\n\nimport { Badge } from \"@/components/ui/badge\";\nimport { Button, buttonVariants } from \"@/components/ui/button\";\nimport {\n  Dialog,\n  DialogContent,\n  DialogDescription,\n  DialogHeader,\n  DialogTitle,\n  DialogTrigger,\n} from \"@/components/ui/dialog\";\nimport { Input } from \"@/components/ui/input\";\nimport { Label } from \"@/components/ui/label\";\nimport { Separator } from \"@/components/ui/separator\";\nimport { cn } from \"@/lib/utils\";\n\nexport namespace McpConfigDialog {\n  export type Props = {\n    /** Trigger element. Defaults to a ghost button with a plug icon. */\n    children?: ReactNode;\n  };\n}\n\n/**\n * Drop-in MCP server configuration dialog. Lists app-defined connectors and\n * user-added custom servers, with inline auth controls and an add form.\n *\n * Mount the manager once at the root of your app:\n * ```tsx\n * useAui({ mcp: McpManagerResource({ connectors }) });\n * ```\n * then render `<McpConfigDialog />` anywhere inside the provider.\n */\nexport const McpConfigDialog: FC<McpConfigDialog.Props> = ({ children }) => {\n  return (\n    <Dialog>\n      <DialogTrigger asChild>\n        {children ?? (\n          <Button\n            variant=\"outline\"\n            size=\"sm\"\n            className=\"aui-mcp-config-trigger gap-2\"\n          >\n            <PlugIcon className=\"size-4\" />\n            MCP servers\n          </Button>\n        )}\n      </DialogTrigger>\n      <DialogContent className=\"aui-mcp-config-content sm:max-w-lg\">\n        <DialogHeader>\n          <DialogTitle>MCP servers</DialogTitle>\n          <DialogDescription>\n            Connect to Model Context Protocol servers to expose their tools to\n            this assistant.\n          </DialogDescription>\n        </DialogHeader>\n        <McpManagerPrimitive.Root>\n          <div className=\"flex flex-col gap-4\">\n            <ConnectorsSection />\n            <Separator />\n            <CustomServersSection />\n          </div>\n        </McpManagerPrimitive.Root>\n      </DialogContent>\n    </Dialog>\n  );\n};\nMcpConfigDialog.displayName = \"McpConfigDialog\";\n\nconst ConnectorsSection: FC = () => {\n  return (\n    <section className=\"aui-mcp-connectors flex flex-col gap-2\">\n      <SectionTitle>Connectors</SectionTitle>\n      <div className=\"flex flex-col gap-2\">\n        <McpManagerPrimitive.Connectors>\n          {() => <ServerCard />}\n        </McpManagerPrimitive.Connectors>\n      </div>\n    </section>\n  );\n};\n\nconst CustomServersSection: FC = () => {\n  const [showForm, setShowForm] = useState(false);\n  return (\n    <section className=\"aui-mcp-custom-servers flex flex-col gap-2\">\n      <SectionTitle>Custom servers</SectionTitle>\n      <div className=\"flex flex-col gap-2\">\n        <McpManagerPrimitive.CustomServers>\n          {() => <ServerCard />}\n        </McpManagerPrimitive.CustomServers>\n      </div>\n      {!showForm && (\n        <McpManagerPrimitive.AddCustomTrigger asChild>\n          <Button\n            variant=\"outline\"\n            className=\"aui-mcp-add-trigger h-9 justify-start gap-2 rounded-lg px-3 text-sm\"\n            onClick={() => setShowForm(true)}\n          >\n            <PlusIcon className=\"size-4\" />\n            Add server\n          </Button>\n        </McpManagerPrimitive.AddCustomTrigger>\n      )}\n      {showForm && <AddServerForm onClose={() => setShowForm(false)} />}\n    </section>\n  );\n};\n\nconst SectionTitle: FC<{ children: ReactNode }> = ({ children }) => (\n  <h3 className=\"text-muted-foreground text-xs font-medium tracking-wide uppercase\">\n    {children}\n  </h3>\n);\n\nconst ServerCard: FC = () => {\n  return (\n    <McpServerPrimitive.Root\n      className={cn(\n        \"aui-mcp-server-card flex flex-col gap-2 rounded-lg border p-3\",\n        \"data-[connection-state=error]:border-destructive/40\",\n      )}\n    >\n      <div className=\"flex items-center gap-3\">\n        <ServerAvatar />\n        <div className=\"flex min-w-0 flex-1 flex-col\">\n          <span className=\"truncate text-sm font-medium\">\n            <McpServerPrimitive.Name />\n          </span>\n          <StatusLine />\n        </div>\n        <div className=\"flex items-center gap-1\">\n          <ServerActions />\n          <McpServerPrimitive.RemoveButton asChild>\n            <Button\n              variant=\"ghost\"\n              size=\"icon\"\n              className=\"aui-mcp-server-remove text-muted-foreground hover:text-destructive size-7\"\n            >\n              <Trash2Icon className=\"size-4\" />\n              <span className=\"sr-only\">Remove</span>\n            </Button>\n          </McpServerPrimitive.RemoveButton>\n        </div>\n      </div>\n      <ServerError />\n    </McpServerPrimitive.Root>\n  );\n};\n\nconst ServerAvatar: FC = () => {\n  const icon = useAuiState((s) => s.mcpServer.icon ?? null);\n  const name = useAuiState((s) => s.mcpServer.name);\n  return (\n    <div className=\"bg-muted text-muted-foreground flex size-8 shrink-0 items-center justify-center overflow-hidden rounded-md border\">\n      {icon ? (\n        <img src={icon} alt={name} className=\"size-full object-cover\" />\n      ) : (\n        <ServerIcon className=\"size-4\" />\n      )}\n    </div>\n  );\n};\n\nconst STATUS_VARIANT: Record<\n  MCPConnectionState,\n  \"default\" | \"secondary\" | \"destructive\"\n> = {\n  connected: \"default\",\n  connecting: \"secondary\",\n  authRequired: \"secondary\",\n  authPending: \"secondary\",\n  error: \"destructive\",\n  disconnected: \"secondary\",\n};\n\nconst STATUS_LABEL: Record<MCPConnectionState, string> = {\n  connected: \"Connected\",\n  connecting: \"Connecting…\",\n  authRequired: \"Auth required\",\n  authPending: \"Authorizing…\",\n  error: \"Error\",\n  disconnected: \"Disconnected\",\n};\n\nconst StatusLine: FC = () => {\n  const status = useAuiState((s) => s.mcpServer.connectionState);\n  const variant = STATUS_VARIANT[status];\n  const label = STATUS_LABEL[status];\n  return (\n    <div className=\"text-muted-foreground flex items-center gap-1.5 text-xs\">\n      <Badge variant={variant}>\n        {status === \"connecting\" && (\n          <Loader2Icon className=\"size-3 animate-spin\" />\n        )}\n        {label}\n      </Badge>\n    </div>\n  );\n};\n\nconst ServerError: FC = () => {\n  const message = useAuiState((s) => s.mcpServer.lastError?.message ?? null);\n  if (!message) return null;\n  return (\n    <div className=\"border-destructive/40 bg-destructive/5 text-destructive flex items-start gap-2 rounded-md border px-2 py-1.5 text-xs\">\n      <ShieldAlertIcon className=\"mt-0.5 size-3.5 shrink-0\" />\n      <span className=\"break-words\">{message}</span>\n    </div>\n  );\n};\n\nconst ServerActions: FC = () => (\n  <div className=\"flex flex-wrap gap-2\">\n    <McpServerPrimitive.ConnectButton asChild>\n      <Button\n        size=\"sm\"\n        variant=\"default\"\n        className=\"aui-mcp-server-connect h-8 gap-2 text-xs\"\n      >\n        <PlugZapIcon className=\"size-3.5\" />\n        Connect\n      </Button>\n    </McpServerPrimitive.ConnectButton>\n    <McpServerPrimitive.OAuthLink\n      className={cn(\n        buttonVariants({ variant: \"default\", size: \"sm\" }),\n        \"aui-mcp-server-authorize h-8 gap-2 text-xs\",\n      )}\n    >\n      Authorize\n    </McpServerPrimitive.OAuthLink>\n    <McpServerPrimitive.DisconnectButton asChild>\n      <Button\n        size=\"sm\"\n        variant=\"outline\"\n        className=\"aui-mcp-server-disconnect h-8 text-xs\"\n      >\n        Disconnect\n      </Button>\n    </McpServerPrimitive.DisconnectButton>\n  </div>\n);\n\nconst AddServerForm: FC<{ onClose: () => void }> = ({ onClose }) => {\n  return (\n    <McpAddFormPrimitive.Root onSubmitted={onClose} onCancel={onClose}>\n      <div className=\"aui-mcp-add-form flex flex-col gap-3 rounded-lg border p-3\">\n        <div className=\"flex items-center justify-between\">\n          <h4 className=\"text-sm font-medium\">New server</h4>\n          <McpAddFormPrimitive.Cancel asChild>\n            <Button\n              type=\"button\"\n              variant=\"ghost\"\n              size=\"icon\"\n              className=\"text-muted-foreground size-7\"\n            >\n              <XIcon className=\"size-4\" />\n              <span className=\"sr-only\">Close</span>\n            </Button>\n          </McpAddFormPrimitive.Cancel>\n        </div>\n        <FormRow label=\"Name\">\n          <McpAddFormPrimitive.NameField asChild>\n            <Input placeholder=\"My MCP server\" />\n          </McpAddFormPrimitive.NameField>\n        </FormRow>\n        <FormRow label=\"URL\">\n          <McpAddFormPrimitive.UrlField asChild>\n            <Input placeholder=\"https://example.com/mcp\" />\n          </McpAddFormPrimitive.UrlField>\n        </FormRow>\n        <FormRow label=\"Auth\">\n          <McpAddFormPrimitive.AuthSelect className=\"aui-mcp-auth-select bg-background h-9 w-full rounded-md border px-2 text-sm\" />\n          <div\n            className={cn(\n              // Style the default `<input>` inside AuthFields without\n              // needing to thread useAddForm out of the primitive. Mirrors\n              // the shadcn <Input> look.\n              \"[&_input]:border-input empty:hidden [&_input]:flex [&_input]:h-9 [&_input]:w-full [&_input]:rounded-md [&_input]:border [&_input]:bg-transparent [&_input]:px-3 [&_input]:py-1 [&_input]:text-sm [&_input]:transition-colors [&_input]:outline-none\",\n              \"[&_input:focus-visible]:border-ring [&_input:focus-visible]:ring-ring/50 [&_input:focus-visible]:ring-[3px]\",\n              \"[&_input::placeholder]:text-muted-foreground\",\n            )}\n          >\n            <McpAddFormPrimitive.AuthFields />\n          </div>\n        </FormRow>\n        <McpAddFormPrimitive.Error className=\"text-destructive text-xs\" />\n        <div className=\"flex justify-end gap-2\">\n          <McpAddFormPrimitive.Cancel asChild>\n            <Button type=\"button\" variant=\"ghost\" size=\"sm\">\n              Cancel\n            </Button>\n          </McpAddFormPrimitive.Cancel>\n          <McpAddFormPrimitive.Submit asChild>\n            <Button type=\"submit\" size=\"sm\">\n              Add server\n            </Button>\n          </McpAddFormPrimitive.Submit>\n        </div>\n      </div>\n    </McpAddFormPrimitive.Root>\n  );\n};\n\nconst FormRow: FC<{ label: string; children: ReactNode }> = ({\n  label,\n  children,\n}) => (\n  <div className=\"flex flex-col gap-1.5\">\n    <Label className=\"text-xs\">{label}</Label>\n    <div className=\"flex flex-col gap-2\">{children}</div>\n  </div>\n);\n"
    }
  ]
}