sudo-mcp
An MCP server that lets a coding agent run sudo commands without piping a password through the model.
Disclaimer: this tool has no command denylist. Whatever command the agent passes, sudo will run it. Add your own guardrails (sudoers Cmnd_Alias, wrapper script, sandbox) if you want any.
The problem
Claude Code's Bash tool redirects stdin to /dev/null, so any sudo invocation that needs a password fails immediately with a terminal is required to read the password. The usual workarounds are bad: NOPASSWD: ALL in sudoers gives the agent permanent passive root; running sudo claude gives the entire session permanent root; pasting the password into chat exposes it to the model context, the transcript, and any future training set.
What this does
sudo-mcp is a single static Go binary that exposes one MCP tool, sudo_run(argv, reason, timeout_seconds?, cwd?). When the agent calls it:
- The server sets
SUDO_ASKPASS to its own path and exec's sudo -A -- argv....
- sudo invokes the askpass helper. The same binary handles that role too, dispatched on
SUDO_MCP_ROLE=askpass.
- The askpass role pops a native OS dialog (
osascript on macOS, ssh-askpass/ksshaskpass/zenity/kdialog on Linux) showing the reason so the user knows what they are authorizing.
- The user types the password into the dialog. It goes to sudo's stdin only.
- The server captures stdout, stderr, and exit code, returns them as the tool result.
The password never touches the MCP transport, the model context, the conversation transcript, or ~/.bash_history. The agent learns the command's exit code and output, nothing else.
Install
Requires sudo. macOS works out of the box. On Linux you need at least one of ssh-askpass, ksshaskpass, zenity, or kdialog.
Prebuilt binary (recommended)
Grab the archive for your OS/arch from the latest release: https://github.com/0xMH/sudo-mcp/releases/latest
# macOS arm64 example; substitute the asset for your platform
curl -L -o sudo-mcp.tar.gz https://github.com/0xMH/sudo-mcp/releases/latest/download/sudo-mcp_0.3.0_darwin_arm64.tar.gz
tar -xzf sudo-mcp.tar.gz
install -m 0755 sudo-mcp ~/.local/bin/sudo-mcp
Verify the SHA-256 against checksums.txt from the same release before running.
From source
Requires Go 1.22+.
go install github.com/0xMH/sudo-mcp@latest
The binary lands in $(go env GOBIN) (typically ~/go/bin/sudo-mcp). Make sure that directory is on your $PATH, or use the absolute path when registering.
Wire into Claude Code
Register the server at user scope so it is available in every project:
claude mcp add sudo-mcp --scope user -- ~/go/bin/sudo-mcp
Verify:
claude mcp list
You should see sudo-mcp: ~/go/bin/sudo-mcp - ✓ Connected.
To make the agent actually prefer sudo_run over typing sudo into Bash, deny Bash(sudo *) in ~/.claude/settings.json:
{
"permissions": {
"deny": ["Bash(sudo *)", "Bash(sudo)"],
"allow": ["mcp__sudo-mcp__sudo_run"]
}
}
sudo_run takes:
argv (string array, required): command and arguments. Pass as a list, not a shell string. Example: ["apt", "install", "-y", "htop"]. The server exec's directly with no shell, so injection through arg concatenation is structurally impossible.
reason (string, required): one-line justification rendered in the password dialog so the user sees what they are authorizing before typing the password.
timeout_seconds (int, optional, default 120, max 3600): hard kill if sudo hangs longer than this.
cwd (string, optional): working directory for the command.
Returns a text block with the exit code, stdout, and stderr. Output is truncated at 256 KiB per stream.
Security properties
- The password is entered into a native OS dialog. The dialog is rendered by the user's window server, not the agent. The agent cannot read it, screenshot it, or replay it.
- The MCP transport carries argv, reason, exit code, stdout, stderr. It never carries credentials.
argv is a list, never a shell string. There is no bash -c involved, so no quoting bugs and no injection through reason interpolation.
- Sudo's timestamp cache is left at its system default (typically 5 minutes on macOS). Calls inside that window skip the dialog. Drop
-A and add -k in main.go if you want a fresh prompt every time.
- The server only exposes one tool. There is no shell access, no arbitrary subprocess execution outside of
sudo.
License
MIT. See LICENSE.