Skip to content

Tools — Complete Real-World Guide

Every action Claude takes is a tool call. Understanding tools helps you understand exactly what Claude is doing and why.


Reads the content of a file.

When Claude uses it: To understand existing code before editing, to check config files, to read test files, to follow import chains.

You say:

Fix the bug where users can't log in after password reset

Claude does (internally):

Read("src/auth/reset.ts") ← find the reset logic
Read("src/auth/login.ts") ← find the login logic
Read("src/models/user.ts") ← check the user model
Read("tests/auth/reset.test.ts") ← check existing tests

What you see: Claude explains the bug (reset sets a temporary token but doesn’t clear it after use), then fixes it.

Name the file in your prompt to help Claude start in the right place:

# Faster:
"Fix the bug in src/auth/reset.ts — users can't log in after password reset"
# Slower (Claude has to search first):
"Fix the login bug"

Modifies a specific section of an existing file. Claude replaces an exact string with new content.

When Claude uses it: Any time it needs to change existing code — fixing bugs, adding features, refactoring.

You say:

Add input validation to the createUser function in src/services/user.ts

Claude does:

Read("src/services/user.ts") ← reads the whole file first
Edit("src/services/user.ts",
old: 'async function createUser(data) {\n return db.user.create({ data })',
new: 'async function createUser(data) {\n const validated = UserSchema.parse(data)\n return db.user.create({ data: validated })'
)

What you see: The exact line is changed, everything else preserved.

Claude edits by exact string match, not by line number. If the file changed since Claude read it, the edit fails (rather than silently editing the wrong place).


Creates a new file or completely overwrites an existing one.

When Claude uses it: Creating new files (tests, components, config), generating boilerplate, writing scripts.

You say:

Create a Zod validation schema for the User model based on prisma/schema.prisma

Claude does:

Read("prisma/schema.prisma")
Write("src/validators/user.ts",
content: `import { z } from 'zod'
export const UserSchema = z.object({
email: z.string().email(),
name: z.string().min(1).max(100),
role: z.enum(['admin', 'user', 'guest']).default('user'),
})
export type UserInput = z.infer<typeof UserSchema>
`
)

Runs a shell command and returns the output.

When Claude uses it: Running tests, installing dependencies, git operations, build commands, checking system state.

You say:

Add a test for the password reset flow, then run the tests

Claude does:

Read("tests/auth/reset.test.ts") ← understand existing test style
Write("tests/auth/reset-flow.test.ts") ← writes new test file
Bash("npm test tests/auth/") ← runs and checks output

If tests fail:

Bash output: "Error: resetToken is not cleared after use"
→ Claude reads the source again, finds the bug, edits it, runs tests again

You say:

Commit these changes with a meaningful message

Claude does:

Bash("git status") ← see what changed
Bash("git diff --staged") ← review staged changes
Bash('git commit -m "fix: clear reset token after successful login"')

You say:

Why is the dev server failing to start?

Claude does:

Bash("npm run dev 2>&1 | head -50") ← capture first 50 lines of output
Bash("lsof -i :3000") ← check if port is in use
Bash("cat .env | grep -v SECRET") ← check env vars (without leaking secrets)

Finds files matching a pattern. Returns a list of paths.

When Claude uses it: Discovering project structure, finding all files of a type, finding test files.

You say:

How is error handling done across the codebase?

Claude does:

Glob("src/**/*.ts") ← find all TypeScript files
Glob("src/**/*error*") ← find error-related files
Glob("src/**/*Error*")

Then reads the relevant ones.

src/**/*.ts all .ts files under src/
**/*.test.ts all test files anywhere
src/api/*/index.ts index files in each api subdirectory
*.config.{js,ts} config files in the root

Searches file contents with a regex pattern. Faster than reading every file.

When Claude uses it: Finding where a function is defined, finding all usages of a variable, searching for specific patterns.

You say:

Rename the sendEmail function to sendTransactionalEmail everywhere

Claude does:

Grep("sendEmail", "**/*.ts") ← find all usages
→ Returns: src/services/mailer.ts:14, src/handlers/auth.ts:87, src/workers/reset.ts:23
Read("src/services/mailer.ts") ← read each file
Edit("src/services/mailer.ts", ...) ← rename
Edit("src/handlers/auth.ts", ...)
Edit("src/workers/reset.ts", ...)
Bash("npm run build") ← verify no errors

You say:

Check if we're exposing any sensitive fields in API responses

Claude does:

Grep("password|secret|token|key", "src/handlers/**/*.ts")
Grep("res.json|res.send", "src/handlers/**/*.ts")

Reads the matches and reports any leaks.

