# Tool call

> A tool call row for agent transcripts: the tool's mark and the call in plain words, a live state while it runs, and the input and output a click away, with approval and retry when needed.

- Type: Component (special)
- Access: Arc Pro
- Page: https://uiarc.dev/components/tool-call
- Markdown: https://uiarc.dev/components/tool-call/markdown
- Source file: `registry/components/tool-call/tool-call.tsx`
- Dependencies: motion, lucide-react
- Keywords: special, motion, ai, new, tool call, tool use, function call, agent transcript, ai chat, used tools, approval, human in the loop, retry, tool result, mcp, agent, llm tools, inline approval

## When to use

- Showing what tools an AI agent used inside a chat or agent transcript, with the arguments and results one click away.
- Human in the loop actions where a call must be allowed or denied inline before it runs, such as posting a message or writing to a database.
- Making failures recoverable in place: a timed out query with its reason and a Retry button.
- Debug and audit views where developers need to copy exact tool inputs and outputs.

## When not to use

- Use agent-run for a full multi step run with a plan, a timeline and live logs rather than inline rows in a message.
- Use agent-approvals for a queue of pending approvals across many agents and runs.
- Use research-run when the work is mostly web research with sources and a streamed report.
- Use mcp-servers to manage which tools an agent can reach, not to show individual calls.

## Installation

Tool call is part of Arc Pro. The live preview is public; the source and install command need Pro.

### CLI with a Pro token

1. Create a token in your account and set it in the environment (or `.env.local`). Never commit it.

```bash
export ARC_PRO_TOKEN=arc_pro_...
```

2. Add the Pro registry to `components.json`:

```json
{
  "registries": {
    "@uiarc": "https://uiarc.dev/r/{name}.json",
    "@uiarc-pro": {
      "url": "https://uiarc.dev/r/pro/{name}.json",
      "headers": {
        "Authorization": "Bearer ${ARC_PRO_TOKEN}"
      }
    }
  }
}
```

3. Install:

```bash
npx shadcn@latest add @uiarc-pro/tool-call
```

### Manual

Signed-in Pro members can copy the source from the Manual tab on the docs page.

- Plans: https://uiarc.dev/pricing
- Create a Pro token: https://uiarc.dev/account#pro-access
- Setup guide: https://uiarc.dev/docs/ai#pro-access

## Usage

```tsx
import { ToolCall, ToolCallGroup } from "@/components/arc/tool-call/tool-call";

// One call on its own
<ToolCall
  tool="github"
  title="Searched issues in arc-ui/web"
  titles={{ running: "Searching issues in arc-ui/web" }}
  status="success"
  input={{ repo: "arc-ui/web", query: "is:open label:release-blocker" }}
  output={{ type: "list", items: [{ lead: "#1284", title: "Checkout button stays disabled after a coupon is removed", tag: "checkout" }] }}
  durationMs={1180}
/>

// Every call from one assistant turn, wired to your agent loop
export function ToolCalls({ calls, working, agent }) {
  return (
    <ToolCallGroup
      calls={calls}
      active={working}
      onApprove={call => agent.approve(call.id)}
      onDeny={call => agent.deny(call.id)}
      onRetry={call => agent.retry(call.id)}
    />
  );
}
```

## API reference

### ToolCall

