By Sagar Shankaran, Founder of CallSphere
A hands-on walkthrough to stand up a self-hosted sandbox, run an MCP server, open an MCP tunnel, and drive a Claude managed agent.
Key takeaways
Reading about managed-agent architecture is one thing; getting a Claude agent to actually run a command inside a container you control is another. This walkthrough is the hands-on version. We will build a small but real setup: an ephemeral sandbox container, an MCP server inside it that exposes two scoped tools, an outbound tunnel to the control plane, and a system prompt that tells the agent how to behave. By the end you will have a working loop you can extend, plus the exact failure modes to watch for the first time you run it.
Begin with a disposable container that holds only what the task needs. The goal is a clean, per-run environment: when the run ends, you destroy it, and nothing leaks into the next task. Mount just the working directory, inject just the credentials the tools require, and nothing else.
docker run --rm -it \
--name agent-sandbox \
-e DB_URL="$DB_URL" \
-e ALLOWED_ORG=acme \
-v "$PWD/workspace:/work" \
--network egress-only \
agent-sandbox:latest
The --network egress-only piece matters: the sandbox can dial out (to reach the control plane and any allowed APIs) but accepts no inbound connections. That single constraint is what lets you skip firewall holes entirely. The --rm flag makes the container ephemeral so state cannot survive the run.
Inside the sandbox, run an MCP server that exposes the agent's tools. Define each tool with a strict schema so the model knows the exact argument shape. Start narrow: a lookup and a list, both read-only.
{
"name": "get_invoice",
"description": "Fetch one invoice by id for the current org.",
"input_schema": {
"type": "object",
"properties": {
"invoice_id": { "type": "string", "pattern": "^inv_[a-z0-9]+$" }
},
"required": ["invoice_id"],
"additionalProperties": false
}
}
The pattern and additionalProperties: false are doing quiet but important work — they reject malformed calls before any code runs, which catches a surprising share of agent mistakes at the boundary instead of deep in your handler. The server's job is to validate, execute against your scoped credentials, and return a compact result.
Now connect the sandbox to the control plane. The sandbox initiates an outbound, authenticated session and holds it open; the agent's tool calls flow down it. The exact command depends on your runtime, but the shape is always the same: authenticate once, then serve.
Hear it before you finish reading
Talk to a live CallSphere AI voice agent in your browser — 60 seconds, no signup.
mcp-tunnel connect \
--server http://localhost:8931 \
--control-plane wss://agent.anthropic-control.example \
--token "$AGENT_SESSION_TOKEN" \
--keepalive 20s
flowchart TD
A["Start sandbox container"] --> B["Launch MCP server on localhost"]
B --> C["Tunnel dials control plane (outbound)"]
C --> D["Authenticate session with token"]
D --> E["Register tool schemas"]
E --> F{"Agent run starts"}
F -->|Tool call| G["Server validates & executes"]
G --> H["Compact result back over tunnel"]
H --> F
Once the session is up, the control plane can see your tools but the wider internet cannot. The --keepalive matters because the tunnel is stateful; if it drops mid-run, in-flight calls fail and the agent may stall. Plan for reconnect from the start.
With tools live, give the agent its operating instructions. Keep the system prompt about behavior and boundaries, not about restating the tool schemas — the schemas already describe the tools. Tell the agent its goal, its constraints, and when to stop.
You are an accounts assistant operating in a sandbox for org "acme".
Use get_invoice and list_overdue to answer billing questions.
Never guess an invoice id; if you do not have one, call list_overdue first.
Return a short summary plus the specific invoice ids you used.
Stop as soon as the question is answered; do not call tools speculatively.
That last line is doing real work. Without an explicit stop instruction, agents tend to keep exploring, and every extra tool call adds latency and tokens. Be concrete about what "done" looks like.
It also helps to state the output contract precisely, because a managed agent's result is consumed by something downstream — a ticket system, a Slack message, another agent. "Return a short summary plus the specific invoice ids you used" tells the model the shape you expect, and that shape becomes something you can parse and validate. Vague instructions like "explain what you found" produce prose that varies run to run and is annoying to consume programmatically. Treat the agent's output like an API response: define it, then hold the agent to it in the prompt.
Send a goal and watch the tool-call trace rather than just the final answer. The trace tells you whether the agent picked the right tools, passed valid arguments, and got compact results back. A healthy first run for "which acme invoices are overdue?" looks like one list_overdue call, maybe a couple of get_invoice calls for detail, then a final summary — not a dozen speculative lookups.
If the agent calls a tool with arguments your schema rejects, that is good news: the boundary caught it, and you will see a clear validation error in the trace instead of corrupt behavior. Feed those errors back as observations; capable models usually self-correct on the next turn.
Reading the trace is also how you catch the subtler failure where the agent technically succeeds but reasons poorly — calling get_invoice on every invoice one by one when a single list_overdue would do. The final answer might still be correct, so you would never notice from output alone, but the cost and latency are quietly several times higher than they should be. Make trace review a habit for the first dozen runs of any new agent; it is the cheapest, highest-signal feedback you will get, and most prompt and tool refinements come straight out of watching what the agent actually did.
Still reading? Stop comparing — try CallSphere live.
CallSphere ships complete AI voice agents per industry — 14 tools for healthcare, 10 agents for real estate, 4 specialists for salons. See how it actually handles a call before you book a demo.
Only after read tools behave should you add anything that mutates state. Gate write tools behind explicit confirmation — either a human approval step or a tool that requires a token the agent had to fetch first — so a hallucinated call cannot silently change data. When the run finishes, destroy the container. Ephemeral sandboxes mean no secret or scratch file survives into the next task, which is the cheapest security control you will ever add.
required and additionalProperties: false, the agent can send malformed calls that fail deep in your code instead of at the boundary.| Symptom | Likely cause | Fix |
|---|---|---|
| Tool never called | Schema description too vague | Sharpen the tool description and name |
| Validation errors | Agent guessed an argument | Add a discovery tool it must call first |
| Run stalls mid-task | Tunnel dropped | Enable keepalive + reconnect |
| Costs spike | Oversized tool results | Trim and paginate at the server |
No. The sandbox dials out to the control plane and holds the session open, so the MCP server only ever listens on localhost inside the container. Nothing is exposed to inbound traffic.
Inside the sandbox container's environment, used only by the MCP server when it executes a tool. The model never receives them — it only sees the tool's structured result.
Give it an explicit stop condition in the system prompt and define tools narrowly. Watching the trace on early runs lets you catch over-exploration and tighten the instructions.
Design tools to be idempotent — the same arguments produce the same effect — so a reconnect-and-retry cannot double-apply a change. Pair that with keepalive to reduce drops in the first place.
The same sandbox, tunnel, and tool-trace discipline powers CallSphere's voice and chat agents — they answer every call, call internal tools mid-conversation, and book work 24/7. Try it at callsphere.ai.
Source & attribution: This is an independent, original explainer inspired by Anthropic's coverage on the Claude blog. Claude, Claude Code, Claude Cowork, Claude Opus, and the Model Context Protocol are products and trademarks of Anthropic. CallSphere is not affiliated with or endorsed by Anthropic.

Written by
Sagar Shankaran· Founder, CallSphere
LinkedInSagar Shankaran is the founder of CallSphere, where he builds production AI voice and chat agents deployed across healthcare, hospitality, real estate, and home services. He writes about agentic AI, LLM engineering, and shipping voice agents that handle real calls in production.
See how AI voice agents work for your industry. Live demo available -- no signup required.
One reschedule text hits your scheduler, package balance, tutor shift and invoice. Here is what MCP changed for tutoring and test-prep center owners in 2026.
Anthropic's Claude Fable 5 and Mythos 5 explained: pricing, availability, frontier benchmarks, the dual-model safeguard architecture, and what they mean for AI agents.
Where Claude Code, MCP, and multi-agent systems are taking GTM engineering next, and how to prepare your team now for standing and multi-agent workflows.
Where Claude Cowork and the Claude agent ecosystem are heading next — standing agents, MCP, skills as a moat — and the concrete moves to prepare your team now.
The metrics, leading signals, and anti-metrics that prove Claude Cowork is working — acceptance rate, time-to-outcome, and why usage counts mislead.
Shipping an agentic GTM workflow is easy; proving it works is hard. The metrics, signals, and eval loops that show a Claude Code rebuild is paying off.
© 2026 CallSphere Inc. All rights reserved.
Made within San Francisco
Watch how CallSphere handles real customer calls, schedules appointments, and processes payments — live.
Try Live DemoBook a DemoCalculate Your ROI