Tool Picker
The catalogue of tools an agent may call, with a switch on each. Destructive capabilities are marked rather than hidden — the whole risk of an agent is a capability nobody noticed it had.
src/components/ui/tool-picker.tsxAbout
From the component's own source, where it was written.
The catalogue of tools an agent may call, with a switch on each.
Distinct from ToolCall, which shows one invocation after the fact. This is the permission screen: what the agent is allowed to reach for, decided before it runs.
**Destructive tools are marked, not hidden.** A tool that deletes, refunds or sends carries destructive, which puts a warning glyph beside it and keeps it visible when the list is filtered to enabled ones. The whole risk of an agent is a capability nobody noticed it had, and a picker that renders delete_customer the same as list_customers is helping that happen.
Search matches the name *and* the description, because people look for tools by what they do ("refund") at least as often as by what they are called. Groups collapse to nothing when empty rather than leaving a bare heading.
Install
npx astralyx-ui add tool-picker
Usage
import { ToolPicker } from '@/components/ui/tool-picker'
<ToolPicker tools={tools} value={enabled} onValueChange={setEnabled} />Composer
Change the props and the source below follows.
Read
Full-text search over the help centre.
Profile, plan and lifetime value by id.
One invoice, with its line items.
Write
Opens a ticket in the queue.
Moves money. Capped at £200 per run by policy.
Irreversible. Cascades to invoices and tickets.
Sends from the support address.
Needs a verified sending domain.
3 of 7 enabled
<ToolPicker
tools={tools}
value={enabled}
onValueChange={setEnabled}
searchable={true}
/>Examples
Grouped, with destructive tools marked
<ToolPicker tools={tools} value={enabled} onValueChange={setEnabled} />API
Generated from the source, so it cannot fall behind the code. One table per exported component, then hooks, functions and types.
ToolPicker
Extends Omit<ComponentProps<'div'>, 'onSelect' | 'onChange'>.
| Prop | Type | Default | Description |
|---|---|---|---|
| tools | Tool[] | required | { id, name, description?, group?, destructive?, disabled?, disabledReason?, meta? }. |
| value | string[] | required | Controlled — the caller owns the enabled set, because it is usually part of a larger form. |
| onValueChange | (next: string[]) => void | required | Controlled — the caller owns the enabled set, because it is usually part of a larger form. |
| searchable | boolean | true | Matches name, group and description — people look for tools by what they do at least as often as by what they are called. |
| searchPlaceholder | string | 'Filter tools…' | — |
| searchLabel | string | 'Filter tools' | Accessible name for the filter field. |
| emptyLabel | string | 'No tools match' | — |
| emptyHint | string | 'Try a different word, or clear the filter.' | — |
| destructiveLabel | string | 'Destructive' | Marks a tool as risky in the label. |
| summary | (enabled: number, total: number) => ReactNode | (enabled, total) => `${enabled} of ${total} enabled` | Caption under the list. Counted from the data, so it cannot disagree with the rows. |
Types
Tool
The catalogue of tools an agent may call, with a switch on each.
| Field | Type | Description |
|---|---|---|
| id | string | — |
| name | string | — |
| description? | ReactNode | — |
| group? | string | Headings, in the order the tools appear. Empty groups collapse rather than leaving a bare heading. |
| destructive? | boolean | Puts a warning badge on the row. For anything that deletes, refunds or sends. |
| disabled? | boolean | Cannot be switched, with the reason in disabledReason. |
| disabledReason? | ReactNode | — |
| meta? | ReactNode | Trailing slot — a schema peek, a docs link, a usage count. |