SSH MCP Server
by quiloos39
README.md
# SSH MCP Server
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.
## Tools
| 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 |
## Setup
```bash
git clone <repo-url> && cd ssh-mcp-server
npm install
npm run build
```
### Claude Code
```bash
claude mcp add ssh node /absolute/path/to/ssh-mcp-server/build/index.js
```
### Claude Desktop
Add to `claude_desktop_config.json`:
```json
{
"mcpServers": {
"ssh": {
"command": "node",
"args": ["/absolute/path/to/ssh-mcp-server/build/index.js"]
}
}
}
```
## Authentication
Auth is resolved in this order — the first match wins:
1. **Explicit password** — `password` param
2. **Explicit private key** — `privateKeyPath` param (supports `passphrase`)
3. **SSH config IdentityFile** — reads `~/.ssh/config`, tries each key in order
4. **SSH agent** — uses `SSH_AUTH_SOCK` (default when nothing else is specified)
### Using `~/.ssh/config`
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.
## Tool Reference
### ssh_connect
| 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.
### ssh_execute
| 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.
### ssh_disconnect
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `connectionId` | string | yes | Connection ID to close |
### ssh_list_connections
No parameters. Returns all connections with host, port, user, and status (connected/dead).
## Broken Pipe Handling
- **Keepalive packets** are sent every 10 seconds to detect dead connections early
- If a connection drops, `ssh_execute` returns 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`
## Example Session
```
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".
```
## Security Notes
- **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.
## License
MIT
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues