# Tool permissions

What each tool an agent may call is allowed to do: always, ask first, or never. Tools are grouped by effect (reads, writes, deploys, calls outside the app) so the risky ones are easy to find. An option policy doesn't allow stays visible and says why.

- Registry name: `tool-permissions` (`@candor/tool-permissions`)
- Tier: Pro. Installing it needs a Pro, Premium or Team licence key: the person runs `npx candor-ui key <key>` themselves; never ask for the key in chat or write it to a file.
- Docs: https://candor.themethodic.com/docs/tool-permissions

## Install

```bash
npx candor-ui add tool-permissions
```

Run `npx candor-ui@latest init` once first (https://candor.themethodic.com/get-started.md). Without the CLI: `npx shadcn@latest add @candor/tool-permissions`.
It brings in: know, locale, tooltip, utils.

## Example

```tsx
"use client"

import * as React from "react"
import { ToolPermissions } from "@/components/tool-permissions"
import type { ToolSetting } from "@/lib/know"

export default function Example() {
  const [tools, setTools] = React.useState<ToolSetting[]>([
    { id: "t1", name: "db.describe", description: "Read tables, columns, indexes and constraints", effect: "read", permission: "always" },
    { id: "t2", name: "repo.search", description: "Search the connected repositories", effect: "read", permission: "always" },
    { id: "t3", name: "migration.apply", description: "Apply a migration to Staging", effect: "write", permission: "ask" },
    { id: "t4", name: "flags.set", description: "Turn a feature flag on or off in Staging", effect: "write", permission: "ask" },
    { id: "t5", name: "prod.deploy", description: "Deploy to Production", effect: "deploy", permission: "never", locked: { always: "Production deploys always go through an approval request." } },
    { id: "t6", name: "slack.post", description: "Post a message to a Slack channel", effect: "outside", permission: "ask", locked: { always: "Messages outside Keel need someone to approve each one." } },
  ])
  return <ToolPermissions className="max-w-2xl" tools={tools} onChange={(id, permission) => setTools((ts) => ts.map((t) => (t.id === id ? { ...t, permission } : t)))} />
}
```

## <ToolPermissions>

Props:

- `tools: ToolSetting[]`: Every tool the agent may call, with its effect, current permission and any options policy rules out.
- `onChange: (id: string, permission: ToolPermission) => void`: Called with the tool id and the chosen permission.
- `className?: string | undefined`

## Strings (`toolPermissions`)

The fixed words, English by default. Override any of them under `toolPermissions` in `<LocaleProvider strings={{ toolPermissions: { … } }}>`; the table is exported as `toolPermissionsStrings`.

- `toolPermissions`: Tool permissions
- `tools`: Tools
- `countAlways`: {count} always
- `countAsk`: {count} ask first
- `countNever`: {count} never
- `read`: Reads
- `write`: Writes
- `deploy`: Deploys
- `outside`: Sends data out
- `permission`: {name} permission
- `always`: Always
- `ask`: Ask
- `never`: Never

## Rules for using Candor UI

- Components take plain typed data. Never pass model output as HTML or render it as a link; messages are typed blocks.
- A control that can't be used says why: wrap it in `<Locked reason="…">`, don't just disable it.
- Don't show a value you don't have: leave it out (it renders as —) rather than guess. An estimate is labelled as one.
- Nothing is pre-selected in a decision a person makes, and the agent is named from your app's data, never from model output.
- Your app's words go in `LocaleProvider` (fixed strings) and `TermsProvider` (nouns); entity kinds and agent roles in `EntityKindsProvider` and `AgentRolesProvider`.
