# mcp-sandbox

> Run any MCP server inside an isolated cloud sandbox instead of on your machine – one command, no code changes. Removes the blast radius of untrusted agent tooling.

- **Role:** Author & Maintainer
- **Year:** 2026
- **Tech:** TypeScript, Node.js, E2B, MCP, Vitest, Security
- **Repository:** https://github.com/ondraulehla/mcp-sandbox
- **Canonical page:** https://ulehla.dev/projects/mcp-sandbox

**Problem.** People launch MCP servers straight from a registry (`npx -y whatever-mcp`) with full access to their filesystem, secrets and network – a malicious or compromised one can read everything the agent can.

**Solution.** A transparent stdio bridge that runs the server in a disposable E2B micro-VM. To the client it is the server; in reality it executes in the cloud with allowlist-only access to your environment.

**Outcome.** Verified end-to-end against a live E2B sandbox – the worst a hostile server can do is trash its own throwaway VM.

---

## Context

MCP servers are the way agents get tools – file access, web scraping, database
queries, and more. But an MCP server is _code you run with your agent's
privileges_, and the community norm is to launch it straight from a registry:
`npx -y some-mcp-server`. Nobody reads it first. This is the same trust problem
[agent-audit](https://github.com/ondraulehla/agent-audit) detects in
configuration – here I wanted to remove the danger, not just flag it.

## Problem

A malicious or merely compromised MCP server runs with everything you have:
it can read `~/.ssh`, your browser profile, every secret exported in your
shell, and reach your local network. Static analysis can warn you; it can't
contain the code once it runs.

## Solution

A **transparent stdio bridge**. `mcp-sandbox` spawns a disposable
[E2B](https://e2b.dev) micro-VM, starts the MCP server _inside it_, and pipes
JSON-RPC both ways:

```bash
npx mcp-sandbox run -- npx -y some-mcp-server-from-the-internet
```

To the MCP client (Claude Code, Cursor, …) this _is_ the server – the protocol
is bridged verbatim. But the server executes in the cloud with:

- **no access** to your filesystem, environment or local network
- **allowlist-only** env crossing (`--env KEY` / `--env KEY=value`) – the exact
  inverse of the usual "inherit everything" default
- **`--trace`** capturing every JSON-RPC message to JSONL for inspection
- a **TTL** that reaps the sandbox no matter what

The bridge is line-buffered so the client never sees a partial JSON-RPC frame,
and the whole E2B integration sits behind an injected `SandboxBackend`
interface – so the 11-test suite runs offline with an in-memory fake.

## Outcome

- **Verified live**: a sandboxed `@modelcontextprotocol/server-everything`
  completed the `initialize` handshake and returned its 13 tools through the
  bridge, with traffic captured to a trace file
- Blast radius gone: a hostile server can only harm its own throwaway VM
- Drops into an existing setup with zero code changes:
  `claude mcp add name -- npx -y mcp-sandbox run -- <server>`

## What I learned

The interesting bug was a cold-start race: `npx` downloads the package on first
run and re-execs mid-startup, so a client's immediate `initialize` was lost.
Diagnosing it meant probing the sandbox directly – confirming the runtime,
isolating the unstable-PID window – and the fix (pre-install in a setup step,
run the resulting binary) came straight from understanding _why_ the process
identity changed. Building against a real cloud runtime rewards that kind of
first-principles debugging.
