Featured

Deploy OpenClaw in 60 seconds — 20% off logoDeploy OpenClaw in 60 seconds — 20% off

Launch OpenClaw on Hostinger in about 60 seconds and keep your agent live 24/7. Our referral link gives you 20% off, no coupon code needed.

Launch on Hostinger
Run your Hermes agent on Hostinger, fully managed logoRun your Hermes agent on Hostinger, fully managed

Launch Hermes on Hostinger in one click, fully managed, no VPS knowledge needed. Use code ZACAARON10 for 10% off.

Launch on Hostinger
Crawl and scrape any site into clean data, 10% off logoCrawl and scrape any site into clean data, 10% off

Firecrawl crawls and scrapes any site into clean markdown for your agent. Get 1,000 free credits, and new users get 10% off their first purchase.

Try Firecrawl free
6,000+ web scrapers for your AI agent, start free logo6,000+ web scrapers for your AI agent, start free

Apify gives your agent live web data: 6,000+ prebuilt scrapers and actors, MCP-ready. Sign up free with $5 in usage credits.

Try Apify free
One API to scrape, enrich, and extract the internet. logoOne API to scrape, enrich, and extract the internet.

Context.dev gives your agents a single API to scrape, enrich, and extract live web data — no proxies, no parsers, no maintenance.

Start building free
SetupClaw: done-for-you OpenClaw for founders & exec teams logoSetupClaw: done-for-you OpenClaw for founders & exec teams

White-glove OpenClaw for founders and exec teams (4–50+ employees): we install, harden, integrate your tools, and maintain it — secured from day one.

Get it set up for you
SEO data APIs for your agent, $1 free credit logoSEO data APIs for your agent, $1 free credit

DataForSEO gives your agent live access to SERP results, keyword data, backlinks, and on-page SEO data through one API. New accounts get a $1 credit, good for up to 20,000 keyword or backlink lookups.

Try DataForSEO free
Reach 48,000+ AI builders

A flat monthly placement in front of developers actively installing AI tools. No lock-in, cancel anytime.

Advertise here

Works with

Claude CodeClaude DesktopCursorVS CodeClineCodex CLIOpenClaw+ any MCP client

Install to Claude Code

This server doesn't publish a one-line install command. Follow the setup in the source repository.

Summary

Execute scientific scripts (R, Python, GMT, LaTeX, Octave, Julia, etc.) in isolated, disposable containers with no host filesystem access.

README.md

<!-- SPDX-FileCopyrightText: 2026 Peter Lemenkov <lemenkov@gmail.com> SPDX-License-Identifier: Apache-2.0 -->

mcp-sandbox

MCP server for isolated scientific script execution. Run R, Python, GMT, GRASS GIS, LaTeX, Octave, Julia, and more in disposable containers — fully stateless, no host filesystem access.

Tools

| Tool | Description | | --------------- | ------------------------------------------------- | | run_script | Execute a script in an isolated sandbox container | | list_runtimes | List available scientific runtimes |

Supported Runtimes

| Runtime | Tools | | --------- | ----------------------------------------------------------------------- | | r | R + ggplot2, dplyr, tidyr, jsonlite | | python | Python 3 + numpy, scipy, pandas, matplotlib, sympy, scikit-learn, geopandas, pyproj, rasterio, obspy | | gmt | Generic Mapping Tools | | grass | GRASS GIS — headless; each run gets a throwaway location | | latex | pdflatex (texlive) | | octave | GNU Octave | | julia | Julia | | gnuplot | Gnuplot | | bash | Bash scripts |

Design

  • Each run spawns a fresh container — fully stateless, no reuse
  • Container deleted after execution (--rm)
  • Network access enabled
  • Memory limit: 2GB, CPU limit: 2 cores
  • Input files injected at /sandbox/input/ (base64)
  • Output files collected from /sandbox/output/, returned by reference: a

capability URL plus metadata (name, bytes, SHA-256, MIME, expiry). Small text outputs (< 4 KB) are still inlined. See "Output handling" below.

Install

The sandbox runs containers rootless, as a dedicated sandbox user. The image and the running service must live in that user's podman store. This is the single most important thing to get right — see Troubleshooting.

1. Create the service user

useradd -r -m -d /var/lib/mcp-sandbox -s /bin/bash sandbox
echo "sandbox:100000:65536" >> /etc/subuid
echo "sandbox:100000:65536" >> /etc/subgid
# rootless podman needs a runtime dir (/run/user/<uid>) present at boot:
loginctl enable-linger sandbox

2. Build the image — as the sandbox user

Build into the sandbox user's rootless store. Not your login user's, not root's — the server only ever reads sandbox's store:

sudo -u sandbox -H podman build -t mcp-sandbox:latest -f Containerfile .

