An MCP (Model Context Protocol) server that lets Claude execute commands on remote servers over SSH. Supports ~/.ssh/config, private keys, password auth, and SSH agent forwarding.
| Tool | Description |
|---|---|
ssh_connect |
Open an SSH connection to a remote host |
ssh_execute |
Run a command on a connected host |
ssh_disconnect |
Close a connection |
ssh_list_connections |
Show all connections and their status |
git clone <repo-url> && cd ssh-mcp-server
npm install
npm run buildclaude mcp add ssh node /absolute/path/to/ssh-mcp-server/build/index.jsAdd to claude_desktop_config.json:
{
"mcpServers": {
"ssh": {
"command": "node",
"args": ["/absolute/path/to/ssh-mcp-server/build/index.js"]
}
}
}Auth is resolved in this order — the first match wins:
- Explicit password —
passwordparam - Explicit private key —
privateKeyPathparam (supportspassphrase) - SSH config IdentityFile — reads
~/.ssh/config, tries each key in order - SSH agent — uses
SSH_AUTH_SOCK(default when nothing else is specified)
The server reads ~/.ssh/config on every connect call, so edits are picked up without restarting. All standard directives are supported:
Host myserver
HostName 10.0.1.50
User deploy
Port 2222
IdentityFile ~/.ssh/id_ed25519
Then just connect with host: "myserver" — the alias, hostname, user, port, and key are all resolved automatically.
| Parameter | Type | Required | Description |
|---|---|---|---|
host |
string | yes | Hostname, IP, or SSH config alias |
username |
string | no | Override SSH config user |
port |
number | no | Override SSH config port (default 22) |
password |
string | no | Password auth |
privateKeyPath |
string | no | Path to private key (e.g. ~/.ssh/id_ed25519) |
passphrase |
string | no | Passphrase for encrypted private key |
useAgent |
boolean | no | Use SSH agent (default: true if no other auth given) |
connectTimeout |
number | no | Timeout in ms for TCP + handshake (default 20000) |
connectionId |
string | no | Custom ID for this connection (default: host value) |
Returns a connection ID used by the other tools.
| Parameter | Type | Required | Description |
|---|---|---|---|
connectionId |
string | yes | Connection ID from ssh_connect |
command |
string | yes | Shell command to run |
cwd |
string | no | Working directory (wraps as cd <cwd> && <command>) |
timeout |
number | no | Command timeout in ms (default 30000) |
errOnNonZero |
boolean | no | Mark non-zero exit as MCP error (default false) |
Returns stdout, stderr, and exit code. Output is capped at 1MB per stream.
errOnNonZero defaults to false because many commands like grep (no match = exit 1) and diff (differences = exit 1) use non-zero exits for normal results.
| Parameter | Type | Required | Description |
|---|---|---|---|
connectionId |
string | yes | Connection ID to close |
No parameters. Returns all connections with host, port, user, and status (connected/dead).
- Keepalive packets are sent every 10 seconds to detect dead connections early
- If a connection drops,
ssh_executereturns a clear error telling you to reconnect - An outer timeout wraps the entire TCP + SSH handshake so connections to firewalled/black-hole IPs don't hang forever
- All connections are cleaned up on
SIGTERM/SIGINT
1. ssh_connect({ host: "myserver" })
→ Connected to 10.0.1.50:2222 as deploy. Connection ID: "myserver"
2. ssh_execute({ connectionId: "myserver", command: "uname -a" })
→ Linux prod-01 5.15.0 #1 SMP x86_64 GNU/Linux
→ Exit code: 0
3. ssh_execute({ connectionId: "myserver", command: "ls", cwd: "/var/log" })
→ syslog auth.log kern.log ...
→ Exit code: 0
4. ssh_list_connections()
→ - myserver: 10.0.1.50:2222 as deploy [connected]
5. ssh_disconnect({ connectionId: "myserver" })
→ Disconnected "myserver".
- Host key verification is not enforced (TOFU — trust on first use). The server accepts any host key.
- Passwords and passphrases are passed as tool parameters and are not persisted, but may appear in MCP client logs depending on your client.
- The server runs with the permissions of the user who started it.
MIT