One inline tool call for an agent or chat transcript: the tool's mark, a verb phrase, quiet meta and a status icon. Clicking the row springs it open into Input (the arguments) and Output (a readable result), each with a copy button. Awaiting approval it shows inline Allow and Deny; on an error it shows the reason and Retry. With no props it renders a sample GitHub search.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` | – | Stable id for the call. A group uses it as the React key, so a call that changes status keeps its row and its open state. |
| `tool` | `"github" \| "linear" \| "postgres" \| "slack" \| "gmail" \| "web" \| { name: string; mark: ReactNode }` | – | Which tool ran. The built in ids draw the real brand mark (web draws a globe for web search); a custom tool passes its own name and a 16px mark. |
| `title` | `string` | – | The settled verb phrase, such as "Searched issues in arc-ui/web". Keep it to one line; it truncates. |
| `titles` | `Partial<Record<ToolCallStatus, string>>` | – | Phrasing per status, such as { running: "Searching issues in arc-ui/web", approval: "Post a summary to #release" }. Falls back to title. |
| `status` | `"running" \| "success" \| "error" \| "approval" \| "denied"` | – | Running shimmers the title with a spinner; success shows a check, the summary and the duration; error shows the reason and Retry; approval shows Allow and Deny on an accent outline; denied shows that the call never ran. |
| `input` | `Record<string, ToolCallValue>` | – | The arguments the model passed, shown as a mono key and value list with a copy button. |
| `output` | `{ type: "list"; items } \| { type: "table"; columns; rows } \| { type: "json"; value } \| { type: "text"; text }` | – | What came back. Lists show a number, a title and a tag per row; tables and JSON (with a syntax tint) fold after five rows or ten lines behind "Show 18 more lines". Copy gives JSON, lines or tab separated rows. |
| `summary` | `string` | – | Quiet meta before the duration, such as "4 results". Derived from the output when left out (results, rows, fields). |
| `message` | `string` | – | One line under the title: the error reason, or what allowing the call will do. |
| `durationMs` | `number` | – | How long the call took, shown as 640ms or 1.2s with tabular numerals. The group header shows the total. |
| `defaultOpen` | `boolean` | `false` | Opens the Input and Output panel on first render. |
| `open` | `boolean` | – | Controls the panel. Pair with onOpenChange. |
| `onOpenChange` | `(open: boolean) => void` | – | Called when the row is opened or closed. |
| `onApprove` | `() => void` | – | Allow on a call awaiting approval. Update status yourself (usually to running, then success). Without it the preview simulates the call and says so to assistive technology. |
| `onDeny` | `() => void` | – | Deny on a call awaiting approval. Without it the call is marked denied locally. |
| `onRetry` | `() => void` | – | Retry on a failed call. Without it the preview simulates a successful retry. |
| `renderOutput` | `(call: ToolCallData) => ReactNode \| undefined` | – | Draws a custom result for a successful call inside the Output well, such as a chart or a file preview. Return undefined to fall back to the built in view. |
| `className` | `string` | – | Class on the call's outer element. |

### ToolCallGroup

Several calls from one assistant turn under a quiet header, "Used 4 tools", with an overlapping stack of the tools' marks and the total duration. Opening it staggers the rows in; while the agent works the header reads "Using 3 tools" with a shimmer. A call that needs a decision opens a closed group, and the group folds itself away once every call has finished unless the person has opened or closed anything.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `calls` | `ToolCallData[]` | `four settled sample calls` | The calls in order, each with id, tool, title, status and optional titles, input, output, summary, message and durationMs. Append to the array as the agent starts new calls; each new row grows in. |
| `active` | `boolean` | `any call running` | The agent is still working in this turn, so the header keeps saying "Using" between calls instead of flickering to "Used". |
| `defaultOpen` | `boolean` | `open while any call is running, waiting or failed` | Whether the list is open on first render. A finished transcript starts folded. |
| `open` | `boolean` | – | Controls the list. Pair with onOpenChange. Calls that need a decision only open the group on their own when it is uncontrolled. |
| `onOpenChange` | `(open: boolean) => void` | – | Called when the header is toggled or the group folds itself. |
| `autoCollapse` | `boolean` | `true` | Folds the list 450ms after every call has finished, so the answer reads right under "Used 4 tools". Skipped once the person has opened or closed the group or any row. |
| `onApprove` | `(call: ToolCallData) => void` | – | Allow on a call awaiting approval. |
| `onDeny` | `(call: ToolCallData) => void` | – | Deny on a call awaiting approval. |
| `onRetry` | `(call: ToolCallData) => void` | – | Retry on a failed call. |
| `renderOutput` | `(call: ToolCallData) => ReactNode \| undefined` | – | Custom result renderer applied to every successful call in the group. |
| `className` | `string` | – | Class on the group's outer element. |

### ToolCallData

