Claude Certification Program · v1.0 · Effective July 2026 · All four tracks open

Home › Study guides › CCDV-F › Domain 1 › Lesson 1.2

CCDV-F · Domain 1 · 14.7% of the exam · Lesson 1.2 · 23 min read

Building Claude agents: the Agent SDK, custom loops, hosting and hooks

How to build a Claude agent with the Agent SDK or your own loop, when to self-host or use Managed Agents, and how hooks make required steps happen every time.

Written against skill 1.2 of the official CCDV-F exam guide (Version 1.0, effective July 2026). An independent resource, not affiliated with Anthropic; the practice questions are written from scratch.

1.2.1 What it takes to turn Claude into a working agent

Picture the IT team at a 300-person company: three people, one shared inbox and the same five requests every morning. "I'm locked out." "Is my laptop enrolled?" "Any news on ticket 4410?" Linnea, the IT manager, wants an assistant in the staff chat that can look up a ticket, check a laptop in the device-management system and reset a password. Hamid, the developer on her team, has been asked to build it.

The model is the easy part. Claude can read "my laptop LT-0042 won't let me log in" and decide to check the device first and then offer a reset. But a model call only returns a decision. Something has to run the check_device_status function, hand the result back, ask Claude again and stop when the job is done. It also has to remember the conversation, hold back a reset nobody approved and run somewhere reliable. That surrounding code is the agent harness: the loop, the tool execution, the permissions, the session state and the runtime it all lives in.

Hamid has three ways to get a harness. He can BORROW one: the Claude Agent SDK (software development kit), a Python and TypeScript library that runs the agent loop and tools behind Claude Code, Anthropic's coding agent. He can WRITE one on the Messages API, the plain endpoint that takes a conversation and returns Claude's next reply. Or he can RENT one: with Claude Managed Agents, Anthropic hosts the loop. Whichever he picks, Linnea's one fixed requirement is that every tool call is recorded on the ticket, every time. That is a job for a hook, your own code that the harness runs at a fixed moment in the loop.

The harness around the model

Claudedecides which tool to call next
The loopsend, run, feed back, repeat
Tool executionyour functions and systems
Permissionswhich calls may run
Sessionsthe conversation so far
Hookscode that runs at fixed moments
Runtimeyour servers or Anthropic's
Claude decides the next step; everything that turns that decision into a safe, repeatable action is harness code you borrow, write or rent.

1.2.2 The Agent SDK: Claude Code's loop as a library

Here is the first question a team asks: do you have to build the agent loop yourself? No. The Agent SDK gives you the loop, tools and context management that run Claude Code, as a library you call in a process you operate. You hand it a prompt and some options. It calls Claude, runs each tool Claude asks for (if your permissions allow it), feeds the results back and repeats until Claude answers without asking for a tool. Your code watches a stream of messages go past and reads the final result message.

What makes it more than a loop is everything that comes built in. Learn the six capabilities in the first column; the option names are there to recognise, not to recite.

What the SDK gives you What it does In the helpdesk agent
Built-in tools Read, Edit, Bash, Glob, Grep, WebSearch, WebFetch and more Removed with tools=[]: a helpdesk agent has no business running shell commands
Custom tools Your functions, declared with @tool and served by an in-process MCP (Model Context Protocol) server, the standard way to expose tools to Claude lookup_ticket, check_device_status, reset_password
Permissions allowed_tools pre-approves calls; the rest go through a permission mode and your approval callback Lookups run freely; a reset waits for approval
Sessions Every run is saved; pass resume with a session ID to continue it A colleague comes back after lunch
Limits max_turns and max_budget_usd stop a runaway run Capped at 15 turns and 50 cents
Hooks Callbacks at fixed points: before a tool runs, after it returns, when the agent stops Record every call on the ticket

Here is Hamid's first version; lookup_ticket and reset_password are defined the same way as the tool shown. Look at three lines: mcp_servers registers his tools (Claude sees them as mcp__helpdesk__lookup_ticket and so on), tools=[] removes every built-in tool, and allowed_tools pre-approves only the two read-only lookups.

from claude_agent_sdk import tool, create_sdk_mcp_server, query, ClaudeAgentOptions, ResultMessage

@tool("check_device_status", "Look up a laptop by asset tag: owner, OS version, encryption, last check-in.",
      {"asset_tag": str})
async def check_device_status(args):
    record = await mdm_client.get_device(args["asset_tag"])        # your device-management API
    return {"content": [{"type": "text", "text": record.summary()}]}

