Chapter 3
20 min read
Section 19 of 175

Tool System: Read, Write, Execute

How Claude Code Works

Introduction

Claude Code's power comes from its tools. These aren't just simple functions - they're carefully designed interfaces that let Claude interact with your filesystem, run commands, and modify code safely.

Tools are the API to the World: Without tools, Claude is just a text generator. With tools, it becomes a capable agent that can read your code, make changes, run tests, and verify its work.

Tool Overview

Claude Code's tool system is organized into three categories:

CategoryToolsPurpose
ReadRead, Glob, Grep, LSPGather information
WriteWrite, EditModify files
ExecuteBash, TaskRun code and spawn subagents

Tool Schema Example

🐍tool_schema.py
1# Every tool has a standardized schema for LLM function calling
2read_tool = {
3    "name": "Read",
4    "description": "Read the contents of a file from the filesystem",
5    "input_schema": {
6        "type": "object",
7        "properties": {
8            "file_path": {
9                "type": "string",
10                "description": "The absolute path to the file to read",
11            },
12            "offset": {
13                "type": "number",
14                "description": "Line number to start reading from (optional)",
15            },
16            "limit": {
17                "type": "number",
18                "description": "Maximum number of lines to read (optional)",
19            },
20        },
21        "required": ["file_path"],
22    },
23}

Read Tools

Read

The fundamental file reading tool:

🐍read_tool.py
1class ReadTool:
2    """Read file contents with optional pagination."""
3
4    name = "Read"
5    description = """
6    Read the contents of a file from the filesystem.
7    - Returns file contents with line numbers
8    - Supports binary file detection
9    - Handles various encodings
10    - Can read specific line ranges for large files
11    """
12
13    def execute(
14        self,
15        file_path: str,
16        offset: int | None = None,
17        limit: int | None = None,
18    ) -> ToolResult:
19        path = Path(file_path)
20
21        if not path.exists():
22            return ToolResult(
23                success=False,
24                error=f"File not found: {file_path}"
25            )
26
27        # Detect binary files
28        if self.is_binary(path):
29            return ToolResult(
30                success=True,
31                output=f"[Binary file: {path.stat().st_size} bytes]"
32            )
33
34        # Read with line numbers
35        lines = path.read_text().splitlines()
36
37        if offset is not None:
38            lines = lines[offset:]
39        if limit is not None:
40            lines = lines[:limit]
41
42        # Format with line numbers
43        formatted = []
44        start = offset or 0
45        for i, line in enumerate(lines):
46            formatted.append(f"{start + i + 1:4d} | {line}")
47
48        return ToolResult(
49            success=True,
50            output="\n".join(formatted)
51        )

Glob

Find files matching patterns:

🐍glob_tool.py
1class GlobTool:
2    """Find files matching glob patterns."""
3
4    name = "Glob"
5    description = """
6    Find files matching a glob pattern.
7    Patterns: **/*.ts, src/*.tsx, **/test_*.py
8    Returns file paths sorted by modification time.
9    """
10
11    def execute(
12        self,
13        pattern: str,
14        path: str | None = None,
15    ) -> ToolResult:
16        root = Path(path) if path else Path.cwd()
17
18        matches = list(root.glob(pattern))
19
20        # Sort by modification time (newest first)
21        matches.sort(key=lambda p: p.stat().st_mtime, reverse=True)
22
23        # Format results
24        lines = [str(m.relative_to(root)) for m in matches[:100]]
25
26        return ToolResult(
27            success=True,
28            output=f"Found {len(matches)} files:\n" + "\n".join(lines)
29        )

Grep

Search file contents:

🐍grep_tool.py
1class GrepTool:
2    """Search file contents using regex."""
3
4    name = "Grep"
5    description = """
6    Search for patterns in file contents.
7    Uses ripgrep for fast searching.
8    Supports regex, file type filters, and context lines.
9    """
10
11    def execute(
12        self,
13        pattern: str,
14        path: str | None = None,
15        file_type: str | None = None,
16        context_lines: int = 2,
17    ) -> ToolResult:
18        cmd = ["rg", "--color=never", "-n"]
19
20        if context_lines:
21            cmd.extend(["-C", str(context_lines)])
22        if file_type:
23            cmd.extend(["-t", file_type])
24
25        cmd.append(pattern)
26
27        if path:
28            cmd.append(path)
29
30        result = subprocess.run(cmd, capture_output=True, text=True)
31
32        if result.returncode == 0:
33            return ToolResult(success=True, output=result.stdout)
34        elif result.returncode == 1:
35            return ToolResult(success=True, output="No matches found")
36        else:
37            return ToolResult(success=False, error=result.stderr)

Write Tools

Write

Create or overwrite entire files:

🐍write_tool.py
1class WriteTool:
2    """Write content to a file."""
3
4    name = "Write"
5    description = """
6    Write content to a file, creating it if necessary.
7    Overwrites existing content. Use Edit for partial changes.
8    """
9
10    def execute(self, file_path: str, content: str) -> ToolResult:
11        path = Path(file_path)
12
13        # Create parent directories
14        path.parent.mkdir(parents=True, exist_ok=True)
15
16        # Track if file existed for reporting
17        existed = path.exists()
18        old_content = path.read_text() if existed else None
19
20        # Write the file
21        path.write_text(content)
22
23        # Generate diff for user review
24        if existed and old_content:
25            diff = self.generate_diff(old_content, content)
26            action = "Modified"
27        else:
28            diff = f"+++ New file with {len(content)} bytes"
29            action = "Created"
30
31        return ToolResult(
32            success=True,
33            output=f"{action}: {file_path}\n{diff}"
34        )

Edit

Make precise, surgical changes to files:

