Secure your Managed Agents deployment
The settings that limit what your agents can reach and do, most important first.
This guide lists the settings that limit what an agent on Claude Managed Agents can reach and do, most important first. Each section links to the page that covers the setting in full.
| Section | Setting or decision | Recommended |
|---|---|---|
| Choose where the agent runs | Cloud or self-hosted sandbox | Cloud, unless files and processes must stay on your infrastructure |
| Limit the network | Environment networking.type | limited, with only the hosts the agent needs |
| Keep credentials in vaults | injection_location and networking on each environment variable credential | Request headers only, and limited networking with only the hosts the key is for |
| Restrict web search and web fetch | allowed_domains on each web tool | An allowlist on each tool. On web_fetch, list only the sites the agent needs. On web_search, list the sites you want results from |
| Approve risky actions | permission_policy on each tool | auto on bash, write, edit, web_fetch, and MCP tools that only read data. always_ask instead on a tool if you cannot undo its changes outside the sandbox, or if relayed end-user text alone should not make it run |
| Give the agent the smallest tool set | default_config.enabled on the agent toolset | false, then enable the tools the agent needs |
| Keep untrusted input away from powerful tools | Which tools and credentials an agent holds when it reads untrusted input | No agent that reads untrusted input has a tool on always_allow that writes files, runs commands, or changes records |
| Set limits and a way to stop | budget on each session and deployment | A cap sized to what you expect a run to cost |
| Optionally have an offline reviewer read every transcript | An offline review job you run that reads each session's events | Optional. A review of each session that went idle or terminated, on a schedule |
| Monitor sessions and review retention | Webhooks and session deletion | An alert when a session goes idle, reaches its budget, or terminates, and a check of why it went idle. Store the events you want to keep in your own logs, and delete sessions you no longer need |
Security risks and sandbox boundaries explains why these settings matter and what they do not protect against. To review and monitor sessions, see Review and monitor agent sessions.
Choose where the agent runs
Choose a cloud environment unless you need files and processes to stay on infrastructure you control. Session history and tool inputs and outputs go to Anthropic either way.
If you use a self-hosted sandbox, keep these four points in mind:
- Network: Limit the network applies to cloud environments. For a self-hosted sandbox, set the equivalent limits on its traffic in your own firewall. Your firewall does not limit the MCP servers an agent declares, so review them, and any that your application sets for a session.
- Web tools: The web tools run on Anthropic's servers, so your firewall does not limit them. Limit them with their domain lists. See Restrict web search and web fetch.
- Credentials: Environment variable credentials are not yet supported with self-hosted sandboxes.
- Isolation and hardening: You are responsible for isolating sessions from each other, hardening the sandbox image, and storing the environment service key. See the Security model.
Neither option changes the compliance coverage of Managed Agents or how long it keeps session history. See Monitor sessions and review retention.
Limit the network
Set networking.type to limited and list only the hosts the agent needs in allowed_hosts. An environment create request that omits networking might get unrestricted, so set networking in every create request. See Risks of unrestricted networking.
Files, memory stores, and GitHub repositories that you attach stay available to the agent regardless of allowed_hosts.
The examples on this page use a support agent. It reads a customer's ticket, looks up the order, reads your help center, and drafts a reply. Your application runs the order lookup, which needs no host, so this environment allows one host, the help center, for web_fetch:
ant apply environment.yaml# yaml-language-server: $schema=https://platform.claude.com/schemas/ant/beta/environment.json
name: support-agent
config:
type: cloud
networking:
type: limited
allowed_hosts:
- help.example.comA limited environment has three networking settings:
| Setting | What it opens to code in the sandbox | Recommended |
|---|---|---|
allowed_hosts | The hosts you list, for any request | Only the hosts the agent needs, including the host of each MCP server you operate or trust |
allow_package_managers | The package manager hosts. They include every repository and path on github.com, gitlab.com, and bitbucket.org, where anyone can publish content and where code in the sandbox can upload files | false, unless the environment's packages setting lists packages, which requires true. If the agent installs packages while it runs, list only the registry hosts it needs in allowed_hosts instead |
allow_mcp_servers | No host. Managed Agents connects to MCP servers from Anthropic's servers | false, with the host of each MCP server you operate or trust listed in allowed_hosts |
The allow_package_managers and allow_mcp_servers settings add no hosts for web search and web fetch.
Leave allow_mcp_servers off, which is the default, and list the host of each MCP server you operate or trust in allowed_hosts. Each host you list is also open to code in the sandbox. With allow_mcp_servers off, a check at session creation rejects the request if an MCP server's host is not listed. See Provide authentication at session creation. The same check runs when a session update changes mcp_servers.
When you choose hosts for allowed_hosts, follow these two rules:
- List only hosts you operate or trust.
- Avoid
*.wildcards and shared-hosting domains where anyone can publish content.
If the agent keeps bash, set the bash tool's permission policy as Approve risky actions describes.
See Cloud environment setup for every networking setting.
Keep credentials in vaults
Store every secret in a vault. For each environment variable credential, set the injection location to request headers only and list only the hosts the secret is for. Code in the sandbox sees a placeholder instead of the secret. Managed Agents swaps in the real secret only in outgoing HTTPS requests to the hosts the credential allows. So the secret stays out of direct reach of the model, unless a service sends it back, such as an endpoint that echoes request headers. If the credential's networking is unrestricted, Managed Agents swaps in the real secret in requests to any host the environment lets the sandbox reach. To use a vault, pass its ID in vault_ids when you create the session or deployment.
| Setting | Set it to | Why | What it does not do |
|---|---|---|---|
injection_location | {"header": true, "body": false}, after you check that the service reads the key from a header | Request bodies are often built from content the agent reads, so a key in a body is more exposed. Name both fields: a create request that omits the object enables both locations, and an update changes only the fields you send | Code can still use the key on every host the credential allows. A service that expects the key in the body receives the placeholder and rejects the request |
networking on the credential | limited, with the exact hosts the key is for | Responses reach the sandbox unredacted, so avoid wildcards, shared-hosting domains, and hosts that echo request headers | It does not open the host. The environment must also allow it |
| The key's permissions, at the service that issued the key | Only the permissions the agent needs | The agent can do anything the key allows | Not applicable |
| A mounted repository's GitHub token | A fine-grained token with the minimum permissions | Code in the sandbox can fetch from and push to the mounted repository with it for the whole session. Grant write access only if the agent must push | Not applicable |
The support agent needs no vault credential, because your application holds the API key for your order API. If your agent does hold a vault credential, see Add a credential for the request in every language.
Keep keys out of system prompts, agent definitions, and messages. Do not give the agent a Claude API key. Even when a Claude API key is in a vault, code in the sandbox can use it to call the Claude API directly. It can do anything that key allows, such as starting or deleting sessions, or using web search and web fetch. The agent's tool settings, approvals, and budget do not apply to those calls. In a limited environment, code can make these calls only if allowed_hosts lets the sandbox reach api.anthropic.com.
Fetch tokens yourself and store them in the vault, because a token that code in the sandbox fetches with a stored secret comes back unredacted.
Keep vaults for workloads with different trust levels in separate workspaces, because any API key with access to a workspace can reference its vaults. Use API keys scoped to one workspace.
See Authenticate with vaults for MCP credentials and rotation.
Restrict web search and web fetch
Set allowed_domains on web_fetch to the sites the agent needs. Any URL the agent fetches can carry data to the site it points to, so this list is a security control. On web_search, set allowed_domains to the sites you want results from, so that results come only from sites you trust to be accurate. The list does not stop the search query from reaching the search provider. Search results include text from pages, so the list also limits which sites' text reaches the agent. Set allowed_domains on both tools for every type of environment.
A blocked_domains list blocks the hosts you list and their subdomains, and no other host. The same content can still reach the agent from web archives, caches, mirrors, or related domains you did not list. Do not rely on blocked_domains for security. Use allowed_domains and name the hosts you trust.
In a limited environment, the allowed_hosts list from Limit the network also limits the web tools. An unrestricted environment or a self-hosted sandbox does not limit the web tools.
In a limited environment | Result |
|---|---|
An allowed_domains entry on an enabled web tool is outside allowed_hosts | Creating a session fails with a 400 error, and so does a session update that adds such an entry. A session that a deployment starts, on its schedule or by a manual run, is created anyway. The tool then returns nothing from hosts outside allowed_hosts |
| A web tool has no list of its own | The tool returns pages and results only from hosts in allowed_hosts. Set allowed_domains on the tool as well |
allowed_hosts has no entries | Neither web tool returns a page or a search result |
You add a host to allowed_hosts for a web tool | The host is also open to code in the sandbox |
The two lists match hosts differently:
| List | Entry | Matches | Does not match |
|---|---|---|---|
allowed_hosts (environment) | example.com | example.com | www.example.com |
allowed_hosts (environment) | *.example.com | Every subdomain | example.com |
allowed_domains (web tool) | example.com | example.com and every subdomain | Other domains |
If the agent needs a site and all of its subdomains, list both example.com and *.example.com in allowed_hosts, and otherwise list exact hosts. If a tool lists example.com and allowed_hosts lists only *.example.com, the session is created. The check at session creation counts example.com as within *.example.com, but the tool returns nothing from example.com itself. See Set domain lists on an agent for how the two lists match.
The web_fetch tool also opens only URLs that have already appeared in the session:
| Where the URL appears | web_fetch can open it |
|---|---|
| The text of a user message, including end-user text that you relay | Yes |
The description of a user.define_outcome event | Yes |
| The address of a web search result | Yes |
The text of a page that web_fetch returned earlier (not a PDF), where the full URL is shown | Yes |
| The text of a custom tool's result, when Claude called the tool directly | Yes, unless the result is marked is_error: true |
| The system prompt | No |
The agent's configuration, including allowed_domains entries | No |
| Claude's own output | No |
Output of bash and the other sandbox tools | No |
| An MCP tool result | No |
| A file attached to a user message | No |
In a multiagent session, web_fetch opens a URL from another agent only if it reached that agent through a source marked Yes. A custom tool's result from a direct call counts as a source marked Yes. This holds even if Claude wrote its text, for example when the tool repeats its input. A URL can count even when it is written without https://, such as example.com/docs. The web_fetch tool cannot always open a link that shows words instead of its URL, such as "Contact support." If the agent must fetch a URL that has not appeared in the session, send it in a user message.
This rule reduces the chance that injected text builds a URL that carries private data. End-user text, search results, fetched pages, and custom tool results can still add URLs, so keep allowed_domains set. The Messages API applies a similar rule with a different list of sources. See URL validation.
A domain list on web_fetch does not limit the bash tool. Limit the network as well. See Domain list rules.
Approve risky actions
Set auto on bash, write, edit, and web_fetch, and on MCP tools that only read data. Use always_ask instead on a tool if you cannot undo its changes outside the sandbox. Also use it on a tool that should not run just because end-user text you relay asks for it. A permission policy determines whether a tool call runs, waits for your approval, or is evaluated by the server.
| Tool | Recommended policy | Why |
|---|---|---|
read, glob, grep | always_allow | They read files in the sandbox and change nothing |
bash, write, edit | auto. Use always_ask on a tool if you cannot undo its changes outside the sandbox. Use always_ask on bash if you relay end-user text | The agent toolset default is always_allow, which runs every call without approval |
web_fetch | auto | A URL the agent fetches can carry data to the site it points to |
web_search | always_ask if the agent handles data you would not want sent in a search query. Otherwise always_allow | allowed_domains limits which sites results come from, not what the query contains |
| MCP tools that only read data | auto, set for each tool in the toolset's configs | The toolset default stays always_ask, so a tool the MCP server adds later still waits for approval |
| MCP tools that change data | always_ask | This is the MCP toolset default. Keep it |
| Custom tools | Your code decides whether each call runs | Permission policies do not apply |
The toolset in Give the agent the smallest tool set applies these policies.
The auto policy lets the server run a call, deny it, or pause for approval. A person cannot approve every call in a long run, so the server checks each one instead. Pair auto with limited networking, a key with narrow permissions, a budget, and an alert on idle sessions. auto is not a human checkpoint, so if a person must review a tool's calls before they run, configure always_ask on that tool.
auto treats text in user.message events and the description in a user.define_outcome event as your instruction, including end-user text you relay.
A paused session waits indefinitely, so an unattended session needs a client that answers paused calls or interrupts the session.
Review each paused call before you allow it. A client that allows every call without showing it to a person is the same as always_allow. See Respond to confirmation requests and See how each call was evaluated.
Give the agent the smallest tool set
Set default_config.enabled to false and enable only the tools the agent needs. The agent toolset enables all eight built-in tools by default.
| Tool | What it does |
|---|---|
bash | Runs any command, including requests to allowed hosts |
write, edit | Change files in the sandbox |
read, glob, grep | Read files in the sandbox |
web_fetch, web_search | Return pages and search results from the web, through Anthropic's servers |
The support agent needs three built-in tools: read and write for its draft, and web_fetch for the help center. It looks up orders through a custom tool, lookup_order, that your application runs. Your application holds the API key for your order API, so the agent needs neither bash nor a credential.
The following sample limits web_fetch to help.example.com with allowed_domains. It sets auto on write and web_fetch, as Approve risky actions recommends. If a customer's ticket leads the agent to use these tools, the agent can only change files in the sandbox or open help center pages. That is why neither tool needs always_ask. The sample also sets always_ask in default_config, so a tool you enable later without its own policy uses always_ask:
ant apply agent.md---
name: Support Agent
model: claude-opus-5-5
tools:
- type: agent_toolset_20260401
default_config:
enabled: false
permission_policy:
type: always_ask
configs:
- name: read
enabled: true
permission_policy:
type: always_allow
- name: write
enabled: true
permission_policy:
type: auto
- name: web_fetch
enabled: true
allowed_domains: [help.example.com]
permission_policy:
type: auto
- type: custom
name: lookup_order
description: Look up one order by its ID. Returns the status, the items, and the delivery date.
input_schema:
type: object
properties:
order_id:
type: string
description: The order ID from the ticket
required:
- order_id
---An agent update replaces the whole tools array, so send every entry you want to keep.
An agent update reaches only new sessions, so also check deployments, agents that list this agent, and existing sessions:
- A deployment pins an agent version, so point each deployment at the new one.
- An agent that lists this one in
subagents.predefined_agentsorworkflows.predefined_agentskeeps the version it pinned, so update that agent too. - To tighten a session that already exists, update that session's tools. The session must be
idle, and the update replaces the wholetoolsarray. In a multiagent session, newallowed_domainsandblocked_domainslists on the web tools of the agent the session runs also apply to every other agent. They apply from each agent's next turn. Those agents' enabled tools and permission policies do not change. To change those, update each agent, then start a new session.
Limit MCP servers and custom tools to what the agent needs, too:
- MCP servers: An MCP toolset enables every tool the server exposes by default. Set
default_config.enabledtofalseand enable named tools, so tools a server adds later stay off. Remove servers the agent does not use. See MCP connector. - Custom tools: Your application runs them, and permission policies do not apply. Validate each call's input in your application and limit what the tool can touch.
Keep untrusted input away from powerful tools
Check which tools and credentials an agent holds whenever it reads untrusted input, which is text that someone outside your team can write. This section uses two terms:
- Untrusted input: Web pages, search results, support tickets, emails, repository files, uploads, MCP server results, and end-user messages that you relay.
- Powerful tools:
bash,write,edit,web_fetch,web_search, MCP tools and custom tools that write or send data, and any tool that uses a credential.
Text in untrusted input can tell the agent to use a powerful tool it holds. The support agent reads tickets, so it reaches your order API only through the custom tool lookup_order. Your application checks that the order belongs to the ticket's sender.
| Input the agent reads | Power it also has | Do this |
|---|---|---|
| Tickets, emails, or uploads | bash with a credential | Replace bash with a custom tool that does one thing. If the agent must keep bash, approve each call with always_ask |
| Web pages or search results | Writing files or changing records | Turn off the web tools, or limit them to sites you operate or trust |
| MCP results or repository files | MCP tools that write | Keep the MCP toolset on always_ask, and enable only the tools the agent needs |
| A mounted GitHub repository | Any tool | Mount only repositories you trust. An agent with read loads skills from the repository's .claude/skills directory as agent instructions, with no review step. See Load skills from a GitHub repository |
| Any untrusted input | Writing to a memory store | Attach the store as read_only unless the agent must write. The default is read_write, and later sessions read saved text as trusted memory |
In a cloud environment, reading a file with read can also load CLAUDE.md and AGENTS.md files as instructions. They load from the file's directory and its parent directories, up to the sandbox's working directory. Files they reference with @ and a path, such as @docs/setup.md, also load, up to five references deep. A reference can point anywhere in the sandbox, for example @/etc/hosts. No setting turns this off except disabling read, so treat a repository, package, or archive that someone outside your team can write as untrusted input.
When an agent must read untrusted input and also act, this guide recommends one of three designs:
- Split reading from acting. In one multiagent session, a lead agent gives tasks to a fetcher, which reads the web, and a writer, which changes files or records.
- Give the agent one narrow custom tool. Your application runs the tool, checks its input, and holds the credential. The support agent uses this design, not the split. See Answer tool calls that pause the session.
- Keep a person in the loop. Use
always_askon the powerful tool so a person approves each call.
The split uses three agents:
| Agent | Reads | Holds | Passes on |
|---|---|---|---|
| Lead (the agent that the session runs) | Your message, and what the other agents send back | No built-in tools. Its subagents.predefined_agents list names the fetcher and the writer | A task to each agent |
| Fetcher | Web pages and search results | web_fetch and web_search only, each with allowed_domains | Short results, in a format you ask for in its system prompt |
| Writer | The task from the lead | The tools that write files, run commands, or change records, on auto or always_ask. No web tools | Its result |
The fetcher's results are built from untrusted pages, and the lead can pass their text on to the writer. Keep auto or always_ask on the writer's tools. The fetcher can still send some of what it reads to the sites on its web_fetch list, and to the search provider in its queries.
Agents in one multiagent session share the sandbox, its files, and the vault credentials, so this split separates only their tools and conversations. For a stronger split, use two sessions with separate environments and vaults. Have your application pass only fixed fields, such as an order ID, between the two sessions.
Set limits and a way to stop
Set a budget when you create each session, and decide in advance how you stop an agent.
| Control | What it does |
|---|---|
| Session budget | A cap on spend at public list prices. At the cap, the session and each thread pause. Requests in progress can finish and their tool calls still run, so spend can pass the cap. See When a session reaches its budget |
| Deployment budget | The same cap, applied to each session a deployment starts |
user.interrupt event | Stops the model response immediately. A running tool call can finish first. The session goes idle, unless a workflow run keeps it running, and resumes on your next message. An interrupt ends no workflow run |
| Archive or delete a session | Archiving keeps the history and blocks new events. Deleting removes the record, events, and sandbox |
| Pause or archive a deployment | Pausing stops new scheduled runs but still allows manual runs. Archiving ends the schedule permanently. Sessions already running keep running |
| Archive or delete a vault or credential | Removes the secret from running sessions without a restart. Allow for a delay. The sessions keep running, so interrupt them too. A token the agent already fetched with the secret stays in the sandbox |
To stop a running agent, follow these steps:
- Send
user.interruptby itself. - For a scheduled agent, also pause the deployment so that no new scheduled run starts. To end the schedule permanently, archive the deployment.
- If a key might be exposed, rotate it at the service that issued it.
- If the agent has workflow runs open, also send a
user.messagethat asks it to stop them, because an interrupt ends no run. See Interrupt a session with runs open for the tool calls to cancel first. - Wait for the session to go
idle. A running session cannot be archived or deleted. - Archive the session to keep its history. Delete it when you no longer need the history.
An interrupt does not undo what the agent already did or sent. Archiving an agent does not stop its running sessions.
A budget's max_list_cost.amount is a whole number of US cents written as a string, so "125" means $1.25. See Set a budget at session creation.
You cannot add a budget to a session created without one. To change or remove a cap, see Resume a session at its budget. After you remove a session's cap, you cannot set a new one.
A budget caps spending, but it does not limit what the agent does before the cap. To keep workloads with different trust levels apart, use separate workspaces. Give each workload an API key scoped to one workspace, because a key that can reach several workspaces can act in any of them.
See Session budgets, Session operations, and Scheduled deployments.
Next steps
Networking modes, allowed hosts, and package managers.
Approval policies and confirmation requests.
Credential types, injection locations, and rotation.
Caps, resuming at a budget, and budgets on deployments.
Was this page helpful?