Agent Construction with Claude: CCDV-F study guide
CCDV-F · Agents and Workflows, topic weight 5.3% of the exam
Agent Construction with Claude is the largest topic in Domain 1, Agents and Workflows (14.7% of the CCDV-F exam), at 5.3%. It tests whether you can pick the right way to build and run a Claude agent (the Claude Agent SDK, your own loop, or Claude Managed Agents) and decide where its tools execute.
What the official guide covers
The Claude Certified Developer Foundations exam guide (version 1.0, effective July 2026) describes this topic as the methods, tools and platforms for constructing Claude agents:
| What the guide lists | What it means in practice |
|---|---|
| The Claude Agent SDK | Build an agent in Python or TypeScript with Claude Code's loop, tools, permissions and sessions |
| Custom agent loops and harnesses | Write the loop yourself on the Messages API for full control |
| Managed agent deployment models (self-hosted vs Anthropic-hosted) | Decide who runs the loop and where tools execute |
| Hooks for deterministic actions | Run your own code before or after a tool call so a rule holds every time |
Three ways to build the loop
| Option | Who runs the agent loop | What you get | You still build |
|---|---|---|---|
| Messages API with a client SDK | Your code | Raw access to the API; the client SDK's beta tool runner can drive the loop for you | Tool execution, stop handling, context management, sessions |
| Claude Agent SDK | Your process (the SDK runs the Claude Code binary as a subprocess) | Built-in tools (Read, Edit, Bash, Grep and more), permissions, sessions, subagents, hooks, MCP | Hosting, scaling and session storage |
| Claude Managed Agents | Anthropic | A hosted harness: you create an agent, an environment and a session, then send events and stream results | Your application logic and any self-hosted sandbox workers |
A custom loop fits a small, fixed tool set inside an existing service, such as a support bot calling three internal APIs; CCAR-F 1.1 covers the mechanics. The Agent SDK fits an agent that needs files, a shell, subagents or context management, because writing those yourself is a large job.
Building with the Agent SDK
The SDK packages are claude_agent_sdk (Python) and @anthropic-ai/claude-agent-sdk (TypeScript). Use query() for one-off tasks and ClaudeSDKClient (Python) when one session handles several exchanges, such as a chat. You shape the agent with ClaudeAgentOptions:
| Option | What it controls |
|---|---|
tools | Which built-in tools exist in Claude's context at all ([] removes them all) |
allowed_tools | Tools that run without a permission prompt; it does not hide other tools |
disallowed_tools | Tools to deny; a bare name removes the tool from context |
permission_mode | Baseline approval behaviour: default, acceptEdits, plan, dontAsk, auto or bypassPermissions |
mcp_servers | External or in-process MCP servers; their tools are named mcp__<server>__<tool> |
agents | Subagents, each an AgentDefinition |
setting_sources | Whether to load CLAUDE.md, skills and settings from disk; [] loads none |
max_turns | Cap on tool-use round trips |
The stream ends with a ResultMessage. Check its subtype before reading result: success carries the answer, while error_max_turns, error_max_budget_usd and error_during_execution do not. A single-shot query() raises after yielding an error result, so wrap it in try.
An Agent SDK agent with one custom tool (Python)
import asyncio
from typing import Any
from claude_agent_sdk import tool, create_sdk_mcp_server, query, ClaudeAgentOptions, ResultMessage
ORDERS = {"A100": "shipped", "A101": "processing"} # stand-in for your order system
@tool("get_order_status", "Return the shipping status of one order. IDs look like A100.", {"order_id": str})
async def get_order_status(args: dict[str, Any]) -> dict[str, Any]:
status = ORDERS.get(args["order_id"])
if status is None:
return { # a clear error result lets Claude correct itself
"content": [{"type": "text", "text": f"No order {args['order_id']}. Check the ID with the customer."}],
"is_error": True,
}
return {"content": [{"type": "text", "text": f"Order {args['order_id']}: {status}"}]}
orders = create_sdk_mcp_server(name="orders", version="1.0.0", tools=[get_order_status])
async def main():
options = ClaudeAgentOptions(
mcp_servers={"orders": orders},
tools=[], # no file or shell tools for this agent
allowed_tools=["mcp__orders__get_order_status"],
permission_mode="dontAsk", # anything not pre-approved is denied
setting_sources=[], # do not load CLAUDE.md or settings from this host
max_turns=10,
)
try:
async for message in query(prompt="Where are orders A100 and A999?", options=options):
if isinstance(message, ResultMessage):
print(message.subtype, message.result if message.subtype == "success" else "")
except Exception as error:
print(f"Run stopped: {error}")
asyncio.run(main())
The in-process MCP server runs inside your application. An uncaught exception in the handler reaches Claude as a raw error result; returning is_error yourself lets you write a message Claude can act on.
Self-hosted or Anthropic-hosted
Agent SDK on your own infrastructure. You run the loop. Each session is a claude subprocess that owns a shell, a working directory and transcripts on local disk. The hosting guide describes ephemeral, long-running, hybrid and multi-agent container patterns. Local disk is lost on restart, so a session users expect to resume needs a SessionStore adapter.
Managed Agents with an Anthropic-managed cloud sandbox. Anthropic runs the loop and the sandbox. You define an agent (model, system prompt, tools, MCP servers, skills), an environment and a session, then send events and stream results back. Event history is kept server-side. It suits long-running, asynchronous work. Because sessions are stored by Anthropic, the docs state that Managed Agents does not currently qualify for HIPAA BAA coverage or Zero Data Retention, so a workload under either requirement goes to the Agent SDK or a custom loop on a covered configuration, however well the hosted option fits otherwise.
Managed Agents with a self-hosted sandbox. Orchestration stays with Anthropic, but tools run on a worker on your infrastructure, so the agent's files, processes and network traffic stay in your environment. The worker needs only outbound HTTPS. Use it when the agent must reach internal services that are not publicly routable.
| Situation | Choose | Why |
|---|---|---|
| Small fixed tool set inside an existing service | Custom Messages API loop | Full control, nothing extra to host |
| Agent needs files, shell and subagents, and you run your own platform | Agent SDK, self-hosted | Claude Code's tools and loop, on your containers |
| Long-running asynchronous tasks, no wish to build loop or sandbox | Managed Agents, cloud sandbox | Anthropic hosts loop, sandbox and session history |
| Workload needs Zero Data Retention or carries PHI | Agent SDK or custom loop on a covered configuration | Managed Agents stores sessions server-side and is not currently eligible |
| Tools must run inside your network, but you do not want to run the loop | Managed Agents, self-hosted sandbox | Anthropic orchestrates; your worker executes tools |
| A rule must hold on every tool call | PreToolUse hook | Enforced in code, not left to Claude |
Wire the loop so it stops, and asks, on purpose
Whichever option runs the loop, the same four parts have to be right:
- Register only the tools the task needs. Each extra tool with an overlapping description makes routing less reliable. Prefer a few general tools Claude can combine over many narrow ones, and add a tool when a real gap shows up.
- Scope the system prompt to the task and the tools it really has. Never describe a tool that is not registered.
- Answer every tool call. Each
tool_useblock gets atool_resultwith the same ID, and all results from one turn go back together in a single user message. - Define exit conditions that do not rely on Claude choosing to stop. A step cap, a budget or a checked goal ends the run.
Give the agent a way to see what each action did, too: read a file before editing it, run the validator or tests after. An agent that cannot observe results cannot correct itself.
Then decide where a person must look. Ask one question of every tool: what is the worst outcome if this runs with nobody checking?
| Checkpoint | Triggered when | Catches |
|---|---|---|
| Before an irreversible call | The agent is about to write, delete, send or deploy | Actions that cannot be undone |
| After a plan, before execution | The agent has proposed a sequence of steps | A wrong plan whose steps would all succeed |
| On an unexpected result | A tool returns an error, nothing, or a value out of range | Failures that a retry will not fix |
A worked case: an agent repaired configuration files and exited when its validator passed. In a customer environment it corrected an out-of-range value, the validator passed, and the loop ended cleanly, but other services depended on the old value and began failing. The missing piece was a pause between "change proposed" and "change written" for a tool that touches a live system. In a custom loop that pause is a few lines:
import os
import anthropic
client = anthropic.Anthropic()
MODEL = os.environ["CLAUDE_MODEL"]
NEEDS_APPROVAL = {"write_config"} # tools that change a live system
MAX_STEPS = 8 # the run ends here even if Claude keeps calling tools
def run(task, tools, execute, approve):
messages = [{"role": "user", "content": task}]
for _ in range(MAX_STEPS):
response = client.messages.create(model=MODEL, max_tokens=2048, tools=tools, messages=messages)
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason != "tool_use":
return response # caller checks end_turn, max_tokens, refusal
results = []
for block in response.content:
if block.type != "tool_use":
continue
if block.name in NEEDS_APPROVAL and not approve(block.name, block.input):
results.append({"type": "tool_result", "tool_use_id": block.id, "is_error": True,
"content": "A reviewer rejected this change. Propose another fix or stop."})
continue
results.append({"type": "tool_result", "tool_use_id": block.id,
"content": execute(block.name, block.input)})
messages.append({"role": "user", "content": results})
raise RuntimeError(f"No result after {MAX_STEPS} steps")
The gate keys on the tool name inside the loop, so reads pass through, and it runs before execute: approval after the call is too late, and one approval at the start cannot cover a call Claude has not yet proposed.
In the Agent SDK the same gate is the can_use_tool callback, which receives any tool call that no rule or mode has already approved and returns allow or deny. It does not fire for calls an allow rule or mode has already approved, such as a tool in allowed_tools or an edit under acceptEdits, so a check that must cover every call belongs in a hook.
Hooks for deterministic actions
A hook is your own function that the SDK runs at a set point, such as PreToolUse before a tool runs. A PreToolUse hook that denies a call stops it, and Claude receives your reason instead. The Agent SDK hook API is covered in CCAR-F 1.5 Agent SDK hooks, and Claude Code hooks configured in settings.json are covered in Claude Hooks.
Rules that decide exam answers
- Match the build option to who runs the loop. Your code: Messages API or Agent SDK. Anthropic: Managed Agents.
- Self-hosted sandbox is not self-hosted orchestration. With a Managed Agents self-hosted sandbox, tools run on your worker while Anthropic still runs the loop.
allowed_toolsapproves; it does not restrict. To remove a tool, leave it out oftoolsor list it indisallowed_tools.- Check the result subtype. Only
successcarriesresult; a turn or budget limit returns an error subtype. - Hard rules go in hooks. A prompt instruction is guidance; a
PreToolUsehook is enforcement. - Gate the irreversible tool, not the whole run. Put the human check before the call that cannot be undone, and give the loop an exit that does not wait for Claude to stop.
Where it appears in the exam
Agent Construction with Claude carries 5.3% of the exam within Domain 1, Agents and Workflows (14.7%), roughly three items out of 53. Expect scenarios that describe a team's constraints and ask which build or deployment option fits, plus questions on Agent SDK options and result handling.
Two sample questions
These are original Timo practice questions. They are not official exam questions.
Build exercise
- Run the order-status agent with a valid and an invalid order. Confirm Claude reads your
is_errormessage. - Set
max_turns=1, give it a task needing two tool calls, and confirm the error subtype path runs. - Add a
PreToolUsehook that denies IDs starting with "X" and check that Claude receives your reason. - Write a one-page note choosing between the self-hosted Agent SDK and Managed Agents for this agent.
Practise this topic
- Claude Certified Developer practice exam: free, 20 questions, no sign-up
- CCDV-F study guide: all topics
- Worked example: Production Claude tool loop
- Same topic in another exam: 1.3 Subagent invocation and context
- Previous topic: Agent Architecture
- Next topic: Agent Patterns and Frameworks
Sources
- Claude Certified Developer Foundations Exam Guide, version 1.0, effective July 2026 (Anthropic), Domain 1 topic: Agent Construction with Claude
- Claude Agent SDK documentation: How the agent loop works
- Claude Agent SDK documentation: Hosting the Agent SDK
- Claude Platform documentation: Claude Managed Agents overview
- Claude Platform documentation: Self-hosted sandboxes
By Amotion AI