SSH MCP Server
README.md
# SSH MCP Server
An [MCP](https://modelcontextprotocol.io) server that lets an AI assistant (Claude Code, Claude Desktop, etc.) run commands and transfer files over SSH — **without ever seeing your real connection details**. The assistant only works with aliases like `prod` or `server1`; hostnames, usernames, keys, and passwords stay on your machine.
Built on the official `@modelcontextprotocol/sdk` and `ssh2`, with an optional passthrough mode that shells out to your system `ssh`/`scp` and reads `~/.ssh/config`.
## Why
- **Credentials never reach the model.** Real host/user/IP live in your local config (or `~/.ssh/config`) and are scrubbed from all output before the assistant sees it.
- **Alias-only surface.** The assistant calls `ssh_exec("prod", "...")`; it has no idea what `prod` resolves to.
- **Real `scp`/`rsync`/`ProxyJump` support** via passthrough mode when you want it.
## Tools
| Tool | Description |
|------|-------------|
| `ssh_exec` | Run a command on a server. Returns stdout/stderr/exit code. |
| `ssh_upload` | Upload a file, a glob (`logs/*.gz`), or a whole directory (`recursive=true`). `preserve=true` keeps mode+timestamps (`scp -p`). |
| `ssh_download` | Download a file, a glob, or a whole directory (`recursive=true`). Same `preserve` option. |
| `ssh_list` | List a remote directory. |
| `ssh_list_servers` | List configured aliases and their descriptions. Never exposes hosts. |
## Install
```bash
git clone <this-repo> sshmcp
cd sshmcp
npm install
```
Requires Node 18+. Passthrough mode additionally needs the OpenSSH client (`ssh`/`scp`) on your PATH — built into Windows 10/11, macOS, and Linux.
## Configure your servers
Create `~/.claude/ssh-servers.json` (or anywhere, and point `SSH_CONFIG_PATH` at it). Each entry is an alias. Pick **one** auth method per server.
### A. SSH key (recommended)
```json
{
"server1": {
"description": "Main deploy box",
"host": "10.0.0.5",
"port": 22,
"username": "deploy",
"privateKeyPath": "~/.ssh/id_ed25519"
}
}
```
### B. Password from an environment variable
```json
{
"box": {
"host": "10.0.0.6",
"username": "admin",
"passwordEnv": "BOX_PASS"
}
}
```
Then export `BOX_PASS` before the server starts.
### C. Direct password (local/testing only)
```json
{
"box": { "host": "10.0.0.6", "username": "admin", "password": "secret" }
}
```
### D. SSH-config passthrough (most secure, full scp/rsync)
```json
{
"prod": { "description": "Main prod", "sshConfigHost": "prod" }
}
```
When an entry has `sshConfigHost`, the tools shell out to the system `ssh`/`scp` using that `Host` alias from `~/.ssh/config`. HostName, User, IdentityFile, ProxyJump, etc. all come from there and **never appear in this file** — no credentials live in `ssh-servers.json` at all. Real hostnames/users/IPs are scrubbed from output.
- `"sshConfigHost": true` reuses the JSON alias as the Host name.
- `"sshConfigHost": "prod"` maps to a differently-named Host in `~/.ssh/config`.
- Requires key/agent auth (ssh config holds no passwords).
Methods can be mixed freely across servers in one file. See `ssh-servers.example.json`.
### ProxyJump (e.g. reaching Proxmox VMs / hosts behind a bastion)
Add `proxyJump` to reach a target that isn't directly reachable — such as a VM on a Proxmox host's internal network. It works in both modes:
- **Passthrough:** `proxyJump` is an ssh host/alias passed to `scp -J`/`ssh -J`.
```json
"vm-web": { "sshConfigHost": "vm-web", "proxyJump": "pmx" }
```
(Equivalent to putting `ProxyJump pmx` in the Host block in `~/.ssh/config`, which also just works.)
- **Native (ssh2):** `proxyJump` names another configured **native** alias to tunnel through. Chains are supported (a jump may have its own jump).
```json
"vm-web": { "host": "10.10.0.50", "username": "root", "privateKeyPath": "~/.ssh/vm_key", "proxyJump": "server1" }
```
The jump host's real address is scrubbed from output alongside the target's.
## Register with Claude Code
```bash
claude mcp add ssh --scope user -- node /absolute/path/to/sshmcp/ssh-server.js
```
> MCP servers are **not** configured in `settings.json` — use `claude mcp add` (writes `~/.claude.json`) or a project `.mcp.json`.
Set a custom config path with the `SSH_CONFIG_PATH` env var if you don't use `~/.claude/ssh-servers.json`.
After adding (or editing the server), **fully restart Claude Code** — MCP tools load at session startup, so a running session won't pick up changes.
## Usage examples
```
# run a command
ssh_exec(serverAlias="prod", command="systemctl status nginx")
# upload a build, preserving permissions and timestamps
ssh_upload(serverAlias="prod", localPath="./dist", remotePath="/var/www/app", recursive=true, preserve=true)
# pull all logs matching a glob
ssh_download(serverAlias="prod", remotePath="/var/log/*.log", localPath="./logs")
```
## Security model
- The config file holds credentials and is `.gitignore`d. `chmod 600 ~/.claude/ssh-servers.json`.
- Connection errors are sanitized — host/IP/username are replaced with the alias before returning.
- Passthrough mode resolves the real host/user via `ssh -G` **server-side only** to scrub them from output; that data never enters the assistant's context.
- Prefer keys or `passwordEnv` over inline passwords.
## Notes & limitations
- **Windows + `preserve`:** timestamps are preserved reliably in both directions; Unix permission bits are limited by what Windows (NTFS) can represent — Windows has no execute bit, so a `755` source becomes `666` when Windows is one end. Linux↔Linux (server-to-server style) is unaffected.
- **Glob** matches a single directory level; use `recursive=true` for whole trees.
- **Symlinks** are skipped during recursive directory walks to avoid cycles.
- Passwords are not supported in passthrough mode (ssh config holds none); give those hosts a key to use mode D.
## License
MIT
TDQS
A3.8/5.0
Scored across 5 tools
Disambiguation5/5
Each tool has a clearly distinct purpose: download, execute, list files, list servers, and upload. No overlap or ambiguity.
Naming Consistency5/5
All tools follow a consistent 'ssh_verb_noun' pattern using snake_case, with descriptive verbs like download, exec, list, and upload.
Tool Count5/5
5 tools is well-scoped for an SSH server, covering core operations (execution, file transfer, listing) without being excessive or insufficient.
Completeness4/5
The set covers essential SSH operations (exec, list, download, upload) but lacks directory creation/deletion or file renaming, which are minor gaps.
Maintenance
ActivityMaintained
ResponsivenessSyncing