SandboxRunner
Click on "Install 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., "@SandboxRunnerRun Python code: print('Hello, world!')"
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.
SandboxRunner ๐ณ
SandboxRunner is a custom Model Context Protocol (MCP) server that enables AI assistants and MCP-compatible clients to securely execute Python and C++ code snippets inside disposable, isolated Docker containers โ and receive back structured results (stdout, stderr, exit code, and timing) in real time.
Built to understand MCP server design, Docker-based process isolation, and security-boundary thinking from the ground up.
Table of Contents
Related MCP server: Docker MCP Server
Features
โ Python execution โ runs snippets inside
python:3.12-slimโ C++ execution โ two-stage compile (
g++ -std=c++17 -O2) + run, usinggcc:14โ Strong isolation per run:
No network access (
--network none)Hard memory cap (default
256m)Hard CPU cap (default
0.5cores)Read-only root filesystem with a temporary scratch-only mount
noexectmpfs for/tmpContainers are ephemeral โ deleted after every run
โ Independent timeout enforcement โ a hung snippet cannot hang the MCP server; the container is force-killed on timeout
โ Output size capping โ stdout/stderr truncated at 100 KB with a clear marker
โ Distinct compile vs. runtime errors for C++ โ compiler errors are surfaced separately from runtime crashes
โ Local execution history โ every run is logged to a local SQLite database and queryable via MCP
โ Actionable Docker errors โ clear error message if Docker is not running, instead of a silent hang
โ All tunables in one place โ
config.pyis the single source of truth for limits, images, and settings
Architecture
MCP Client โโ(stdio / sse)โโโถ SandboxRunner (FastMCP)
โ
โผ
execution.py (orchestration)
โ
โโโโโโโโโโโโโโโดโโโโโโโโโโโโโโ
โผ โผ
Docker (Python) Docker (C++)
python:3.12-slim gcc:14 container
--network none --network none
--memory 256m --memory 256m
--cpus 0.5 --cpus 0.5
--read-only Stage 1: compile (rw /scratch)
Stage 2: run (ro /scratch)
โ
โผ
database.py โ sandbox_history.db (SQLite)Per-execution flow (run_code)
Validates input โ language, code size, timeout ceiling
Writes code to a temporary host directory
Mounts that directory into a fresh Docker container as
/scratchPython: runs
python /scratch/snippet.pydirectlyC++: compiles to
/scratch/a.out(read-write mount), then runs the binary (read-only mount) in a second container โ compile errors are returned distinctly from runtime errorsEnforces timeout at the server level; kills the container if exceeded
Captures and truncates stdout/stderr
Logs run metadata to SQLite
Returns a structured result to the MCP client
Requirements
Python โฅ 3.12
Docker Desktop or Docker Engine โ must be installed and running
Docker images (pre-pull recommended):
python:3.12-slimgcc:14
Python Dependencies
Package | Purpose |
| Official MCP SDK (FastMCP server + CLI) |
| Docker SDK โ container orchestration |
Dev only:
Package | Purpose |
| Test runner |
| Async test support |
Installation
# 1. Clone the repository
git clone https://github.com/huzayfaSiddique/sandbox_runner.git
cd sandbox_runner
# 2. Ensure Docker is running
docker ps
# 3. Install dependencies and create the virtual environment
uv sync
# 4. Pre-pull the Docker images (avoids slow cold start on first run)
docker pull python:3.12-slim
docker pull gcc:14The sandbox-runner console script is installed automatically via [project.scripts] in pyproject.toml.
Configuration
All tunables live in src/sandbox_runner/config.py. No environment variables are required beyond the optional transport flag.
Setting | Default | Description |
|
| Default execution timeout |
|
| Hard ceiling โ cannot be exceeded by callers |
|
| Per-container memory cap |
|
| Per-container CPU share (in cores) |
|
| Maximum allowed snippet size |
|
| Output truncation threshold |
|
| SQLite file for execution history |
|
| Default rows returned by history tool |
To add a new language, add an entry to SUPPORTED_LANGUAGES in config.py with an image, run_cmd, and optionally compile_cmd / source_file for compiled languages.
MCP Tools
Tool | Description | Inputs | Outputs |
| Execute a code snippet in an isolated container |
|
|
| List available languages and Docker images | โ |
|
| Retrieve recent run records |
|
|
status values: success ยท error ยท timeout ยท compile_error (C++ only)
Example run_code response:
{
"status": "success",
"exit_code": 0,
"stdout": "The sum of elements is: 15\n",
"stderr": "",
"duration_ms": 1823.47,
"language": "cpp"
}Isolation & Resource Limits
Each run is sandboxed with the following Docker constraints:
Constraint | Value | Effect |
| Enforced | No outbound or inbound network access |
| Configurable | Hard memory ceiling per container |
| Configurable | CPU share cap |
| Always on | Root filesystem is immutable |
|
| Small, non-executable temp space |
| Always on | Container is deleted after each run |
Timeout kill | Server-level | Server kills container if it exceeds timeout |
โ ๏ธ This uses Docker-level isolation โ suitable for personal/local use. It is not a hardened multi-tenant sandbox (e.g. gVisor, Firecracker) and is not intended for running untrusted third-party code.
Execution History
Every run is stored in a local SQLite database (sandbox_history.db):
CREATE TABLE IF NOT EXISTS runs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp REAL NOT NULL,
language TEXT NOT NULL,
code_snippet TEXT NOT NULL, -- first 500 chars only
status TEXT NOT NULL,
exit_code INTEGER,
duration_ms REAL NOT NULL,
stdout_size INTEGER NOT NULL DEFAULT 0,
stderr_size INTEGER NOT NULL DEFAULT 0
);Only the first 500 characters of each snippet are persisted as a preview.
stdout_size/stderr_sizestore byte counts, not the full content.Timestamps are returned as ISO 8601 UTC strings via the
get_execution_historytool.
Usage
Registering with an MCP Client
Add SandboxRunner to your MCP client's configuration. Example for Claude Desktop (claude_desktop_config.json):
{
"mcpServers": {
"sandbox-runner": {
"command": "uv",
"args": [
"--directory",
"/absolute/path/to/sandbox-runner",
"run",
"sandbox-runner"
]
}
}
}Replace
/absolute/path/to/sandbox-runnerwith your actual cloned directory path.
On Windows:C:\\Users\\YourName\\path\\to\\sandbox-runner
Restart the MCP client after saving the config. The three tools will become available immediately.
Running Standalone
# Start with stdio transport (default โ for MCP clients like Claude Desktop)
uv run sandbox-runner
# Start with SSE transport (for web-based or HTTP MCP clients)
uv run sandbox-runner --transport sse
# View CLI help
uv run sandbox-runner --helpProject Structure
sandbox-runner/
โโโ src/
โ โโโ sandbox_runner/
โ โโโ __init__.py # Package version
โ โโโ config.py # All tunables โ limits, images, DB path
โ โโโ database.py # SQLite connection, record_run, fetch_history
โ โโโ execution.py # Docker orchestration, validation, truncation
โ โโโ main.py # CLI entrypoint (argparse + transport)
โ โโโ server.py # FastMCP server + tool definitions
โโโ tests/
โ โโโ test_execution.py # Input validation + mocked Docker execution tests
โ โโโ test_server.py # Mocked MCP tool-level tests
โโโ pyproject.toml # Project metadata, deps, build config
โโโ uv.lock # Locked dependency tree
โโโ .python-version # Pinned Python version
โโโ sandbox_history.db # SQLite history (auto-created at runtime)
โโโ README.mdTesting
The test suite uses mocked Docker calls โ Docker does not need to be running to run the tests.
uv run pytestCurrent coverage:
test_execution.pyValid input passes without errors
Unsupported language raises
ValueErrorEmpty code raises
ValueErrorOversized code raises
ValueErrorTimeout exceeding max raises
ValueErrorMocked end-to-end Python execution โ asserts
status == "success"and correct stdout
test_server.pyrun_codesuccess path โ verifies result format and DB logging callrun_codeinvalid language โ verifies error response and that DB is not writtenlist_supported_languagesโ returns entries for bothpythonandcppget_execution_historyโ timestamps formatted as ISO 8601 strings
Known Limitations
Docker must be running. The server returns a clear error if the Docker daemon is unreachable โ no silent hangs.
Docker-level isolation only. Not a hardened cloud-grade sandbox. Designed for single-user, local use.
No multi-tenancy. No auth, no rate limiting โ assumes a single trusted operator.
Fixed resource limits by default. 256 MB / 0.5 CPU / 10s default timeout may be restrictive for heavy compute workloads.
History stores only snippet previews. Full code content is not persisted; only the first 500 characters are stored.
Contributing
Contributions are welcome!
Fork the repo and create a feature branch
Run
uv run pytestand ensure all tests passKeep new tunables in
config.pyโ avoid hardcoding values elsewhereOpen a Pull Request with a clear description of the change and its motivation
Available Tools
3 toolsget_execution_historyA
Retrieve a log of recent code executions performed by run_code, most recent first.
Useful for reviewing what code was run previously, checking whether a
past run succeeded, or auditing recent sandbox activity. Only a preview
(first 500 characters) of each snippet's code is stored, not the full
source, and stdout/stderr are recorded as byte sizes only, not content.
Returns a list of dicts, each with:
- id: unique run identifier
- timestamp: ISO 8601 UTC timestamp of when the run occurred
- language: language that was executed
- code_snippet: first 500 characters of the executed code
- status: "success", "error", "timeout", or "compile_error"
- exit_code: process exit code, or null
- duration_ms: how long the run took, in milliseconds
- stdout_size / stderr_size: byte counts of captured output
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum number of past runs to return, most recent first. Default 20. Values below 1 are treated as 1. |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Discloses that only first 500 characters of code are stored, stdout/stderr recorded as byte sizes only, and order is most recent first. With no annotations, this provides useful behavioral context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose and clear structure. The description is well-organized with paragraphs for use cases and return fields, though the return field list is somewhat redundant given the output schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given one parameter and existence of output schema, the description covers key aspects: what data is stored, what is omitted (full code, output content), and use cases. Minor omissions like pagination but overall complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents the limit parameter adequately. The description does not add extra semantics beyond what the schema provides.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool retrieves a log of recent code executions by run_code, most recent first. It distinguishes itself from siblings run_code (which executes code) and list_supported_languages.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides concrete use cases: reviewing past code runs, checking success, auditing. No explicit when-not-to-use, but context makes it clear this is for history retrieval.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_supported_languagesA
List every programming language this sandbox can execute, along with the Docker image used to run it.
Call this first if you're unsure what values are valid for the
`language` parameter of `run_code`. Takes no arguments.
Returns a list of dicts, each with:
- language: the identifier to pass to run_code (e.g. "python", "cpp")
- image: the Docker image used to execute code in this language
- description: a short human-readable description of the runtime
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries full burden. It explains the return format (list of dicts with three fields) and that it has no side effects. It doesn't mention auth or rate limits, but for a read-only listing tool that is acceptable.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Extremely concise: three short paragraphs. Front-loads the action and result, then usage advice, then return schema. Every sentence earns its place with no redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given zero parameters and the availability of an output schema (presumably documenting the return fields), the description is complete. It describes the output structure clearly, compensating for any potential gaps in the output schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
There are zero parameters. The description adds no parameter info, but the baseline for 0 params is 4. No need for further detail.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states it lists every programming language and Docker image for the sandbox. It distinguishes itself from sibling tools (run_code, get_execution_history) by focusing on discovery of supported languages.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says 'Call this first if you're unsure what values are valid for the `language` parameter of `run_code`.' This provides clear context for when to use it. It also notes 'Takes no arguments,' which is helpful. Lacks explicit when-not-to-use or alternative suggestions, but sufficient.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
run_codeA
Execute a Python or C++ code snippet inside an isolated, network-disabled Docker container and return its output.
Each run happens in a fresh, ephemeral container with no network access, a
256 MB memory cap, a 0.5 CPU cap, and a read-only root filesystem. The
container is destroyed immediately after the run. Use this tool to run
untrusted or exploratory code snippets, verify that code works, or inspect
program output, without touching the host machine.
Returns a dict with:
- status: one of "success", "error", "timeout", or "compile_error" (C++ only)
- exit_code: the process exit code, or null if it never ran
- stdout / stderr: captured output, truncated at 100 KB
- duration_ms: wall-clock execution time in milliseconds
- language: the normalized language that was actually run
Note: this sandbox provides Docker-level isolation suitable for personal/
local use. It is not a hardened multi-tenant sandbox and should not be
used to run code from untrusted third parties in a production setting.
| Name | Required | Description | Default |
|---|---|---|---|
| code | Yes | The full source code to execute, as a single string. Max size 50 KB. For C++, this must be a complete, compilable program including a main() function and any necessary #include directives. For Python, this is executed directly as a standalone script. | |
| language | Yes | Programming language of the snippet. Supported values: 'python' (runs in python:3.12-slim) and 'cpp' (compiled with g++ -std=c++17 -O2 then run in gcc:14). Case-insensitive. | |
| timeout_seconds | No | Maximum time in seconds to allow the code to run before it is force-killed. Default 10, hard ceiling 30 (requests above this are rejected). For C++, this timeout applies to the run stage only, not the compile stage. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description fully discloses isolation level, resource caps (256 MB, 0.5 CPU), ephemeral nature, return structure, and limitations. No contradictions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Description is thorough but well-organized; each sentence adds value. Could be slightly shorter, but effective.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers purpose, behavior, parameters, return values, and limitations comprehensively. Output schema is described, so no gaps.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema descriptions are already detailed, but the description adds context like max code size 50KB, case-insensitive language, and timeout ceiling 30s, which are not in schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states it executes Python or C++ code in an isolated container and returns output. It distinguishes itself from siblings like list_supported_languages and get_execution_history by focusing on execution.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit use cases (exploratory code, verification) and a warning about not being a hardened sandbox. Does not explicitly compare to siblings, but context makes it clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
2 tool updates
v0.1.1- Changed
get_execution_history1 field changed- added
Input schema / properties / limit / descriptionAdded value: +"Maximum number of past runs to return, most recent first. Default 20. Values below 1 are treated as 1."
- Changed
run_code3 fields changed- added
Input schema / properties / code / descriptionAdded value: +"The full source code to execute, as a single string. Max size 50 KB. For C++, this must be a complete, compilable program including a main() function and any necessary #include directives. For Python, this is executed directly as a standalone script." - added
Input schema / properties / language / descriptionAdded value: +"Programming language of the snippet. Supported values: 'python' (runs in python:3.12-slim) and 'cpp' (compiled with g++ -std=c++17 -O2 then run in gcc:14). Case-insensitive." - added
Input schema / properties / timeout_seconds / descriptionAdded value: +"Maximum time in seconds to allow the code to run before it is force-killed. Default 10, hard ceiling 30 (requests above this are rejected). For C++, this timeout applies to the run stage only, not the compile stage."
3 tool updates
v0.1.0- First observed
get_execution_history - First observed
list_supported_languages - First observed
run_code
TDQS
Each tool has a clearly distinct purpose: run_code executes code snippets, list_supported_languages returns available runtimes, and get_execution_history provides a log of past runs. There is no functional overlap.
All tool names follow the verb_noun pattern (run_code, list_supported_languages, get_execution_history) with consistent snake_case style.
Three tools is a well-scoped set for a code sandbox server. It covers the essential operations: running code, querying supported languages, and reviewing execution history, without unnecessary extras.
The tool surface is complete for the stated purpose of running code in an isolated sandbox. It includes language discovery, code execution, and history auditing, with no obvious missing functionality.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Execute code in 8 languages (Python, JS, TS, Go, Java, C++, C, Bash) in gVisor sandboxes.
- mcp-serverOAuthai.cdbx
Build Apps and run code in 30 languages โ sandboxed, with persistent sessions for agent loops.
Run Python code in a secure sandbox without local setup. Declare inline dependencies and execute sโฆ
Code intelligence for LLMs. Analyze, search, and retrieve code from any public git repository.
Related MCP Servers
FlicenseNot gradedqualityCmaintenanceEnables AI assistants to execute Python, JavaScript, Bash, and Go code in blazing-fast (~0.1ms startup), isolated cloud containers with secure, ephemeral environments that auto-destroy after use.155-- 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
- AlicenseNot gradedqualityCmaintenanceEnables LLMs to execute Python code securely in a sandboxed environment. Supports configurable restrictions like no network access and returns results including files.MIT
- FlicenseAqualityDmaintenanceEnables AI assistants to securely execute Python and JavaScript code in sandboxed environments, with file management and package installation.788-
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/huzayfaSiddique/sandbox_runner'
If you have feedback or need assistance with the MCP directory API, please join our Discord server