# Claude Code built-in tools: a panoramic inventory (source investigation notes)

> Based on the actual `claude-code/src` tree (39 tool directories + testing/ + shared/). Line numbers are relative to `claude-code/src/`.
> Registration: `getAllBaseTools()` (`tools.ts:193-251`) is the single source of truth; about 14 core tools are enabled unconditionally, the rest are gated by `feature()` / environment variables.

## 1. File operations

### Read (`FileReadTool/FileReadTool.ts:338`)
- Job: read local files (text by line; images/PDFs via multimodal). The model's main "look at the code" entry. Read-only ✅ concurrency-safe ✅ enabled by default ✅
- Parameters: `file_path` (absolute path, required), `offset` (start line), `limit` (line count)
- Example: before fixing a bug, `Read {file_path:"/src/index.ts", offset:100, limit:80}`
- Details: dual output caps — files >256KB are refused outright (`utils/file.ts:48` `MAX_OUTPUT_SIZE`); output over 25000 tokens errors (`limits.ts:18`, overridable by env `CLAUDE_CODE_FILE_READ_MAX_OUTPUT_TOKENS`); `maxResultSizeChars: Infinity` (`:342`) — truncation is done in-house; comments at `limits.ts:9-13` record an A/B rollback of "truncate vs error"

### Edit (`FileEditTool/`)
- Job: exact string replace `old_string`→`new_string`. The workhorse for editing code. Read-only ❌ concurrent ❌ default ✅
- Parameters (`types.ts:6-19`): `file_path`, `old_string`, `new_string`, `replace_all` (default false)
- Example: `Edit {file_path:"…/app.ts", old_string:"const x = 1", new_string:"const x = 2"}`
- Details: **read before write** — a file that has not been Read errors with "File has not been read yet" (`FileEditTool.ts:281`); after an external change, a fresh Read is required; `.ipynb` is refused and the model is steered to NotebookEdit (`:270`)

### Write (`FileWriteTool/FileWriteTool.ts:95`)
- Job: write / overwrite a whole file. Parameters: `file_path`, `content`
- Details: overwriting an existing file likewise forces "Read first"; detects "the file was changed by the user / a linter since I last read it" (`:203`, `:216`) to prevent blind overwrite

### NotebookEdit (`NotebookEditTool/`)
- Job: edit a Jupyter notebook cell. Parameters: `notebook_path`, `cell_id`, `new_source`, `cell_type`(code/markdown), `edit_mode`(replace/insert/delete)
- Details: `shouldDefer: true` (`:94`) — delayed-loaded by ToolSearch

## 2. Search

### Glob (`GlobTool/GlobTool.ts:58`)
- Job: find filenames by glob pattern, sorted by mtime. Read-only ✅ concurrent ✅
- Parameters: `pattern` (e.g. `**/*.ts`), `path` (search root, default cwd)
- Example: "find all test files" → `Glob {pattern:"**/*.test.ts"}`
- Details: in the ant internal build, after bfs/ugrep are inlined, Glob/Grep are removed from the tool list entirely (`tools.ts:198-201`)

### Grep (`GrepTool/GrepTool.ts:161`)
- Job: content regex search via ripgrep. Read-only ✅ concurrent ✅
- Parameters: `pattern`, `path`, `glob`, `output_mode`(content/files_with_matches/count), `-A/-B/-C`, `-n`, `-i`, `type`, `head_limit`(default 250), `offset`, `multiline`
- Example: `Grep {pattern:"fetchUser", output_mode:"content", -n:true}`
- Details: `maxResultSizeChars: 20_000` (`:164`) — one of the tightest output caps, forcing the model to close the search precisely

## 3. Command execution

### Bash (`BashTool/BashTool.tsx:421`)
- Job: run a shell command. The model's "two hands." Default ✅
- Parameters (`:227-247`): `command`, `timeout`, `description` (required plain-language description), `run_in_background`, `dangerouslyDisableSandbox`
- Example: `Bash {command:"npm test", description:"Run test suite"}`
- Detail 1: **dynamic read-only judgment** — `isReadOnly(input)` analyzes the command itself, parsing compounds to decide whether they are purely read-only (`:434-441`); `preparePermissionMatcher` splits `ls && git push` into subcommands and matches each against `Bash(git *)` rules (`:447-465`)
- Detail 2: **hidden internal field** — `_simulatedSedEdit` is an internal field used after a sed preview is approved; it is deliberately omitted from the model-visible schema so the model cannot use it to bypass the permission sandbox and write arbitrary files (`:249-259` comments); output cap 30000 characters; oversize results are persisted to disk and return `persistedOutputPath` (`:424`, `:292`)

