Skip to main content
Glama

Safe Code Sandbox

An MCP server that runs untrusted, model-generated Python in a locked-down throwaway container, and reports back what happened.

AI agents write code. Running that code directly on a machine is unsafe: it can loop forever, eat all the memory, delete things, or be the payload of a prompt injection. This gives an agent somewhere to run it and get a result, without giving it the host.

The idea in one line

The model gets three tools. It never gets Docker.

  MCP client (Claude Desktop, Cursor, your agent)
        │  start_run / get_result / stop_run
        ▼
  Safe Code Sandbox  ── validates limits, builds a locked-down container
        │
        ▼
  Docker ── no network, no host paths, not root, destroyed after the run

Every restriction lives in server code, not in a prompt. A prompt is a request; code is a rule.

Related MCP server: Docker MCP Server

Tools

Tool

Does

start_run(code, files?, limits?)

Starts a run, returns a run_id immediately

get_result(run_id)

"running", or the full report once finished

stop_run(run_id)

Kills the run and removes the container

Runs are asynchronous because a job can legitimately take an hour, and MCP has no way to hold a call open that long. The run_id is the handle.

start_run(code="print(sum(range(10)))")
# → {"run_id": "8f2c...", "status": "running", "limits": {...}}

get_result("8f2c...")
# → {"status": "succeeded", "exit_code": 0, "stdout": "45\n", ...}

Retrying a failed run is the agent's job, not the sandbox's. The sandbox reports; the agent decides.

Statuses

The distinctions here are the point. An agent deciding what to do next needs to know why a run stopped.

Status

Meaning

What a client should do

succeeded

Exit code 0

Use the output

failed

Non-zero exit

Fix the code

timed_out

Hit the wall-clock limit

Make it faster, or ask for more time

out_of_memory

Killed by the memory cap

Ask for more memory, up to the max

stopped

Cancelled via stop_run

Nothing

sandbox_error

The sandbox itself broke

Not your code; needs a human

Limits

The client chooses, the server enforces.

Default

Maximum

Memory

5 GB

16 GB

Timeout

10 minutes

1 hour

CPUs

2

4

Processes

256

not client-settable

Asking for more than the maximum is rejected, not quietly reduced:

Requested 32 GB of memory, max is 16 GB

Clamping would be worse than failing. A client that asked for 32 GB and silently got 16 would read the resulting out-of-memory as a bug in its own code.

At most four runs execute concurrently, because four runs each individually allowed 16 GB is 64 GB.

Isolation

Short version: no network at all, no host filesystem, never root, all Linux capabilities dropped, memory and CPU and process count capped, scratch space mounted non-executable, container destroyed on every exit path.

assert_posture() re-checks the critical flags before each container is created, so a refactor that drops one fails the run instead of silently weakening the sandbox.

docs/SECURITY.md is the real document, including what this does not protect against. The headline limitation: containers share the host kernel, so a kernel escape is not prevented by any of the above. If you need a boundary that survives that, you want a VM, not a container.

Install

Requires Docker and Python 3.11 or newer.

git clone https://github.com/evrabhinav/safe-code-sandbox.git
cd safe-code-sandbox

python -m venv .venv
.venv/bin/pip install -e ".[dev]"      # Windows: .venv\Scripts\pip

docker build -t safe-code-sandbox:py312 docker/

The image is deliberately plain: a Python interpreter, a non-root user, and a workspace owned by them. No package manager cache, no credentials, and no CMD, so the image cannot decide what runs.

Run it

safe-code-sandbox          # speaks MCP over stdio

In an MCP client's config:

{
  "mcpServers": {
    "sandbox": {
      "command": "/path/to/.venv/bin/safe-code-sandbox"
    }
  }
}

Design decisions

The server owns Docker; the model gets high-level tools. The model cannot name an image, a mount, a network or a capability.

Code travels in the request, not through a mount. The container is created but not started, the files are copied in as a tar, and then it starts. That ordering is what makes it possible to never expose a host path.

Fresh container per run. Nothing carries between runs, so one run cannot leave a payload for the next.

Limits rejected, not clamped. See above.

The sandbox does not retry. It reports precisely enough for the agent to decide. A sandbox that retried would hide the signal the agent needs.

Tests

.venv/bin/pytest

83 tests, no Docker required. The Docker client is injected, so a fake drives the control flow: the four outcomes, the create-copy-start ordering, and the guarantee that a container is removed even when copying the code fails.

tests/test_sandbox.py is the one worth reading. It asserts each isolation flag individually, names what each one stops, and checks that assert_posture rejects a spec with any of them removed.

Known limitations

No integration tests against a real daemon. Docker was not available on the machine this was written on, so the control flow is covered by a fake and the container specification is asserted structurally. The flags are correct per Docker's API, but nobody has yet watched a real container get OOM-killed here. That is the first thing to add.

Python only. The entrypoint is python -I main.py.

No internet, not even opt-in. Which means no pip install inside a run.

No auth. Whoever can reach the MCP server can run code. Appropriate for a local tool, not for a shared deployment.

In-memory run registry. Reports are kept for an hour and lost on restart. Deliberate: a sandbox that accumulates other people's code on disk is a worse problem than one that forgets.

License

MIT. See LICENSE.

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables safe Python code execution in isolated Docker containers through the AgentRun framework. Provides automatic container lifecycle management and comprehensive error handling for secure and reproducible code execution.
    -
  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables LLMs to safely execute code in isolated Docker containers with resource limits and security controls, supporting session management and automatic dependency installation.
    MIT
  • F
    license
    Not graded
    quality
    C
    maintenance
    Enables AI agents to safely execute Python, JavaScript, and Bash code in an isolated Docker sandbox with strict security constraints.
    1
    -