Already built the image elsewhere? Don't rebuild — transfer it across the store boundary:

podman save mcp-sandbox:latest | sudo -u sandbox -H podman load

3. Install the server

python3 -m venv venv
venv/bin/pip install -e .

Running as a service

Run the server as sandbox so it reads that user's image store. Minimal unit (adjust the checkout path and entrypoint to your install):

# /etc/systemd/system/mcp-sandbox.service
[Unit]
Description=mcp-sandbox MCP server
After=network.target

[Service]
User=sandbox
ExecStart=/var/lib/mcp-sandbox/mcp-sandbox/venv/bin/mcp-sandbox
Restart=on-failure

[Install]
WantedBy=multi-user.target

Adding dependencies

The image is the single source of truth for what a runtime can do. Because every run is a fresh --rm container, a runtime pip install inside a sandbox does not persist — it re-downloads and rebuilds on every invocation, and on current Fedora it hits PEP 668 (externally-managed-environment) anyway. So:

  • Missing package? Add the python3- (or R-, etc.) RPM to the

Containerfile and rebuild the image — don't pip-install at runtime. Prefer Fedora RPMs; they keep the image reproducible and versioned in git.

  • No Fedora RPM for it? Add a build-time RUN pip install … layer to the

Containerfile, baked into the image — never a runtime install in the sandbox.

Rebuild after any change (as the sandbox user — see Install/Troubleshooting):

sudo -u sandbox -H podman build -t mcp-sandbox:latest -f Containerfile .

Output handling

Files written to /sandbox/output/ are returned by reference, not inlined as base64. After a run, the server stages each output under a fresh unguessable token and returns a compact metadata record in place of the bytes:

{ "name": "figure.png", "bytes": 29901, "sha256": "…", "mime": "image/png", "url": "https://host.example/sandbox-out/<token>/figure.png", "expires_in": 3600 }

  • Capability URL. Each file is staged at /<token>/<name>, where <token>

is ~256 bits from a CSPRNG (secrets.token_urlsafe(32)). Possessing the URL is the authorization to fetch it — there is no separate credential.

  • Small text is still inlined. Text outputs below 4 KB come back inline; a

round-trip to read three lines isn't worth it. Everything else is referenced.

  • Bounded lifetime. Staged files live for a TTL (default 60 min), after

which the GC timer deletes them and the URL 404s — this bounds the exposure window of any leaked URL.

  • Integrity. The sha256 lets a consumer verify the bytes, or confirm a

file's identity without fetching it.

Serving the staged outputs

nginx serves the staging directory read-only, with listing, logging, and auth off (the token is the authentication):

location /sandbox-out/ { alias /var/cache/mcp-sandbox/outputs/; auth_basic off; # the capability token is the authorization autoindex off; # non-enumerable: no directory listing access_log off; # keep tokens out of logs add_header X-Robots-Tag "noindex" always; }

On SELinux, the staging dir must carry httpd_sys_content_t, applied before it is populated:

semanage fcontext -a -t httpd_sys_content_t "/var/cache/mcp-sandbox/outputs(/.*)?" restorecon -RFv /var/cache/mcp-sandbox/outputs

Expiry is a systemd .timer firing every 15 min that deletes token directories older than the TTL.

Configuration

HOST=127.0.0.1
PORT=8815
SANDBOX_IMAGE=mcp-sandbox:latest
DEFAULT_TIMEOUT=60
MAX_TIMEOUT=300
# Output-by-reference — REPLACE names with the actual ones from the code:
SANDBOX_PUBLIC_URL=https://host.example    # base for capability links
OUTPUT_DIR=/var/cache/mcp-sandbox/outputs  # staging directory
OUTPUT_TTL=3600                            # seconds; keep in sync with GC timer

Troubleshooting

Rebuilt the image but the server still runs the old one? You almost certainly built into the wrong podman store. Containers run rootless as sandbox, which reads its own store under /var/lib/mcp-sandbox/.local/share/containers. An image built as your login user or as root lands in a different store the server never reads — so the build "succeeds" and changes nothing.

Confirm what the service actually sees, and rebuild/load into that store:

sudo -u sandbox -H podman images mcp-sandbox
sudo -u sandbox -H podman build -t mcp-sandbox:latest -f Containerfile .
# or: podman save mcp-sandbox:latest | sudo -u sandbox -H podman load

No MCP restart is needed afterwards — each run is a fresh --rm container that resolves mcp-sandbox:latest from the sandbox store at launch. Restart the service only when you change the server code or RUNTIMES.

License

Apache-2.0

See related servers & alternatives →

Related MCP servers

Browse all →

Related guides

Hand-picked reading to help you choose and use Files & Docs servers.