helpdesk = create_sdk_mcp_server(name="helpdesk", version="1.0.0",
                                 tools=[lookup_ticket, check_device_status, reset_password])
options = ClaudeAgentOptions(
    system_prompt=HELPDESK_PROMPT,
    mcp_servers={"helpdesk": helpdesk},             # tools become mcp__helpdesk__<name>
    tools=[],                                       # no built-in Bash, Read or Edit
    allowed_tools=["mcp__helpdesk__lookup_ticket", "mcp__helpdesk__check_device_status"],
    max_turns=15, max_budget_usd=0.50,              # safety nets, not the finish line
)
async for message in query(prompt=staff_message, options=options):
    if isinstance(message, ResultMessage):
        print(message.subtype, message.result)      # "success", or which limit it hit

Notice what is missing: reset_password is not pre-approved. When Claude asks for it, the call goes through the SDK's permission flow, where a can_use_tool callback can put the question to a person. In the default mode with no callback, the call is refused, and Claude reads the refusal as the tool result. The run itself ends when Claude replies without a tool call. ResultMessage.subtype then reads success, or error_max_turns or error_max_budget_usd if a safety net fired first. In that case query() also raises an error right after yielding the result, so production code wraps the loop in try.

1.2.3 When to write the loop yourself

At the design review, a colleague asks Hamid a fair question. The helpdesk agent needs three tools and a chat window, so why ship a library that runs the Claude Code binary, built around files, shell commands and coding work? The alternative is a custom agent loop: your own code calling the Messages API in a while loop. Every reply carries a stop_reason field that says why Claude stopped, and "tool_use" means the reply holds a tool request. Your code runs the tool, appends a tool_result to the conversation and sends the whole history again, until stop_reason says Claude is done.

Writing it yourself means owning everything the SDK did for you. The API is stateless, so your code keeps the conversation and resends it on every turn. It dispatches each tool call, retries a timeout, counts turns, decides who approves a reset and writes the log. That is more code, but every line of it is yours. It can sit inside the web service you already run and reuse its login, retries and database. For simple cases there is a middle path: the client SDKs offer a beta tool runner that drives the loop over your own tools. Approval steps and custom logging, though, still call for the loop you write.

Borrow the loop or write it

Agent SDK a borrowed harness

Loop, tools, sessionsalready built
Permissions and hooksconfigured, not coded
Runs the Claude Code binaryin a process you operate

Custom loop a written harness

while stop_reason == "tool_use"your code
History, dispatch, retries, limitsall yours to build
Fits inside a service you already run
The SDK hands you a finished harness to configure; a custom loop hands you complete control and the work of building it.

The requirement decides, not taste. Reach for the SDK when the agent's work resembles Claude Code's: long multi-step tasks, files and commands, sessions to resume, and hooks and permissions you would rather configure than build. Write the loop when the agent is a small part of an application that already owns state, security and logging, or when you need a control the SDK does not expose. Language can decide it too: the Agent SDK comes in Python and TypeScript, while the client SDKs also cover Go, Java, C#, PHP and Ruby. Avoid the worst of both: a hand-written loop that slowly rebuilds the SDK.

Hamid keeps the SDK. He needs sessions that resume after lunch, an approval step for resets and a hook for the ticket log, and the SDK gives him all three as options instead of projects.

1.2.4 Where the agent runs: self-hosted or Anthropic-hosted

The helpdesk agent works on Hamid's laptop. Now it has to serve 300 people, so where does the harness run? There are two answers, and one has a variant that trips people up.

Self-hosted means you run the harness. The SDK or your custom loop lives in your own containers or servers. With the SDK, each running session is a Claude Code process with its working directory and session files on local disk. A container that restarts loses those files unless you give the SDK a session store adapter that copies transcripts to your own backend. You own scaling, isolation, uptime and logs.

Anthropic-hosted means Claude Managed Agents: Anthropic runs the agent loop and its infrastructure for you. You create an agent once, an environment that says where its sessions run, and then one session per task. Your application talks to a session through events: you send user messages, and Claude runs tools and streams its progress back. The session keeps the conversation history on Anthropic's side. The harness also adds prompt caching (reusing the unchanged start of each request, which cuts cost and delay) and compaction (summarising old turns when the conversation grows too long). Managed Agents is in beta, and every request carries the managed-agents-2026-04-01 beta header, which the client SDKs add for you.

Managed Agents in four objects

