Skip to main content

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.

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. 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

Steps 2–6 happen on the server. Step 3 only fires if the static rules say the operation needs explicit user permission — see Permissions.

What a tool looks like to the model

A tool is declared with three things:
{
  "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:
{
  "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