Secure Shell Sandboxes for MCP Tool Execution
Design secure sandboxes for bash and shell tool calls in MCP. Implement Docker cgroups, gVisor isolation, network boundaries, and read-only bind mounts.
Generate & Validate Multi-Client MCP Config
One-click export with environment variables & path locators for Claude Desktop, Cursor, Windsurf, and OpenAI Codex CLI.
Secure Shell Sandboxes for MCP Tool Execution
Autonomous coding agents derive substantial utility from the ability to run terminal commands. Whether compiling TypeScript codebases, running unit test suites, inspecting directory listings, or querying package managers, command-line interfaces provide the ultimate developer surface. However, granting a large language model unconstrained shell access (/bin/bash, sh, or cmd.exe) on a host workstation presents severe security vulnerabilities.
A model experiencing prompt injection or catastrophic hallucination can inadvertently execute destructive file system operations, expose sensitive environment variables, overwrite SSH keys, or download malicious external payloads. Implementing a hardened execution sandbox for Model Context Protocol (MCP) shell tools is essential for maintaining production safety and developer trust.
1. Problem Statement and Architecture
Unrestricted shell execution suffers from several critical attack vectors and reliability vulnerabilities:
- ▸Host Filesystem Destruction: Unchecked commands such as
rm -rf /or recursive permission changes can permanently destroy operating system files or developer projects. - ▸Secret and Credential Exfiltration: Unrestricted shells inherit host environment variables containing AWS credentials, GitHub personal access tokens, and private keys. Agents with internet connectivity could exfiltrate these secrets via outbound HTTP requests.
- ▸Denial of Service and Fork Bombs: Rogue loops (
:(){ :|:& };:) or runaway compiler processes can exhaust host CPU threads, memory allocations, and process table handles.
+-----------------------------------------------------------------------------------+
| Host Boundary |
| |
| +--------------------+ JSON-RPC 2.0 (stdio) +-------------------+ |
| | Claude / Cursor | <================================> | Sandboxed Daemon | |
| +--------------------+ +---------+---------+ |
+----------------------------------------------------------------------|------------+
| docker run --rm
| --network none
v
+-----------------------------------+
| Ephemeral Container (gVisor/Alpine)|
| - CPU Quota: 0.5 Cores |
| - Memory Limit: 512 MB |
| - Read-Only Root Filesystem |
| - Ephemeral /tmp tmpfs |
+-----------------------------------+A production-grade MCP sandbox intercepts incoming tool invocations and executes shell commands within ephemeral, resource-constrained container runtimes such as Docker, gVisor (runsc), or Linux Bubblewrap. By dropping outbound network capabilities, enforcing memory limits, and providing read-only project mounts, the host remains completely isolated from unintended side effects.
2. Prerequisites and Environment Matrix
Building and deploying a sandboxed MCP execution daemon requires an active container runtime and a modern Node.js or Python service runner.
| Component | Minimum Version | Recommended | Notes |
|---|---|---|---|
| Container Engine | Docker 24.0+ | Docker Engine / OrbStack | Ephemeral container execution |
| Sandboxed Runtime | runc / gVisor | gVisor runsc | Kernel-level syscall interception |
| Node.js | 18.18.0+ | 20.x LTS or 22.x | Runtime for the custom MCP server daemon |
| Claude Desktop | 0.7.0+ | Latest | Supported desktop client |
| Cursor IDE | 0.42.0+ | Latest | Agent composer tool runner |
| OpenAI Codex CLI | 0.1.0+ | Latest | Terminal-driven workflow harness |
Ensure the Docker daemon is active and responsive prior to starting your server:
docker info --format '{{.ServerVersion}}'3. Sandboxed Server Implementation
Below is a complete, production-ready TypeScript implementation of a sandboxed execution server built with the official @modelcontextprotocol/sdk. It dispatches shell commands inside ephemeral Alpine Linux containers with hardened security constraints.
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { spawn } from "node:child_process";
const server = new Server(
{ name: "safe-shell-sandbox", version: "1.0.0" },
{ capabilities: { tools: {} } }
);
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "run_sandboxed_command",
description: "Safely execute a shell command inside an isolated, ephemeral Linux container.",
inputSchema: {
type: "object",
properties: {
command: { type: "string", description: "The shell command string to evaluate" },
timeout_seconds: { type: "integer", default: 15, description: "Maximum execution time in seconds" }
},
required: ["command"]
}
}
]
}));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name !== "run_sandboxed_command") {
throw new Error("Unknown tool requested");
}
const { command, timeout_seconds = 15 } = request.params.arguments as {
command: string;
timeout_seconds?: number;
};
const dockerArgs = [
"run", "--rm",
"--network", "none",
"--memory", "512m",
"--cpus", "0.5",
"--pids-limit", "64",
"--read-only",
"--tmpfs", "/tmp:rw,noexec,nosuid,size=64m",
"alpine:3.20",
"sh", "-c", command
];
return new Promise((resolve) => {
const proc = spawn("docker", dockerArgs);
let output = "";
let isFinished = false;
const timer = setTimeout(() => {
if (!isFinished) {
isFinished = true;
proc.kill("SIGKILL");
resolve({
content: [{ type: "text", text: `Execution timed out after ${timeout_seconds} seconds.` }],
isError: true
});
}
}, timeout_seconds * 1000);
proc.stdout.on("data", (chunk) => { output += chunk.toString(); });
proc.stderr.on("data", (chunk) => { output += chunk.toString(); });
proc.on("close", (code) => {
if (isFinished) return;
isFinished = true;
clearTimeout(timer);
resolve({
content: [{ type: "text", text: output.trim() || `Process exited with code ${code}` }],
isError: code !== 0
});
});
});
});
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
}
main().catch(console.error);4. Multi-Host Configuration Snippets
Once built into dist/index.js, configure your AI hosts to launch the sandboxed daemon.
Claude Desktop
Edit your claude_desktop_config.json:
{
"mcpServers": {
"safe-shell": {
"command": "node",
"args": ["/absolute/path/to/sandbox-server/dist/index.js"]
}
}
}Cursor IDE
In .cursor/mcp.json or your global workspace configuration:
{
"mcpServers": {
"safe-shell": {
"command": "node",
"args": ["/absolute/path/to/sandbox-server/dist/index.js"]
}
}
}OpenAI Codex CLI
In ~/.codex/config.toml:
[mcp_servers.safe_shell]
command = "node"
args = ["/absolute/path/to/sandbox-server/dist/index.js"]5. Common Errors and Production Troubleshooting
1. docker: Got permission denied while trying to connect to the Docker daemon socket
- ▸Symptom: Spawning sandboxed commands returns exit code 1 with permission denied errors.
- ▸Root Cause: The MCP client process runs under a standard non-root user that is not a member of the host's
dockergroup. - ▸Solution: On Linux, add your user account to the docker group and reload group memberships:
sudo usermod -aG docker $USER
newgrp dockerOn macOS and Windows, verify that Docker Desktop is active and "Allow the default Docker socket to be used" is enabled in settings.
2. Container Exit Code 137 (Out of Memory Kill)
- ▸Symptom: Commands compiling TypeScript or installing dependencies abruptly exit with code 137.
- ▸Root Cause: The process exceeded the container's memory ceiling (
--memory 512m), triggering the Linux kernel Out-Of-Memory (OOM) killer. - ▸Solution: Increase memory limits to 1 GB or 2 GB (
--memory 2048m) for resource-intensive compilation workflows, while preserving CPU throttling.
3. Read-Only Filesystem Errors (EROFS)
- ▸Symptom: Commands attempting to create temporary files fail with
Read-only file system. - ▸Root Cause: The sandbox enforces
--read-onlyroot filesystems for container security. - ▸Solution: Mount dedicated temporary in-memory filesystems via
--tmpfs /tmp:rw,size=128mor provide designated workspace bind mounts using-v /path/to/workspace:/workspace:rw.
6. External Citations and Security Guardrails
Operating terminal execution sandboxes requires continuous vigilance:
- ▸Network Disconnection: Always enforce
--network noneunless the agent is explicitly task-bound to package downloads, preventing unauthorized socket listeners and data exfiltration. - ▸Process Table Limits: Always configure
--pids-limit 64to immediately neutralize recursive fork bombs. - ▸Hypervisor Isolation: For enterprise multi-tenant environments, run containers under gVisor (
--runtime=runsc) or AWS Firecracker microVMs to prevent container breakout vulnerabilities.
Weekly Protocol Diffs & Verified Drops
New server guides, benchmarks, and security audits. No spam. Unsubscribe anytime.
Zero marketing spam. 1-click unsubscribe anytime.
Build your full agent toolstack in the Visual Generator
Combine Secure Shell Sandboxes for MCP Tool Execution with databases, search APIs, and memory graphs in a single configuration file.
Did this setup guide work with your AI host?
Help test this configuration across Claude, Cursor, and Codex CLI. Be the first to report:
Secure Shell Sandboxes for MCP Tool Execution FAQ
What is the Secure Shell Sandboxes for MCP Tool Execution?
Design secure sandboxes for bash and shell tool calls in MCP. Implement Docker cgroups, gVisor isolation, network boundaries, and read-only bind mounts.
How do I configure Secure Shell Sandboxes for MCP Tool Execution in Claude Desktop or Cursor?
You can copy the configuration JSON from our guide or launch the interactive MCP Codex Config Generator at https://mcp-codex.com/generator to export valid configs in 1 click.
Can I use Secure Shell Sandboxes for MCP Tool Execution with the OpenAI Codex CLI?
Yes, OpenAI Codex CLI supports Model Context Protocol. You can add it directly to ~/.codex/config.toml or pass arguments to codex mcp add.
Specializing in Model Context Protocol (MCP) integrations, autonomous AI agent orchestration, and distributed developer toolchains. Researches and benchmarks production MCP client-server architectures across OpenAI Codex, Claude, and Cursor.
Related Guides
OpenCode Free AI Agent Guide
Guide to running OpenCode coding agent completely free without API keys using Zen free tier, local LLMs, and OAuth accounts.
Dev ToolsSelf-Correcting Verification Loops in Agents
Build robust self-correcting /loop verification harnesses for AI agents using Test-Driven Agent Development, dual-agent critics, and MCP test sandboxes.
Dev ToolsAutomated Doc Verification Loops with Claude CLI
Build automated documentation verification loops with Claude CLI and MCP. Validate code snippets, test tool schemas, and sync SDK docs in CI/CD. Verified.