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:
| Category | Tools | Purpose |
|---|---|---|
| Read | Read, Glob, Grep, LSP | Gather information |
| Write | Write, Edit | Modify files |
| Execute | Bash, Task | Run 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 testsBest 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:
- Read tools: Gather context (Read, Glob, Grep)
- Write tools: Modify files (Write, Edit)
- Execute tools: Run commands and spawn subagents (Bash, Task)
- Coordination: Tools work together in sequences
- Safety: Dangerous operations require permission
Next: Let's explore CLAUDE.md, the context engineering system that lets you customize Claude Code's behavior.