Install
$ agentstack add mcp-n0madic-ssh-mcp Open-source listing, not yet scanned by AgentStack. Follow the source repository for install instructions.
Security review
⚠ Flagged1 finding(s); flagged for manual review. · v0.1.0 How review works →
- • Prompt-injection patterns
- • Secret / credential exfiltration
- • Dangerous shell & filesystem operations
- • Untrusted network calls
- • Known-malicious package signatures
- high Destructive filesystem operation.
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
SSH MCP Server
A Model Context Protocol (MCP) server that provides AI agents with secure SSH access to remote hosts. Supports connection pooling, key-based and password authentication, sudo, file operations via SFTP, SSH tunnels (local port forwarding), command filtering, rate limiting, output truncation, and graceful shutdown.
Features
- SSH Connection Pool — reuses connections, auto-reconnect on failure, idle cleanup, auto-detection of remote OS and shell
- Authentication — explicit
key_pathfirst, then ssh-agent, then auto-discovered~/.ssh/id_*keys (when no agent), then password; automatic~/.ssh/configalias resolution - Command Execution — with sudo support, working directory, timeout, graceful kill (SIGTERM → SIGKILL), ANSI stripping
- SFTP File Operations — upload/download files and directories, read files with line offset/limit, edit files (replace/patch/create), file info with directory listing,
~path expansion - Interactive PTY Terminals — buffered PTY sessions for interactive programs (vim, htop, REPL), dialogs, and real-time output (opt-in with
--enable-terminal) - SSH Tunnels — local port forwarding (localhost:port → remote:port via SSH) for accessing remote services like databases, APIs, and web servers (opt-in with
--enable-tunnels) - Output Truncation — configurable per-stream output size limit (
--max-output-size) to prevent LLM context overflow - Security — host/command allowlist/denylist (regex + CIDR), per-host rate limiting, path traversal protection, filename length validation
- Transports — stdio (default) and Streamable HTTP (
localhostonly) - Graceful Shutdown — closes all tunnels, SSH connections, and terminal sessions on SIGINT/SIGTERM
Installation
go install github.com/n0madic/ssh-mcp@latest
Or build from source:
git clone https://github.com/n0madic/ssh-mcp.git
cd ssh-mcp
go build -o ssh-mcp .
Usage
Stdio transport (default)
./ssh-mcp
HTTP transport
./ssh-mcp --enable-http
# Listens on localhost:8081/mcp
Both transports
./ssh-mcp --enable-http
# Stdio + HTTP on localhost:8081/mcp
HTTP only (no stdio)
./ssh-mcp --enable-http --disable-stdio
CLI Flags
| Flag | Env Var | Default | Description | |------|---------|---------|-------------| | --enable-http | MCP_SSH_ENABLE_HTTP | false | Enable HTTP transport | | --http-port | MCP_SSH_HTTP_PORT | 8081 | HTTP transport port | | --disable-stdio | MCP_SSH_DISABLE_STDIO | false | Disable stdio transport | | --no-verify-host-key | MCP_SSH_NO_VERIFY_HOST_KEY | false | Disable host key verification | | --known-hosts | MCP_SSH_KNOWN_HOSTS | ~/.ssh/known_hosts | Path to knownhosts file | | --ssh-config | MCP_SSH_CONFIG | ~/.ssh/config | Path to SSH config file | | --enable-sudo | MCP_SSH_ENABLE_SUDO | false | Allow sudo execution | | --command-timeout | MCP_SSH_COMMAND_TIMEOUT | 60s | Command execution timeout | | --host-allowlist | MCP_SSH_HOST_ALLOWLIST | (empty) | Host allowlist (can be specified multiple times) | | --host-denylist | MCP_SSH_HOST_DENYLIST | (empty) | Host denylist (can be specified multiple times) | | --command-allowlist | MCP_SSH_COMMAND_ALLOWLIST | (empty) | Command allowlist regex (can be specified multiple times) | | --command-denylist | MCP_SSH_COMMAND_DENYLIST | (empty) | Command denylist regex (can be specified multiple times) | | --rate-limit | MCP_SSH_RATE_LIMIT | 60 | Rate limit (requests per minute per host) | | --rate-limit-file-ops | MCP_SSH_RATE_LIMIT_FILE_OPS | false | Apply rate limiting to SFTP file operations | | --local-base-dir | MCP_SSH_LOCAL_BASE_DIR | (empty) | Restrict local file operations to this directory | | --max-file-size | MCP_SSH_MAX_FILE_SIZE | 0 | Maximum file size for read operations (0=unlimited) | | --max-connections | MCP_SSH_MAX_CONNECTIONS | 0 | Maximum concurrent SSH connections (0=unlimited) | | --http-token | MCP_SSH_HTTP_TOKEN | (empty) | Bearer token for HTTP transport authentication | | --disable-tools | MCP_SSH_DISABLE_TOOLS | (empty)_ | Disable specific tools (can be specified multiple times) | | --enable-terminal | MCP_SSH_ENABLE_TERMINAL | false | Allow interactive PTY terminal sessions (ssh_open_terminal) | | --max-terminals | MCP_SSH_MAX_TERMINALS | 0 | Maximum concurrent PTY terminal sessions (0=unlimited) | | --max-output-size | MCP_SSH_MAX_OUTPUT_SIZE | 0 | Maximum output size per stream in bytes for execute/terminal results (0=unlimited) | | --enable-tunnels | MCP_SSH_ENABLE_TUNNELS | false | Allow SSH tunnel creation (ssh_tunnel_create) | | --max-tunnels | MCP_SSH_MAX_TUNNELS | 0 | Maximum concurrent SSH tunnels (0=unlimited) | | --version | — | — | Show version and exit |
Priority: CLI flags > environment variables > defaults.
Examples
Allow only specific hosts (regex patterns):
./ssh-mcp --host-allowlist "192.168.1.*" --host-allowlist "10.0.0.*"
Allow hosts by CIDR range:
./ssh-mcp --host-allowlist "10.0.0.0/8" --host-allowlist "192.168.0.0/16"
Block dangerous commands (multiple flags):
./ssh-mcp --command-denylist "rm\s+-rf.*" --command-denylist "shutdown.*" --command-denylist "reboot.*"
> Note: Command/host filter patterns are auto-anchored with ^ and $ for full-string matching. Use .* for substring matching (e.g., rm\s+-rf.* matches rm -rf / but rm alone won't match format). Host patterns also support CIDR notation (e.g., 10.0.0.0/8) — CIDR patterns are detected automatically and match by IP range instead of regex.
Using environment variables (comma-separated):
export MCP_SSH_HOST_ALLOWLIST="host1.example.com,host2.example.com,host3.example.com"
export MCP_SSH_COMMAND_DENYLIST="rm\s+-rf.*,shutdown.*,reboot.*"
export MCP_SSH_ENABLE_SUDO=true
./ssh-mcp
Restrict local file operations to a directory:
./ssh-mcp --local-base-dir /tmp/ssh-workspace
Enable HTTP transport with bearer token authentication:
./ssh-mcp --enable-http --http-token "my-secret-token"
Limit concurrent connections and file size:
./ssh-mcp --max-connections 5 --max-file-size 10485760
Limit output size to prevent LLM context overflow:
./ssh-mcp --max-output-size 65536
Limit concurrent SSH tunnels:
./ssh-mcp --max-tunnels 5
Disable specific tools (multiple flags):
./ssh-mcp --disable-tools ssh_execute --disable-tools ssh_edit_file
Disable tools via environment variable:
export MCP_SSH_DISABLE_TOOLS="ssh_execute,ssh_edit_file"
./ssh-mcp
MCP Tools
ssh_connect
Connect to a remote host via SSH.
Minimal (key auth from ssh-agent or ~/.ssh/id_*):
{
"host": "example.com"
}
Full format — user, password, port inline:
{
"host": "admin:secret@example.com:2222"
}
Explicit parameters (override inline values):
{
"host": "example.com",
"port": 2222,
"user": "admin",
"key_path": "~/.ssh/id_ed25519"
}
SSH config alias (resolved automatically from ~/.ssh/config):
{
"host": "my-server"
}
SSH config aliases are resolved automatically — no extra flags needed. Explicit parameters (port, user, key_path) override values from the config.
Returns session_id for use with other tools. Also auto-detects remote OS, architecture, and shell.
ssh_execute
Execute a command on a remote host. On timeout, sends SIGTERM first (5s grace period) then SIGKILL, and returns partial stdout/stderr with a [TIMEOUT] marker in stderr.
{
"session_id": "admin@example.com:22",
"command": "ls -la /var/log",
"timeout": 30,
"sudo": true,
"sudo_password": "secret",
"working_dir": "/home/admin"
}
ssh_disconnect
Disconnect an SSH session.
{
"session_id": "admin@example.com:22"
}
sshlistsessions
List all active SSH sessions with their connection details, statistics, active terminal sessions, and active tunnels (no parameters required).
ssh_upload
Upload a local file or directory to a remote host via SFTP. Automatically detects whether the local path is a file or directory. Preserves file permissions and directory structure. Supports ~ for remote home directory.
Upload a file:
{
"session_id": "admin@example.com:22",
"local_path": "/tmp/config.yaml",
"remote_path": "~/config.yaml"
}
Upload a directory (recursive):
{
"session_id": "admin@example.com:22",
"local_path": "/tmp/myapp",
"remote_path": "/opt/myapp"
}
ssh_download
Download a file or directory from a remote host via SFTP. Automatically detects whether the remote path is a file or directory. Preserves file permissions and directory structure. Supports ~ for remote home directory.
Download a file:
{
"session_id": "admin@example.com:22",
"remote_path": "~/app.log",
"local_path": "/tmp/app.log"
}
Download a directory (recursive):
{
"session_id": "admin@example.com:22",
"remote_path": "/opt/myapp",
"local_path": "/tmp/myapp-backup"
}
ssheditfile
Edit a file on a remote host. Two modes:
Replace mode (default) — full content replacement or new file creation:
{
"session_id": "admin@example.com:22",
"remote_path": "/etc/myapp/config.yaml",
"mode": "replace",
"content": "new file content here",
"backup": true
}
Patch mode — find and replace:
{
"session_id": "admin@example.com:22",
"remote_path": "/etc/myapp/config.yaml",
"mode": "patch",
"old_string": "old_value",
"new_string": "new_value",
"backup": true
}
sshreadfile
Read a file from a remote host with optional line offset and limit. Returns content with line numbers (like cat -n). Supports ~ for home directory.
Read entire file:
{
"session_id": "admin@example.com:22",
"remote_path": "~/app.log"
}
Read with offset and limit (pagination):
{
"session_id": "admin@example.com:22",
"remote_path": "/var/log/syslog",
"offset": 100,
"limit": 50
}
Read with max file size limit:
{
"session_id": "admin@example.com:22",
"remote_path": "~/large-file.csv",
"max_size": 1048576
}
Returns file content with line numbers, total line count, file size, and which lines are shown.
Interactive PTY Terminal Tools
These four tools provide buffered PTY access for interactive programs. Requires --enable-terminal.
Typical workflow:
ssh_open_terminal → opens shell, returns terminal_id + initial prompt
ssh_send_input → write text or keystrokes, get new output
ssh_read_output → poll for new output without writing
ssh_close_terminal → close the session
Active terminals are also listed in ssh_list_sessions output.
sshopenterminal
Open a PTY terminal session. Requires --enable-terminal flag on the server.
{
"session_id": "admin@example.com:22",
"cols": 120,
"rows": 50,
"term_type": "xterm-256color",
"wait_ms": 500,
"protect_exit": true
}
Returns terminal_id and the initial shell output (prompt). cols/rows/term_type/wait_ms/protect_exit are optional.
protect_exit (default true) overrides the shell's exit command with a no-op function so an accidental exit from the agent cannot kill the session. Use ssh_close_terminal to terminate the session explicitly. Subshells (sudo, python, ssh into another host) are unaffected. Automatically disabled for Windows hosts.
sshsendinput
Send text or a special key to a terminal and read new output.
Send a command:
{
"terminal_id": "term-1",
"text": "ls -la\n",
"wait_ms": 300
}
Send a special key:
{
"terminal_id": "term-1",
"special_key": "CTRL_C"
}
Supported special keys: CTRL_C, CTRL_D, CTRL_Z, ESC, TAB, BACKSPACE, ENTER, ARROW_UP, ARROW_DOWN, ARROW_LEFT, ARROW_RIGHT.
Use \n for newline and \r for carriage return in text. Exactly one of text or special_key must be provided — setting both is an error.
sshreadoutput
Read buffered output since the last read without writing anything.
{
"terminal_id": "term-1",
"wait_ms": 1000,
"limit": 100
}
wait_ms— wait up to N milliseconds for new data (default 0 = return immediately).limit— maximum number of complete lines to return per call (default 0 = return everything). Remaining lines stay in the buffer for subsequent calls. Response includeslines(count returned) andhas_more(true if more lines are buffered); the text response also appends a marker line when more output is pending.
sshcloseterminal
Close a PTY terminal session.
{
"terminal_id": "term-1"
}
SSH Tunnel Tools
These three tools provide local port forwarding through SSH connections. Useful for accessing remote databases, web servers, or APIs that aren't directly reachable. Requires --enable-tunnels.
Typical workflow:
ssh_tunnel_create → binds local port, forwards to remote address
ssh_tunnel_list → show active tunnels
ssh_tunnel_close → stop forwarding and release the port
Active tunnels are also listed in ssh_list_sessions output. Tunnels are automatically closed when the parent SSH session is disconnected.
sshtunnelcreate
Create a local port forwarding tunnel.
Auto-assign local port:
{
"session_id": "admin@example.com:22",
"remote_addr": "localhost:5432",
"local_port": 0
}
Specific local port:
{
"session_id": "admin@example.com:22",
"remote_addr": "10.0.0.5:3306",
"local_port": 13306
}
Returns tunnel_id, the bound local address and port, and the remote address.
sshtunnellist
List all active tunnels. Optionally filter by session ID.
{
"session_id": "admin@example.com:22"
}
sshtunnelclose
Close an active tunnel.
{
"tunnel_id": "admin@example.com:22-1"
}
Codex Configuration
Add to Codex using the CLI:
codex mcp add ssh \
--env SSH_AUTH_SOCK="$SSH_AUTH_SOCK" \
-- /path/to/ssh-mcp --enable-terminal
Verify the saved configuration:
codex mcp get ssh
For passphrase-protected SSH keys, ssh-mcp must be able to use your ssh-agent. It does not prompt for private-key passphrases itself. If Codex starts ssh-mcp without a working SSH_AUTH_SOCK, ssh_connect can fail before any network connection attempt with:
connect failed: auth config: no authentication methods available
Before adding the MCP server, make sure the key is loaded:
ssh-add -l
On macOS, load the key into the agent and store the passphrase in Keychain:
/usr/bin/ssh-add --apple-use-keychain ~/.ssh/id_rsa
If SSH_AUTH_SOCK changes after reboot or after restarting your terminal, update the Codex MCP entry by running codex mcp add again with the current value. Existing Codex sessions may keep an already-started ssh-mcp process with the old environment; restart Codex or remove the stale ssh-mcp process after changing the config.
Manual equivalent in ~/.codex/config.toml:
[mcp_servers.ssh]
command = "/path/to/ssh-mcp"
args = ["--enable-terminal"]
[mcp_servers.ssh.env]
SSH_AUTH_SOCK = "/var/run/com.apple.launchd.XYZ/Listeners"
Claude Code Configuration
Add to Claude Code using the CLI:
claude mcp add --transport stdio --scope user ssh -- /path/to/ssh-mcp --no-verify-host-key
Or manually edit ~/.claude.json:
{
"mcpServers": {
"ssh": {
"type": "stdio",
"command": "/path/to/ssh-mcp",
"args": ["--no-verify-host-key"]
}
}
}
Claude Desktop Configuration
Add to your Claude Desktop config (`~/Library
…
Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: n0madic
- Source: n0madic/ssh-mcp
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.