llm-sandbox

MCP serverAI & models

llm-sandbox gives your AI a safe place to run the code it writes, inside isolated containers rather than directly on your system. It works across 7 programming languages and 3 container backends. Once added, your AI can execute its own code and use the results to complete your task.

Unavailable. This server has no hosted endpoint yet, so ahel can't serve it.

After adding it, ask your AI to write and run code for your task, and it will execute inside an isolated container. Start with a small script to see the results come back.

What your AI can do with it

  • Run AI-generated code in an isolated container
  • Execute code in 7 programming languages
  • Choose between 3 container backends
  • Keep generated code separated from your own system while it runs

From the project's README

As published by vndee/llm-sandbox in README.md.

LLM Sandbox

Securely Execute LLM-Generated Code with Ease

LLM Sandbox is a lightweight and portable sandbox environment designed to run Large Language Model (LLM) generated code in a safe and isolated mode. It provides a secure execution environment for AI-generated code while offering flexibility in container backends and comprehensive language support, simplifying the process of running code generated by LLMs.

Documentation: https://vndee.github.io/llm-sandbox/

New: This project now supports the Model Context Protocol (MCP) server, which allows your MCP clients (e.g. Claude Desktop) to run code generated by LLMs in a secure sandbox environment.

🚀 Key Features

🛡️ Security First

  • Isolated Execution: Code runs in isolated containers with no access to host system
  • Security Policies: Define custom security policies to control code execution
  • Resource Limits: Set CPU, memory, and execution time limits
  • Network Isolation: Control network access for sandboxed code

🏗️ Flexible Container Backends

  • Docker: Most popular and widely supported option
  • Kubernetes: Enterprise-grade orchestration for scalable deployments
  • Podman: Rootless containers for enhanced security

🌐 Multi-Language Support

Execute code in multiple programming languages with automatic dependency management:

  • Python - Full ecosystem support with pip packages
  • JavaScript/Node.js - npm package installation
  • Java - Maven and Gradle dependency management
  • C++ - Compilation and execution
  • Go - Module support and compilation
  • R - Statistical computing and data analysis with CRAN packages

🔌 LLM Framework Integration

Runnable examples for eleven agent frameworks — OpenAI Agents SDK, Claude Agent SDK, LangChain, DeepAgents, LlamaIndex, Google ADK, CrewAI, Pydantic AI, smolagents, Strands and AG2. See examples/agent_sdks/.

📊 Advanced Features

  • Artifact Extraction: Automatically capture plots and visualizations
  • Library Management: Install dependencies on-the-fly
  • File Operations: Copy files to/from sandbox environments
  • Custom Images: Use your own container images
  • Fast Production Mode: Skip environment setup for faster container startup
  • Container Pooling: Pre-warm and reuse containers for improved performance (NEW!)

📦 Installation

Basic Installation

pip install llm-sandbox

With Specific Backend Support

# For Docker support (most common)
pip install 'llm-sandbox[docker]'

# For Kubernetes support
pip install 'llm-sandbox[k8s]'

# For Podman support
pip install 'llm-sandbox[podman]'

# All backends
pip install 'llm-sandbox[docker,k8s,podman]'

Development Installation

Dev dependencies live in the dev uv dependency group, so install them with uv (or the make install shortcut):

git clone https://github.com/vndee/llm-sandbox.git
cd llm-sandbox
make install   # uv sync + pre-commit install

See CONTRIBUTING.md for the full workflow.

🏃‍♂️ Quick Start

Basic Usage

from llm_sandbox import SandboxSession

# Create and use a sandbox session
with SandboxSession(lang="python") as session:
    result = session.run("""
print("Hello from LLM Sandbox!")
print("I'm running in a secure container.")
    """)
    print(result.stdout)

Installing Libraries

from llm_sandbox import SandboxSession

with SandboxSession(lang="python") as session:
    result = session.run("""
import numpy as np

# Create an array
arr = np.array([1, 2, 3, 4, 5])
print(f"Array: {arr}")
print(f"Mean: {np.mean(arr)}")
    """, libraries=["numpy"])

    print(result.stdout)

Multi-Language Support

JavaScript
with SandboxSession(lang="javascript") as session:
    result = session.run("""
const greeting = "Hello from Node.js!";
console.log(greeting);

const axios = require('axios');
console.log("Axios loaded successfully!");
    """, libraries=["axios"])
