Dev Tools·
advanced
·14 min read·Jul 9, 2026·Updated Oct 10, 2026
By Rad Tome·Lead AI Systems Architect·How we verify this

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.

sandboxsecurityshell executiondockergvisormodel context protocollinux namespacescontainerization
Interactive Tool
1-Click Export

Generate & Validate Multi-Client MCP Config

One-click export with environment variables & path locators for Claude Desktop, Cursor, Windsurf, and OpenAI Codex CLI.

Open in Generator

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:

  1. ▸Host Filesystem Destruction: Unchecked commands such as rm -rf / or recursive permission changes can permanently destroy operating system files or developer projects.
  2. ▸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.
  3. ▸Denial of Service and Fork Bombs: Rogue loops (:(){ :|:& };:) or runaway compiler processes can exhaust host CPU threads, memory allocations, and process table handles.
code
+-----------------------------------------------------------------------------------+
|                              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.

ComponentMinimum VersionRecommendedNotes
Container EngineDocker 24.0+Docker Engine / OrbStackEphemeral container execution
Sandboxed Runtimerunc / gVisorgVisor runscKernel-level syscall interception
Node.js18.18.0+20.x LTS or 22.xRuntime for the custom MCP server daemon
Claude Desktop0.7.0+LatestSupported desktop client
Cursor IDE0.42.0+LatestAgent composer tool runner
OpenAI Codex CLI0.1.0+LatestTerminal-driven workflow harness

Ensure the Docker daemon is active and responsive prior to starting your server:

bash
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.

typescript
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:

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:

json
{
  "mcpServers": {
    "safe-shell": {
      "command": "node",
      "args": ["/absolute/path/to/sandbox-server/dist/index.js"]
    }
  }
}

OpenAI Codex CLI

In ~/.codex/config.toml:

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 docker group.
  • ▸Solution: On Linux, add your user account to the docker group and reload group memberships:
bash
sudo usermod -aG docker $USER
newgrp docker

On 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-only root filesystems for container security.
  • ▸Solution: Mount dedicated temporary in-memory filesystems via --tmpfs /tmp:rw,size=128m or 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:

  1. ▸Network Disconnection: Always enforce --network none unless the agent is explicitly task-bound to package downloads, preventing unauthorized socket listeners and data exfiltration.
  2. ▸Process Table Limits: Always configure --pids-limit 64 to immediately neutralize recursive fork bombs.
  3. ▸Hypervisor Isolation: For enterprise multi-tenant environments, run containers under gVisor (--runtime=runsc) or AWS Firecracker microVMs to prevent container breakout vulnerabilities.
Technical Changelog

Weekly Protocol Diffs & Verified Drops

New server guides, benchmarks, and security audits. No spam. Unsubscribe anytime.

Zero marketing spam. 1-click unsubscribe anytime.

Ready to Deploy?

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.

Customize in Generator
Developer Verification & Feedback
0 reports

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.

RT

Written by Rad Tome

Lead AI Systems Architect & Maintainer, MCP Codex

@RadTome

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.

Editorial Integrity: Package names are checked against the npm registry and repositories against the GitHub API. Where a package could not be verified, the guide says so instead of giving you a command to run.

Related Guides