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.

6 of 21 tools exposed
ToolPresetsSideExecutorExposed
Render a visual card for the user. Use this whenever a result is easier to SEE than to read:…
observerreadin-process✓
Replace a card you previously rendered, by its id. Use for progress updates instead of emitting …
observerreadin-process✓
Load a skill's full instructions before acting in its area. Skill ids are listed in your instruc…
observerreadin-process✓
Save a durable fact about the user for future conversations — a preference, a standing constrain…
observerwritein-process✓
List the documents that exist, newest first — id, title and size, never the body. Use it when th…
observerreadin-process✓
Read part of a document. Ask for the outline first, then the spans you actually need — the whole…
observerreadin-process✓
Create a document the user will keep and come back to — a draft, a report, notes. Use instead of…
executorwritein-process—
Propose changes to a document. The user sees them as a diff and accepts them one at a time; noth…
executorconfirmin-process—
Restore an earlier version of a document. With no toRevision it steps back one version, and call…
executorwritein-process—
Draft an email for the user to review and send. It is never sent for them — they get an editable…
executordraftwritein-process—
Review an agreement and return clause-level findings with severity, plus an overall risk score. …
none — privatereadin-process—
Check a named framework's requirements and their current status. Render as a `checklist`; put un…
none — privatereadin-process—
Find authority supporting or limiting a legal position. ALWAYS render the result as a `citations…
none — privatereadin-process—
Conversion funnel for a period. Render as a `funnel` card and name the worst step.
none — privatereadin-process—
Channel performance for a period. Render as a `table`; lead with the rate, not the total.
none — privatereadin-process—
Positioning matrix against named competitors. Render as a `comparison` card and highlight the re…
none — privatereadin-process—
Search available instruments by name or symbol. Call this when the user's term could mean more t…
none — privatereadin-process—
Current price and 24h change for one instrument. Renders a stats card.
none — privatereadin-process—
Daily OHLC history for one instrument. Render as a `chart` — variant `candlestick` for price act…
none — privatereadin-process—
Compare several instruments over the same window. Renders a table plus a normalised trend chart.
none — privatereadin-process—
Set a price alert. This is a real action, so it asks the user to confirm the exact terms first. …
executorconfirmin-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: [...] });