CertSafari
    CLAUDE-CERTIFIED-DEVELOPER-FOUNDATIONS-CCDV-F · Lessons

    Domain 1 · Lesson 2/25

    Claude Agent SDK: The Agent Loop, Messages and Hooks

    Agent Construction with Claude

    9 min read
    4.9% of exam
    4 sources
    Published 29 Sep 2026
    Docs as of 24 Sep 2026

    What you will be able to do

    • Choose between the Agent SDK, the Client SDK with your own tool loop, and Managed Agents for a given build
    • Trace one run of the Agent SDK loop from the init message to the ResultMessage, and handle each message type correctly
    • Use hooks to enforce deterministic rules on tool calls, and know which hook events each SDK language supports

    Key concept

    The agent loop — An agent is a repeating cycle. Claude looks at the current state and either asks for tools or answers. The harness runs the requested tools and sends the results back. The cycle ends only when Claude replies without asking for any tool. Every construction choice in this subdomain comes down to who runs that loop and where.

    1.Three ways to build a Claude agent

    Before you write any agent code, decide who runs the loop. Anthropic offers three routes, and they differ on exactly that point. The Agent SDK is a library you embed in your own Python or TypeScript application. It runs the Claude Code binary for you, so your process gets the same loop, built-in tools, permissions, sessions and hooks that Claude Code uses. The Client SDK is one level lower. It calls the Claude API directly, and you write the tool loop yourself (or hand it to the Client SDK's beta tool runner). This is the custom-loop route: you own every step of the harness. Managed Agents moves the loop to Anthropic. It is a hosted harness, configured through the Claude API.

    Who runs the agent loop in each construction option
    OptionWhat you getWho drives the loop
    Agent SDKA library that runs the Claude Code binary, with built-in tools, permissions, sessions and hooksThe SDK, inside a process you operate
    Client SDKDirect access to the Claude APIYou write the tool loop, or use the beta tool runner
    Managed AgentsA hosted agent harness, used from your language's SDK, the ant CLI or the REST APIAnthropic's hosted harness
    Claude Code CLIThe terminal interface for daily interactive useThe CLI, driven by a person at a terminal

    The Agent SDK is more than a loop runner. It ships the capabilities you would otherwise have to build: built-in tools for files, commands and web search; hooks; subagents that spawn specialised agents for focused subtasks; MCP connections to external tools and data; permissions that decide which tools run automatically; and sessions you can resume or fork. It also loads skills, commands and memory from .claude/ and ~/.claude/, the same way Claude Code does. The Managed Agents docs describe the trade-off from the other side: the Messages API is best when you need custom agent loops and fine-grained control.

    Sources12

    2.One run of the loop, message by message

    Calling query() starts the loop. First, Claude receives your prompt together with the system prompt, the tool definitions and the conversation history. This is where your agent's system prompt and registered tools take effect. The SDK announces the start with a SystemMessage of subtype "init", which carries session metadata. Claude then evaluates the state and returns text, one or more tool calls, or both. The SDK runs each requested tool and feeds the results back for Claude's next decision. Each full cycle of evaluate-then-execute counts as one turn. In the docs' bug-fix example, Claude runs npm test, reads auth.ts and its test file, edits the fix and runs the tests again. It then ends with a text-only reply, and that reply closes the loop.

    Message types the Agent SDK yields during a run
    MessageWhen it appearsWhat to use it for
    SystemMessageSession lifecycle events; subtypes include "init" and "compact_boundary"Session metadata, and noticing when compaction has happened
    AssistantMessageOnce per content block in Claude's responses, including the final text-only oneProgress: what Claude is doing each turn and which tools it called
    UserMessageAfter each tool execution, and for user input you stream in mid-loopThe tool result content that was sent back to Claude
    StreamEventOnly when partial messages are enabledRaw streaming events such as text deltas and tool input chunks
    ResultMessageEnd of the agent loopFinal text, token usage, cost and session ID; check subtype
    Handling progress and the final result in Python: branch on the ResultMessage subtypepython
    async def main():
        try:
            async for message in query(prompt="Summarize this project"):
                if isinstance(message, AssistantMessage):
                    # Each AssistantMessage carries one content block
                    for block in message.content:
                        if isinstance(block, TextBlock):
                            print(f"Claude: {block.text}")
                        elif isinstance(block, ToolUseBlock):
                            print(f"Tool call: {block.name}")
                if isinstance(message, ResultMessage):
                    if message.subtype == "success":
                        print(message.result)
                    else:
                        print(f"Stopped: {message.subtype}")

    Two details trip up real code. First, a ResultMessage does not guarantee success: its subtype tells you whether the task succeeded or hit a limit. Second, a few trailing system events, such as prompt_suggestion, can arrive after the result, so iterate the stream to the end instead of breaking out at the result. The languages also differ in how you read messages. Python checks types with isinstance(). TypeScript checks the type string and wraps the raw API message, so content blocks live at message.message.content. A single-shot query() also raises after yielding an error result, so wrap the loop in a try block.

    A platform team is building an internal code-fixing bot. Their first prototype calls the Messages API directly: it sends the prompt, checks whether stop_reason is "tool_use", executes the tool locally, and manually appends the tool result before calling the API again to continue the loop. They want to eliminate this hand-written loop and get tool execution, permission handling, and context management out of the box, while keeping the bot running inside their own Kubernetes cluster. Which approach should they adopt?

    Sources3

    3.Hooks: deterministic code at fixed points in the loop

    The model decides which tool to call next. Some rules should never be left to that decision: never write .env, log every tool call, get a human's approval before a database write. Hooks cover these cases. A hook is your own code, and the SDK runs it at a fixed point in the agent lifecycle, whatever the model intended. The loop has a clear place for them: between Claude's request and the tool's execution, a hook can intercept, modify or block the call. The flow is always the same. An event fires, the SDK collects the hooks registered for it, matchers filter which ones run, the callbacks execute, and each callback returns a decision.

    Registering a PreToolUse hook whose matcher limits it to Write and Edit callspython
    options = ClaudeAgentOptions(
            hooks={
                # Register the hook for PreToolUse events
                # The matcher filters to only Write and Edit tool calls
                "PreToolUse": [HookMatcher(matcher="Write|Edit", hooks=[protect_env_files])]
            }
        )
    The callback's decision: deny with a reason, or return an empty object to allowpython
    # Block the operation if targeting a .env file
        if file_name == ".env":
            return {
                "hookSpecificOutput": {
                    "hookEventName": input_data["hook_event_name"],
                    "permissionDecision": "deny",
                    "permissionDecisionReason": "Cannot modify .env files",
                }
            }
    
        # Return empty object to allow the operation
        return {}

    Blocking is only one use. The docs also list auditing every tool call, changing inputs and outputs (sanitising data, injecting credentials, redirecting file paths), requiring human approval, and tracking the session lifecycle. Which events you can hook depends on the language. The TypeScript SDK exposes far more events than the Python SDK.

    Selected hook events and SDK support
    Hook eventPython SDKTypeScript SDKExample use
    PreToolUseYesYesBlock dangerous shell commands (can block or modify)
    PostToolUseYesYesLog all file changes to audit trail
    PostToolBatchNoYesInject conventions once for the whole batch
    PreCompactYesYesArchive full transcript before summarizing
    SubagentStopYesYesAggregate results from parallel tasks
    StopYesYesSave session state before exit
    SessionStartNoYesInitialize logging and telemetry
    PermissionRequestYesYesCustom permission handling

    A startup wants to offer an AI teammate feature that runs long, asynchronous multi-hour sessions for its customers, but the small engineering team does not want to build or operate any sandboxing, container orchestration, or session-storage infrastructure themselves. Which deployment model best fits their constraints?

    Sources341

    Exam traps

    Each one states something that sounds right. Open it to see what is actually true.

    1. 1.As soon as the ResultMessage arrives, it is safe to break out of the message loop.Why is that wrong?

      A small number of trailing system events, such as prompt_suggestion, can arrive after the result. The docs say to iterate the stream to completion.

      Covered in One run of the loop, message by message

    2. 2.In TypeScript, an assistant message's content blocks are at message.content, just as in Python.Why is that wrong?

      The TypeScript SDK wraps the raw API message in a .message field, so the blocks are one level deeper.

      Covered in One run of the loop, message by message

    3. 3.Every hook event is available in both the Python and TypeScript Agent SDKs.Why is that wrong?

      Many events are TypeScript-only. SessionStart, SessionEnd, PostToolBatch and PostCompact, for example, are not available in the Python SDK.

      Covered in Hooks: deterministic code at fixed points in the loop

    Sources

    Every claim above is drawn from one of these pages, quoted as it was written on the date shown.

    1. 1.
      “A library that runs the Claude Code binary, with Claude Code’s capabilities, such as built-in tools, permissions, sessions, and hooks.”
      ↩︎ Three ways to build a Claude agent
      “You write the tool loop yourself, or let the client SDK’s beta tool runner drive it.”
      ↩︎ Three ways to build a Claude agent
      “Run custom code at key points in the agent lifecycle”
      ↩︎ Hooks: deterministic code at fixed points in the loop
    2. 3.
      “Claude receives your prompt, along with the system prompt, tool definitions, and conversation history.”
      ↩︎ One run of the loop, message by message
      “Each full cycle is one turn.”
      ↩︎ One run of the loop, message by message
      “Check the subtype field to determine whether the task succeeded or hit a limit.”
      ↩︎ One run of the loop, message by message
      “You can use hooks to intercept, modify, or block tool calls before they run.”
      ↩︎ Hooks: deterministic code at fixed points in the loop
      “Claude continues calling tools and processing results until it produces a response with no tool calls.”
      ↩︎ Key concept
      “iterate the stream to completion rather than breaking on the result”
      ↩︎ Exam trap 1
      “content blocks are at message.message.content, not message.content.”
      ↩︎ Exam trap 2
    3. 4.
      “Block dangerous operations before they execute, like destructive shell commands or unauthorized file access”
      ↩︎ Hooks: deterministic code at fixed points in the loop
      “SessionStart | No | Yes | Session initialization”
      ↩︎ Exam trap 3

    Continue to page 2 of 2

    Deploying Claude Agents: Self-Hosted SDK vs Managed Agents