loading…
Search for a command to run...
loading…
An MCP server that provides sandboxed shell command execution with configurable security policies, enabling safe AI-assisted command runs within a project repos
An MCP server that provides sandboxed shell command execution with configurable security policies, enabling safe AI-assisted command runs within a project repository.
npm version npm downloads CI License: MIT
Independent agentic coding without handing over the keys to the castle. Stop getting approval prompts that are unimportant.
git worktree list and added to an allowlist for the session. Disable with worktree_detection = false in the [security] section of your config file.shell_exec with configurable limit_bytes, limit_lines, and on_large_output modesspill_uri for safe, paginated readingread_file_chunk Tool: Read paginated data from spilled files using cursor and limit_bytes for safe streamingSHEMCP_ROOT or MCP_SANDBOX_ROOT.shell_set_cwd tool; shell_exec cwd can be relative to the sandbox root, or an absolute path within the sandbox or a valid git worktree.shell_info tool for introspection (reports sandbox_root and resolves relative cwd inputs, including within_sandbox checks).ensureCwd with realpath and boundary checks to prevent symlink escapes and ensure directory accessibility.This MCP server provides sandboxed shell command execution with comprehensive security policies. It allows AI assistants to safely execute shell commands while enforcing strict access controls through configurable TOML files.
To avoid the sandbox accidentally shrinking to a nested subdirectory, shemcp derives a stable sandbox root at startup using the following precedence:
The chosen root remains fixed for the duration of the process. Working directories for command execution can be relative paths inside this sandbox, or absolute paths within the sandbox or a valid git worktree.
This ensures that if the client happens to start the agent several levels deep, the sandbox still resolves to the project root (typically the Git root), preventing the MCP from becoming unable to access sibling paths in the repository.
You can explicitly override the root for special cases with SHEMCP_ROOT or MCP_SANDBOX_ROOT.
SHEMCP_ROOT or MCP_SANDBOX_ROOT.git worktree add)cwd can be relative or absolute within sandbox/worktree boundaries. Realpath boundary checks prevent symlink escapes.limit_bytes and limit_linesread_file_chunk tool for reading spilled files in token-safe chunksThe server implements multiple layers of security:
cwd can be relative or absolute within these boundaries. Override root via SHEMCP_ROOT or MCP_SANDBOX_ROOT.The server writes debug logs to ~/.shemcp/debug.log which can help diagnose issues:
# View the debug log
tail -f ~/.shemcp/debug.log
# Clear the debug log
> ~/.shemcp/debug.log
The log captures:
SHEMCP_ROOT or MCP_SANDBOX_ROOT.shell_execExecute an allow-listed command inside the sandbox with support for pagination and large output handling.
Parameters:
cmd (required): Command to run (e.g., "git", "npm", "python")args: Array of string arguments (e.g., ["status", "--short"])cwd: Optional working directory (relative to sandbox root, or absolute path within sandbox/worktree)timeout_ms: Command timeout in milliseconds (deprecated, use timeout_seconds)timeout_seconds: Command timeout in seconds (1-600, clamped to policy limits)max_output_bytes: Maximum output size in bytes (1000-10M, clamped to policy limits)page (required): Pagination configuration object:cursor: Opaque position marker (e.g., "bytes:0")limit_bytes: Maximum bytes per page (default: 40000, ~10k tokens)limit_lines: Maximum lines per page (default: 2000, stops on whichever hits first)on_large_output: How to handle large outputs: "spill" (default), "truncate", or "error"Rules:
page object must be supplied; otherwise the request is rejected with Error: pagination parameters are requiredcwd can be relative to sandbox root, or an absolute path within the sandbox or a valid git worktreeon_large_output modeResponse Format:
{
"exit_code": 0,
"stdout_chunk": "first 40k of data...",
"stderr_chunk": "",
"bytes_start": 0,
"bytes_end": 39999,
"total_bytes": 58112234,
"truncated": false,
"next_cursor": "bytes:40000",
"spill_uri": "mcp://tmp/exec-abc123.out",
"mime": "text/plain",
"line_count": 1780,
"stderr_count": 0,
"cmdline": ["git", "log"],
"cwd": "/path/to/project",
"limits": {
"timeout_ms": 60000,
"max_output_bytes": 2000000
}
}
read_file_chunkRead paginated data from a spilled file created by shell_exec when on_large_output is set to "spill".
Parameters:
uri (required): URI of the spilled file (e.g., "mcp://tmp/exec-abc123.out")cursor: Opaque position marker (default: "bytes:0")limit_bytes: Maximum bytes to read (default: 40000)Response Format:
{
"data": "chunk of file content...",
"bytes_start": 0,
"bytes_end": 39999,
"total_bytes": 58112234,
"next_cursor": "bytes:40000",
"mime": "text/plain"
}
shell_infoIntrospection utility for the sandbox.
cwd: Relative path to resolve and validate against the sandbox rootsandbox_root, and if cwd is provided, resolved_path and within_sandbox flagsshell_set_cwdThis command has been removed. Use shell_exec with a relative cwd instead.
Ask your MCP client to call these tools with the following inputs:
shell_info examples:
{ "cwd": "." } → returns sandbox_root, resolves to the root, and confirms within sandbox{ "cwd": "src" } → returns resolved src path and within_sandbox: true if it exists/insideshell_exec examples:
{ "cmd": "git", "args": ["status"], "cwd": ".", "page": { "cursor": { "cursor_type": "bytes", "offset": 0 } } }
{ "cmd": "npm", "args": ["test"], "cwd": ".", "page": { "cursor": { "cursor_type": "bytes", "offset": 0 } } }
{ "cmd": "ls", "args": ["-la"], "cwd": "src", "page": { "cursor": { "cursor_type": "bytes", "offset": 0 } } }
Pagination examples:
{ "cmd": "git", "args": ["log"], "page": { "cursor": { "cursor_type": "bytes", "offset": 0 }, "limit_bytes": 32768 } } → First 32KB of git log{ "cmd": "cat", "args": ["large.log"], "page": { "cursor": { "cursor_type": "bytes", "offset": 40000 } } } → Next page from byte 40000{ "cmd": "find", "args": [".", "-name", "*.ts"], "page": { "cursor": { "cursor_type": "bytes", "offset": 0 } }, "on_large_output": "spill" } → Spill large find results to fileSpill file reading examples:
{ "uri": "mcp://tmp/exec-abc123.out", "cursor": { "cursor_type": "bytes", "offset": 0 }, "limit_bytes": 16384 } → Read first 16KB of spilled file{ "uri": "mcp://tmp/exec-abc123.out", "cursor": { "cursor_type": "bytes", "offset": 16384 }, "limit_bytes": 16384 } → Read next 16KB chunkAdd the MCP server using Claude Code's CLI with npx (recommended):
# Add the shell MCP server to Claude Code (uses latest version)
claude mcp add shell -- npx -y shemcp@latest
# Verify it was added successfully
claude mcp list
Alternative scopes:
# Add for current project only (default)
claude mcp add shell -- npx -y shemcp@latest
# Add for current user (available in all projects)
claude mcp add --scope user shell -- npx -y shemcp@latest
# Add for project team (creates .mcp.json in project root)
claude mcp add --scope project shell -- npx -y shemcp@latest
For Cursor/VS Code with MCP:
{
"mcp.servers": {
"shell": {
"command": "npx",
"args": ["-y", "shemcp@latest"],
"env": {}
}
}
}
For Desktop MCP Clients:
npx["-y", "shemcp@latest"]The server works out of the box with sensible defaults. If you need to customize the configuration:
# Create config directory
mkdir -p ~/.config/shemcp
# Download and customize the example config
curl -o ~/.config/shemcp/config.toml https://raw.githubusercontent.com/acartine/shemcp/main/config.example.toml
# Edit the config to match your needs
nano ~/.config/shemcp/config.toml
If you prefer to install shemcp globally instead of using npx:
# Global installation
npm install -g shemcp
# Then use direct command in MCP config
claude mcp add shell -- shemcp
For development from source:
git clone https://github.com/acartine/shemcp.git
cd shemcp
npm install
npm run build
# Add local version to Claude Code
claude mcp add shell -- node /absolute/path/to/shemcp/dist/index.js
The server works with sensible built-in defaults. Configuration files are optional and only needed for customization.
If present, configuration is loaded from (in priority order):
~/.config/shemcp/config.toml (user config - highest priority)/etc/shemcp/config.toml (system config - lower priority)# Configuration format version (not the package version)
config_version = 1
[server]
name = "shemcp"
[directories]
# The sandbox root defaults to the Git repository root (fallback to the current
# working directory) and remains fixed for the process lifetime.
# Override with SHEMCP_ROOT or MCP_SANDBOX_ROOT environment variables if needed.
[commands]
allow = ["^git(\\s|$)", "^npm(\\s|$)", "^make(\\s|$)"]
deny = ["^git\\s+push\\s+(origin\\s+)?(main|master)"]
[limits]
timeout_seconds = 600
max_output_bytes = 2000000
[environment]
whitelist = ["PATH", "HOME", "USER", "LANG"]
[security]
require_secure_permissions = false
worktree_detection = true # Enable automatic git worktree detection
See config.example.toml for a complete example with documentation.
Once configured with Claude Code or another MCP client, you can ask the AI to execute shell commands:
Example interactions:
git statusfind . -name "*.ts"npm testgit log --oneline -10git checkout -b feature-nameThe AI can only execute commands that match your allow patterns and run in directories you've permitted, providing a secure sandbox for shell operations.
When dealing with commands that produce large outputs (like logs, large files, or directory listings), use pagination to avoid token limits:
Scenario 1: Paginating through git log
{
"cmd": "git",
"args": ["log", "--oneline"],
"page": { "cursor": { "cursor_type": "bytes", "offset": 0 }, "limit_bytes": 32768 }
}
Returns first 32KB of git history with next_cursor for continuation.
Scenario 2: Reading large files in chunks
{
"cmd": "cat",
"args": ["huge.log"],
"on_large_output": "spill",
"page": { "cursor": { "cursor_type": "bytes", "offset": 0 }, "limit_bytes": 40000 }
}
Spills large log file and returns first 40KB with spill_uri for continued reading.
Scenario 3: Processing large directory listings
{
"cmd": "find",
"args": [".", "-type", "f", "-name", "*.js"],
"page": { "cursor": { "cursor_type": "bytes", "offset": 0 }, "limit_lines": 1000 }
}
Returns up to 1000 lines of file listing, whichever comes first.
When shell_exec returns a spill_uri, use read_file_chunk to read the data in manageable chunks:
Scenario 4: Reading spilled output
{
"uri": "mcp://tmp/exec-abc123.out",
"cursor": { "cursor_type": "bytes", "offset": 0 },
"limit_bytes": 16384
}
Reads first 16KB of the spilled file.
Scenario 5: Continuing to read spilled output
{
"uri": "mcp://tmp/exec-abc123.out",
"cursor": { "cursor_type": "bytes", "offset": 16384 },
"limit_bytes": 16384
}
Reads the next 16KB chunk using the next_cursor from the previous response.
Automatic Pagination Loop:
// Pseudo-code for automatic pagination
let result = shell_exec(cmd, args, { page: { limit_bytes: 40000 } });
while (result.next_cursor) {
// Process current chunk
processChunk(result.stdout_chunk);
// Get next chunk
result = shell_exec(cmd, args, {
page: { cursor: result.next_cursor }
});
}
Spill File Handling:
// Pseudo-code for handling spilled files
let result = shell_exec(cmd, args, { on_large_output: "spill", page: {} });
if (result.spill_uri) {
let chunk = read_file_chunk(result.spill_uri, 40000);
while (chunk.next_cursor) {
processChunk(chunk.data);
chunk = read_file_chunk(result.spill_uri, chunk.next_cursor);
}
}
# Run tests
npm test
# Run tests with UI
npm test:ui
# Build TypeScript
npm run build
# Development mode
npm run dev
shemcp automatically supports git worktrees, which are commonly used for parallel development workflows. When an agent creates a worktree (e.g., git worktree add ../repo-feature -b feature), the worktree is created as a sibling directory outside the primary sandbox.
git worktree list to verify it's a legitimate worktree/Users/user/myproject # Primary sandbox (git root)
/Users/user/myproject-feature # Worktree - automatically allowed
/Users/user/myproject-bugfix # Worktree - automatically allowed
/Users/user/other-project # NOT allowed - different project
If you prefer stricter security and don't need worktree support, disable it in your config file:
# In ~/.config/shemcp/config.toml
[security]
worktree_detection = false
When disabled, only paths within the primary sandbox root are allowed.
⚠️ Important Security Notes:
Configuration Security: Config files should not be world-writable. The server warns about insecure permissions.
No Project-Level Configs: By design, there are no .shemcp.toml files in working directories to prevent AI from modifying its own security constraints.
Principle of Least Privilege: Start with restrictive settings and gradually add permissions as needed.
Regular Auditing: Review your allowed commands and directories periodically.
The project includes a comprehensive test suite covering:
Run tests with: npm test
"Command not allowed" errors:
commands.allow patterns in the configcommands.deny list"Directory not allowed" errors:
Server not connecting:
npx -y shemcp@latest in your MCP client confignpm run build~/.shemcp/debug.logTo see your current configuration:
# List Claude Code MCP servers
claude mcp list
# Get details about your shell server
claude mcp get shell
# Remove server if needed
claude mcp remove shell
# Check which config files exist (optional - only if you created custom config)
ls -la ~/.config/shemcp/config.toml
ls -la /etc/shemcp/config.toml
MIT
Run in your terminal:
claude mcp add shemcp -- npx Security
Low riskAutomated heuristic from public metadata — not a security guarantee.