🐍edit_tool.py
1class EditTool:
2    """Make precise edits to files."""
3
4    name = "Edit"
5    description = """
6    Replace specific text in a file.
7    Requires exact match of old_string.
8    More surgical than Write - use for small changes.
9    """
10
11    def execute(
12        self,
13        file_path: str,
14        old_string: str,
15        new_string: str,
16        replace_all: bool = False,
17    ) -> ToolResult:
18        path = Path(file_path)
19
20        if not path.exists():
21            return ToolResult(
22                success=False,
23                error=f"File not found: {file_path}"
24            )
25
26        content = path.read_text()
27
28        # Check for exact match
29        if old_string not in content:
30            # Try to find similar text to help debug
31            similar = self.find_similar(content, old_string)
32            return ToolResult(
33                success=False,
34                error=f"old_string not found in file. "
35                      f"Similar text: {similar[:200]}"
36            )
37
38        # Count occurrences
39        count = content.count(old_string)
40        if count > 1 and not replace_all:
41            return ToolResult(
42                success=False,
43                error=f"old_string appears {count} times. "
44                      f"Set replace_all=True or make old_string more specific."
45            )
46
47        # Perform replacement
48        if replace_all:
49            new_content = content.replace(old_string, new_string)
50        else:
51            new_content = content.replace(old_string, new_string, 1)
52
53        path.write_text(new_content)
54
55        return ToolResult(
56            success=True,
57            output=f"Replaced {count} occurrence(s) in {file_path}"
58        )

Edit Requires Exact Match

The Edit tool fails if old_string isn't found exactly. This is intentional - it prevents accidental wrong edits. Always read the file first to get exact content.

Execute Tools

Bash

Execute shell commands:

🐍bash_tool.py
1class BashTool:
2    """Execute bash commands."""
3
4    name = "Bash"
5    description = """
6    Execute a bash command in a persistent shell session.
7    Use for: running tests, git commands, installations.
8    NOT for: file reading (use Read), file editing (use Edit).
9    """
10
11    dangerous = True  # Requires permission
12
13    def __init__(self):
14        self.session = ShellSession()
15
16    def execute(
17        self,
18        command: str,
19        timeout: int = 120,
20        working_dir: str | None = None,
21    ) -> ToolResult:
22        # Validate command isn't obviously dangerous
23        if self.is_dangerous(command):
24            return ToolResult(
25                success=False,
26                error="Potentially dangerous command. "
27                      "Requires explicit user approval."
28            )
29
30        try:
31            result = self.session.run(
32                command,
33                timeout=timeout,
34                cwd=working_dir,
35            )
36
37            output = result.stdout
38            if result.stderr:
39                output += f"\n[stderr]\n{result.stderr}"
40
41            return ToolResult(
42                success=result.returncode == 0,
43                output=output[:30000],  # Truncate long output
44                error=None if result.returncode == 0 else f"Exit code: {result.returncode}"
45            )
46
47        except subprocess.TimeoutExpired:
48            return ToolResult(
49                success=False,
50                error=f"Command timed out after {timeout}s"
51            )
52
53    def is_dangerous(self, command: str) -> bool:
54        """Check for potentially dangerous commands."""
55        dangerous_patterns = [
56            r"rm\s+-rf\s+/",
57            r"chmod\s+777",
58            r"curl.*\|.*sh",
59            r"git\s+push.*--force",
60        ]
61        return any(re.search(p, command) for p in dangerous_patterns)

Task

Spawn subagent for complex tasks:

🐍task_tool.py
1class TaskTool:
2    """Spawn a subagent for complex subtasks."""
3
4    name = "Task"
5    description = """
6    Launch a subagent to handle a complex subtask.
7    Use for: exploration, research, parallel work.
8    The subagent has access to the same tools.
9    """
10
11    def execute(
12        self,
13        description: str,
14        prompt: str,
15        subagent_type: str = "general",
16    ) -> ToolResult:
17        # Create a subagent
18        subagent = Agent(
19            type=subagent_type,
20            parent_context=self.context,
21        )
22
23        # Run the subtask
24        result = subagent.run(prompt)
25
26        return ToolResult(
27            success=True,
28            output=f"Subtask '{description}' completed:\n{result.summary}"
29        )

Tool Coordination

Claude Code often uses multiple tools in sequence:

📝tool_sequence.txt
1Goal: "Add input validation to the signup form"
2
31. Glob("**/signup*.tsx") → Find signup components
42. Read("src/components/SignupForm.tsx") → Understand current code
53. Grep("validate", file_type="ts") → See existing validation patterns
64. Read("src/utils/validation.ts") → Understand validation utilities
75. Edit(
8     "src/components/SignupForm.tsx",
9     old_string="const handleSubmit = () => {",
10     new_string="const handleSubmit = () => {\n  if (!validateForm()) return;"
11   ) → Add validation call
126. Bash("npm run typecheck") → Verify no type errors
137. Bash("npm test -- --testPathPattern=signup") → Run related tests

Best Practices for Tool Use

  • Read before edit: Always read a file before editing it
  • Verify after change: Run tests or typecheck after modifications
  • Use the right tool: Edit for small changes, Write for new files
  • Be specific: Use Glob with patterns, not "*"

Tool Selection

The LLM chooses tools based on their descriptions. Clear, specific descriptions lead to better tool selection.

Summary

Claude Code's tool system:

  1. Read tools: Gather context (Read, Glob, Grep)
  2. Write tools: Modify files (Write, Edit)
  3. Execute tools: Run commands and spawn subagents (Bash, Task)
  4. Coordination: Tools work together in sequences
  5. Safety: Dangerous operations require permission
Next: Let's explore CLAUDE.md, the context engineering system that lets you customize Claude Code's behavior.