Build your own Claude Code
Reading the world·5 min

Tool definitions

0 / 1

In Chapter 1 you built the loop that turns tool_use into tool_result and back. But the model only emits a tool_use if you've told it the tool exists. That declaration is a tool definition, and it's smaller than you'd guess: a name, a sentence of prose, and a JSON Schema for the arguments. Get those three fields right and the model writes the call for you — get them wrong and it hallucinates a shape your executor can't run.

Every tool you hand the Messages API is a { name, description, input_schema }. The name is the key your loop dispatches on. The description is the model's only documentation — it reads it to decide when to reach for the tool. The input_schema is a JSON Schema object describing the arguments; the model emits a tool_use block whose input is an object that validates against it.

A custom tool definition — name, description, JSON Schema
{
  "name": "view",
  "description": "Read a file or list a directory. Returns 1-indexed, line-numbered content.",
  "input_schema": {
    "type": "object",
    "properties": {
      "path": { "type": "string", "description": "File or directory path" },
      "range": {
        "type": "array",
        "items": { "type": "number" },
        "description": "Optional [start, end] line range; -1 = end of file"
      }
    },
    "required": ["path"]
  }
}

The file tools you'll build over the next few chapters mirror Anthropic's first-party text-editor tool. It's worth seeing how Anthropic packages it, because it makes one design choice you should notice: instead of four separate tools, it ships one tool (type text_editor_20250728, name str_replace_based_edit_tool) whose behavior is selected by a command field.

One tool, four commands (text_editor_20250728)
command: "view"         → read a file (optionally a line range) or list a directory
command: "create"       → write a new file with file_text
command: "str_replace"  → replace an exact, unique old_str with new_str
command: "insert"       → insert text after a given line number
Why bundle four behaviors under one `command`?

Anthropic trained the model on this exact tool type, so a built-in like text_editor_20250728 ships with no input_schema — the schema is implied by the type name and baked into the model's weights. The command discriminator keeps related file edits under one familiar interface the model already knows. In this course you'll build the same surface from scratch as plain custom tools (each with its own name + input_schema), so you control every byte of behavior and grade it deterministically — but the mental model is the same: one path-shaped argument set, four verbs over a file.

Whether the verb arrives as a distinct tool name or as a command field, your loop does the same thing: read the discriminator, route to the matching function, run it for real, and feed the result back as a tool_result. The next three steps build the read half of that surface — view, glob, grep — as pure functions over a seed file tree, before they ever touch the loop.