Advanced Tool Use & Agent Engineering

Agent SDK Lifecycle Hooks and the Permission Model

An autonomous agent that can edit files and run shell commands needs guardrails that don't depend on the model behaving. The Claude Agent SDK gives you two enforcement layers — a permission model and lifecycle hooks — and both run in your own code.

Claude 3P 101 · Updated July 2026 · Unofficial guide

The Claude Agent SDK packages the same tools, agent loop, and context management that power Claude Code into a library you can call from Python or TypeScript. Unlike the plain client SDK — where you implement the stop_reason == "tool_use" loop yourself and therefore sit in the middle of every action — the Agent SDK runs the tool loop autonomously inside a single query() call. That autonomy is the point, but it raises an obvious enterprise question: if Claude decides on its own to run a bash command or overwrite a file, where do your controls go?

The answer is a layered policy model. The layers matter because they fail differently: one is a static allowlist evaluated before anything runs, the other is arbitrary code you write that fires at defined moments in the agent's lifecycle.

Layer one: allowed_tools and permission_mode

The coarsest control is which tools the agent gets at all. The SDK ships built-in tools including Read, Write, Edit, Bash, Glob, Grep, WebSearch, WebFetch, and AskUserQuestion. The allowed_tools option (allowedTools in TypeScript) pre-approves a subset — a research agent that should never modify anything gets only Read, Glob, and Grep, and the rest simply don't exist for it. This is the cheapest guardrail you will ever configure, and for many internal agents it is the only one you need.

The second knob is permission_mode, which controls approval behavior for the tools that are allowed. Values such as "acceptEdits" let you auto-approve one class of action (file edits) while leaving others subject to interactive approval. The official docs include a dedicated guide to handling approvals and user input, so the agent can pause and ask a human rather than proceed.

from claude_agent_sdk import query, ClaudeAgentOptions

options = ClaudeAgentOptions(
    allowed_tools=["Read", "Glob", "Grep"],  # read-only agent
    permission_mode="acceptEdits",           # relevant when edits are allowed
)

async for message in query(prompt="Audit the config files", options=options):
    print(message)

Layer two: lifecycle hooks

Allowlists are static; hooks are dynamic. A hook is a callback you register that the SDK invokes at defined lifecycle points, and it can validate, log, block, or transform what the agent is doing. The documented hook events include:

HookFiresTypical enterprise use
PreToolUseBefore a tool executesBlock writes outside a project directory; deny shell commands matching a pattern
PostToolUseAfter a tool returnsAudit logging; redacting sensitive output
UserPromptSubmitWhen a prompt is submittedInput validation, prompt logging
StopWhen the agent finishesFinal checks before results are accepted
SessionStart / SessionEndSession boundariesSetup, teardown, per-session audit records

The docs note there are more events beyond these. The pattern that matters for governance is PreToolUse: because it runs before execution, it is a genuine enforcement point, not just observability. A PreToolUse hook that rejects any Bash command touching production credentials holds even if the model is confused or has been prompt-injected — the hook is your code, running in your process, and the model cannot talk its way past it.

Rule of thumb: use allowed_tools to remove entire capabilities, permission_mode to decide what needs a human, and PreToolUse hooks for the fine-grained rules in between ("Bash is allowed, but not that command"). Defense in depth means all three, not the fanciest one.

Why this matters on 3P platforms

This whole model travels to third-party platforms unchanged, because the Agent SDK is a library running in your process — only the model endpoint changes. Setting CLAUDE_CODE_USE_BEDROCK=1 routes it to Amazon Bedrock, CLAUDE_CODE_USE_VERTEX=1 to Google Cloud, CLAUDE_CODE_USE_FOUNDRY=1 to Microsoft Foundry, and CLAUDE_CODE_USE_ANTHROPIC_AWS=1 to Claude Platform on AWS. Your hooks and permission settings are identical on all of them. That makes the Agent SDK the most portable place to encode agent policy: server-side agent features like Managed Agents exist only on the first-party API and Claude Platform on AWS, but a hook-guarded SDK agent runs anywhere your cloud contract lives.

One more layer worth knowing about: the SDK loads Claude Code filesystem configuration — skills, commands, CLAUDE.md memory, plugins — from .claude/ directories by default. In a locked-down deployment you can restrict this with the setting_sources option, so an agent doesn't pick up instructions from files an attacker (or a well-meaning colleague) dropped into the working directory.

Where to go next

See how the same SDK is configured per platform in Agent SDK on Bedrock, on Vertex AI, and on Foundry, or compare the self-hosted approach with Anthropic's hosted harness in Agent SDK vs Managed Agents. For delegation inside one agent, read Building Subagent Systems with the Agent SDK.

Sources