Safe Code Sandbox
Drives Docker to run untrusted, model-generated Python code in throwaway locked-down containers. The server validates client-specified limits (memory, timeout, CPUs) and builds a container with no network, no host filesystem, and a non-root user, copying code in as a tar after creating but before starting the container, then destroying it on every exit path. It manages container lifecycle (create, start, stop/remove) and reports run outcomes such as succeeded, failed, timed_out, out_of_memory, stopped, or sandbox_error.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Safe Code Sandboxrun this Python code and tell me what it prints: print(sum(range(10)))"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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 runEvery 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 |
| Starts a run, returns a |
|
|
| 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 |
| Exit code 0 | Use the output |
| Non-zero exit | Fix the code |
| Hit the wall-clock limit | Make it faster, or ask for more time |
| Killed by the memory cap | Ask for more memory, up to the max |
| Cancelled via | Nothing |
| 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 GBClamping 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 stdioIn 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/pytest83 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.
This server cannot be deployed
Maintenance
Related MCP Connectors
- briqOAuthcloud.briq
Give your agent its own cloud: run Postgres, Redis or any Docker image on a microVM in one call.
Run Python code in a secure sandbox without local setup. Declare inline dependencies and execute s…
- mcp-serverOAuthai.cdbx
Build Apps and run code in 30 languages — sandboxed, with persistent sessions for agent loops.
Linux microVM sandboxes for AI agents: run commands, files, processes, pause and wake.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables 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.-
- AlicenseNot gradedqualityDmaintenanceEnables LLMs to safely execute code in isolated Docker containers with resource limits and security controls, supporting session management and automatic dependency installation.MIT
- AlicenseAqualityBmaintenanceEnables AI assistants to securely execute Python and C++ code snippets inside disposable, isolated Docker containers and receive structured results.31MIT
- FlicenseNot gradedqualityCmaintenanceEnables AI agents to safely execute Python, JavaScript, and Bash code in an isolated Docker sandbox with strict security constraints.1-