Tutorial
Beginner

Building Your First Claude Agent from Scratch

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.

Claude Collective8 min readFebruary 24, 2026

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:

  • Gather context — read files, call APIs, search databases
  • Take action — write code, send emails, update records
  • Verify work — check the output, handle errors
  • 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:

  • You send the task (or tool results) as a user message
  • Claude responds with either text (end_turn) or tool calls (tool_use)
  • If tool calls, you execute them and return results as another user message
  • Claude sees the results and decides next steps
  • 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
      Building Your First Claude Agent from Scratch