The shape of one call, shared by ToolCall props and ToolCallGroup calls.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `id` (required) | `string` | – | Stable id for the call. A group uses it as the React key, so a call that changes status keeps its row and its open state. |
| `tool` (required) | `"github" \| "linear" \| "postgres" \| "slack" \| "gmail" \| "web" \| { name: string; mark: ReactNode }` | – | Which tool ran. The built in ids draw the real brand mark (web draws a globe for web search); a custom tool passes its own name and a 16px mark. |
| `title` (required) | `string` | – | The settled verb phrase, such as "Searched issues in arc-ui/web". Keep it to one line; it truncates. |
| `titles` | `Partial<Record<ToolCallStatus, string>>` | – | Phrasing per status, such as { running: "Searching issues in arc-ui/web", approval: "Post a summary to #release" }. Falls back to title. |
| `status` (required) | `"running" \| "success" \| "error" \| "approval" \| "denied"` | – | Running shimmers the title with a spinner; success shows a check, the summary and the duration; error shows the reason and Retry; approval shows Allow and Deny on an accent outline; denied shows that the call never ran. |
| `input` | `Record<string, ToolCallValue>` | – | The arguments the model passed, shown as a mono key and value list with a copy button. |
| `output` | `{ type: "list"; items } \| { type: "table"; columns; rows } \| { type: "json"; value } \| { type: "text"; text }` | – | What came back. Lists show a number, a title and a tag per row; tables and JSON (with a syntax tint) fold after five rows or ten lines behind "Show 18 more lines". Copy gives JSON, lines or tab separated rows. |
| `summary` | `string` | – | Quiet meta before the duration, such as "4 results". Derived from the output when left out (results, rows, fields). |
| `message` | `string` | – | One line under the title: the error reason, or what allowing the call will do. |
| `durationMs` | `number` | – | How long the call took, shown as 640ms or 1.2s with tabular numerals. The group header shows the total. |

## Keyboard interactions

| Keys | Action |
| --- | --- |
| Tab | Moves through the group header, each call row, a row's Allow, Deny or Retry buttons, and inside an open row its copy buttons and the show more control. |
| Enter, Space | On the group header, opens or closes the list. On a call row, opens or closes Input and Output. On Allow, Deny, Retry, copy and show more, activates them. |

## Accessibility

- The group header and every call row are real buttons with aria-expanded and aria-controls; a row's name reads the tool, the verb phrase and the status, such as "GitHub: Searched issues in arc-ui/web Done, 4 results, 1.2s".
- Status is never color alone: each status has its own icon shape (spinner, check, alert, shield, ban), failed and waiting calls show a reason line, and the row name spells the status out.
- Each call announces its status changes through a polite live region, including that the preview only simulates an action when no handler is passed.
- Allow, Deny and Retry are labelled with the call they act on, such as "Allow: Post a summary to #release".
- The input list is a description list; tables keep table semantics with column headers. Folded rows and lines are hidden from assistive technology until shown.
- Copy buttons confirm success or failure in place and through a live region, without changing width.
- Pointer focus draws no ring; keyboard focus shows Arc's shared ring, inset inside rows so it is never clipped.

## Motion

- Opening a row, opening the group and showing more lines animate height on the critically damped smooth spring, with a short opacity fade; chevrons turn on the shared CSS spring.
- Opening the group staggers its rows in 20ms apart with a 6px settle. Rows appended by a live run grow in from zero height instead, so nothing below them jumps.
- A row's height follows its content on the same spring when a reason line or wrapped actions appear, so a status change never resizes in one frame.
- Status icons trade places with a snappy scale and fade; the success check draws itself once. The running title uses the text shimmer and settles to solid when the call finishes.
- The mark stack widens on the morph spring as a new tool joins, so the header label glides instead of jumping.
- Reduced motion removes the stagger, spinner rotation, shimmer travel and springs. Panels, rows and statuses change immediately and every label, announcement and focus path stays.

## Responsive behavior

- Rows are container aware: below 440px the meta keeps only the duration, the reason line wraps, and Allow, Deny and Retry move under the reason so the title keeps its width.
- Under 440px the Input and Output panel drops its title indent and uses the full row width; wide JSON and tables scroll inside their own well, never the page.
- Rows are 40px tall and inline actions grow to 32px on touch screens. The whole row is the tap target for opening it.

