Ssh MCP
SSH MCP server
Installation
npx ssh-mcpAsk AI about Ssh MCP
Powered by Claude Β· Grounded in docs
I know everything about Ssh MCP. Ask me about installation, configuration, usage, or troubleshooting.
0/500
Reviews
Documentation
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 known_hosts 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.*matchesrm -rf /butrmalone won't matchformat). 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"
}
ssh_list_sessions
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"
}
ssh_edit_file
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
}
ssh_read_file
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.
ssh_open_terminal
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
}
Returns terminal_id and the initial shell output (prompt). cols/rows/term_type/wait_ms are optional.
ssh_send_input
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.
ssh_read_output
Read buffered output since the last read without writing anything.
{
"terminal_id": "term-1",
"wait_ms": 1000
}
Set wait_ms to wait up to N milliseconds for new data (default 0 = return immediately).
ssh_close_terminal
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.
ssh_tunnel_create
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.
ssh_tunnel_list
List all active tunnels. Optionally filter by session ID.
{
"session_id": "admin@example.com:22"
}
ssh_tunnel_close
Close an active tunnel.
{
"tunnel_id": "admin@example.com:22-1"
}
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/Application Support/Claude/claude_desktop_config.json):
Stdio transport
{
"mcpServers": {
"ssh": {
"command": "/path/to/ssh-mcp",
"args": ["--enable-sudo", "--no-verify-host-key"]
}
}
}
HTTP transport
Start the server manually:
./ssh-mcp --enable-http --disable-stdio
Then configure Claude Desktop to use the HTTP endpoint at http://localhost:8081/mcp.
Security
- HTTP transport is localhost-only β the HTTP server binds to
localhost(hardcoded, not configurable) - HTTP authentication β optional bearer token authentication for HTTP transport (
--http-token); constant-time comparison - HTTP server hardening β
ReadHeaderTimeoutandIdleTimeoutset to prevent slowloris-style attacks - Host key verification β enabled by default using
~/.ssh/known_hosts; fails with a clear error if the file is missing (no silent downgrade to insecure mode) - Sudo disabled by default β must be explicitly enabled with
--enable-sudo - Interactive terminals disabled by default β PTY sessions bypass the command filter; must be explicitly enabled with
--enable-terminal - SSH tunnels disabled by default β tunnel creation must be explicitly enabled with
--enable-tunnels - Host filtering β allowlist/denylist with regex and CIDR support; denylist takes priority; regex patterns are auto-anchored for full-string matching; CIDR patterns (e.g.,
10.0.0.0/8) match by IP range; case-insensitive host matching - Command filtering β allowlist/denylist with regex support; denylist takes priority; patterns are auto-anchored; filter runs on the original command (before cd/sudo prepend); error messages do not expose filter patterns
- Local path restriction β
--local-base-dirrestricts all local file operations (upload/download) to a specific directory - Path traversal protection β rejects paths with
..path segments or null bytes (both local and remote); segment-based check allows names likefoo..bar - Filename validation β rejects filenames longer than 255 characters, containing control characters (including DEL and Unicode Cc), or path separators
- Rate limiting β per-host token bucket rate limiter with automatic stale entry cleanup; optionally applies to SFTP file operations (
--rate-limit-file-ops) - Connection pool limits β
--max-connectionscaps the number of concurrent SSH connections - File size limits β
--max-file-sizecaps remote file read operations to prevent memory exhaustion - Output truncation β
--max-output-sizelimits per-stream output size in execute and terminal tools to prevent LLM context overflow; UTF-8-safe truncation avoids splitting multi-byte characters - Tunnel pool limits β
--max-tunnelscaps the number of concurrent SSH tunnels - No credential persistence β passwords are not stored in the connection pool; only the SSH client config (with key-based auth methods) is retained for auto-reconnect
- Remote path expansion β
~expands to user's home directory on remote server
Development
# Build
go build ./...
# Run unit tests
go test ./internal/...
# Run E2E tests (requires Docker)
go test -v -timeout 120s ./e2e/...
# Run all tests
go test ./...
# Vet
go vet ./...
License
MIT License. See LICENSE for details.