### PowerShell (`PowerShellTool/`)
- Job: the PowerShell edition of Bash on Windows; parameters almost identical (`:228-233`)
- Enablement: `getPlatform()==='windows'` and (ant on by default / external needs `CLAUDE_CODE_USE_POWERSHELL_TOOL`) (`utils/shell/shellToolUtils.ts:17-22`; `tools.ts:242`); the directory also contains `gitSafety.ts`, `destructiveCommandWarning.ts` safety checks

### REPL (`REPLTool/constants.ts:11`)
- Job: ant-internal "batch-operate a VM" mode: when on, the 8 primitive tools Read/Write/Edit/Glob/Grep/Bash/NotebookEdit/Agent are hidden from the model (`REPL_ONLY_TOOLS :37-46`); the model must call them through REPL inside the VM
- Enablement: `USER_TYPE==='ant'` and the CLI entry; `CLAUDE_CODE_REPL=0` can turn it off (`:23-30`); SDK does not enable it by default

## 4. Network

### WebFetch (`WebFetchTool/WebFetchTool.ts`)
- Job: fetch a URL's content, then run a small prompt so the model processes that content and returns the result. Read-only ✅ shouldDefer ✅
- Parameters: `url`, `prompt` (what to do with the fetched content)
- Example: `WebFetch {url:"https://docs…", prompt:"提取安装步骤"}`
- Details: `preapproved.ts:14` maintains a "code-related domain allowlist"; only WebFetch GETs to those domains skip review. The file header is a SECURITY WARNING forbidding other tools from reusing it (`:5-11`)

### WebSearch (`WebSearchTool/WebSearchTool.ts`)
- Job: web search. Parameters: `query` (≥2 characters), `allowed_domains`, `blocked_domains`. shouldDefer ✅
- Details: `isEnabled()` is decided by API provider — firstParty is on directly; Vertex requires a Claude 4.0+ model

## 5. Sub-agents and task management

### Agent (`AgentTool/AgentTool.tsx:226`)
- Job: spawn a sub-agent to finish a task independently (isolated context; can run in parallel or in the background). Default ✅
- Parameters (`:82-126`): `description`(3–5 words), `prompt`(the brief), `subagent_type`, `model`(sonnet/opus/haiku), `run_in_background`, `name`/`team_name`, `mode`, `isolation:worktree`, `cwd`
- Example: three `Agent {description:"调研方案A", prompt:"…", run_in_background:true}` exploring in parallel
- Details: `isReadOnly()=true` — Agent itself does not write disk; permission checks are all delegated to tools inside the sub-agent (`:1264-1266`); output cap 100000 characters (`:229`)

### TaskOutput (`TaskOutputTool/`)
- Job: read background-task output; can block and wait. Parameters: `task_id`, `block`(default true), `timeout`(≤600000ms, default 30000)
- Details: read-only; `isEnabled()` is hard-coded `"external"!=='ant'` — the ant internal build actually disables it (near `:163`)

### TaskStop (`TaskStopTool/`)
- Job: terminate a background task. Parameters: `task_id` (compatible with the old `shell_id`). shouldDefer ✅ (`:53`)

### TaskCreate / TaskGet / TaskUpdate / TaskList (TodoV2)
- Job: structured task-list create/get/update/list; supports blocking relations `addBlocks`/`addBlockedBy`, `owner`, `metadata`; TaskUpdate status accepts `'deleted'`
- Enablement: `isTodoV2Enabled()` (`tools.ts:218-220`) — forced on in non-interactive sessions; `CLAUDE_CODE_ENABLE_TASKS` forces it on (`utils/tasks.ts:133-139`). All shouldDefer ✅

### TodoWrite (`TodoWriteTool/`)
- Job: classic todo-list write (`content`/`status`/`activeForm`)
- Details: mutually exclusive with the TaskCreate family — `isEnabled(){return !isTodoV2Enabled()}` (near `:51`); pick one of the old and new systems

## 6. Multi-agent collaboration

### SendMessage (`SendMessageTool/`)
- Job: send a message to a teammate agent (`to` supports teammate name, `*` broadcast; under `UDS_INBOX` also `uds:<socket>`, `bridge:<session>` cross-process delivery)
- Parameters: `to`, `summary`(5–10 words UI preview), `message`
- Details: `isReadOnly(input)` is dynamic — a pure-text message counts as read-only; `isEnabled()=isAgentSwarmsEnabled()`

### TeamCreate / TeamDelete
- Job: create / dissolve an agent team (TeamCreate parameters: `team_name`, `description`, `agent_type`)
- Enablement: `isAgentSwarmsEnabled()` — always on for ant; external needs `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS` or `--agent-teams` (`utils/agentSwarmsEnabled.ts:24-32`; `tools.ts:228-230` lazy require to break a cyclic dependency)

## 7. User interaction and configuration

