The built-in tools
A built-in tool is a tool the Claude Code process already carries, such as Read, Write, Edit, Bash or WebSearch. Your options decide which of them a session offers Claude.
Last updated: 20 Oct, 2026 · Claude Agent SDK
The runs so far answered out of the desk's own code, with no tools at all. A real Claude Code session starts with a set of tools already in it, before you write one of your own, and the first thing to know about them is their exact names: those strings are what every rule, hook matcher and tool list is written with.
The tools a session starts with
The tools reference lists every one. These are the ones most agents meet, with the column the reference calls Permission required: whether Claude Code asks you before the call runs, for a path inside the working directory.
| Tool | What it does | Asks first |
|---|---|---|
Read | Reads a file and returns its contents with line numbers. Also images, PDFs and notebooks | No |
Write | Creates a file, or overwrites one whole | Yes |
Edit | Replaces one exact string in a file with another | Yes |
Bash | Runs a shell command, each in its own process | Yes |
Glob | Finds files by name pattern | No |
Grep | Searches inside file contents | No |
WebSearch | Runs a web search and returns titles and URLs | Yes |
WebFetch | Fetches one URL | Yes |
AskUserQuestion | Asks you a multiple-choice question | No |
Agent | Starts a subagent with its own context window | No |
Two of those rows are worth reading twice. Read is marked No, so a read inside your working directory runs without asking, while a read outside it prompts. Bash is marked Yes, with a built-in set of read-only commands that run without a prompt. The tools reference also notes that Glob and Grep are absent by default on macOS, Linux and WSL, so do not count on them being there.
Writing a built-in's name in a rule
Every place that takes a tool name takes the same format, ToolName(specifier), and the specifier depends on the tool:
- Bash takes a command pattern:
Bash(npm run *). - Read, Grep and Glob take a path pattern:
Read(~/secrets/**). - Edit takes a path pattern, and the same rule covers
WriteandNotebookEdit:Edit(/src/**). AWrite(path)rule is never matched. - WebFetch takes a domain:
WebFetch(domain:example.com). - WebSearch takes no specifier at all; the bare name is the only form.
An MCP tool of your own is named differently, and the in-process server lesson comes to that. A built-in carries no prefix: it is Read, not mcp__something__read.
Reading the command line the SDK builds
The SDK passes most options to Claude Code as command-line flags, and its transport builds them in a method you can call to look, without starting anything. This helper does that:
import shlex
from dataclasses import replace
from claude_agent_sdk._internal.transport.subprocess_cli import SubprocessCLITransport
def flags(options):
"""The command line the SDK would start Claude Code with."""
# a fixed cli_path keeps the printed command the same on every machine
options = replace(options, cli_path="claude")
command = SubprocessCLITransport(prompt="", options=options)._build_command()
# command[0] is the binary itself, which is not the interesting part
return shlex.join(command[1:])_build_command is the SDK's own method, so what it prints is the real command line, and a fixed cli_path keeps it the same on every machine.
from claude_agent_sdk import ClaudeAgentOptions
from flags import flags
# with no options at all, and then with two built-ins named
print(flags(ClaudeAgentOptions()))
print(flags(ClaudeAgentOptions(tools=["Read", "Bash"])))--output-format stream-json --verbose --system-prompt '' --input-format stream-json --output-format stream-json --verbose --system-prompt '' --tools Read,Bash --input-format stream-json
The tools option is the availability layer: it decides which built-ins are in Claude's context at all. Three more values are worth seeing, because each lands on a different flag:
from claude_agent_sdk import ClaudeAgentOptions
from flags import flags
# the preset is every default tool; the empty list is none of them
print(flags(ClaudeAgentOptions(tools={"type": "preset", "preset": "claude_code"})))
print(flags(ClaudeAgentOptions(tools=[])))
print(flags(ClaudeAgentOptions(disallowed_tools=["Bash"])))--output-format stream-json --verbose --system-prompt '' --tools default --input-format stream-json --output-format stream-json --verbose --system-prompt '' --tools '' --input-format stream-json --output-format stream-json --verbose --system-prompt '' --disallowedTools Bash --input-format stream-json
What each of those four lines changed
- No options asks for JSON in and JSON out and sets an empty system prompt. Nothing about tools, so Claude Code brings its own default set.
- tools=["Read", "Bash"] becomes
--tools Read,Bash. Only those two are in Claude's context; the rest are gone. - The preset
{"type": "preset", "preset": "claude_code"}becomes--tools default, which is every tool Claude Code normally has. - tools=[] becomes
--tools '', an empty list: no built-in tools at all, leaving Claude only the tools you give it. - disallowed_tools=["Bash"] becomes
--disallowedTools Bash. A bare name there removes the tool the same way leaving it out oftoolsdoes.
Availability against permission
The custom tools page splits these options into two layers, and the difference matters for every rule you write later:
| Option | Layer | Effect |
|---|---|---|
tools=["Read", "Grep"] | Availability | Only those built-ins are in Claude's context. MCP tools are untouched |
tools=[] | Availability | Every built-in is removed |
allowed_tools | Permission | A listed tool runs without a prompt. An unlisted tool still exists |
disallowed_tools=["Bash"] | Both | A bare name removes the tool from context, so Claude never attempts it |
disallowed_tools=["Bash(rm *)"] | Permission | Bash stays in context; only calls matching the pattern are denied |
What the stand-in has, and what it does not
These tools live in the Claude Code process. The stand-in of this course is not that process, so it has none of them, and no run on this page can read a file with the real Read or search the web with the real WebSearch. That needs the Claude Code binary and an API key. What the pages above do show for real is the command line the SDK builds from your options. The Which tools Claude can see lesson gives the desk a small Read of its own, so that the later rules have a path to match.
tools=[] takes away AskUserQuestion along with everything else, so Claude can no longer ask you a clarifying question. The user input page says to put AskUserQuestion back in the list when you restrict tools and still want those questions.Related
- Previous: When the CLI will not start
- Next: Custom tools with @tool
- Reference: tools reference and the custom tools page
- Print
flags(ClaudeAgentOptions(tools=["Read", "Glob", "Grep"]))and find how a three-tool list is written. - Add
allowed_tools=["WebSearch"]and read which flag it lands on. - Compare
disallowed_tools=["Bash"]withdisallowed_tools=["Bash(rm *)"]. The flag is the same shape; the layer they act on is not.
Every expert started right here.