Java
with SandboxSession(lang="java") as session:
    result = session.run("""
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello from Java!");
    }
}
    """)
C++
with SandboxSession(lang="cpp") as session:
    result = session.run("""
#include <iostream>

int main() {
    std::cout << "Hello from C++!" << std::endl;
    return 0;
}
    """)
Go
with SandboxSession(lang="go") as session:
    result = session.run("""
package main
import "fmt"

func main() {
    fmt.Println("Hello from Go!")
}
    """)
R
with SandboxSession(
    lang="r",
    image="ghcr.io/vndee/sandbox-r-451-bullseye",
    verbose=True,
) as session:
    result = session.run(
        """
# Basic R operations
print("=== Basic R Demo ===")

# Create some data
numbers <- c(1, 2, 3, 4, 5, 10, 15, 20)
print(paste("Numbers:", paste(numbers, collapse=", ")))

# Basic statistics
print(paste("Mean:", mean(numbers)))
print(paste("Median:", median(numbers)))
print(paste("Standard Deviation:", sd(numbers)))

# Work with data frames
df <- data.frame(
    name = c("Alice", "Bob", "Charlie", "Diana"),
    age = c(25, 30, 35, 28),
    score = c(85, 92, 78, 96)
)

print("=== Data Frame ===")
print(df)

# Calculate average score
avg_score <- mean(df$score)
print(paste("Average Score:", avg_score))
        """
    )

Interactive Sessions

For notebook-style workflows you can use InteractiveSandboxSession, which keeps the Python interpreter state across multiple run calls.

from llm_sandbox import InteractiveSandboxSession

with InteractiveSandboxSession(
    lang="python",
    kernel_type="ipython",
    history_size=200,
) as session:
    session.run("value = 21 * 2")
    result = session.run("print(f'Result: {value}')")
    print(result.stdout)  # -> Result: 42

    # Use magic command to install libraries
    session.run("%pip install pandas")
    result = session.run("import pandas as pd; print(pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]}))")
    print(result.stdout)

Interactive sessions support Docker, Podman, and Kubernetes backends and currently target Python language. They spin up a long-running IPython kernel inside the sandbox, so each run() behaves like a notebook cell—state, imports, and magic commands stay alive until the context manager exits, without any extra networking or manual serialization.

Capturing Plots and Visualizations

Python Plots
from llm_sandbox import ArtifactSandboxSession
import base64
from pathlib import Path

with ArtifactSandboxSession(lang="python") as session:
    result = session.run("""
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 100)
y = np.sin(x)

plt.figure(figsize=(10, 6))
plt.plot(x, y)
plt.title("Sine Wave")
plt.xlabel("x")
plt.ylabel("sin(x)")
plt.grid(True)
plt.savefig("sine_wave.png", dpi=150, bbox_inches="tight")
plt.show()
    """, libraries=["matplotlib", "numpy"])

    # Extract the generated plots
    print(f"Generated {len(result.plots)} plots")

    # Save plots to files
    for i, plot in enumerate(result.plots):
        plot_path = Path(f"plot_{i + 1}.{plot.format.value}")
        with plot_path.open("wb") as f:
            f.write(base64.b64decode(plot.content_base64))
R Plots
from llm_sandbox import ArtifactSandboxSession
import base64
from pathlib import Path

with ArtifactSandboxSession(lang="r") as session:
    result = session.run("""
library(ggplot2)

# Create sample data
data <- data.frame(
    x = rnorm(100),
    y = rnorm(100)
)

# Create ggplot2 visualization
p <- ggplot(data, aes(x = x, y = y)) +
    geom_point(alpha = 0.6) +
    geom_smooth(method = "lm", se = FALSE) +
    labs(title = "Scatter Plot with Trend Line",
         x = "X values", y = "Y values") +
    theme_minimal()

print(p)

# Base R plot
hist(data$x, main = "Distribution of X",
     xlab = "X values", col = "lightblue", breaks = 20)
    """, libraries=["ggplot2"])

    # Extract the generated plots
    print(f"Generated {len(result.plots)} R plots")

    # Save plots to files
    for i, plot in enumerate(result.plots):
        plot_path = Path(f"r_plot_{i + 1}.{plot.format.value}")
        with plot_path.open("wb") as f:
            f.write(base64.b64decode(plot.content_base64))