### AskUserQuestion (`AskUserQuestionTool/AskUserQuestionTool.tsx:110`)
- Job: pop 1–4 multiple-choice questions at the user (with option annotations) and take a structured answer. Default ✅ read-only ✅ concurrent ✅
- Parameters: `questions` (array of 1–4, with uniqueness-check refine)
- Details: when `--channels` (Telegram/Discord) is active, `isEnabled()=false` — nobody is sitting at the terminal, so a dialog would hang the session (`:135-145`)

### Brief / SendUserMessage (`BriefTool/BriefTool.ts:137`)
- Job: in KAIROS (proactive assistant) mode, send a rich-text message to the user, optionally with attachments; `status:proactive` for a proactive push
- Details: the directory is named Brief but the API name is SendUserMessage (`prompt.ts:1`); `isEnabled()` requires both `feature('KAIROS')` and a runtime entitlement (`:122-135`)

### Skill (`SkillTool/SkillTool.ts`)
- Job: invoke an installed Skill by name (inject its `SKILL.md` instructions). Parameters: `skill`, `args`. Example: `Skill {skill:"commit"}`

### Config (`ConfigTool/ConfigTool.ts:68`)
- Job: read/change a setting. Parameters: `setting`, `value` (omit = read)
- Details: `isReadOnly(input)` is dynamic — a read with no `value` counts as read-only (`:90-92`); registered only when `USER_TYPE==='ant'` (`tools.ts:214`)

## 8. Mode switching

### EnterPlanMode / ExitPlanMode
- EnterPlanMode: no parameters; enter "plan only, do not act" (`EnterPlanModeTool.ts:21-26`)
- ExitPlanMode (V2 `:148`): submit a plan for approval; parameter `allowedPrompts` — declare the permission categories needed to implement the plan (describes action categories, not concrete commands); when channels are active both enter and exit tools are disabled together, to avoid "you can enter but cannot leave" (`:167-178`)

### EnterWorktree / ExitWorktree
- EnterWorktree: create a temporary git worktree as an isolated workspace; parameter `name` (slug-validated, optional)
- ExitWorktree: `action: keep|remove`; **`isDestructive` is decided from the input** — only `remove` counts as destructive (`:168-170`); uncommitted changes require an explicit `discard_changes:true`; a session-scoped scope guard only recognizes worktrees created by this session (`:177-182`)
- Enablement: `isWorktreeModeEnabled()` is currently always true (`tools.ts:225`)

## 9. Scheduling and triggers

### Sleep (`SleepTool/prompt.ts:3`)
- Job: wait a specified duration (does not occupy a shell process; can be interrupted; the prompt recommends it over `Bash(sleep)`, and reminds "each wake-up is one API call; the prompt cache expires in 5 minutes")
- Enablement: `feature('PROACTIVE')||feature('KAIROS')` (`tools.ts:25-28`); the snapshot only has `prompt.ts`

### CronCreate / CronDelete / CronList (`ScheduleCronTool/`)
- Job: register a cron timed task that enqueues a prompt when due. CronCreate parameters: `cron`(5-field local time), `prompt`, `recurring`(false = one-shot), `durable`(true persists to `.claude/scheduled_tasks.json` across restarts)
- Enablement: `feature('AGENT_TRIGGERS')` (`tools.ts:29-35`). All shouldDefer ✅

### RemoteTrigger (`RemoteTriggerTool/`)
- Job: manage remote triggers; `action: list|get|create|update|run` + `trigger_id` + `body`
- Enablement: `feature('AGENT_TRIGGERS_REMOTE')` (`tools.ts:36-38`)

## 10. MCP and tool discovery (important)

### MCPTool wrapping

`MCPTool/MCPTool.ts` is a placeholder template (`name:'mcp'`, methods annotated "Overridden in mcpClient.ts"). Real wrapping is in `services/mcp/client.ts:1743-1832` `fetchToolsForClient`:
1. send `tools/list` to the MCP server
2. each tool is expanded/overridden as `{...MCPTool, name: fullyQualifiedName, ...}` (`:1769-1773`)
3. **naming rule** `mcp__<normalize(server)>__<tool>` (`mcpStringUtils.ts:48`); an SDK server can set `CLAUDE_AGENT_SDK_MCP_NO_PREFIX` to skip the prefix (`:1760-1763`)
4. **safety metadata comes from MCP annotations**: `annotations.readOnlyHint`→`isReadOnly`/`isConcurrencySafe`, `destructiveHint`→`isDestructive`, `openWorldHint`→`isOpenWorld` (`:1795-1809`); `mcpInfo` feeds permission rules (deny a whole server with `mcp__server`)
5. overlong descriptions are truncated at `MAX_MCP_DESCRIPTION_LENGTH` (`:1789-1794`); permissions default to passthrough, with a suggestion to add a `localSettings` allow rule

