{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "tool-inspector",
  "title": "AI Tool Inspector",
  "description": "Debug panel for inspecting AI tool executions with timing, status indicators, and expandable JSON input/output views. Perfect for debugging MCP tools, function calls, and agent actions.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/blocks/ai/ai-tool-inspector/components/elements/ai-tool-inspector.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport {\n  Check,\n  CheckCheck,\n  ChevronDown,\n  Clock,\n  Copy,\n  Loader2,\n  Timer,\n  Wrench,\n  X,\n} from \"lucide-react\";\n\nimport { cn } from \"@/lib/utils\";\n\ntype ToolExecutionStatus = \"pending\" | \"running\" | \"success\" | \"error\";\n\ninterface ToolExecution {\n  id: string;\n  name: string;\n  description?: string;\n  input: unknown;\n  output?: unknown;\n  status: ToolExecutionStatus;\n  startTime?: Date;\n  endTime?: Date;\n  error?: string;\n}\n\ninterface AiToolInspectorContextValue {\n  tools: ToolExecution[];\n  expandedIds: Set<string>;\n  toggleExpanded: (id: string) => void;\n}\n\nconst AiToolInspectorContext =\n  React.createContext<AiToolInspectorContextValue | null>(null);\n\nfunction useToolInspectorContext() {\n  const context = React.useContext(AiToolInspectorContext);\n  if (!context) {\n    throw new Error(\n      \"AiToolInspector components must be used within <AiToolInspector>\",\n    );\n  }\n  return context;\n}\n\ninterface AiToolInspectorProps {\n  tools: ToolExecution[];\n  defaultExpandedIds?: string[];\n  className?: string;\n  children?: React.ReactNode;\n}\n\nfunction AiToolInspector({\n  tools,\n  defaultExpandedIds = [],\n  className,\n  children,\n}: AiToolInspectorProps) {\n  const [expandedIds, setExpandedIds] = React.useState<Set<string>>(\n    () => new Set(defaultExpandedIds),\n  );\n\n  const toggleExpanded = React.useCallback((id: string) => {\n    setExpandedIds((prev) => {\n      const next = new Set(prev);\n      if (next.has(id)) {\n        next.delete(id);\n      } else {\n        next.add(id);\n      }\n      return next;\n    });\n  }, []);\n\n  const contextValue = React.useMemo(\n    () => ({ tools, expandedIds, toggleExpanded }),\n    [tools, expandedIds, toggleExpanded],\n  );\n\n  return (\n    <AiToolInspectorContext.Provider value={contextValue}>\n      <div\n        data-slot=\"ai-tool-inspector\"\n        className={cn(\n          \"rounded-lg border border-border bg-card text-card-foreground\",\n          className,\n        )}\n      >\n        {children}\n      </div>\n    </AiToolInspectorContext.Provider>\n  );\n}\n\ninterface AiToolInspectorHeaderProps {\n  title?: string;\n  children?: React.ReactNode;\n  className?: string;\n}\n\nfunction AiToolInspectorHeader({\n  title = \"Tool Executions\",\n  children,\n  className,\n}: AiToolInspectorHeaderProps) {\n  const { tools } = useToolInspectorContext();\n\n  const stats = React.useMemo(() => {\n    const running = tools.filter((t) => t.status === \"running\").length;\n    const success = tools.filter((t) => t.status === \"success\").length;\n    const error = tools.filter((t) => t.status === \"error\").length;\n    return { running, success, error, total: tools.length };\n  }, [tools]);\n\n  return (\n    <div\n      data-slot=\"ai-tool-inspector-header\"\n      className={cn(\n        \"flex items-center gap-3 px-4 py-3 border-b border-border\",\n        className,\n      )}\n    >\n      <div className=\"flex size-8 shrink-0 items-center justify-center rounded-md bg-violet-100 dark:bg-violet-950\">\n        <Wrench className=\"size-4 text-violet-600 dark:text-violet-400\" />\n      </div>\n      <div className=\"flex flex-1 items-center gap-2\">\n        <span className=\"font-medium text-sm\">{title}</span>\n        <div className=\"flex items-center gap-1.5\">\n          {stats.running > 0 && (\n            <span className=\"inline-flex items-center gap-1 rounded-full bg-blue-100 dark:bg-blue-950 px-2 py-0.5 text-xs font-medium text-blue-700 dark:text-blue-300\">\n              <Loader2 className=\"size-3 animate-spin\" />\n              {stats.running}\n            </span>\n          )}\n          {stats.success > 0 && (\n            <span className=\"inline-flex items-center gap-1 rounded-full bg-green-100 dark:bg-green-950 px-2 py-0.5 text-xs font-medium text-green-700 dark:text-green-300\">\n              <Check className=\"size-3\" />\n              {stats.success}\n            </span>\n          )}\n          {stats.error > 0 && (\n            <span className=\"inline-flex items-center gap-1 rounded-full bg-red-100 dark:bg-red-950 px-2 py-0.5 text-xs font-medium text-red-700 dark:text-red-300\">\n              <X className=\"size-3\" />\n              {stats.error}\n            </span>\n          )}\n        </div>\n      </div>\n      {children}\n    </div>\n  );\n}\n\ninterface AiToolInspectorListProps {\n  children?: React.ReactNode;\n  className?: string;\n}\n\nfunction AiToolInspectorList({\n  children,\n  className,\n}: AiToolInspectorListProps) {\n  const { tools } = useToolInspectorContext();\n\n  return (\n    <div\n      data-slot=\"ai-tool-inspector-list\"\n      className={cn(\"divide-y divide-border\", className)}\n    >\n      {children ||\n        tools.map((tool) => <AiToolInspectorItem key={tool.id} tool={tool} />)}\n    </div>\n  );\n}\n\ninterface AiToolInspectorItemProps {\n  tool: ToolExecution;\n  className?: string;\n}\n\nfunction AiToolInspectorItem({ tool, className }: AiToolInspectorItemProps) {\n  const { expandedIds, toggleExpanded } = useToolInspectorContext();\n  const isExpanded = expandedIds.has(tool.id);\n\n  const statusConfig = React.useMemo(() => {\n    const configs: Record<\n      ToolExecutionStatus,\n      { icon: React.ReactNode; label: string; className: string }\n    > = {\n      pending: {\n        icon: <Clock className=\"size-3.5\" />,\n        label: \"Pending\",\n        className: \"bg-muted text-muted-foreground\",\n      },\n      running: {\n        icon: <Loader2 className=\"size-3.5 animate-spin\" />,\n        label: \"Running\",\n        className:\n          \"bg-blue-100 text-blue-700 dark:bg-blue-950 dark:text-blue-300\",\n      },\n      success: {\n        icon: <Check className=\"size-3.5\" />,\n        label: \"Success\",\n        className:\n          \"bg-green-100 text-green-700 dark:bg-green-950 dark:text-green-300\",\n      },\n      error: {\n        icon: <X className=\"size-3.5\" />,\n        label: \"Error\",\n        className: \"bg-red-100 text-red-700 dark:bg-red-950 dark:text-red-300\",\n      },\n    };\n    return configs[tool.status];\n  }, [tool.status]);\n\n  const executionTime = React.useMemo(() => {\n    if (!tool.startTime) return null;\n    const end = tool.endTime || new Date();\n    const ms = end.getTime() - tool.startTime.getTime();\n    if (ms < 1000) return `${ms}ms`;\n    return `${(ms / 1000).toFixed(2)}s`;\n  }, [tool.startTime, tool.endTime]);\n\n  return (\n    <div\n      data-slot=\"ai-tool-inspector-item\"\n      data-status={tool.status}\n      className={cn(\"\", className)}\n    >\n      <button\n        type=\"button\"\n        onClick={() => toggleExpanded(tool.id)}\n        className=\"flex w-full items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-muted/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset\"\n      >\n        <div className=\"flex flex-1 items-center gap-2 min-w-0\">\n          <span className=\"font-mono text-sm truncate\">{tool.name}</span>\n          <span\n            className={cn(\n              \"inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium shrink-0\",\n              statusConfig.className,\n            )}\n          >\n            {statusConfig.icon}\n            {statusConfig.label}\n          </span>\n        </div>\n        {executionTime && (\n          <span className=\"flex items-center gap-1 text-xs text-muted-foreground shrink-0\">\n            <Timer className=\"size-3\" />\n            {executionTime}\n          </span>\n        )}\n        <ChevronDown\n          className={cn(\n            \"size-4 shrink-0 text-muted-foreground transition-transform duration-200\",\n            isExpanded && \"rotate-180\",\n          )}\n        />\n      </button>\n      {isExpanded && (\n        <div className=\"px-4 pb-4 space-y-4\">\n          {tool.description && (\n            <p className=\"text-sm text-muted-foreground\">{tool.description}</p>\n          )}\n          <AiToolInspectorJson label=\"Input\" data={tool.input} />\n          {tool.output !== undefined && (\n            <AiToolInspectorJson label=\"Output\" data={tool.output} />\n          )}\n          {tool.error && (\n            <div className=\"space-y-1.5\">\n              <span className=\"text-xs font-medium text-red-600 dark:text-red-400 uppercase tracking-wider\">\n                Error\n              </span>\n              <div className=\"rounded-md bg-red-50 dark:bg-red-950/30 border border-red-200 dark:border-red-900 p-3 text-sm text-red-700 dark:text-red-300 font-mono\">\n                {tool.error}\n              </div>\n            </div>\n          )}\n        </div>\n      )}\n    </div>\n  );\n}\n\ninterface AiToolInspectorJsonProps {\n  label: string;\n  data: unknown;\n  className?: string;\n}\n\nfunction AiToolInspectorJson({\n  label,\n  data,\n  className,\n}: AiToolInspectorJsonProps) {\n  const [copied, setCopied] = React.useState(false);\n\n  const formattedJson = React.useMemo(\n    () => JSON.stringify(data, null, 2),\n    [data],\n  );\n\n  const handleCopy = React.useCallback(async () => {\n    await navigator.clipboard.writeText(formattedJson);\n    setCopied(true);\n    setTimeout(() => setCopied(false), 2000);\n  }, [formattedJson]);\n\n  return (\n    <div\n      data-slot=\"ai-tool-inspector-json\"\n      className={cn(\"space-y-1.5\", className)}\n    >\n      <div className=\"flex items-center justify-between\">\n        <span className=\"text-xs font-medium text-muted-foreground uppercase tracking-wider\">\n          {label}\n        </span>\n        <button\n          type=\"button\"\n          onClick={handleCopy}\n          className=\"inline-flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground transition-colors\"\n        >\n          {copied ? (\n            <>\n              <CheckCheck className=\"size-3\" />\n              Copied\n            </>\n          ) : (\n            <>\n              <Copy className=\"size-3\" />\n              Copy\n            </>\n          )}\n        </button>\n      </div>\n      <pre className=\"rounded-md bg-muted/50 p-3 overflow-x-auto text-xs font-mono text-foreground\">\n        {formattedJson}\n      </pre>\n    </div>\n  );\n}\n\nexport {\n  AiToolInspector,\n  AiToolInspectorHeader,\n  AiToolInspectorList,\n  AiToolInspectorItem,\n  AiToolInspectorJson,\n};\nexport type { AiToolInspectorProps, ToolExecution, ToolExecutionStatus };\n",
      "type": "registry:component"
    }
  ],
  "docs": "A compound component for debugging AI tool calls. Features include tool name and description display, input/output JSON with copy functionality, execution time tracking, and status indicators (pending, running, success, error). Use AiToolInspector as the root with AiToolInspectorHeader, AiToolInspectorList, and AiToolInspectorItem sub-components.",
  "categories": [
    "ai"
  ],
  "type": "registry:ui"
}