## Performance

- Folded outputs use fixed row heights, so folding and unfolding never measures the DOM.
- Input and Output mount only while a row is open; a closed group unmounts its rows.
- Height follows content through one ResizeObserver per row, and width changes from the container cut straight to the new height without animating.
- No dependencies beyond motion and lucide-react; brand marks are inline SVG.

## Notes for AI

- Use ToolCallGroup for the tool calls of one assistant turn and ToolCall for a single call shown on its own. Render the group between the assistant's intro sentence and its answer.
- Append calls to the array as the agent starts them and change status in place; keep ids stable. Pass active while the agent is still working so the header does not flicker between calls.
- Give each call a past tense title and a present tense titles.running, and for approval calls an imperative titles.approval ("Post a summary to #release") plus a message saying what allowing will do.
- Pick the output type that reads best: list for search results and issues, table for SQL rows, json for structured API responses, text for a sent message or a short log. Use renderOutput for anything richer.
- Approve, deny and retry handlers should update the call's status; the component never changes status itself when a handler is passed. Without handlers it simulates locally, which suits previews only.
- Brand marks are sample content and never imply endorsement. For tools without a built in mark pass { name, mark } with a 16px inline SVG.

## Related

- [AI chat](https://uiarc.dev/components/blocks/ai-chat/markdown): A full-screen AI chat where answers stream in as markdown with folding reasoning, tool chips and cited sources. People can switch chats and models, edit or retry a message into versions, rate replies and stop a reply mid-stream.
- [Agent run](https://uiarc.dev/components/blocks/agent-run/markdown): A single card that follows one AI coding agent through a task: steps stream in with the live one shimmering, tool calls open in place, and the agent stops on a two-file diff for you to approve or send back before it writes. When the run ends, the steps fold into one line and a result shows the files changed, the checks that passed and a pull request button.
- [Agent approvals](https://uiarc.dev/components/blocks/agent-approvals/markdown): One approval card for agents: the waiting tool call reads like a permission prompt with the tool's mark, the agent's reason and its arguments, and you approve, deny or allow it from now on.
- [Agent loop](https://uiarc.dev/components/agent-loop/markdown): An illustration of the agent loop: gather context, take action, verify, repeat, with a bright head circling three stations around the model.
- [MCP servers](https://uiarc.dev/components/blocks/mcp-servers/markdown): Manage the MCP servers that give an agent its tools: see what the enabled tools cost in context, connect or pause a server, and switch single tools on or off.
- [Text shimmer](https://uiarc.dev/components/text-shimmer/markdown): Show ongoing work with a calm light across the words.

## Also in ai

- [Agent status](https://uiarc.dev/components/agent-status/markdown): A compact live status pill for a running AI agent: the agent's mark in a live ring, the current activity and an elapsed timer, opening into the steps so far.
- [Agent plan](https://uiarc.dev/components/agent-plan/markdown): The plan an agent writes before it acts: edit, reorder or add steps while it is a draft, then watch it work through them as a live checklist.
- [Agent timeline](https://uiarc.dev/components/agent-timeline/markdown): A compact timeline of one orchestrated agent run: the orchestrator and its subagents share a time axis, handoffs draw in between rows, and a playhead replays thinking, tool calls and waiting while the header counts time, tokens and cost. Hover a span for what it did, or click it to trace its handoff path.
- [Context window](https://uiarc.dev/components/context-window/markdown): An AI context window as a grid of 200 cells: the system prompt, tools, files, summary and messages fill it in order, a threshold row marks where compaction starts, and sending a message, attaching a file or compacting shows exactly how much still fits.

## Guidance for AI tools

Tool call: A tool call row for agent transcripts: the tool's mark and the call in plain words, a live state while it runs, and the input and output a click away, with approval and retry when needed. Follow the declared prop types and do not invent props. Keep keyboard access, reduced motion support, and both light and dark themes intact when adapting it.

Full library index: https://uiarc.dev/llms.txt
