Tools & presets
Presets are explicit allowlists, not blocklists — a newly registered tool is invisible until someone puts it in one. The inverse (everything exposed unless denied) means the day you add a destructive tool, it is already live on every key you ever minted. Toggle the presets and watch the exposed set change.
| Tool | Presets | Side | Executor | Exposed |
|---|---|---|---|---|
Render a visual card for the user. Use this whenever a result is easier to SEE than to read:… | observer | read | in-process | ✓ |
Replace a card you previously rendered, by its id. Use for progress updates instead of emitting … | observer | read | in-process | ✓ |
Load a skill's full instructions before acting in its area. Skill ids are listed in your instruc… | observer | read | in-process | ✓ |
Save a durable fact about the user for future conversations — a preference, a standing constrain… | observer | write | in-process | ✓ |
List the documents that exist, newest first — id, title and size, never the body. Use it when th… | observer | read | in-process | ✓ |
Read part of a document. Ask for the outline first, then the spans you actually need — the whole… | observer | read | in-process | ✓ |
Create a document the user will keep and come back to — a draft, a report, notes. Use instead of… | executor | write | in-process | — |
Propose changes to a document. The user sees them as a diff and accepts them one at a time; noth… | executor | confirm | in-process | — |
Restore an earlier version of a document. With no toRevision it steps back one version, and call… | executor | write | in-process | — |
Draft an email for the user to review and send. It is never sent for them — they get an editable… | executordraft | write | in-process | — |
Review an agreement and return clause-level findings with severity, plus an overall risk score. … | none — private | read | in-process | — |
Check a named framework's requirements and their current status. Render as a `checklist`; put un… | none — private | read | in-process | — |
Find authority supporting or limiting a legal position. ALWAYS render the result as a `citations… | none — private | read | in-process | — |
Conversion funnel for a period. Render as a `funnel` card and name the worst step. | none — private | read | in-process | — |
Channel performance for a period. Render as a `table`; lead with the rate, not the total. | none — private | read | in-process | — |
Positioning matrix against named competitors. Render as a `comparison` card and highlight the re… | none — private | read | in-process | — |
Search available instruments by name or symbol. Call this when the user's term could mean more t… | none — private | read | in-process | — |
Current price and 24h change for one instrument. Renders a stats card. | none — private | read | in-process | — |
Daily OHLC history for one instrument. Render as a `chart` — variant `candlestick` for price act… | none — private | read | in-process | — |
Compare several instruments over the same window. Renders a table plus a normalised trend chart. | none — private | read | in-process | — |
Set a price alert. This is a real action, so it asks the user to confirm the exact terms first. … | executor | confirm | in-process | — |
Rendered tool specs
What ToolRegistry.toSpecs() hands the adapter for the current preset selection. The adapter then reshapes it per dialect — flat for Responses, nested under function for Chat Completions. See the wire panel.
[
{
"name": "listDocuments",
"description": "List the documents that exist, newest first — id, title and size, never the body. Use it when the user refers to something they worked on before and you do not have its id.",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Optional filter on the title, case-insensitive."
}
}
}
},
{
"name": "loadSkill",
"description": "Load a skill's full instructions before acting in its area. Skill ids are listed in your instructions. Loading a skill may also unlock tools specific to it.",
"parameters": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"description": "The skill id to load."
},
"topic": {
"type": "string",
"description": "Optional sub-topic, to load only the relevant section."
}
},
"additionalProperties": false
}
},
{
"name": "readDocument",
"description": "Read part of a document. Ask for the outline first, then the spans you actually need — the whole body is never returned at once.",
"parameters": {
"type": "object",
"required": [
"docId"
],
"properties": {
"docId": {
"type": "string"
},
"mode": {
"type": "string",
"enum": [
"outline",
"span",
"search"
],
"description": "outline: block index + heading + size for the whole document — start here. span: the text of a block range. search: blocks containing a phrase."
},
"from": {
"type": "number",
"description": "First block, for mode=span."
},
"to": {
"type": "number",
"description": "Last block, for mode=span. Defaults to `from`."
},
"query": {
"type": "string",
"description": "Phrase to look for, for mode=search."
}
}
}
},
{
"name": "remember",
"description": "Save a durable fact about the user for future conversations — a preference, a standing constraint, a correction. Use a stable key so updating a fact replaces it instead of duplicating it. Pass value: null to forget a fact. Do NOT store conversation history or anything the user would not expect to be remembered.",
"parameters": {
"type": "object",
"required": [
"key"
],
"properties": {
"key": {
"type": "string",
"description": "Stable slug, e.g. \"preferred-language\"."
},
"value": {
"type": [
"string",
"null"
],
"description": "The fact to remember, or null to forget the key."
}
},
"additionalProperties": false
}
},
{
"name": "updateCard",
"description": "Replace a card you previously rendered, by its id. Use for progress updates instead of emitting a new card each time.",
"parameters": {
"type": "object",
"required": [
"cardId",
"spec"
],
"properties": {
"cardId": {
"type": "string"
},
"spec": {
"type": "object",
"additionalProperties": true
}
},
"additionalProperties": false
}
},
{
"name": "visualize",
"description": "Render a visual card for the user. Use this whenever a result is easier to SEE than to read:\ncomparisons, trends, sequences, multi-field records, long operations, or a decision the user must make.\n\nAvailable kinds:\n- table: Many items sharing the same fields. Search results, inventories, case lists, line items, survey rows.\n- stats: A row of headline numbers, optionally with change vs a prior period. Totals, counts, rates, before/after.\n- comparison: A matrix of options against criteria. Reach for this whenever the user is CHOOSING between things — vendors, clauses, plans, candidates, approaches. Boolean values render as ✓/✗.\n- keyvalue: Labelled detail rows describing ONE thing. A record, a contract's key terms, a config, a profile.\n- tree: Nested structure. Document outlines, org charts, taxonomies, decision trees, dependency chains, file layouts.\n- chart: Plot shape over time or across categories. `variant:\"series\"` with `series[]` for line/area/bar/scatter; `variant:\"candlestick\"` with `candles[]` for OHLC.\n- funnel: Staged drop-off, in order. Conversion funnels, hiring pipelines, application intake, case attrition. Stages must be ordered widest to narrowest.\n- gauge: One value on a scale, with optional named bands. Risk levels, readiness scores, sentiment, confidence, utilisation. Label the bands so the number means something.\n- timeline: An ordered sequence of events with times and status. Best for history, schedules, execution traces.\n- progress: Multi-step work with per-step status. Best for long or background operations the user is waiting on.\n- checklist: Items with a state. Compliance reviews, launch readiness, document requirements, audit findings, QA passes. Use `group` to section a long list.\n- markdown: Formatted long-form text. Use only when structure genuinely needs headings/lists — plain replies do not need a card.\n- document: A document the user will keep, read in a previewer and come back to. Use for something worth revising — a draft, a report, a set of notes — not for a long reply. Returns an id later turns use to re-read or edit it.\n- callout: One short highlighted note. Disclaimers, caveats, prerequisites, limits on what you checked. Use `danger` for anything the reader would regret missing.\n- citations: The sources behind what you just said. Use whenever a claim rests on specific documents — case law, statutes, papers, filings, articles, internal docs. Quote the passage relied on in `snippet`.\n- code: A code block with language and optional filename.\n- diff: Before/after comparison of two texts.\n- media: Images, audio, or video with captions.\n- choice (interactive — the user replies): Ask the user to pick from options. Use instead of guessing when several candidates fit. Each option may carry `meta` facts shown on its row.\n- form (interactive — the user replies): Collect structured input. Use when you need several values at once rather than a back-and-forth.\n- confirm (interactive — the user replies): Get explicit approval before a consequential action. `summary` must state exactly what will happen — amounts, targets, and anything irreversible.\n- editreview (interactive — the user replies): Propose changes to a document and let the user accept them hunk by hunk. The only way to change a document — nothing is written until this is answered.\n- email: A drafted message for the user to send themselves. Use when the outcome of the work is something that has to reach someone.\n\nRules:\n- Put the data IN the card. Do not also repeat every row in your prose — say what it means instead.\n- Interactive kinds (choice, form, confirm) pause for the user's answer, which is returned to you as this tool's result. Use them instead of guessing.\n- Use `confirm` before anything consequential or irreversible.\n- One card per distinct idea. Do not wrap a one-line answer in a card.",
"parameters": {
"type": "object",
"required": [
"kind",
"spec"
],
"properties": {
"kind": {
"type": "string",
"enum": [
"table",
"stats",
"comparison",
"keyvalue",
"tree",
"chart",
"funnel",
"gauge",
"timeline",
"progress",
"checklist",
"markdown",
"document",
"callout",
"citations",
"code",
"diff",
"media",
"choice",
"form",
"confirm",
"editreview",
"email"
],
"description": "Which card to render."
},
"spec": {
"type": "object",
"description": "The card body. Must match the schema for `kind`, and must include `kind` itself.",
"additionalProperties": true
}
},
"additionalProperties": false
}
}
]Import from MCP
importMcpTools() maps an MCP server's tool list onto ToolDefinitions — namespaced, with an executor that calls back through the server. Imported tools register into the shared registry with no preset (private — import grants existence, never authority), and createMcpSkill() registers a matched skill naming them — so on /run, asking about e.g. a word count surfaces and unlocks them like any domain tool. The allow toggle below simulates that match manually. This public showcase runs that same protocol against a browser-local mock, so no request leaves your device.
Three ways a tool attaches a card
All three work, so a tool author never has to know which one the runtime checks. Whichever is used, the card reaches the UI and is stripped from the output before the next provider call.
// 1. explicit result field
return { output: { n: 1 }, card: { kind: "stats", items: [...] } };
// 2. embedded in the output
return { output: withCard({ n: 1 }, { kind: "stats", items: [...] }) };
// 3. emitted mid-execution — for progress on a long tool
ctx.emitCard?.({ kind: "progress", steps: [...] });
// and to ASK rather than assume — this suspends the run:
const answer = await ctx.requestCard?.({ kind: "confirm", title: "…", summary: [...] });