Agentmodel, prompt, tools, MCP servers; created once
Environmentcloud sandbox or self-hosted sandbox
Sessionone running task, history kept by Anthropic
Eventsyour messages in, results streamed out
You define the agent and its environment once; each task is a session your application talks to through events.

Here is the variant. The environment can be an Anthropic-managed cloud sandbox or a self-hosted sandbox: Anthropic still runs the loop, but the tools execute on infrastructure you control. That suits data and systems that must stay inside your network. Think of a dispatcher and a local crew: the dispatcher picks each job, the crew does it on your premises and radios back the result. Claude, like the dispatcher, needs every result to choose the next step, so tool inputs and outputs still travel to Anthropic. Your own business tools stay yours in every model: a custom tool such as reset_password reaches your code as an event, and your code runs it and returns the result.

Deployment model Who runs what Choose it when
Self-hosted (Agent SDK or custom loop) You run the loop and the tools, on your infrastructure You need full control of the runtime, logging and data retention
Managed Agents, cloud sandbox Anthropic runs the loop and the sandbox You want minimal infrastructure, or long-running and scheduled work
Managed Agents, self-hosted sandbox Anthropic runs the loop; tools run on your infrastructure You want Anthropic to run the loop, but execution must stay in your network

Memorise the three rows and what decides between them. One more fact sharpens the choice. Managed Agents stores sessions on Anthropic's side by design, so it is not currently eligible for Zero Data Retention (ZDR) or for coverage under a HIPAA Business Associate Agreement (BAA). ZDR means Anthropic stores no prompts or responses after replying; a BAA is the contract US health-privacy law (HIPAA) requires before a vendor handles patient data. A workload bound by either needs a harness you run, built only on features Anthropic lists as eligible.

For the helpdesk, the chats are short and interactive, and the approval step and ticket hook belong in the internal service that already holds the credentials for the company's user directory, where passwords are reset. Hamid's team already runs that service, so they self-host the SDK agent. They note Managed Agents for another job Linnea has in mind: a nightly, hour-long review of every laptop that has not checked in for a month, which nobody wants to babysit a container for.

1.2.5 Hooks: making a step happen every time

Here is the failure that makes hooks necessary. Hamid's first attempt at Linnea's audit rule was one line in the system prompt: "After every tool call, call add_ticket_note." In testing it worked. In the first week, Linnea found password resets with no note. In long chats the instruction had been crowded out, and in short ones Claude had judged a note unnecessary. A prompt is a request to the model, and whether the model follows it is a probability, however firmly it is worded.

A hook takes that decision away from the model. It is a callback you register for an event in the loop, and the SDK runs it at that moment every time, whatever Claude decided. Hooks run in your application process, not inside the context window (the text Claude reads on each turn), so they take up none of it.

Three events matter most. PreToolUse fires before a tool runs and can allow, deny or ask about the call, or rewrite its input. PostToolUse fires after a tool succeeds (a failed call fires PostToolUseFailure instead) and can add context or replace the output Claude sees. Stop fires when the agent is about to finish. A matcher narrows a tool hook to certain tool names; leave it out and the hook fires for every tool call.

Think of a shop's card terminal. However friendly or forgetful the cashier is, every sale prints a receipt, because the terminal does it, not the cashier. Here is the helpdesk's receipt printer. Look at tool_name and tool_input, which say what just ran, and tool_use_id, which pairs this call with its PreToolUse event. The empty return means "carry on", and registering the callback for both post-tool events puts failed calls on the ticket too.

from claude_agent_sdk import HookMatcher

async def record_on_ticket(input_data, tool_use_id, context):
    try:
        await tickets.add_note(
            ticket_for_session(input_data["session_id"]),   # your own lookup
            tool=input_data["tool_name"],                    # e.g. mcp__helpdesk__reset_password
            arguments=redact(input_data["tool_input"]),      # never log secrets
            call_id=tool_use_id,                             # pairs Pre and Post events
        )
    except Exception as err:
        alert_it_team(f"Audit note failed: {err}")           # catch errors inside the hook
    return {}                                                # {} = let the loop carry on

options = ClaudeAgentOptions(
    # ...everything from before, plus:
    hooks={"PostToolUse": [HookMatcher(hooks=[record_on_ticket])],          # no matcher = every tool
           "PostToolUseFailure": [HookMatcher(hooks=[record_on_ticket])]},  # failed calls too
)

The same mechanism covers the three kinds of deterministic action the helpdesk needs. Learn the pattern: which event, what the callback returns.