Grep("TODO:", "**/*.ts") find all TODOs
Grep("console\\.log", "src/**") find debug logs
Grep("any", "src/**/*.ts") find TypeScript 'any' usage
Grep("SELECT \*", "**/*.ts") find unoptimized SQL
Grep("0\.0\.0\.0|localhost", "**") find hardcoded hosts

Fetches the content of a URL.

When Claude uses it: Reading documentation, fetching API specs, checking package changelogs.

You say:

Upgrade our Stripe integration to use the new Payment Intents API

Claude does:

WebFetch("https://stripe.com/docs/api/payment_intents")
WebFetch("https://stripe.com/docs/upgrades")
Read("src/services/stripe.ts")
Edit("src/services/stripe.ts", ...)

You say:

Is there a breaking change in the Prisma 5 upgrade?

Claude does:

WebFetch("https://www.prisma.io/docs/guides/upgrade-guides/upgrading-versions/upgrading-to-prisma-5")

Then summarizes what you need to change.


Searches the web and returns relevant results.

When Claude uses it: Finding solutions to errors, researching unfamiliar APIs, finding examples.

You say:

I'm getting "ECONNREFUSED 127.0.0.1:5432" in tests — fix it

Claude does:

WebSearch("vitest postgres ECONNREFUSED test setup")
WebSearch("prisma test database connection refused jest vitest")

Reads the top results, identifies that test setup needs DATABASE_URL pointing to the test DB, fixes vitest.config.ts.


Spawns a subagent — a separate Claude instance with its own context.

When Claude uses it: Complex research tasks, parallel work, tasks requiring isolation.

You say:

Audit the entire codebase for security vulnerabilities

Without agents, Claude reads everything into one context window (expensive, slow, limited).

With agents:

Agent("Explore", "scan src/handlers/ for input validation gaps")
Agent("Explore", "scan src/services/ for SQL injection risks")
Agent("Explore", "scan src/auth/ for authentication weaknesses")

Three agents run in parallel, each focused on their domain, each returns a focused report. Claude combines them into one security audit.

Real Example 2: Parallelizing independent tasks

Section titled “Real Example 2: Parallelizing independent tasks”

You say:

Write tests for the payment service AND update the API documentation

Claude does:

Agent("test-writer", "write tests for src/services/payment.ts")
Agent("doc-writer", "update docs/api.md with payment endpoint docs")

Both happen simultaneously — done in half the time.


Manages an in-session task list.

When Claude uses it: Breaking down complex multi-file tasks, tracking progress on long refactors.

You say:

Refactor all the API handlers to use the new error format

Claude does:

Glob("src/handlers/**/*.ts") ← find all handlers: 8 files
TodoWrite([
{ id: 1, task: "Update users.ts", status: "pending" },
{ id: 2, task: "Update orders.ts", status: "pending" },
{ id: 3, task: "Update payment.ts", status: "pending" },
... 5 more ...
])
Read("src/handlers/users.ts")
Edit("src/handlers/users.ts", ...)
TodoWrite([{ id: 1, status: "done" }, ...]) ← mark complete
... repeats for each file ...

You can see the progress as it works through the list.


You say:

Add rate limiting to all API endpoints — max 100 requests per minute per IP

Here is every tool call Claude makes, in order:

1. Glob("src/handlers/**/*.ts") ← discover all handlers
2. Read("src/handlers/index.ts") ← understand routing
3. Read("src/middleware/") ← find existing middleware
Glob("src/middleware/**")
4. WebSearch("express rate limiting middleware npm 2024")
5. Bash("npm list express-rate-limit") ← check if already installed
6. Bash("npm install express-rate-limit") ← install package
7. Write("src/middleware/rateLimiter.ts") ← create middleware
8. Read("src/app.ts") ← find where middleware is applied
9. Edit("src/app.ts", ...) ← add the middleware
10. Read("tests/middleware/") ← check test patterns
Glob("tests/**/*middleware*")
11. Write("tests/middleware/rateLimiter.test.ts") ← write tests
12. Bash("npm test tests/middleware/") ← run tests
13. Bash("npm run build") ← verify TypeScript compiles

13 tool calls, all automatic, all building on each other’s output. You wrote one sentence; Claude did the research, the implementation, and the tests.

  • Trick:Grep finds words, Glob finds names, Read before Edit, Bash for the rest.”
  • Show: run a task and pause at each tool call: ‘why this tool here?’
  • They’ll trip on: pasting file contents into chat instead of letting Read fetch selectively.

Learning path:06-how-claude-code-works.md · Index · ➡ 08-sessions-and-checkpointing.md

Written by Fenil Patel