🔧 Configuration

Basic Configuration

from llm_sandbox import SandboxSession

# Create a new sandbox session
with SandboxSession(image="python:3.9.19-bullseye", keep_template=True, lang="python") as session:
    result = session.run("print('Hello, World!')")
    print(result)

# With custom Dockerfile
with SandboxSession(dockerfile="Dockerfile", keep_template=True, lang="python") as session:
    result = session.run("print('Hello, World!')")
    print(result)

# Or default image
with SandboxSession(lang="python", keep_template=True) as session:
    result = session.run("print('Hello, World!')")
    print(result)

LLM Sandbox also supports copying files between the host and the sandbox:

from llm_sandbox import SandboxSession

with SandboxSession(lang="python", keep_template=True) as session:
    # Copy a file from the host to the sandbox
    session.copy_to_runtime("test.py", "/sandbox/test.py")

    # Run the copied Python code in the sandbox
    result = session.execute_command("python /sandbox/test.py")
    print(result)

    # Copy a file from the sandbox to the host
    session.copy_from_runtime("/sandbox/output.txt", "output.txt")
Custom runtime configs
from llm_sandbox import SandboxSession

pod_manifest = {
    "apiVersion": "v1",
    "kind": "Pod",
    "metadata": {
        "name": "test",
        "namespace": "test",
        "labels": {"app": "sandbox"},
    },
    "spec": {
        "containers": [
            {
                "name": "sandbox-container",
                "image": "test",
                "tty": True,
                "volumeMounts": {
                    "name": "tmp",
                    "mountPath": "/tmp",
                },
            }
        ],
        "volumes": [{"name": "tmp", "emptyDir": {"sizeLimit": "5Gi"}}],
    },
}
with SandboxSession(
    backend="kubernetes",
    image="python:3.9.19-bullseye",
    dockerfile=None,
    lang="python",
    keep_template=False,
    verbose=False,
    pod_manifest=pod_manifest,
) as session:
    result = session.run("print('Hello, World!')")
    print(result)
Remote Docker Host
import docker
from llm_sandbox import SandboxSession

tls_config = docker.tls.TLSConfig(
    client_cert=("path/to/cert.pem", "path/to/key.pem"),
    ca_cert="path/to/ca.pem",
    verify=True
)
docker_client = docker.DockerClient(base_url="tcp://<your_host>:<port>", tls=tls_config)

with SandboxSession(
    client=docker_client,
    image="python:3.9.19-bullseye",
    keep_template=True,
    lang="python",
) as session:
    result = session.run("print('Hello, World!')")
    print(result)
Kubernetes Support
from kubernetes import client, config
from llm_sandbox import SandboxSession

# Use local kubeconfig
config.load_kube_config()
k8s_client = client.CoreV1Api()

with SandboxSession(
    client=k8s_client,
    backend="kubernetes",
    image="python:3.9.19-bullseye",
    lang="python",
    pod_manifest=pod_manifest, # None by default
) as session:
    result = session.run("print('Hello from Kubernetes!')")
    print(result)

⚠️ Important for Custom Pod Manifests:

When using custom pod manifests, ensure your container configuration includes:

  • "tty": True (keeps container alive)
  • Proper securityContext at both pod and container levels
  • Container name can be any valid name (no restrictions)

See the Configuration Guide for complete requirements.

Podman Support
from llm_sandbox import SandboxSession

with SandboxSession(
    backend="podman",
    lang="python",
    image="python:3.9.19-bullseye"
) as session:
    result = session.run("print('Hello from Podman!')")
    print(result)

⚡ Container Pooling (Performance Optimization)

Container pooling dramatically improves performance by reusing pre-warmed containers instead of creating new ones for each execution. This is particularly beneficial for applications that execute code frequently.

Key Benefits

  • Faster Execution: Eliminate container creation overhead (up to 10x faster)
  • Pre-warmed Environments: Containers are initialized with your dependencies
  • Thread-Safe: Safely handle concurrent requests
  • Resource Efficient: Automatic container lifecycle management
  • Flexible Configuration: Control pool size, timeouts, and behavior

Basic Pool Usage

