Building Your First Claude Agent from Scratch

By Claude Collective

8 min read

In short

Learn how to build your first Claude AI agent from scratch using the Anthropic Python SDK. A step-by-step tutorial covering tool use, agentic loops, and real-world automation patterns.

On this page
  1. What Makes an Agent Different from a Chatbot?
  2. Prerequisites
  3. Step 1: Define Your Tools
  4. Step 2: Implement the Tool Functions
  5. Step 3: Build the Agentic Loop
  6. Step 4: Run Your Agent
  7. Understanding the Message Flow
  8. Best Practices for Production Agents
  9. What to Build Next
  10. Conclusion

AI agents are transforming how developers build software. Unlike a simple chatbot that responds and forgets, an agent can reason over a problem, use tools, take actions, verify results, and loop until the job is done. Claude is one of the most capable models for building agents — and with Anthropic's Python SDK, you can have your first working agent running in under 30 minutes.

In this tutorial, we'll build a real agent from scratch: one that can search files, read them, and summarize their contents — all autonomously.

What Makes an Agent Different from a Chatbot?

A standard LLM call is stateless: you send a message, get a reply. An agent is a loop:

  1. Gather context — read files, call APIs, search databases
  2. Take action — write code, send emails, update records
  3. Verify work — check the output, handle errors
  4. Repeat — continue until the task is complete

The key ingredient is tool use: giving Claude the ability to call functions you define, so it can interact with the real world beyond just generating text.

Prerequisites

Before we start, you'll need:

  • Python 3.10+
  • An Anthropic API key (get one at console.anthropic.com)
  • Basic familiarity with Python
pip install anthropic

Set your API key as an environment variable:

export ANTHROPIC_API_KEY=your_key_here

Step 1: Define Your Tools

Tools are functions Claude can decide to call. You describe them using JSON Schema, and Claude figures out when and how to use them. Let's define three tools for our file summarization agent:

tools = [
    {
        "name": "list_files",
        "description": "List all files in a given directory path.",
        "input_schema": {
            "type": "object",
            "properties": {
                "directory": {
                    "type": "string",
                    "description": "The directory path to list files from"
                }
            },
            "required": ["directory"]
        }
    },
    {
        "name": "read_file",
        "description": "Read the contents of a file.",
        "input_schema": {
            "type": "object",
            "properties": {
                "filepath": {
                    "type": "string",
                    "description": "The full path to the file to read"
                }
            },
            "required": ["filepath"]
        }
    },
    {
        "name": "write_summary",
        "description": "Write a summary to an output file.",
        "input_schema": {
            "type": "object",
            "properties": {
                "filename": {"type": "string"},
                "content": {"type": "string"}
            },
            "required": ["filename", "content"]
        }
    }
]

Step 2: Implement the Tool Functions

Now write the actual Python functions that execute when Claude calls a tool:

import os

def execute_tool(tool_name: str, tool_input: dict) -> str:
    if tool_name == "list_files":
        directory = tool_input["directory"]
        try:
            files = os.listdir(directory)
            return "\n".join(files)
        except Exception as e:
            return f"Error: {str(e)}"

    elif tool_name == "read_file":
        filepath = tool_input["filepath"]
        try:
            with open(filepath, "r") as f:
                return f.read()
        except Exception as e:
            return f"Error reading file: {str(e)}"

    elif tool_name == "write_summary":
        filename = tool_input["filename"]
        content = tool_input["content"]
        try:
            with open(filename, "w") as f:
                f.write(content)
            return f"Summary written to {filename}"
        except Exception as e:
            return f"Error writing file: {str(e)}"

    return f"Unknown tool: {tool_name}"

Step 3: Build the Agentic Loop

This is the heart of any agent: a loop that keeps running until Claude stops requesting tool calls.

import anthropic

client = anthropic.Anthropic()

def run_agent(task: str) -> str:
    messages = [
        {"role": "user", "content": task}
    ]

    print(f"Starting agent with task: {task}\n")

    while True:
        response = client.messages.create(
            model="claude-opus-4-6",
            max_tokens=4096,
            tools=tools,
            messages=messages
        )

        print(f"Agent step — stop_reason: {response.stop_reason}")

        # If Claude is done, return the final text response
        if response.stop_reason == "end_turn":
            for block in response.content:
                if hasattr(block, "text"):
                    return block.text

        # If Claude wants to use tools, execute them
        if response.stop_reason == "tool_use":
            # Add Claude's response to message history
            messages.append({
                "role": "assistant",
                "content": response.content
            })

            # Execute each tool call and collect results
            tool_results = []
            for block in response.content:
                if block.type == "tool_use":
                    print(f"  Calling tool: {block.name} with {block.input}")
                    result = execute_tool(block.name, block.input)
                    print(f"  Result: {result[:100]}...")
                    tool_results.append({
                        "type": "tool_result",
                        "tool_use_id": block.id,
                        "content": result
                    })

            # Feed tool results back to Claude
            messages.append({
                "role": "user",
                "content": tool_results
            })

Step 4: Run Your Agent

if __name__ == "__main__":
    result = run_agent(
        "List all .py files in the current directory, "
        "read each one, and write a summary of what "
        "each file does to a file called SUMMARY.md"
    )
    print("\nFinal response:", result)

Run it:

python agent.py

You'll see Claude autonomously listing files, reading each one, and writing the final summary — all without any manual intervention.

Understanding the Message Flow

Here's what happens in each loop iteration:

  1. You send the task (or tool results) as a user message
  2. Claude responds with either text (end_turn) or tool calls (tool_use)
  3. If tool calls, you execute them and return results as another user message
  4. Claude sees the results and decides next steps
  5. Repeat until end_turn

This back-and-forth is the fundamental pattern behind every Claude-powered agent, whether it's a coding assistant, a data pipeline, or a customer service bot.

Best Practices for Production Agents

Add a max iteration limit to prevent infinite loops:

MAX_ITERATIONS = 20
for iteration in range(MAX_ITERATIONS):
    # ... your loop logic

Use structured system prompts to guide agent behavior:

system = """You are a file analysis agent. Always:
1. List files before reading them
2. Read files completely before summarizing
3. Be concise in summaries — one paragraph per file"""

Log everything — agents are hard to debug without visibility into each tool call and result.

Handle errors gracefully — return meaningful error strings from tools so Claude can decide whether to retry or skip.

What to Build Next

With this foundation, you can extend your agent to:

  • Search the web by adding a web_search tool using a Serper or Brave Search API
  • Query databases with a SQL execution tool
  • Send Slack messages or create GitHub issues
  • Write and execute code using a sandboxed Python runner

The pattern is always the same: define the tool, implement the function, feed results back to Claude. The model handles all the reasoning about when and how to use them.

Conclusion

Building a Claude agent boils down to three things: defining tools, implementing them, and running an agentic loop. Claude's intelligence handles the rest — deciding which tools to call, in what order, and when the task is complete.

This is just the beginning. In upcoming posts, we'll cover multi-agent orchestration, connecting agents to external services via MCP, and deploying agents to production with proper observability. For now, experiment with your new agent — add a tool, change the task, watch Claude adapt.

The future of software isn't just code you write. It's agents that write and run code for you.

  • claude-agent
  • python
  • api
  • automation
  • getting-started

Written by

Claude Collective

All articles

Explore the directory

Claude SkillsInstall Skills that teach Claude your workflows.MCP serversConnect Claude to your tools and data.Claude AgentsSubagents for coding, review, testing and research.