Job Event and what the callback returns In the helpdesk agent
LOG an action PostToolUse (plus PostToolUseFailure), returning {} Record every tool call on the ticket
NORMALISE an input PreToolUse, returning updatedInput Rewrite "lt-0042 " as LT-0042 before the device lookup
ENFORCE a step Stop, returning decision: "block" and a reason Refuse to finish until the ticket has a resolution note

A blocking Stop hook should check the event's stop_hook_active field, so it never holds the agent in a loop over a condition that cannot be met. Hook callbacks come with the Agent SDK, but the idea travels. In a custom loop, a hook is a function you call before and after running each tool. In Managed Agents, your application already sees every custom tool call as an event before it answers, so the logging goes in that handler, and built-in tools can be set to wait for your approval.

1.2.6 The exam traps

Questions on this skill usually give a requirement and four ways to build it. The wrong answers use a mechanism that cannot guarantee the requirement, or a platform that does not fit it.

  • ✗ Putting "always log every action" or any other must-happen step in the system prompt. ✓ Register a hook. A prompt makes the step likely; a hook runs at a fixed point in the loop every time, outside the model's judgement.
  • ✗ Hand-writing a loop, then rebuilding sessions, permission prompts and compaction piece by piece. ✓ Use the Agent SDK when its loop and tools fit the job. Write your own loop when a stated requirement, such as living inside an existing service, needs control the SDK does not give.
  • ✗ Believing Anthropic-hosted means Anthropic runs your business tools. ✓ Managed Agents hosts the loop and the sandbox. A custom tool such as reset_password still runs in your code, which receives the call as an event and returns the result.
  • ✗ Choosing Managed Agents for a workload that needs Zero Data Retention or HIPAA coverage. ✓ Check data-retention requirements first. Managed Agents stores sessions server-side and is not currently eligible for either, so such workloads need a harness you run, built on features Anthropic lists as eligible.
  • ✗ Treating max_turns or a budget cap as the way the agent finishes. ✓ The loop ends when Claude replies without a tool call; the caps are safety nets. Check ResultMessage.subtype, so a run that hit a limit is never reported as a success.

Four ways to ask, one way to guarantee

A firmer system prompt"ALWAYS add a note"
A bigger modelfollows instructions more often, not always
An add_ticket_note toolClaude still chooses whether to call it
A higher max_turnsmore turns, same choice
A hook on the loop eventruns every time, outside the model
Each wrong fix leaves the audit note to the model's choice. Only code that runs on the loop event makes it certain.

1.2.7 Put it together: build a helpdesk agent with an audit hook

You now have every piece: a harness borrowed from the SDK, written as a custom loop or rented from Managed Agents, and hooks for the steps that must never be skipped. The fastest way to feel the difference between asking and enforcing is to build the helpdesk in miniature and watch its audit trail break.

The rest of Domain 1 builds on this harness. Agent patterns and frameworks (1.3) look at the shapes that recur inside it, such as subagents, memory and context management, and at frameworks such as LangGraph that package them. Beyond this domain, hooks return as safety controls (7.3), where a PreToolUse hook blocks a destructive action instead of logging it. Tool implementation (8.1) covers designing tools like reset_password and the approval patterns around them.

Key takeaways

  • ✓ An agent is Claude plus a harness: the loop, tool execution, permissions, session state and runtime that turn its decisions into actions.
  • ✓ The Agent SDK lends you Claude Code's loop as a library, with built-in and custom tools, permissions, sessions, limits and hooks configured rather than coded.
  • ✓ A custom loop on the Messages API gives full control and fits inside an existing service, but you build the history, dispatch, limits, approvals and logging yourself.
  • ✓ Self-hosted means you run the harness; Claude Managed Agents means Anthropic runs the loop, with tools in its cloud sandbox or in a self-hosted sandbox you control.
  • ✓ Your own business tools run in your code in every model, and Managed Agents stores sessions server-side, so check data-retention needs before choosing it.
  • ✓ A hook is code the harness runs at a fixed loop event every time, which makes logging, normalising and enforcing a step deterministic where a prompt is only likely to be followed.

Check your understanding

4 questions written for this lesson, then one from the CCDV-F question bank on the same topic. Every answer option is explained, including the ones you did not pick. Nothing is stored.

24 CCDV-F questions on Domain 1, free

Every question in the bank is tagged to a domain, so you can drill 24 questions on Agents and Workflows alone, or sit the full 53-question timed simulator.

Open the CCDV-F question bank → Back to Domain 1 →

The question bank is free. It asks for an account only because the quiz engine has to store answers to score them and show which domains are weak. The questions on this page need nothing.

Sources