Claude Agent SDKclaude-agent-sdk 0.2.157 · Python 3.10+
Dashboard
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
46 small wins to finish your pathNext lesson →

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.

ToolWhat it doesAsks first
ReadReads a file and returns its contents with line numbers. Also images, PDFs and notebooksNo
WriteCreates a file, or overwrites one wholeYes
EditReplaces one exact string in a file with anotherYes
BashRuns a shell command, each in its own processYes
GlobFinds files by name patternNo
GrepSearches inside file contentsNo
WebSearchRuns a web search and returns titles and URLsYes
WebFetchFetches one URLYes
AskUserQuestionAsks you a multiple-choice questionNo
AgentStarts a subagent with its own context windowNo

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 Write and NotebookEdit: Edit(/src/**). A Write(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:

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

Example
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"])))

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:

Example
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"])))

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 of tools does.

Availability against permission

The custom tools page splits these options into two layers, and the difference matters for every rule you write later:

OptionLayerEffect
tools=["Read", "Grep"]AvailabilityOnly those built-ins are in Claude's context. MCP tools are untouched
tools=[]AvailabilityEvery built-in is removed
allowed_toolsPermissionA listed tool runs without a prompt. An unlisted tool still exists
disallowed_tools=["Bash"]BothA bare name removes the tool from context, so Claude never attempts it
disallowed_tools=["Bash(rm *)"]PermissionBash 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.

Watch out. 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.
Try it yourself
  • 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"] with disallowed_tools=["Bash(rm *)"]. The flag is the same shape; the layer they act on is not.

Every expert started right here.