TimoBy Amotion AI

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 listsWhat it means in practice
The Claude Agent SDKBuild an agent in Python or TypeScript with Claude Code's loop, tools, permissions and sessions
Custom agent loops and harnessesWrite 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 actionsRun your own code before or after a tool call so a rule holds every time

Three ways to build the loop

OptionWho runs the agent loopWhat you getYou still build
Messages API with a client SDKYour codeRaw access to the API; the client SDK's beta tool runner can drive the loop for youTool execution, stop handling, context management, sessions
Claude Agent SDKYour process (the SDK runs the Claude Code binary as a subprocess)Built-in tools (Read, Edit, Bash, Grep and more), permissions, sessions, subagents, hooks, MCPHosting, scaling and session storage
Claude Managed AgentsAnthropicA hosted harness: you create an agent, an environment and a session, then send events and stream resultsYour 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:

OptionWhat it controls
toolsWhich built-in tools exist in Claude's context at all ([] removes them all)
allowed_toolsTools that run without a permission prompt; it does not hide other tools
disallowed_toolsTools to deny; a bare name removes the tool from context
permission_modeBaseline approval behaviour: default, acceptEdits, plan, dontAsk, auto or bypassPermissions
mcp_serversExternal or in-process MCP servers; their tools are named mcp__<server>__<tool>
agentsSubagents, each an AgentDefinition
setting_sourcesWhether to load CLAUDE.md, skills and settings from disk; [] loads none
max_turnsCap 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.

SituationChooseWhy
Small fixed tool set inside an existing serviceCustom Messages API loopFull control, nothing extra to host
Agent needs files, shell and subagents, and you run your own platformAgent SDK, self-hostedClaude Code's tools and loop, on your containers
Long-running asynchronous tasks, no wish to build loop or sandboxManaged Agents, cloud sandboxAnthropic hosts loop, sandbox and session history
Workload needs Zero Data Retention or carries PHIAgent SDK or custom loop on a covered configurationManaged Agents stores sessions server-side and is not currently eligible
Tools must run inside your network, but you do not want to run the loopManaged Agents, self-hosted sandboxAnthropic orchestrates; your worker executes tools
A rule must hold on every tool callPreToolUse hookEnforced 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:

  1. 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.
  2. Scope the system prompt to the task and the tools it really has. Never describe a tool that is not registered.
  3. Answer every tool call. Each tool_use block gets a tool_result with the same ID, and all results from one turn go back together in a single user message.
  4. 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?

CheckpointTriggered whenCatches
Before an irreversible callThe agent is about to write, delete, send or deployActions that cannot be undone
After a plan, before executionThe agent has proposed a sequence of stepsA wrong plan whose steps would all succeed
On an unexpected resultA tool returns an error, nothing, or a value out of rangeFailures 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_tools approves; it does not restrict. To remove a tool, leave it out of tools or list it in disallowed_tools.
  • Check the result subtype. Only success carries result; a turn or budget limit returns an error subtype.
  • Hard rules go in hooks. A prompt instruction is guidance; a PreToolUse hook 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.

Question 1

A bank wants an agent that reads files on internal file shares and calls internal services that are not reachable from the internet. The platform team does not want to build or operate an agent loop or store session history. Which option fits best?

Answer: B. A self-hosted sandbox keeps tool execution and network traffic inside the bank while Anthropic runs the loop and keeps session history. A moves the files out of the bank's network and still cannot reach the internal services. C and D both require the team to run the loop and session storage, which they want to avoid.

Question 2

An Agent SDK service sets max_turns=15. On long tasks the service logs a None answer and the calling code then fails. The code reads message.result from the ResultMessage without other checks. What should the developer change first?

Answer: D. The result field exists only on the success subtype, and a turn-limit stop is reported as error_max_turns, so the code must branch on the subtype. A removes a safety cap and still leaves other error subtypes unhandled. B moves the problem rather than handling it. C throws away the SDK for a check the SDK already gives you.

Build exercise

  1. Run the order-status agent with a valid and an invalid order. Confirm Claude reads your is_error message.
  2. Set max_turns=1, give it a task needing two tool calls, and confirm the error subtype path runs.
  3. Add a PreToolUse hook that denies IDs starting with "X" and check that Claude receives your reason.
  4. Write a one-page note choosing between the self-hosted Agent SDK and Managed Agents for this agent.

Practise this topic

Sources