For the past two years, the standard shape of agent infrastructure has been: a human (or the human's harness code) provisions a sandbox, hands it to the agent, and cleans up afterward. In March 2026, Fly.io quietly inverted that with a post titled "Unfortunately, Sprites Now Speak MCP": their Sprites — disposable, persistent Linux machines that cold-start in one to two seconds — now expose their entire lifecycle over the Model Context Protocol at sprites.dev/mcp. The agent authenticates to an organization and provisions machines itself, from inside its own reasoning loop. Spin up a disposable computer, run untrusted generated code on it, checkpoint, restore, throw the machine away — no human in the path.
That inversion changes who the capacity planner is. And if you run a self-hosted platform — a fleet of machines you own, not an elastic cloud — it changes what your control surface has to look like before you let an agent hold the keys. This post specifies that surface concretely: the six-tool sandbox lifecycle a platform MCP server should expose, and the guardrail each tool needs.
Here is the whole deliverable in one table; the rest of the post explains why each row looks the way it does:
| MCP tool | What it does | Guardrail that makes it safe |
|---|---|---|
sandbox.create | Provision a disposable machine | Per-agent concurrency quota + name prefix |
sandbox.exec | Run a command inside it | Per-call credential injection, never persisted |
sandbox.checkpoint | Snapshot state before a risky step | Cheap enough to require before destructive ops |
sandbox.restore | Roll back to a checkpoint | Restore-and-retry replaces approval prompts |
sandbox.destroy | Tear the machine down | Max-age TTL reaps what the agent forgets |
sandbox.list | Enumerate what exists | Scoped to the agent's own prefix and tenant |
What Fly.io Actually Shipped
Only a handful of numbers from the Sprites launch matter for the argument, so here they are compressed.
A Sprite is a full Linux VM built on Fly Machines, but with no user-supplied container image — which is why sprite create finishes in "just a second or two" instead of the minute-plus an OCI image pull can take. Each Sprite gets a 100 GB durable root filesystem backed by S3-compatible object storage (local NVMe acts as a read-through cache), and you're billed only for blocks you actually write. Idle Sprites put themselves to sleep and cost "practically nothing."
The two features that matter most for agents are checkpoints and the MCP endpoint. Because data chunks are immutable and object-stored, a checkpoint or restore "merely shuffles metadata around" — restore takes roughly nine seconds. And since March 2026, everything a human could do with the sprite CLI, an agent can do over MCP: authenticate to a Fly.io org, create machines, exec commands, checkpoint, destroy. Fly.io shipped exactly two scope controls alongside it: a cap on how many Sprites a token can create, and name prefixes so an operator can tell which agent made which machine.
Kurt Mackey's January post announcing Sprites ended with a line that reads like a thesis statement for this whole category: "The age of sandboxes is over. The time of the disposable computer has come."
The Agent Is Now the Capacity Planner
To see why agent-callable provisioning is a genuinely different design problem, compare it to the E2B-style shape it replaces.
E2B — probably the best-known dedicated sandbox provider — is oriented around the developer's harness: your Python or TypeScript code calls Sandbox.create(), the sandbox runs the AI-generated code, your code tears it down. The decision to provision — how many sandboxes, how big, for how long — is made by code a human wrote and reviewed. The sandbox count is bounded by the harness's structure. If the harness creates one sandbox per session, you get one sandbox per session, forever, deterministically.
Sandbox-as-MCP-tool removes that bound. The model decides, mid-task, that it wants another machine — to bisect a bug across three dependency versions, to run a benchmark matrix, to fan out a load test. Fly.io's own suggested use cases (dependency updates, benchmarking, load testing, bug reproduction, dataset exploration) are all naturally N-machine jobs where N is chosen by the agent at runtime. That's the point of the feature. It's also the risk.
Three things change when the caller is a model instead of a harness:
- Call frequency and placement. Provisioning moves from session setup (once, at a code path a human wrote) into the inner loop (any step, any number of times, chosen by a probabilistic planner).
- Error contracts. A human debugging a 429 reads the docs. An agent needs machine-legible errors — "quota exceeded: 5 of 5 sandboxes in use; destroy one or wait" — because the error message is the documentation at the moment of use. Fly.io wrote about this shift a year earlier in "Our Best Customers Are Now Robots": platform design reorients from developer experience to what they called robot experience.
- Budget authority. In the E2B shape, the human approval step is implicit — someone wrote and deployed the harness. In the MCP shape there is no approval step, so the budget has to move into enforced policy. Nobody is going to eyeball each
create.
That last point is the one that bites self-hosted platforms hardest, and it's worth its own section. But first: what should the tool surface itself look like?
The Minimal Tool Set: create, exec, checkpoint, restore, destroy, list
The expanded version of the table from the top, with the arguments and per-tool policy a platform MCP server should implement.
sandbox.create(image?, size?, ttl?, name?) — provisions a machine and returns an ID plus a machine-readable capability list. The server, not the caller, enforces a name prefix (agent-<id>-...) so every machine is attributable, and rejects the call with a structured error when the caller's concurrency quota is exhausted. TTL defaults on; the agent can request shorter, never longer than the tenant cap.
sandbox.exec(id, command, env?, timeout?) — runs a command and streams back stdout/stderr/exit code. The critical rule comes from Fly.io's June 2026 post "Building Agents that Don't Break Themselves": credentials are injected into the environment for a single command and never written to the sandbox's disk. If the sandbox is compromised by the untrusted code it exists to run, there's nothing durable to steal. The agent's "home" — its own reasoning loop and its long-lived tokens — lives elsewhere; as that post puts it, "the place it runs untrusted strings should still be somewhere you would be happy to set on fire."
sandbox.checkpoint(id, label?) — snapshots the machine. Because a checkpoint on a Sprites-style architecture is a metadata operation, the right policy is Fly.io's: make it "so fast we want you to use them as a basic feature." A platform can go further and require a checkpoint before exec calls that match destructive patterns — the platform equivalent of forcing a save point.
sandbox.restore(id, checkpoint) — rolls back in seconds (about nine, in Fly.io's implementation). This tool is what makes human approval prompts unnecessary: instead of asking "may I run this migration?", the agent checkpoints, runs it, and restores if the result is wrong. Restore-and-retry replaces ask-and-wait.
sandbox.destroy(id) — tears the machine down. The design assumption to make explicit: the agent will forget to call this. A wedged loop, a truncated context, a crashed session — the destroy call at the end of the plan silently never happens. Destroy must therefore be the tool the platform can execute unilaterally via TTL reaping, not a step the workflow depends on.
sandbox.list(filter?) — returns the caller's own machines only, scoped by prefix and tenant. Agents plan against what they can see; a correct, scoped inventory prevents both duplicate provisioning and cross-tenant snooping.
Six tools. Not sixty. That restraint is deliberate, and the vendor that shipped this feature makes the argument for it better than anyone — more on that below.
Guardrails Against Sandbox-Bombing a Fixed Fleet
Fly.io can afford to treat runaway agent provisioning primarily as a billing event: capacity is elastic, so an agent that creates 200 Sprites generates an invoice, not an outage. A self-hosted platform running on a fixed fleet — say, a Cluster API-managed set of Hetzner machines — has no such cushion. The fleet is real machines with real limits, shared by every tenant. An agent that self-provisions until create fails hasn't overspent; it has taken the platform down for everyone. So the budget can't be advisory. Each guardrail below is a default number plus the scaling rule behind it, because the number transfers only if the rule does:
- Concurrent-sandbox quota, per agent identity. Default: 5. Rule: no single agent identity may hold more than ~10% of its tenant's compute budget in sandboxes, so one wedged loop degrades one tenant's headroom, never the fleet.
- Idle auto-sleep + max-age TTL. Defaults: sleep after 10 minutes idle; hard-reap at 24 hours. Rule: max-age should be a small multiple of your longest legitimate agent task — long enough for a real overnight job, short enough that forgotten machines can't accumulate past a single day's review.
- Per-tenant node budget. Default: sandbox capacity capped at 25% of the tenant's node allocation. Rule: sandboxes are burst capacity, and the cap must leave room for the tenant's actual production workloads — the apps the platform exists to run — to schedule first.
- Name-prefix attribution. Every machine carries the agent identity that created it (Fly.io ships this for Sprites). Rule: if a machine's creator can't be identified in one
listcall, incident response starts with archaeology instead of revocation. - Rate and burst alarms. Default: alert at 10 creates/minute per identity; circuit-break at 3x the alert level. Rule: alarm thresholds sit well above any legitimate fan-out you've seen and well below the rate that could exhaust a node pool before a human wakes up — a Megalodon-style automated campaign operates in minutes, and so must the breaker.
None of these limit what the agent can do inside a sandbox — that's what the sandbox is for. They limit the blast radius of the provisioning loop itself, which is the new attack (and accident) surface the MCP shift created.
Even Fly.io Thinks MCP Is the Wrong Shape — and Shipped It Anyway
The strongest objection to everything above comes from the vendor that shipped the feature. Kurt Mackey's launch post is titled "Unfortunately, Sprites Now Speak MCP," and he's blunt about it: "MCP is the wrong way to extend the capabilities of an agent." His argument: in 2026, capable agents learn tools the way Claude learns Playwright — by running a CLI, reading --help, and discovering capabilities progressively — while MCP front-loads context-heavy tool descriptions into every conversation whether they're needed or not.
He has a point, and the design lesson isn't "skip MCP." It's the one embedded in the six-tool table: keep the MCP surface small and thin, and mirror your CLI. If your platform's MCP server is a veneer over the same commands a human operator runs — the same verbs, the same errors, the same policy checks — then the context cost stays low, agents that prefer shelling out lose nothing, and there's exactly one authorization path to audit instead of two. The platforms that get this wrong will ship forty bespoke MCP tools that drift out of sync with their real API; the ones that get it right will ship a handful of lifecycle verbs and let policy, not tool count, carry the safety story.
Why ship MCP at all, given the ambivalence? Because the callers demand it. Fly.io concluded a year earlier that its best customers are now robots; when the customer speaks MCP, the platform speaks MCP — aesthetics notwithstanding.
The Sandbox Is Now Part of the Platform's API
The through-line from Fly.io's three Sprites posts is easy to state: the sandbox stopped being an accessory a human hands to an agent and became part of the platform's own API surface — with the agent, not the operator, as the caller and the capacity planner. That's a genuine improvement in what agents can do: checkpoint-gated risky operations, restore-and-retry instead of approval prompts, fan-out experiments a harness author never anticipated.
But it moves the safety burden from harness code (reviewed, deterministic) to platform policy (enforced, always-on). For hosted providers with elastic capacity, weak policy is a billing surprise. For self-hosted platforms on owned hardware, weak policy is an outage with your name on it. The six lifecycle tools and five guardrails above are the minimum control surface to have in place before an agent fleet — not a single trusted operator — is on the other end of the connection.
The direction of travel is clear, though. Agent-callable infrastructure is following the same path deploy pipelines did: first a human runs every command, then the commands get an API, then policy replaces approval. The platforms designing for that end state now — quotas, TTLs, attribution, and machine-legible errors as first-class features rather than afterthoughts — are the ones that will be comfortable when the agent, not the operator, is the one holding the keys.
Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own, with AI agents as first-class operators. The sandbox-lifecycle contract sketched here is exactly the kind of scoped, policy-enforced surface bex's roadmapped MCP server is designed around. Star the repo on GitHub or deploy your first app today.