from llm_sandbox import SandboxSession
from llm_sandbox.pool import PoolConfig, create_pool_manager

# Create a pool manager explicitly
pool = create_pool_manager(
    backend="docker",
    config=PoolConfig(
        max_pool_size=10,          # Maximum containers
        min_pool_size=3,           # Keep at least 3 warm
        idle_timeout=300.0,        # Recycle idle containers after 5 min
        enable_prewarming=True,    # Create containers on startup
    ),
    lang="python",
)

# Use the pool in a session
with SandboxSession(
    lang="python",
    pool=pool,
) as session:
    result = session.run("print('Hello from pool!')")

# Container is automatically returned to pool when the session closes
# Clean up the pool when done
pool.close()

Sharing a Pool Across Sessions

For maximum efficiency, share a single pool across multiple sessions:

from llm_sandbox import SandboxSession
from llm_sandbox.pool import create_pool_manager, PoolConfig

# Create a shared pool manager
pool = create_pool_manager(
    backend="docker",
    config=PoolConfig(
        max_pool_size=10,
        min_pool_size=3,
    ),
    lang="python",
    libraries=["numpy", "pandas"],  # Pre-install libraries in all containers
)

# Use the pool in multiple sessions
with SandboxSession(lang="python", pool=pool) as session1:
    result1 = session1.run("import pandas; print(pandas.__version__)")

with SandboxSession(lang="python", pool=pool) as session2:
    result2 = session2.run("import numpy; print(numpy.__version__)")

# Clean up when done
pool.close()

Concurrent Execution

Container pools are thread-safe and handle concurrent requests efficiently:

from concurrent.futures import ThreadPoolExecutor
from llm_sandbox import SandboxSession
from llm_sandbox.pool import create_pool_manager, PoolConfig

# Create shared pool
pool = create_pool_manager(
    backend="docker",
    config=PoolConfig(max_pool_size=5),
    lang="python",
)

def run_code(task_id: int):
    with SandboxSession(lang="python", pool=pool) as session:
        return session.run(f'print("Task {task_id}")')

try:
    # Execute 20 tasks concurrently using only 5 containers
    with ThreadPoolExecutor(max_workers=10) as executor:
        results = list(executor.map(run_code, range(20)))
finally:
    pool.close()

Pool Configuration Options

from llm_sandbox.pool import PoolConfig, ExhaustionStrategy, create_pool_manager

config = PoolConfig(
    # Pool size limits
    max_pool_size=10,                      # Maximum containers in pool
    min_pool_size=2,                       # Minimum warm containers

    # Timeout configuration
    idle_timeout=300.0,                    # Recycle idle containers (seconds)
    acquisition_timeout=30.0,              # Wait time for available container

    # Health and lifecycle
    health_check_interval=60.0,            # Health check frequency
    max_container_lifetime=3600.0,         # Max container lifetime
    max_container_uses=100,                # Max uses before recycling

    # Pool exhaustion behavior
    exhaustion_strategy=ExhaustionStrategy.WAIT,  # WAIT, FAIL_FAST, or TEMPORARY

    # Pre-warming
    enable_prewarming=True,                # Pre-warm containers
)

pool = create_pool_manager(
    backend="docker",
    config=config,
    lang="python",
    libraries=["requests", "numpy"],       # Pre-install libraries
)

Pool Exhaustion Strategies

When all containers are busy, the pool can handle it in different ways:

1. WAIT (Default)

Wait for a container to become available:

config = PoolConfig(
    max_pool_size=5,
    exhaustion_strategy=ExhaustionStrategy.WAIT,
    acquisition_timeout=30.0,  # Wait up to 30 seconds
)
2. FAIL_FAST

Immediately raise an error:

config = PoolConfig(
    max_pool_size=5,
    exhaustion_strategy=ExhaustionStrategy.FAIL_FAST,
)
3. TEMPORARY

Create a temporary container outside the pool:

config = PoolConfig(
    max_pool_size=5,
    exhaustion_strategy=ExhaustionStrategy.TEMPORARY,
)

Monitoring Pool Statistics

from llm_sandbox.pool import create_pool_manager

pool = create_pool_manager(backend="docker", lang="python")

