The Claude Agent SDK hands your application the same tools, agent loop and permission engine that power Claude Code, which means a few lines of configuration decide whether your agent can read a directory or run any shell command on the host. This guide explains how the SDK evaluates permissions, how hooks and the canUseTool callback fit together, how to isolate the agent process and keep credentials outside it, how MCP servers are authorised, and what to log. It is for engineers and security reviewers building custom agents. The interactive CLI is covered in the Claude Code Security Guide; this page covers the SDK as a library inside your own services. The SDK moves quickly and several behaviours are tied to specific Claude Code versions.
This is an independent guide from NHI Mgmt Group, based on Anthropic's public documentation as of November 2026. It is not sponsored or reviewed by Anthropic. Product features change, so confirm settings against Anthropic's current documentation.
Key takeaways
- Permissions run in a fixed six-step order. Hooks, then deny rules, then ask rules, then the permission mode, then allow rules, then your
canUseToolcallback; a deny rule wins even inbypassPermissionsmode. allowed_toolsis not an allowlist. It pre-approves the tools you name; unlisted tools still exist, and underbypassPermissionsthey are approved too. Usedisallowed_toolsordontAskto close the surface.- Put mandatory checks in a
PreToolUsehook. Calls approved by a mode or allow rule never reachcanUseTool, but hooks run before every other step and a hook deny holds in every mode. - The agent should never hold the keys. Anthropic recommends a proxy outside the agent's boundary that injects credentials, enforces a domain allowlist and logs requests.
- Default options are not multi-tenant safe. Omitting
settingSourcesloads user, project and local settings, CLAUDE.md files, hooks and.mcp.json; isolate tenants withsettingSources: [], per-tenant directories and disabled auto memory.
How identity and access work in the Claude Agent SDK
The SDK spawns and supervises a claude CLI subprocess, talking to it over stdio. That subprocess owns a shell, a working directory and JSONL session transcripts on local disk, and it acts with the operating system identity of whatever process launched it. There is no separate agent identity inside the SDK: the agent's effective access is the union of the host account's file and network access, the credentials in its environment, and whatever the permission engine approves. Treat each agent deployment as a non-human identity with its own runtime account, its own API key and its own scoped tool credentials.
Authentication to the model
- Claude API: the subprocess reads
ANTHROPIC_API_KEYfrom its environment. The SDK does not load.envfiles automatically, so the key is wherever your process manager or secret store puts it. - Cloud providers: set
CLAUDE_CODE_USE_BEDROCK=1(Amazon Bedrock),CLAUDE_CODE_USE_VERTEX=1(Google Cloud),CLAUDE_CODE_USE_FOUNDRY=1(Microsoft Foundry) orCLAUDE_CODE_USE_ANTHROPIC_AWS=1withANTHROPIC_AWS_WORKSPACE_ID, and the provider's own credential chain applies. That moves the identity question into your cloud IAM. - Proxying:
ANTHROPIC_BASE_URLsends sampling requests to a proxy you run, which can inject the key so it never sits in the agent's environment. - Consumer login: Anthropic states that, unless previously approved, third-party developers may not offer claude.ai login or rate limits in products built on the SDK. Production agents authenticate with API keys or a cloud provider.
Key handling for the Claude API itself is covered in the Anthropic API Keys Security Guide.
The permission evaluation order
Every tool request passes through the same sequence. Knowing it is the single most useful piece of SDK security knowledge, because most misconfigurations come from assuming a later step can override an earlier one.
| Step | What it does | Security consequence |
|---|---|---|
| 1. Hooks | Registered PreToolUse callbacks and settings-file hooks run first and can deny, ask, defer or allow | A hook allow does not skip later deny and ask rules; a hook deny applies in every mode |
| 2. Deny rules | disallowed_tools and settings.json deny rules | Denied even in bypassPermissions and even if a hook returned allow; canUseTool is not called |
| 3. Ask rules | settings.json ask rules, tools that require user interaction, connector tools an organisation set to ask | Fall through to canUseTool even in bypassPermissions; denied in dontAsk |
| 4. Permission mode | bypassPermissions approves almost everything; acceptEdits approves listed file operations; plan sends edits to the callback | The mode is where broad approval happens, so it is the riskiest single setting |
| 5. Allow rules | allowed_tools and settings.json allow rules; reads inside working directories and read-only Bash commands approve themselves | Bare tool names approve every call to that tool; scoped rules such as Bash(npm test *) approve only matches |
6. canUseTool | Your runtime callback decides anything still unresolved | Skipped in dontAsk (denied instead); never reached by calls approved earlier |
Some actions are never auto-approved by an allow rule: recognised critical-path removals with rm or rmdir (such as removing the home directory), Bash commands flagged as a dangerous rm, and reads from network paths. Anthropic notes the critical-path check can miss some commands, so it is a backstop, not a control you design around.
Admin roles and privileged access
The SDK has no admin console of its own. Privilege is expressed through the permission mode, the tool surface and who can change configuration files the agent loads. Choose the mode deliberately for each agent.
| Mode | Behaviour | When it fits |
|---|---|---|
default | No mode-based approvals; anything needing approval goes to canUseTool. From Claude Code v2.1.302 a non-interactive app starts here unless configured otherwise, and denies calls nobody approves | Interactive products where a human reviews actions |
dontAsk | Anything that would prompt is denied; only pre-approved and no-approval calls run | Headless agents with a fixed, explicit tool list: the recommended locked-down pairing with allowedTools |
acceptEdits | Approves Edit and Write plus mkdir, touch, rm, rmdir, mv, cp, sed inside the working directory and additionalDirectories | Isolated workspaces where file changes are reviewed afterwards; note it includes rm |
plan | Explores and plans; file edits always go to the callback, even if a hook allowed them | Review flows where a human approves a plan before execution |
auto | A classifier model reviews actions such as shell commands and network requests | Where a model-based check is acceptable; a session silently drops to default if switched to an unsupported model |
bypassPermissions | Approves everything that reaches the mode step; Claude Code refuses to start in it as root or under sudo outside a recognised sandbox on Linux and macOS | Only inside a disposable sandbox with no credentials worth stealing |
- Mode changes at runtime:
set_permission_mode()(Python) andsetPermissionMode()(TypeScript) change the mode mid-session. Any code path that calls them is a privilege escalation path; restrict and log it. - Subagent inheritance: a subagent inherits
plan,acceptEdits,autoorbypassPermissionsfrom the main session. Anthropic warns that inheritingbypassPermissionsgives subagents, which may have different prompts, full autonomous system access. Under adefaultordontAskparent, a subagent's ownbypassPermissionsvalue is never applied (v2.1.267 or later). - Configuration as privilege: project
.claude/settings.json, hooks, CLAUDE.md, skills and.mcp.jsonload by default. Whoever can write those files in the agent's working directory can change its rules and instructions. This is the SDK form of the configuration file attacks described in the Coding Agent Configuration File Attacks guide.
Integrations, API tokens and service accounts
MCP servers
MCP servers can run as stdio subprocesses, connect over HTTP or SSE, or run in-process as SDK servers you define. Tools are named mcp__<server>__<tool>, and MCP tools need explicit permission before Claude can call them.
- Scope approvals per tool: prefer
allowedTools: ["mcp__db__query"]over a server wildcard such asmcp__github__*. Unanchored entries like"*"or"mcp__*"are ignored in allow rules, but work in deny rules, where"mcp__*"removes every MCP tool. - Do not use modes for MCP access:
acceptEditsdoes not approve MCP tools, andbypassPermissionsapproves them along with almost everything else. Anthropic recommendsallowedToolsinstead. - Credentials: stdio servers take keys through the
envfield; remote servers takeheaders. In.mcp.json,${VAR}expands environment variables at runtime, so never write a literal token into the file. - OAuth: the SDK does not run an interactive OAuth flow. A server without a usable token reports
needs-authorfailedand the run continues without its tools. Complete OAuth in your application and pass the access token inheaders; see the MCP Authorisation Specification Guide. - Silent fallback: Anthropic notes Claude can fall back to built-in tools when an MCP server is unavailable. Check server status in the
systeminit message before trusting a result. - Server-side limits: restrict what the server itself can do. Anthropic's database example sets the MCP server read-only so it rejects writes even if the agent emits one.
Choosing and vetting servers is covered in the MCP Security Guide and Local MCP Server Security Guide.
Tool credentials and the proxy pattern
Anthropic's recommended approach is a proxy outside the agent's security boundary that adds credentials to outbound requests. The agent never sees the secret, the proxy enforces an endpoint allowlist, and every request is logged in one place. Two routes exist: a custom tool or MCP server that forwards to a service outside the boundary (no TLS interception needed), or traffic forwarding through a TLS-terminating proxy whose CA the agent trusts. Note that HTTPS_PROXY alone only sees an opaque CONNECT tunnel, and some clients ignore proxy variables.
| Credential | Pattern to avoid | Recommended pattern |
|---|---|---|
| Claude API key | ANTHROPIC_API_KEY in the agent container | ANTHROPIC_BASE_URL to a proxy that injects the key, or cloud provider IAM |
| Git and package registries | Mounted ~/.git-credentials, .npmrc, .pypirc | Git MCP server or tool forwarding to a host-side proxy that authenticates |
| Cloud APIs | Mounted ~/.aws/credentials, gcloud ADC, ~/.azure/ | Minimal IAM on the agent's service account, sensitive calls routed through the proxy |
| MCP server tokens | Literal tokens in .mcp.json | ${VAR} expansion from a secret store, short-lived OAuth tokens in headers |
Anthropic lists files to exclude from any mount, even read-only: .env files, ~/.git-credentials, ~/.aws/credentials, gcloud application default credentials, ~/.azure/, ~/.docker/config.json, ~/.kube/config, .npmrc, .pypirc, service account JSON files and *.pem or *.key files.
Sandboxing and isolation
The permission engine is a gate, not a sandbox. Anthropic states that Bash command parsing matches commands against rules but does not infer whether a command is dangerous from its effects. Run the agent inside an isolation boundary sized to the threat.
| Technology | Isolation (per Anthropic) | Notes |
|---|---|---|
| sandbox-runtime | Good, secure defaults, very low overhead | bubblewrap on Linux, sandbox-exec on macOS; built-in proxy with domain allowlists; shares the host kernel and does not inspect TLS, so domain fronting can bypass the allowlist |
| Containers | Setup dependent | Use --cap-drop ALL, no-new-privileges, seccomp, --read-only, --network none with a proxy socket, non-root user, PID and memory limits |
| gVisor | Excellent with correct setup | runsc runtime intercepts syscalls in userspace; file I/O heavy work can be much slower |
| MicroVMs | Excellent with correct setup | Firecracker with no external interface, traffic over vsock to a host proxy |
For multi-tenant hosting, Anthropic warns not to rely on default options. Pass settingSources: [], set CLAUDE_CODE_DISABLE_AUTO_MEMORY=1, point CLAUDE_CONFIG_DIR at a per-tenant directory, pass an explicit per-tenant cwd on every query() call and apply per-tenant egress rules. Server-managed settings still load when the process authenticates with an organisation credential. Broader patterns are in the AI Agent Sandboxing and Code Execution Guide and Agent Egress Control and Network Isolation Guide.
Logging and detection
Three sources give you a usable audit trail. Plan for all three before production.
- Hooks: a
PreToolUsehook with no matcher sees every tool call withtool_name,tool_input,session_id,cwd, andagent_idandagent_typeinside subagents;PostToolUseandPostToolUseFailuresee results. The tool use ID correlates pre and post events. - OpenTelemetry: set
CLAUDE_CODE_ENABLE_TELEMETRY=1and the standardOTEL_*_EXPORTERvariables to export metrics and logs; traces also needCLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1. Prompt text and tool inputs are excluded by default; opting in creates a sensitive data store. - Proxy logs: the credential-injecting proxy is where egress attempts, blocked domains and credential use show up, independent of anything the agent process reports.
- Session state: transcripts live under
~/.claude/projects/by default and disappear with the container. ASessionStoreadapter mirrors them; it drops batches it cannot deliver and emits amirror_errormessage, so alert on that if transcripts are evidence.
| Signal | Where | Why it matters |
|---|---|---|
| Hook deny with reason | Your hook log | Shows attempted policy violations, often the first sign of prompt injection |
setPermissionMode call | Application log | Runtime privilege change |
MCP status failed or needs-auth | system init message, mcpServerStatus() | Silent capability loss or fallback to built-in tools |
| Blocked egress domain | Proxy log | Exfiltration attempt or a tool needing review |
CLAUDE_SDK_CAN_USE_TOOL_SHADOWED warning | Node.js process warnings | Your canUseTool checks are being bypassed by mode or bare allow entries |
error_max_turns result | Result message | Runaway loop or an agent being steered off task |
Useful metrics: agents in bypassPermissions outside sandboxes (target zero), hook deny rate per agent, share of agents with explicit settingSources, credentials in agent environments (target zero) and time to rotate an agent key. Correlating these into investigations is covered in the AI Agent Observability and Incident Response Guide.
Hardening baseline
- Inventory first: list every service that imports
claude-agent-sdkor@anthropic-ai/claude-agent-sdk, its permission mode, tool list, MCP servers and credentials in its environment. - Close the tool surface: pair an explicit
allowedToolslist using scoped rules (Bash(npm test *),Read,Grep) withpermissionMode: "dontAsk", and remove tools the agent never needs with bare names indisallowedTools, for exampleBashorWebFetch. - Write path rules carefully:
Edit(path)rules govern all write tools including Write and NotebookEdit; aWrite(path)rule is never matched. Use//pathfor absolute paths, since/pathanchors at the working directory. - Add a mandatory
PreToolUsehook: enforce invariants (no writes outside the workspace, no secrets paths, no external hosts) there, not incanUseTool. Remember aWrite|Editmatcher does not see a Bash command writing the same file; block those with the sandbox. - Pin settings sources: set
settingSourcesexplicitly. Use[]for agents that process untrusted repositories or serve multiple tenants, and addstrictMcpConfig: true. - Isolate and proxy: run in sandbox-runtime, a hardened container, gVisor or a microVM; route model and tool traffic through a credential-injecting proxy with a domain allowlist; mount code read-only where possible.
- Bound runs: set
maxTurnson sessions and on each subagent'sAgentDefinition, because the SDK has no top-level session timeout. - Update deliberately: the bundled CLI binary is pinned to the SDK package version. Anthropic recommends taking patch releases continuously and reading the changelog before a minor release.
- Test before rollout: run a red-team prompt set (instructions hidden in a README, a request to read
~/.aws/credentials, a request to call an unlisted domain) against a staging agent, confirm each is denied and logged, then promote. Roll back by redeploying the previous options object and SDK version.
Human approval design for canUseTool flows is covered in the Human-in-the-Loop for AI Agents Guide.
Common misconfigurations
- Reading
allowed_toolsas a whitelist. Settingallowed_tools=["Read"]withbypassPermissionsstill approves Bash, Write and Edit. Anthropic calls this out explicitly. - Security logic in
canUseToolonly. Calls approved byacceptEdits,bypassPermissionsor a bare allow entry never reach the callback. The TypeScript SDK emitsCLAUDE_SDK_CAN_USE_TOOL_SHADOWEDin these cases; treat it as a defect. - Default settings loading on untrusted code. An agent that reviews pull requests with default options loads that repository's CLAUDE.md, hooks, skills and
.mcp.json, so the code under review can rewrite the reviewer's instructions. This is prompt injection by configuration; see the Prompt Injection Guide. - Credentials inside the boundary. Mounting a home directory for convenience exposes cloud, Kubernetes and registry credentials to anything the agent runs.
acceptEditsassumed to be harmless. It auto-approvesrm,mvandsedinside the working directory. In a directory holding real data, that is destructive capability.- Broad MCP wildcards.
mcp__github__*approves every tool the server exposes now and any it adds later, including write and admin tools.
Framework mapping
| SDK control | NIST SP 800-53 Rev 5 | NIST CSF 2.0 | OWASP Top 10 for LLM Applications 2025 |
|---|---|---|---|
Tool allowlist, dontAsk, deny rules | AC-6 Least Privilege, CM-7 Least Functionality | PR.AA-05 | LLM06 Excessive Agency |
| Sandbox, container, microVM | SC-39 Process Isolation, SC-7 Boundary Protection | PR.AA-05 | LLM06 Excessive Agency |
| Credential-injecting proxy | IA-5 Authenticator Management, SC-7 | PR.AA-05 | LLM02 Sensitive Information Disclosure |
settingSources: [], strict MCP config | CM-7 | PR.AA-05 | LLM01 Prompt Injection, LLM03 Supply Chain |
| Hooks, OpenTelemetry, proxy logs | AU-2 Event Logging, AU-12 Audit Record Generation, SI-4 System Monitoring | DE.CM-09 | LLM06 Excessive Agency |
maxTurns limits | SI-4 | DE.CM-09 | LLM10 Unbounded Consumption |
Practitioner checklist
- Inventory every SDK deployment with its mode, tools, MCP servers and credentials.
- Remove
bypassPermissionsfrom any agent not running in a disposable sandbox. - Pair scoped
allowedToolswithdontAskfor headless agents and hide unused tools withdisallowedTools. - Enforce non-negotiable rules in a
PreToolUsehook, not incanUseTool. - Set
settingSourcesexplicitly; use[], disabled auto memory and per-tenantCLAUDE_CONFIG_DIRfor untrusted or multi-tenant work. - Approve MCP tools individually, keep tokens out of
.mcp.jsonand check server status at init. - Move the Claude API key and tool credentials into a proxy outside the agent boundary.
- Export hook events, OpenTelemetry signals and proxy logs to your SIEM and alert on denies, mode changes and
mirror_error. - Set
maxTurnson every session and subagent. - Re-test the red-team prompt set after every SDK minor version upgrade.
Standards and references
- Anthropic: Configure permissions (Agent SDK)
- Anthropic: Intercept and control agent behavior with hooks
- Anthropic: Securely deploying AI agents
- Anthropic: Connect to external tools with MCP (Agent SDK)
- Anthropic: Hosting the Agent SDK
- Anthropic: Use Claude Code features in the SDK
- OWASP: LLM06:2025 Excessive Agency
- NIST: SP 800-53 Rev. 5 Security and Privacy Controls
Related NHI Mgmt Group resources: Claude Code Security Guide · Anthropic API Keys Security Guide · MCP Security Guide · AI Agent Sandboxing and Code Execution Guide · AI Coding Agent Incident Playbook