Skip to main content
Glama
sonirico

mcp-shell

by sonirico

mcp-shell

Trust Score glama

MCP server that runs shell commands. Your LLM gets a tool; you get control over what runs and how.

Built on mark3labs/mcp-go. Written in Go.


Run it

Docker (easiest):

docker run -it --rm -v /tmp/mcp-workspace:/tmp/mcp-workspace sonirico/mcp-shell:latest

From source:

git clone https://github.com/sonirico/mcp-shell && cd mcp-shell
make install
mcp-shell

Related MCP server: MCP Shell Server

Configure it

Secure mode is the default. With no config file, mcp-shell boots in secure mode and registers only typed tools: file reads, grep/glob, git inspection, and (opt-in) file/git writes and operator-defined scripts. There is no raw shell command. You only need a config file to change the defaults below. To run fully unrestricted you must opt in explicitly:

MCP_SHELL_ALLOW_UNSAFE=1 mcp-shell   # disables secure mode; the only tool is shell_exec

To customize the policy, point to a YAML config:

export MCP_SHELL_SEC_CONFIG_FILE=/path/to/security.yaml
mcp-shell

Secure mode (default) — typed tools only, every path confined to working_directory:

security:
  enabled: true
  working_directory: /tmp/mcp-workspace
  max_execution_time: 30s
  max_output_size: 1048576
  run_as_user: ""
  audit_log: true

  # Expose file and git write tools (write_file, edit_file, mkdir, move,
  # delete, git_add, git_commit, git_switch, git_restore, git_stash). Off by
  # default.
  writes_enabled: false

  # Operator-defined scripts exposed through the run_script tool. The client
  # picks a name; the argv is yours and cannot be altered.
  # scripts:
  #   test: ["go", "test", "./..."]
  #   lint: ["golangci-lint", "run"]

Wire it up

Claude Desktop — add to your MCP config:

{
  "mcpServers": {
    "shell": {
      "command": "docker",
      "args": ["run", "--rm", "-i", "sonirico/mcp-shell:latest"],
      "env": { "MCP_SHELL_LOG_LEVEL": "info" }
    }
  }
}

For custom config, mount the file and set the env:

{
  "command": "docker",
  "args": ["run", "--rm", "-i", "-v", "/path/to/security.yaml:/etc/mcp-shell/security.yaml", "-e", "MCP_SHELL_SEC_CONFIG_FILE=/etc/mcp-shell/security.yaml", "sonirico/mcp-shell:latest"]
}

Tools

Secure mode (the default) registers these typed tools. * marks a required parameter.

Tool

Parameters

Available

read_file

path*, offset, limit, tail

always

list_dir

path, depth, include_hidden

always

glob

pattern*, path, newer_than, max_results

always

grep

pattern*, path, glob, ignore_case, context, files_only, count, max_results

always

stat

path*

always

diff_files

path_a*, path_b*

always

system_info

always

git_status

always

git_log

max_count, ref, path, author, grep, since, until, oneline, follow

always

git_diff

ref, ref_to, staged, path, stat_only, name_only

always

git_show

ref, path, stat_only

always

git_blame

path*, ref, line_start, line_end

always

git_branches

all, merged

always

git_tags

pattern

always

git_rev_parse

ref*

always

git_ls_files

path, untracked

always

git_stash_list

always

git_remotes

always

write_file

path*, content*, append

writes_enabled

edit_file

path*, old_string*, new_string*, replace_all

writes_enabled

mkdir

path*

writes_enabled

move

from*, to*

writes_enabled

delete

path*, recursive

writes_enabled

git_add

paths, all

writes_enabled

git_commit

message*, all

writes_enabled

git_switch

branch*, create

writes_enabled

git_restore

paths*, staged

writes_enabled

git_stash

action*, message

writes_enabled

run_script

name*

scripts

Every path parameter is resolved against working_directory (symlinks followed); anything outside it is rejected. Git paths and refs are passed positionally and validated: a ref starting with - is rejected. There are no network tools; push, fetch and clone are not offered.

Unrestricted mode: shell_exec exists only with MCP_SHELL_ALLOW_UNSAFE=1, runs the command through bash -c with no validation, by design, and it is the only tool registered in that mode.


Environment variables

Variable

Description

MCP_SHELL_SEC_CONFIG_FILE

Path to security YAML (overrides built-in secure defaults)

MCP_SHELL_ALLOW_UNSAFE

Set 1 (or true) to disable secure mode and expose shell_exec instead of the typed tools (opt-in)

MCP_SHELL_SERVER_NAME

Server name (default: "mcp-shell 🐚")

MCP_SHELL_LOG_LEVEL

debug, info, warn, error, fatal

MCP_SHELL_LOG_FORMAT

json, console

MCP_SHELL_LOG_OUTPUT

stdout, stderr, file


Development

make install dev-tools   # deps + goimports, golines
make fmt test lint
make docker-build       # build image locally
make release            # binary + docker image

Security

  • Default: Secure mode. The server builds every command's argv itself; the client never supplies a shell string. Only typed tools are registered.

  • Path confinement: every path parameter is resolved against working_directory, symlinks followed, and anything that resolves outside it is rejected.

  • Git hardening: paths are passed after --, refs after --end-of-options, and a ref starting with - is rejected. Git runs with GIT_CONFIG_NOSYSTEM=1, GIT_CONFIG_GLOBAL=/dev/null, core.fsmonitor, core.pager and core.hooksPath neutralised, and --no-ext-diff --no-textconv on log/diff/show/blame.

  • Minimal environment: child processes get only PATH, HOME and LANG, never the server's own environment or .env secrets.

  • Writes and scripts are opt-in: writes_enabled: true exposes the file/git write tools; a non-empty scripts map exposes run_script. Both are off by default.

  • Unrestricted: only via MCP_SHELL_ALLOW_UNSAFE=1. The only tool registered is shell_exec, which runs bash -c with no validation. Fine for local dev, dangerous otherwise.

  • Docker: Runs as non-root, Alpine-based. Use it in production. Best paired with an OS sandbox (read-only FS, dropped caps) as defense-in-depth.

Threat model, guarantees, and the scope for vulnerability reports live in SECURITY.md. Read it before opening an advisory.


Migrating from 0.x

Secure mode no longer validates a shell_exec command string; it exposes typed tools instead. A config file's security: block no longer accepts:

Removed key

Replacement

use_shell_execution

not needed; typed tools never shell out

allowed_executables

not needed; each tool runs a fixed, server-built argv

allowed_commands

not needed; same as above

blocked_commands

not needed; same as above

blocked_patterns

not needed; same as above

Loading a config file that still sets one of these fails at startup with an error naming the key. There is no more "legacy mode" and no security-legacy.yaml example. If you need raw shell access, set MCP_SHELL_ALLOW_UNSAFE=1 to get shell_exec back; it is no longer constrained by the security: block at all.


Contributing

Fork, branch, make fmt test, open a PR.

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    Not graded
    maintenance
    A secure and pluggable MCP server to run terminal commands on your local machine or cloud server — remotely, safely, and with LLMs or agentic clients.
    -
  • A
    license
    B
    quality
    A
    maintenance
    A secure MCP server for shell operations, terminal management, and process control, enabling AI assistants to safely execute commands and manage interactive sessions.
    13
    161 npm
    6
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Universal MCP server that wraps any CLI tool, enabling AI assistants to run commands via natural language.
    MIT