# Get pool statistics
stats = pool.get_stats()
print(f"Total containers: {stats['total_size']}")
print(f"Idle containers: {stats['state_counts']['idle']}")
print(f"Busy containers: {stats['state_counts']['busy']}")

pool.close()

Artifact Extraction with Pooling

For capturing plots and visualizations with container pooling, you can use either approach:

from llm_sandbox import ArtifactSandboxSession
from llm_sandbox.pool import create_pool_manager, PoolConfig
import base64
from pathlib import Path

# Create pool with pre-installed visualization libraries
pool = create_pool_manager(
    backend="docker",
    config=PoolConfig(max_pool_size=5, min_pool_size=2),
    lang="python",
    libraries=["matplotlib", "numpy"],
)

try:
    # Option 1: Use pool parameter (recommended for API consistency)
    with ArtifactSandboxSession(pool=pool, enable_plotting=True) as session:
        result = session.run("""
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 100)
y = np.sin(x)
plt.plot(x, y)
plt.title('Pooled Execution - Sine Wave')
plt.show()
        """)

        # Save generated plots
        for i, plot in enumerate(result.plots):
            Path(f"plot_{i}.{plot.format.value}").write_bytes(
                base64.b64decode(plot.content_base64)
            )

        print(f"Generated {len(result.plots)} plots using pooled container")

    # Option 2: Use ArtifactPooledSandboxSession (explicit class)
    # Both approaches work identically
    from llm_sandbox.pool import ArtifactPooledSandboxSession

    with ArtifactPooledSandboxSession(pool_manager=pool, enable_plotting=True) as session:
        result = session.run("print('Same functionality, different API')")

finally:
    pool.close()

Examples

See the examples/ directory for complete demonstrations:

🤖 LLM Framework Integration

Runnable, version-pinned examples for eleven agent frameworks live in examples/agent_sdks/:

OpenAI Agents SDKClaude Agent SDKLangChain
DeepAgentsLlamaIndexGoogle ADK
CrewAIPydantic AIsmolagents
Strands AgentsAG2

Each file is self-contained, names the SDK version it was verified against, and applies container hardening. They all share the same core — a sandbox call with the controls that matter once the code was written by a model:

from llm_sandbox import SandboxSession

SANDBOX_RUNTIME = {
    "network_mode": "none",                        # no egress
    "mem_limit": "512m",
    "pids_limit": 128,                             # bounds fork bombs
    "security_opt": ["no-new-privileges:true"],
}

def run_python(code: str) -> str:
    with SandboxSession(
        lang="python",
        keep_template=True,                        # else the image is re-pulled each call
        runtime_configs=SANDBOX_RUNTIME,
    ) as session:
        result = session.run(code, timeout=30)
    return result.stdout if result.exit_code == 0 else result.stderr

[!IMPORTANT] Security policies are advisory. session.is_safe(code) returns a verdict — it does not block execution, and run() executes code the policy flagged. Check it yourself before calling run(). See the security guide.

🔌 Model Context Protocol (MCP) Server

LLM Sandbox provides a Model Context Protocol (MCP) server that enables AI assistants like Claude Desktop to execute code securely in sandboxed environments. This integration allows LLMs to run code directly with automatic visualization capture and multi-language support.

Features

  • Secure Code Execution: Execute code in isolated containers with your preferred backend
  • Multi-Language Support: Run Python, JavaScript, Java, C++, Go, R, and Ruby code
  • Automatic Visualization Capture: Automatically capture and return plots and visualizations
  • Library Management: Install packages and dependencies on-the-fly
  • Flexible Backend Support: Choose from Docker, Podman, or Kubernetes backends

Installation

Install LLM Sandbox with MCP support using your preferred backend:

# For Docker backend
pip install 'llm-sandbox[mcp-docker]'

# For Podman backend
pip install 'llm-sandbox[mcp-podman]'

# For Kubernetes backend
pip install 'llm-sandbox[mcp-k8s]'

Configuration

Shortened here. Read the whole README on GitHub.

Signals

GitHub stars
1k
Forks
105
Last commit
Sep 2026
Advanced
Delivery
llm-sandbox MCP server → your ahel gateway (mcp.ahel.ai) → every connected AI client.
Catalog kind
mcp-server
Gateway key
io-github-vndee-llm-sandbox
Source
github.com/vndee/llm-sandbox