**McpAuthTool** (`:63`): when a server needs OAuth, dynamically register an `mcp__<server>__authenticate` tool to walk the user through authorization (`client.ts:2318/2331`)

**ListMcpResourcesTool / ReadMcpResourceTool**: list/read MCP resources (parameters `server?` / `server+uri`). Registered at the end of `getAllBaseTools` (`tools.ts:245-246`) but added on demand by `getTools()` as `specialTools` (`:301-307`)

### ToolSearch's `defer_loading` mechanism
Background: too many tools explode the token cost of tool definitions.
1. **Who is deferred**: `isDeferredTool()` (`ToolSearchTool/prompt.ts:62-108`) — every MCP tool is deferred; 26 built-ins marked `shouldDefer:true` are deferred (NotebookEdit, AskUserQuestion, WebFetch, WebSearch, EnterPlanMode, ExitPlanMode, TodoWrite, TaskCreate/Get/Update/List, TaskOutput, TaskStop, SendMessage, TeamCreate/Delete, CronCreate/Delete/List, ReadMcpResource, ListMcpResources, EnterWorktree, ExitWorktree, Config, LSP, RemoteTrigger). `alwaysLoad:true` (MCP can exempt via `_meta['anthropic/alwaysLoad']`, `client.ts:1785`) and ToolSearch itself are never deferred
2. **Modes**: `ENABLE_TOOL_SEARCH` env — `'tst'` (default, always defer), `'tst-auto'` (enable only over a threshold, `checkAutoThreshold` `utils/toolSearch.ts:444-467`), `'standard'` (off). Haiku does not support `tool_reference` and auto-degrades (`:204`, `239-252`)
3. **At request time**: deferred tools are sent to the API with `defer_loading:true` (`utils/api.ts:211-223`); the model sees only a name list, no schema
4. **At use time**: the model calls `ToolSearch {query:"select:Read,Edit"}` for an exact select, or `{query:"slack send"}` for keywords (MCP is tokenized on `mcp__server__action`, built-ins on CamelCase, scored in `ToolSearchTool.ts:132-216`); matching items' full JSONSchema is returned wrapped in a `<functions>` block
5. Registration: ToolSearch is only put on the tool list if `isToolSearchEnabledOptimistic()` is true (`tools.ts:247-249`)

## 11. LSP and internal/test tools

### LSP (`LSPTool/LSPTool.ts`)
- Job: call a language server for semantic code navigation. Parameters: `operation` (goToDefinition/findReferences/hover/documentSymbol/workspaceSymbol/goToImplementation/prepareCallHierarchy/incomingCalls/outgoingCalls), `filePath`, `line`, `character` (1-based)
- Enablement: `ENABLE_LSP_TOOL` env (`tools.ts:224`)

### StructuredOutput (`SyntheticOutputTool/:20`)
- Job: in non-interactive (SDK/headless) mode, have the model emit a structured result matching a caller-supplied JSON Schema (Ajv validation)
- Enablement: non-interactive sessions only (`:24-28`)

### TestingPermissionTool (`tools/testing/`)
- Test-only mock of the permission dialog; registered only when `NODE_ENV==='test'` (`tools.ts:244`)

## Addendum: global mechanisms

1. **Three-flag system**: `isReadOnly`/`isConcurrencySafe`/`isDestructive` all default false (`Tool.ts:750-761`); may be overridden statically or dynamically from input; concurrent scheduling uses `isConcurrencySafe`, permission warnings use `isDestructive`
2. **`maxResultSizeChars`**: Bash 30000, Grep 20000, Read Infinity, most 100000; over the cap the common pattern is "persist to disk + return `persistedOutputPath`"
3. **`getTools()` second filter** (`tools.ts:271-327`): `CLAUDE_CODE_SIMPLE` minimal mode keeps only Bash/Read/Edit; deny rules strip tools from the list before the model ever sees them (`filterToolsByDenyRules :262-269`); REPL mode hides primitives
4. **`assembleToolPool()`** (`:345-367`): merge built-ins with MCP, dedupe (built-ins win), sort each group by name, keep built-ins as a contiguous prefix — for prompt-cache stability; inserting MCP into the middle of built-ins would scramble every downstream cache key
5. **Tools cited by `tools.ts` but missing from the snapshot** (feature-gated experiments / internal): TungstenTool, SuggestBackgroundPRTool, MonitorTool, SendUserFileTool, PushNotificationTool, SubscribePRTool, VerifyPlanExecutionTool (`CLAUDE_CODE_VERIFY_PLAN=true`), OverflowTestTool, CtxInspectTool, TerminalCaptureTool, WebBrowserTool, SnipTool, ListPeersTool, WorkflowTool
