## Tool Call (S—487)

An agent’s tool call as a live card: arguments, status and timer, streamed output, human approval and retry.

- URL: https://sundaeui.dev/components/tool-call
- Category: UI components · Art direction: Refined
- Access: Sundae Pro (authenticated install)
- Stack: React 18+, TypeScript, plain CSS; dependencies: none
- Install: `npx shadcn@latest add https://sundaeui.dev/r/tool-call.json`
- Tags: terminal, agent, tool-call, llm, human-in-the-loop, mcp, permission, approval, function-calling, ai

Each tool an agent calls gets a card: a title and the raw tool name, a status pill with a timer (queued, running, done, failed, denied), the arguments as highlighted JSON, and output that streams in like a terminal and follows itself unless the reader scrolls up.
Calls that need a human wait in an amber state that opens itself and focuses Approve: a risk line says what’s at stake, and the person can approve once, always allow that tool, or deny. Failures open with the error and a retry button; successes settle into a one-line result.
Everything is props, so it maps directly onto tool_use / tool_result events from Claude, OpenAI or your own agent loop. ToolCallList stacks a turn’s calls.

### Usage

```tsx
import { ToolCall, ToolCallList } from '@/components/sundae/tool-call'

<ToolCallList>
  {calls.map((c) => (
    <ToolCall
      key={c.id}
      name={c.name}
      title={c.title}
      icon="database"
      args={c.input}
      status={c.status}            // 'awaiting_approval' | 'running' | 'success' | 'error' | 'denied'
      output={c.stdout}
      result={c.summary}
      error={c.error}
      startedAt={c.startedAt}
      endedAt={c.endedAt}
      risk={{ level: 'high', text: 'Writes to production.' }}
      onApprove={({ always }) => approve(c.id, always)}
      onDeny={() => deny(c.id)}
      onRetry={() => retry(c.id)}
    />
  ))}
</ToolCallList>
```

### Props

- `name` (`string`, required): Tool name as the model calls it.
- `title` (`string`): Human-readable title; the tool name shows under it.
- `icon` (`'terminal' | 'database' | 'globe' | 'file' | 'code' | 'mail'`, default `'terminal'`): Icon in the header.
- `args` (`Record<string, unknown>`): Arguments, shown as highlighted JSON.
- `status` (`'awaiting_approval' | 'queued' | 'running' | 'success' | 'error' | 'denied'`, required): Current state.
- `output` (`string`): Streamed output; auto-scrolls while the reader is at the bottom.
- `result / error` (`string`): One-line result on success, or the error message.
- `startedAt / endedAt` (`number`): Timestamps for the live timer and final duration.
- `risk` (`{ level: 'low' | 'medium' | 'high'; text: string }`): What approval means, shown above the buttons.
- `onApprove / onDeny / onRetry` (`functions`): onApprove receives { always } from the “Always allow” checkbox.
- `defaultOpen` (`boolean`, default `false`): Start expanded. Approval and errors always open.

### When to use

- Agent UIs that call tools (MCP, function calling) and should show what ran.
- Human-in-the-loop approval for risky actions.

### When not to use

- Web research specifically: use Web Search.
- A single confirm dialog unrelated to an agent: use Confirm Dialog.

### Accessibility

- The header is a button with aria-expanded; collapsed details are inert.
- When a call needs approval, the card opens and focus moves to Approve (without scrolling the page).
- Errors use role="alert". The output panel is focusable so keyboard users can scroll it.
- Reduced motion removes the progress bar, spinner, caret and entrances.
