Production Sandboxing for AI Agents: Containment Patterns for Untrusted Tool Execution and MCP Servers
Every tool call your agent makes is arbitrary code execution on your infrastructure. The trust model has three components — harness, compute, and external content — and “just use Docker” stopped being sufficient after the runc escape CVEs. Production sandboxing is a tiered isolation problem: containers-with-seccomp, gVisor, Firecracker microVMs, and capability-based runtimes each cap blast radius at a different strength/cost point, and the environment layer — not the model layer — is the deterministic boundary that stops exfiltration and escape.
How This Guide Was Built
This guide is based on official documentation, vendor docs, and published security research — we did not run any sandbox hands-on. All technical claims are traceable to the cited sources, and the comparison tables reflect documented architecture and published CVE history, not our own benchmarks. Where vendor claims are unverified, we say so.
Your Agent’s Tools Are a Remote-Code-Execution Interface
A tool call is not a function call — it’s a process spawn on your compute. When your agent runs a Python script, executes a shell command, or connects to an MCP server, it’s executing arbitrary code with whatever credentials and network access that environment has. The OWASP Agentic AI — Threats and Mitigations guide (v1.0, Feb 2025 — the first from the OWASP Agentic Security Initiative) frames this as a threat-model problem: you need to know which components can act on your behalf and what they can reach. Anthropic’s engineering team put it more bluntly in How we contain Claude: “the deterministic boundary is what gets hit when everything probabilistic misses.”
Why “Just Use Docker” Stopped Being the Answer
The default Docker seccomp profile is a deny-by-default allowlist that blocks ~44 of 300+ syscalls, including mount, ptrace, bpf, io_uring_*, and socket(AF_ALG) — the latter blocked explicitly due to CVE-2026-31431 (Docker seccomp docs). But containers share the host kernel, and that shared surface has produced escape CVEs: the November 2025 runc procfs/mount-race trio (CVE-2025-52565, CVE-2025-52881, CVE-2025-31133) is documented in arXiv 2606.08433, the most recent comparative security study of AI-code sandboxes. A seccomp profile reduces the attack surface; it does not remove the host kernel from it.
The Three-Component Trust Model
Think of your agent system as three components with different trust levels:
- Harness (control plane): the agent loop, tool routing, approvals, tracing, recovery state. This holds auth, billing, and audit logs.
- Compute (execution plane): the sandbox where tool calls actually run. This should hold narrow credentials and nothing else.
- External content: tool inputs, MCP servers, files the agent reads. This is the untrusted input that drives everything else.
OpenAI’s sandbox agents guide formalizes the harness/compute split explicitly: “the harness can keep auth, billing, audit logs, human review, and recovery state outside any one container” while the sandbox runs with narrow credentials and mounts. This separation is the foundation of every pattern in this post.
The Isolation Tier Taxonomy
Four tiers of isolation form a strength/cost continuum:
| Tier | Mechanism | Host-Kernel Exposure | Overhead / Cost | Best Fit |
|---|---|---|---|---|
| 0 — Hardened container | Shared kernel + seccomp allowlist, AppArmor, dropped caps, read-only rootfs | Full — host kernel in attack path, syscall surface filtered | Minimal | HITL-reviewed tool calls, baseline hardening |
| 1 — Application kernel (gVisor) | Userspace Sentry kernel intercepts syscalls; Gofer mediates filesystem | Guest syscalls never reach host kernel | Per-syscall latency; some compatibility gaps | HITL agents on existing Docker/K8s tooling |
| 2 — MicroVM (Firecracker) | KVM guest kernel per workload; jailer second line of defense | Hardware boundary — host kernel out of attack path | <125 ms boot, <5 MiB memory overhead per VM | Autonomous agents, untrusted code at scale |
| 3 — Capability-based (WASI/workerd) | No ambient authority; only host-granted capabilities | Host-mediated capability grants only | WASM compatibility constraints | Deterministic compute without shell access |
Each tier trades overhead and compatibility for a smaller host-kernel exposure profile.
Tier 0 — Hardened Containers (seccomp, AppArmor, caps, read-only rootfs)
The baseline tier. Docker’s default seccomp profile blocks ~44 of 300+ syscalls, and the OWASP Docker Security Cheat Sheet adds the hardening checklist: read-only rootfs, dropped capabilities, no-new-privileges, AppArmor profiles, and a custom seccomp profile where the default isn’t tight enough. This reduces the surface but leaves the host kernel fully exposed — the runc escape CVEs apply to this tier regardless of how well you harden it.
Tier 1 — Application Kernels (gVisor: Sentry/Gofer/runsc)
gVisor docs describe an application kernel written in Go that intercepts application syscalls in a per-sandbox userspace kernel (Sentry), with a Gofer process mediating filesystem access via 9P. It ships as an OCI runtime (runsc) usable with Docker and Kubernetes, and deliberately is neither a syscall filter nor a VM — it “moves the system interfaces normally implemented by the host kernel into a distinct, per-sandbox application kernel to minimize the risk of a container escape exploit.” Guest syscalls never reach the host kernel. The cost is per-syscall overhead and some compatibility gaps. Per the arXiv study’s engine rollup, gVisor’s CVE history (CVE-2025-2713, CVE-2024-10026, CVE-2024-10603) contains no escape-class vulnerabilities.
Tier 2 — MicroVMs (Firecracker, Cloud Hypervisor, libkrun)
Firecracker docs describe a KVM-based microVM that boots to userspace in <125 ms with <5 MiB memory overhead, supports up to 150 microVM creations/sec per host, and exposes only 5 emulated devices (virtio-net, virtio-block, virtio-vsock, serial console, minimal keyboard controller). A companion “jailer” process provides a second line of defense behind the virtualization barrier. Firecracker is written in Rust and powers AWS Lambda and Fargate. The isolation is a hardware boundary: each workload gets its own guest kernel, and the host kernel is not in the attack path. Cloud Hypervisor and libkrun occupy the same tier in the arXiv study’s engine classification.
Tier 3 — Capability-Based Runtimes (WASI components; workerd/V8 isolates)
WASI’s security page states the model plainly: “a WebAssembly module or component starts with no access to the outside world and can only perform operations that the host explicitly grants.” No ambient authority, deny-by-default. A component without a network import cannot open sockets regardless of what the host process is permitted to do — “which is what makes WASI sandboxing stronger than typical OS-level process isolation.” WASI 0.1 uses preopened fds; 0.2 expresses capabilities as WIT imports; 0.3 makes them implicit at the world level (WASI overview). workerd — the Apache-2.0, self-hostable runtime powering Cloudflare Workers — applies the same capability model to V8 isolates with systemd hardening (unprivileged user, NoNewPrivileges).
What Production Platforms Actually Do (2025–2026 Patterns)
Anthropic runs three products on three different containment patterns (ephemeral gVisor container, human-in-the-loop OS sandbox, sealed VM). OpenAI splits harness from compute and treats sandbox APIs as a commodity provider layer. The managed sandbox API market — E2B, Modal, Daytona, Vercel — wraps these engines behind SDKs so you don’t operate the engine directly.
Anthropic — Three Products, Three Containment Patterns
Anthropic’s How we contain Claude (2026-05-25) documents three production patterns:
- claude.ai code execution runs in an ephemeral gVisor container on isolated infrastructure.
- Claude Code uses a human-in-the-loop OS sandbox (Seatbelt on macOS, bubblewrap on Linux, network denied by default) that cut permission prompts 84% (Claude Code sandboxing).
- Claude Cowork runs in a full VM (Apple Virtualization framework / HCS) with only the user’s mounted workspace visible and credentials kept in the host keychain.
The post’s three containment principles: cap blast radius at the environment layer first; match isolation strength to the user’s capacity for oversight; and “be wary of custom components — battle-tested hypervisors, syscall filters, and container runtimes have survived more adversarial attention than anything you’ll build.” Their custom allowlist proxy was the piece that failed (see the egress section below).
OpenAI — Harness/Compute Split and the Sandbox-Provider Layer
OpenAI’s sandbox agents guide separates the harness (control plane: agent loop, tool routing, approvals, tracing, recovery) from compute (sandbox execution plane: files, commands, ports, snapshots). The SandboxAgent + Manifest workspace contract defines what the sandbox can see and do. The provider list — Blaxel, Cloudflare, Daytona, Docker, E2B, Modal, Runloop, Unix-local, Vercel — confirms sandbox APIs are now a commodity integration surface. Note: the docs flag this as a beta feature; “API details, defaults, and supported capabilities may change.”
Managed Sandbox APIs Under the Hood
The arXiv study classifies the managed products by engine:
- E2B runs Firecracker microVMs (E2B docs describe “fast, secure Linux VM created on demand” for agents).
- Modal runs gVisor-based containers via
Sandbox.create, with a default 5-minute lifetime (max 24 h), idle timeouts, and readiness probes (Modal sandbox docs). - Daytona uses runc containers with Docker Compose as the default deploy path.
- Vercel Sandbox uses Firecracker microVMs (vendor claims millisecond starts; not independently verified).
- Anthropic’s sandbox-runtime is open-sourced (bubblewrap/Seatbelt) and can “sandbox arbitrary processes, agents and MCP servers” (Claude Code sandboxing).
Egress Control Is the Other Half of the Sandbox
Exfiltration does not require escape. Anthropic’s red team exfiltrated ~/.aws/credentials 24 of 25 times through a user-typed prompt, and a Cowork sandbox “worked perfectly” while data left via an approved api.anthropic.com allowlist entry (How we contain Claude). The fix is a defensive MITM proxy inside the VM that only passes requests carrying the VM’s own provisioned session token — plus the rule that credentials never enter the sandbox in the first place.
Allowlists as Capability Grants
Anthropic’s containment post documents two incidents that define the egress problem:
- Feb 2026 internal red-team: a phished employee pasted a prompt that exfiltrated
~/.aws/credentials. Across 25 retries, Claude completed the exfiltration 24 times — because user-typed instructions give a model-layer classifier nothing anomalous to catch. - Cowork allowlist incident: a malicious file in a workspace embedded an attacker-controlled Anthropic API key, and the egress allowlist correctly passed
api.anthropic.com. The sandbox “worked perfectly, and yet the data was exfiltrated.”
The lesson: an egress allowlist is a capability grant, not a destination filter. Every function reachable through an allowed domain is attack surface.
Scoped Credentials and the Credential-Must-Not-Enter Rule
The fix for the Cowork incident was a defensive MITM proxy inside the VM that only passes requests carrying the VM’s own provisioned session token — not any key the workspace happens to contain. Claude Code on the web routes git through a custom proxy with scoped credentials so real credentials never enter the sandbox (Claude Code sandboxing). The rule is simple: secrets live in the harness or a credential vault, and the sandbox gets per-session, per-scope tokens that are revoked on termination.
Sandboxing MCP Servers and Tool Servers
MCP servers are untrusted code with agent-visible capabilities. The MCP security best practices (doc version 2026-07-28) identify the key attack surfaces: token passthrough (the anti-pattern of forwarding user tokens to MCP servers), confused deputy (an MCP server using the agent’s authority to perform unintended actions), and the requirement for per-client consent. Decide per server whether it runs in-sandbox or host-side.
The MCP Attack Surface
The MCP security doc’s core requirements: no token passthrough — the agent should hold its own scoped credentials and the MCP server should never see user tokens; per-client consent MUSTs — each client must explicitly approve what an MCP server can access; and scope minimization — an MCP server should only have the capabilities its tool calls actually need. The Claude Code security docs add trust verification for new MCP servers as a first-class step.
Where to Run MCP Servers — In-Sandbox vs Host-Side
Claude Code’s sandbox runtime can sandbox arbitrary processes, agents, and MCP servers (Claude Code sandboxing) — in-sandbox is the default posture. But Anthropic hit a tradeoff with Cowork: local MCP servers were moved outside the VM because in-VM placement hurt auditability and broke servers that need host processes (e.g., local databases) (How we contain Claude). The decision framework: sandbox what you can, audit/allowlist what you can’t, and apply MCP best practices regardless of placement.
Choosing Isolation Strength by Threat Model
Match isolation tier to user oversight capacity: a developer with human-in-the-loop review can run on a hardened container or gVisor sandbox, while an autonomous agent acting without review needs a sealed VM. The arXiv study scopes to a single-tenant operator threat model (AISI T0.H2.N2) — multi-tenant SaaS isolation is a different problem with different requirements.
Match Isolation to Oversight Capacity
Anthropic’s principle: “match isolation strength to the user’s capacity for oversight.” Claude Code’s HITL sandbox works because a developer is watching and approving actions — and even then, the 84% prompt reduction came from the environment boundary, not from better prompts (Claude Code sandboxing). Claude Cowork’s sealed VM exists because autonomous agents don’t have a human in the loop for every action (How we contain Claude). The more autonomous the agent, the stronger the environment boundary must be.
Single-Tenant Operator vs Multi-Tenant SaaS
The arXiv study explicitly scopes to a single-tenant operator threat model (AISI T0.H2.N2) and refuses a composite ranking of engines. This post does the same: we’re addressing the operator who runs their own agent infrastructure and needs to choose an isolation tier. Multi-tenant SaaS (tenant-vs-tenant isolation) is a different problem with different requirements — side-channel resistance, per-tenant attestation, and hypervisor-level isolation become table stakes.
The CVE Reality Check — What Has Escaped and How Fast It Gets Patched
No isolation engine is immune. Firecracker has shipped CVE-2026-5747 (virtio-pci OOB write, CVSS 8.7) and CVE-2026-1386 (jailer symlink host-write); runc had the November 2025 escape trio; gVisor’s CVEs are not escape-class per the arXiv rollup. The dominant operator-facing variable is pin policy: engine-side patch latency aggregates to ~0 days for coordinated disclosures, but downstream lag spans 0 days to 471+ days to “opaque.”
Engine CVE History
Per arXiv 2606.08433:
- Firecracker: CVE-2026-5747 (virtio-pci OOB write, CVSS 8.7), CVE-2026-1386 (jailer symlink host-write).
- runc: November 2025 procfs/mount-race escape trio (CVE-2025-52565, CVE-2025-52881, CVE-2025-31133), plus CVE-2024-21626 and CVE-2024-45310.
- gVisor: CVE-2025-2713, CVE-2024-10026, CVE-2024-10603 — none escape-class per the study’s rollup.
- Cloud Hypervisor: CVE-2026-45782 (virtio-block UAF), CVE-2026-27211 (QCOW host-leak).
The study’s stance: “unmeasured ≠ safe.” Absence of known escapes is not proof of security.
Pin Policy Is the Dominant Operator Variable
The arXiv study’s most actionable finding: engine-side coordinated disclosures are patched in ~0 days, but downstream lag (the time between an engine fix and a product shipping that fix) spans 0 days to 471+ days to “opaque.” If you’re running a managed sandbox API, you can’t see the pin policy — and that’s a risk. If you’re running the engine yourself, pin, monitor, and track engine CVE feeds. The study explicitly refuses a composite ranking of engines; the operator-facing variable is how fast you can absorb a fix.
Reference Architecture — A Sandboxed Tool-Execution Service
The pattern: harness (control plane) outside the sandbox holding auth and state; a sandbox fleet of per-call ephemeral environments; an egress proxy enforcing allowlists with per-session scoped credentials; and a credential vault that is never mounted into the sandbox. Observability comes from OTLP pull-based logs and sandbox lifecycle events.
Components and Trust Zones
Following OpenAI’s harness/compute split (sandbox agents guide):
- Harness (control plane): agent loop, tool routing, approvals, tracing, recovery. Holds auth, billing, audit logs.
- Sandbox fleet (execution plane): per-call ephemeral environments. Narrow credentials, no persistent state.
- Egress proxy: MITM proxy enforcing domain allowlists, binding each request to the sandbox’s provisioned session token.
- Credential vault: never mounted into the sandbox. The sandbox gets per-session, per-scope tokens via the egress proxy.
Observability and Audit
Sandbox lifecycle events (create, exec, terminate) and OTLP pull-based logs give you an audit trail without exposing sandbox internals to the agent. Recovery state lives in the harness, not the sandbox — if a sandbox is compromised, you terminate it and start fresh. The NIST SP 800-218A framework (Jul 2024) provides compliance framing for AI-specific secure development practices, but it’s a framework document — pair it with current runtime guidance.
Production Sandboxing Checklist
- Threat-model first: identify your three trust components and what each can reach (OWASP Agentic AI).
- Choose engine tier by oversight capacity: HITL developer → hardened container or gVisor; autonomous agent → microVM or sealed VM.
- Harden the container tier: read-only rootfs, dropped caps, no-new-privileges, AppArmor (OWASP Docker Security).
- Treat egress as capability grants, not destination filters: MITM proxy, per-session tokens, credentials never enter the sandbox (How we contain Claude).
- Decide MCP server placement per server: sandbox what you can, audit/allowlist what you can’t (MCP security best practices).
- Pin your engines and track CVE feeds: downstream lag is the dominant risk variable (arXiv 2606.08433).
- Separate harness from compute: auth, billing, and recovery state live outside any one container (OpenAI sandbox agents guide).
- Observability from day one: OTLP pull-based logs, sandbox lifecycle events, recovery state in the harness.
The Bottom Line
Production sandboxing for AI agents is a tiered isolation problem, and the environment layer — not the model layer — is the deterministic boundary that stops exfiltration and escape. Choose your tier by threat model and oversight capacity, treat egress control as half the problem, and pin your engines with a CVE tracking process. The 2025–2026 production patterns from Anthropic, OpenAI, and the sandbox-API vendors all converge on the same architecture: harness and compute separated, sandboxes ephemeral, credentials scoped, and egress proxied.
FAQ
Q1: Is Docker with the default seccomp profile enough to sandbox agent tool calls in production?
No. The default profile blocks ~44 of 300+ syscalls, but containers share the host kernel, and runc has shipped escape CVEs (Nov 2025 procfs/mount-race trio: CVE-2025-52565, CVE-2025-52881, CVE-2025-31133) (Docker seccomp docs; arXiv 2606.08433). Treat containers as a baseline tier; add read-only rootfs, dropped capabilities, no-new-privileges, and AppArmor (OWASP Docker Security Cheat Sheet) — or move up to gVisor or a microVM for truly untrusted code.
Q2: What’s the difference between microVMs (Firecracker) and application kernels (gVisor)?
Firecracker gives each workload its own guest kernel via KVM — no host-kernel exposure, <125 ms boot, <5 MiB overhead (Firecracker docs). gVisor intercepts syscalls in a userspace kernel (Sentry) so they never reach the host, but with per-syscall overhead and some compatibility gaps (gVisor docs). Both are far stronger than plain containers; gVisor integrates with existing OCI tooling, Firecracker gives a hardware boundary.
Q3: How do E2B / Modal / Daytona-style sandbox APIs actually isolate the code?
They wrap isolation engines behind an SDK. E2B runs Firecracker microVMs (E2B docs), Modal runs gVisor-based containers via Sandbox.create (Modal sandbox docs), and Daytona’s default path is Docker Compose with runc per the arXiv engine table (arXiv 2606.08433). The API provides lifecycle (create/exec/terminate), timeouts, and snapshotting so you don’t operate the engine directly.
Q4: Should MCP servers run inside the sandbox?
Treat MCP servers as untrusted. Claude Code’s sandbox runtime can sandbox arbitrary processes and MCP servers (Claude Code sandboxing). But Anthropic moved local MCP servers outside the Cowork VM because in-VM placement hurt auditability and broke servers needing host processes (How we contain Claude). Decide per server: sandbox what you can, audit/allowlist what you can’t, apply MCP security best practices regardless.
Q5: Why do I need egress controls if my sandbox is escape-proof?
Because exfiltration doesn’t require escape. Anthropic’s red team exfiltrated ~/.aws/credentials 24 of 25 times through a user-typed prompt, and a Cowork sandbox “worked perfectly” while data left via an approved api.anthropic.com allowlist entry (How we contain Claude). Treat the egress allowlist as a capability grant and enforce with a MITM proxy plus scoped per-session credentials.
Q6: Can sandboxing replace permission prompts and model-level guardrails?
No — they’re complementary layers. Anthropic found users approve ~93% of permission prompts (approval fatigue) and auto-mode still lets ~17% of overeager actions through (How we contain Claude). The deterministic environment boundary catches what probabilistic defenses miss, but it’s designed so you can relax oversight, not eliminate it. Defense in depth: environment first, model layer second, tool permissions third.
Appendix: Managed Sandbox APIs
| Service | Underlying Engine | Interface | Notable Controls | Notes |
|---|---|---|---|---|
| E2B | Firecracker microVM | Python/JS SDK | Templates, MCP support, snapshots | Firecracker underneath per arXiv study |
| Modal | gVisor containers | Sandbox.create (Python) |
5-min default lifetime (max 24 h), idle timeouts, readiness probes, exec API | Current docs as of 2026 |
| Daytona | runc containers | Docker Compose default deploy per arXiv | Container orchestration | Engine classified in arXiv 2606.08433 |
| Vercel Sandbox | Firecracker microVM | SDK / API | Millisecond starts per vendor | Vendor claims, not independently verified |
| Anthropic sandbox-runtime | bubblewrap / Seatbelt | OSS (anthropic-experimental/sandbox-runtime) | Filesystem + network isolation, unix-socket egress proxy | Sandboxes arbitrary processes incl. MCP servers |



