Back to Learn
Agents
11 min read

Claude Agents Explained: How to Build and Use AI Agents

clauderules.net

What Is a Claude Agent?

An agent is an AI model given tools and the ability to act autonomously over multiple steps. Unlike a one-shot prompt that gets a single response, an agent observes its environment, plans a course of action, executes steps, checks results, and continues until the goal is complete — all without requiring a human prompt at every step.

The key difference: a chat interaction is a single turn. An agent is a loop. The agent loop looks like this:

  1. Observe — receive the task and current context
  2. Plan — decide what actions to take
  3. Act — call a tool (read a file, run a command, query an API)
  4. Reflect — read the tool result and decide what to do next
  5. Repeat until the task is complete

How Claude Code's Agent Mode Works

Claude Code itself is an agent. When you ask it to "add authentication to my Next.js app," Claude doesn't just write code and stop. It:

  • Reads your existing files to understand the current structure
  • Plans which files need to be created or modified
  • Makes edits incrementally, verifying as it goes
  • Runs build commands to check for errors
  • Fixes errors it discovers autonomously
  • Reports what it did and what (if anything) still needs your attention

This agentic behavior is why Claude Code can handle complex, multi-file tasks that would be impractical with a chat interface.

Claude Code asks for confirmation before taking irreversible actions (deleting files, running potentially dangerous commands). You can adjust this with the --dangerously-skip-permissions flag for fully automated workflows where you trust the context.

Building a Custom Agent with the Anthropic SDK

Anthropic's SDK lets you build custom agents in Python or TypeScript. The core pattern is: define tools (functions Claude can call), start a conversation with a system prompt, and run the agent loop.

typescript
import Anthropic from "@anthropic-ai/sdk";
import * as fs from "fs";

const client = new Anthropic();

// Define the tools your agent can use
const tools: Anthropic.Tool[] = [
  {
    name: "read_file",
    description: "Read the contents of a file",
    input_schema: {
      type: "object",
      properties: {
        path: { type: "string", description: "The file path to read" },
      },
      required: ["path"],
    },
  },
  {
    name: "write_summary",
    description: "Write a summary to a file",
    input_schema: {
      type: "object",
      properties: {
        path: { type: "string", description: "Output file path" },
        content: { type: "string", description: "Summary content" },
      },
      required: ["path", "content"],
    },
  },
];

// Tool execution handler
function executeTool(name: string, input: Record<string, string>): string {
  if (name === "read_file") {
    return fs.readFileSync(input.path, "utf-8");
  }
  if (name === "write_summary") {
    fs.writeFileSync(input.path, input.content);
    return `Summary written to ${input.path}`;
  }
  return "Unknown tool";
}

// Agent loop
async function runAgent(task: string) {
  const messages: Anthropic.MessageParam[] = [
    { role: "user", content: task },
  ];

  while (true) {
    const response = await client.messages.create({
      model: "claude-opus-4-6",
      max_tokens: 4096,
      tools,
      messages,
    });

    // Add assistant response to history
    messages.push({ role: "assistant", content: response.content });

    // If Claude is done, exit the loop
    if (response.stop_reason === "end_turn") break;

    // If Claude wants to use a tool, execute it
    if (response.stop_reason === "tool_use") {
      const toolResults: Anthropic.ToolResultBlockParam[] = [];
      for (const block of response.content) {
        if (block.type === "tool_use") {
          const result = executeTool(block.name, block.input as Record<string, string>);
          toolResults.push({ type: "tool_result", tool_use_id: block.id, content: result });
        }
      }
      messages.push({ role: "user", content: toolResults });
    }
  }
}

runAgent("Read the file src/index.ts and write a summary of what it does to SUMMARY.md");

Sub-agents and Orchestration

For complex tasks, you can have Claude spawn sub-agents to work in parallel. In Claude Code, this is done via the Agent tool, which creates a new Claude instance with its own context and tools.

A common pattern is an orchestrator agent that breaks a large task into sub-tasks, spawns specialist sub-agents for each, and then synthesizes the results. For example:

  • Orchestrator receives: "Audit the entire codebase for security issues"
  • Spawns sub-agents: one per directory or module
  • Each sub-agent reads files, identifies issues, and returns a report
  • Orchestrator synthesizes all reports into a final summary
When using sub-agents in Claude Code, give each one a focused, scoped task. Overly broad sub-agent instructions cause the same problems as overly broad prompts.

Agent Permissions and Safety

Giving an agent broad permissions is convenient but risky. Best practices for agent safety:

  • Least privilege. Give agents only the tools they need for the task. A research agent doesn't need write access.
  • Confirm before destructive operations. Deleting files, running migrations, or pushing to production should always require explicit human confirmation.
  • Sandbox untrusted tasks. Run agents that process untrusted input in isolated environments.
  • Log all tool calls. Keep an audit trail of what the agent did — especially for production systems.
  • Test with dry runs. Add a dry_run: boolean flag to your tools so you can verify agent behavior without side effects.

Building a Custom Agent: Step-by-Step

Step 1: Define the tools. What does your agent need to do? List the external operations (file I/O, API calls, DB queries) and model each as a tool with a clear name, description, and input schema.

Step 2: Write the system prompt. This is the agent's "personality" and operating instructions. Be explicit about the agent's goal, constraints, and how it should handle errors. Store it in CLAUDE.md or a dedicated prompt file.

Step 3: Test incrementally. Start with one tool and simple tasks. Add complexity once the core loop works reliably.

Step 4: Add error recovery. What should the agent do when a tool fails? Write explicit instructions: "If read_file fails, try the alternate path. If that also fails, report the error and stop."

Step 5: Add observability. Log each tool call, its inputs, and its result. This makes debugging agent behavior much easier.

Community Agent Prompts

Building an agent from scratch takes time. The community at clauderules.net/agents has contributed pre-built agent system prompts for common workflows — code review agents, documentation agents, testing agents, and more.

Browse the collection, use a prompt as-is, or adapt it to your project's conventions by customizing the system prompt with your CLAUDE.md rules.

Get the Claude Code Starter Pack

Top CLAUDE.md rules for Next.js, TypeScript, Python, Go, and React — delivered free to your inbox.