> ## Documentation Index
> Fetch the complete documentation index at: https://internal.september.wtf/llms.txt
> Use this file to discover all available pages before exploring further.

# Tool use

> How the agent calls tools and how to define them.

Tools are how the agent reaches outside its own context. Without tools,
the model can write text. With tools, it can read files, run code, search
the web, query a database, send a Slack message — anything you've given
it the ability to do. This page covers what tools look like to the
agent, what they look like to you, and the patterns that work.

## Where tools come from

The agent sees three kinds of tools, but they all look the same from
its point of view:

* **Platform tools** — built into the Engine: `read_file`, `write_file`,
  `bash`, `grep`, `web_search`, etc. Run inside the sandbox.
* **MCP tools** — surfaced from external servers connected via the
  [Asset Directory](../api-reference/endpoints/assets). Each connected
  server contributes a set of actions (e.g. `slack.send_message`,
  `gmail.read_thread`).
* **Skills used as tools** — your custom skills, registered in the
  Engine's skill catalog, can be invoked as tools. Useful when you want
  to wrap a recurring prompt-plus-script behavior.

To the model, a tool is just a name, a description, and an input schema.
It doesn't know whether your `slack.send_message` is built-in, MCP, or
a wrapped skill.

## The lifecycle of one tool call

```mermaid theme={null}
flowchart TB
    A["1. model emits tool_use block"]
    B["2. SSE → tool_call event"]
    C{"3. dangerous?"}
    H["permission prompt → HITL"]
    D["4. tool executes<br/>(sandbox / MCP / skill)"]
    E["5. SSE → tool_result event"]
    F["6. result fed back into context"]
    G["7. model emits next block<br/>(text or another tool call)"]

    A --> B --> C
    C -- yes --> H --> D
    C -- no --> D
    D --> E --> F --> G

    classDef io fill:#dbeafe,stroke:#1d4ed8
    classDef branch fill:#fef3c7,stroke:#a16207
    class C branch
    class A,B,D,E,F,G io
```

Steps 2–6 happen on the server. Step 3 only fires if the static rules
say the operation needs explicit user permission — see
[Permissions](../agents-and-tools/permissions).

## What a tool looks like to the model

A tool is declared with three things:

```json theme={null}
{
  "name": "read_file",
  "description": "Read the contents of a file at the given path.",
  "input_schema": {
    "type": "object",
    "properties": {
      "path": {
        "type": "string",
        "description": "Absolute path to the file."
      }
    },
    "required": ["path"]
  }
}
```

The model picks tools by name. The description tells the model when to
use this tool. The input schema is enforced — calls with malformed input
are rejected before execution.

**Description quality matters.** A vague description ("read a file")
makes the model use the tool randomly. A precise description ("Read the
contents of a file at the given absolute path. Returns the file contents
as a string. Use this when you need to inspect a specific file you
already know the path of.") makes the model use it correctly.

## What a tool call looks like in the SSE stream

```
event: tool_call
data: {
  "tool": "read_file",
  "tool_call_id": "tu_01ABC...",
  "input": {
    "path": "/data/report.md"
  }
}

event: tool_result
data: {
  "tool": "read_file",
  "tool_call_id": "tu_01ABC...",
  "output": "# Q4 Report\n\n...",
  "error": null
}
```

If the tool fails, `error` is set and `output` may be empty. The error
feeds back into context. The model can decide to retry, ask the user, or
give up — that's its call, not the Engine's.

## Concurrency

The Engine runs tool calls in parallel up to
`CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY` (default 10). When the model
emits multiple tool calls in one block, they fire concurrently. Order in
the response is preserved.

Two implications:

1. **Tools must be safe to run in any order.** If `tool A` and `tool B`
   are emitted together, the Engine runs them simultaneously. If they
   touch the same resource, expect races.
2. **Errors are independent.** One tool failing doesn't cancel the
   others. The model gets all results back together.

## Defining a custom tool

You define a tool by adding an entry to the Engine's catalog. The catalog
lives at `catalog/` in the engine repo (or wherever `CATALOG_DIR` points).
A tool entry is a JSON file:

```json theme={null}
{
  "name": "my_tool",
  "description": "Does the thing.",
  "input_schema": { ... },
  "implementation": {
    "kind": "platform" | "skill" | "mcp",
    "ref": "..."
  }
}
```

After editing the catalog, hot-reload with
`POST /admin/reload-catalog` — no restart needed.

For MCP-served tools, you don't define them in the catalog. The user
connects the MCP server through `/assets/connect` and the server's
actions are auto-registered.

## Tool descriptions: what works

The model uses the description to decide when to call the tool. A good
description:

* **States the action plainly.** "Send a Slack message to a channel."
* **Names the inputs and what they mean.** "`channel`: the Slack channel
  ID (starts with `C`). `text`: the message body, plain text or
  markdown."
* **Names the output.** "Returns the timestamp of the posted message,
  which can be used to thread replies."
* **Hints when to use it.** "Use this to notify a team or share a quick
  update. Don't use it for direct messages — use `slack.send_dm` instead."
* **Hints when *not* to use it.** Critical. The model will pick this
  tool over a similar one if the description is more specific about
  context.

A bad description:

* **Marketing language.** "Powerful tool for communication."
* **Vague.** "Send a message."
* **Mismatched.** Description says "send to a channel"; schema requires
  a user ID.

## Patterns

### Read-then-act

The model often reads context (`grep`, `read_file`, `web_search`) before
acting (`write_file`, `bash`). Give the agent both. Don't try to limit
it to "act only" — it'll either act blindly or refuse.

### Idempotent acts where possible

Tools that the model might call twice (network flakes, retries) are
safer when idempotent. Where you can't, design for the model to read its
own past output (`tool_result` is in context) and notice when it already
ran.

### Confirm before destructive

For irreversible operations (delete, drop, send to external), let the
permission system gate. Don't try to make the model "be careful" — let
it propose and the user confirm.

## Pitfalls

* **Tool name collisions.** Two tools with similar names confuse the
  model. Prefix tools by namespace if the Engine has many: `git.commit`
  vs. `slack.commit_review`.
* **Schemas that don't match the description.** The model writes against
  the description; the Engine validates against the schema. Keep them in
  sync.
* **Hidden side effects.** A tool that "reads a file" but also writes a
  log to disk surprises the model. Document side effects in the
  description.
* **Tools that return too much data.** A `web_search` that dumps 50 KB
  of HTML eats context. Truncate or summarize at the tool layer.

## See also

* [Defining tools](../agents-and-tools/defining-tools) — full reference
  for the tool definition format.
* [Permissions](../agents-and-tools/permissions) — how dangerous tool
  calls get gated.
* [MCP connectors](../agents-and-tools/built-in-tools/mcp) — the MCP
  surface specifically.
* [Streaming events](../api-reference/streaming-events) — `tool_call` /
  `tool_result` event reference.
