SSH MCP Server
Runs commands and moves files on remote Linux and Windows hosts over SSH, behind roles, approvals and audit
Runs shell commands and moves files on remote machines over SSH, with command allow and deny lists
By Junki (individual) TypeScript Local · stdio, Streamable HTTP DevOps and monitoringFiles and storage
75 popularity score ★ 1.0k ↓ 4.1k/wk
Opens the app, which asks before it adds the server. Then replace each <NAME> in its settings with your value.
claude mcp add --transport http ssh http://127.0.0.1:3000/mcp --header 'Authorization: Bearer <SSH_MCP_HTTP_TOKEN>'{
"mcpServers": {
"ssh": {
"url": "http://127.0.0.1:3000/mcp",
"headers": {
"Authorization": "Bearer <SSH_MCP_HTTP_TOKEN>"
}
}
}
}{
"servers": {
"ssh": {
"type": "http",
"url": "http://127.0.0.1:3000/mcp",
"headers": {
"Authorization": "Bearer <SSH_MCP_HTTP_TOKEN>"
}
}
}
}[mcp_servers.ssh]
url = "http://127.0.0.1:3000/mcp"
http_headers = { "Authorization" = "Bearer <SSH_MCP_HTTP_TOKEN>" }gemini mcp add --scope user --transport http --header 'Authorization: Bearer <SSH_MCP_HTTP_TOKEN>' ssh http://127.0.0.1:3000/mcpOpens the app, which asks before it adds the server. Then replace each <NAME> in its settings with your value.
claude mcp add ssh -- npx -y @fangjunjie/ssh-mcp-server --host '<HOST>' --port 22 --username '<USERNAME>' --password '<PASSWORD>'{
"mcpServers": {
"ssh": {
"command": "npx",
"args": [
"-y",
"@fangjunjie/ssh-mcp-server",
"--host",
"<HOST>",
"--port",
"22",
"--username",
"<USERNAME>",
"--password",
"<PASSWORD>"
]
}
}
}{
"mcpServers": {
"ssh": {
"command": "npx",
"args": [
"-y",
"@fangjunjie/ssh-mcp-server",
"--host",
"<HOST>",
"--port",
"22",
"--username",
"<USERNAME>",
"--password",
"<PASSWORD>"
]
}
}
}{
"servers": {
"ssh": {
"type": "stdio",
"command": "npx",
"args": [
"-y",
"@fangjunjie/ssh-mcp-server",
"--host",
"<HOST>",
"--port",
"22",
"--username",
"<USERNAME>",
"--password",
"<PASSWORD>"
]
}
}
}[mcp_servers.ssh]
command = "npx"
args = ["-y", "@fangjunjie/ssh-mcp-server", "--host", "<HOST>", "--port", "22", "--username", "<USERNAME>", "--password", "<PASSWORD>"]gemini mcp add --scope user ssh npx -- -y @fangjunjie/ssh-mcp-server --host '<HOST>' --port 22 --username '<USERNAME>' --password '<PASSWORD>'| Set together: 2fa | |
|---|---|
SSH_MCP_2FA_CODE secret |
Optional: Verification code answered to keyboard-interactive 2FA prompts when --try-keyboard is set. |
SSH_MCP_HTTP_TOKEN secret |
Optional: Bearer token the HTTP endpoint requires. Used only with --mcp-transport http, and --http-token wins when both are set. |
SSH_MCP_PASSPHRASE secret |
Optional: Passphrase for the private key when --passphrase is not given. |
ssh-mcp-server is a local Node.js process that holds SSH connections to one or more remote hosts and exposes them to an MCP client. The assistant can run a command on a host and get its output, start long tasks as background jobs and poll them, read or write remote text files, and copy files or whole directories in either direction over SFTP. The SSH password or key stays in the server's own configuration and is never passed to the model.
Connections are set with command-line flags, a JSON config file, or host aliases from ~/.ssh/config; several hosts can be registered under names and picked per call with connectionName. It can tunnel through a SOCKS5, HTTP or HTTPS proxy, answer keyboard-interactive 2FA prompts, wrap every command in a template such as `sudo bash -c <quotedCommand>`, and switch to a persistent shell session (`--transport-mode shell`) for bastion hosts and network devices that refuse plain exec.
By default it talks to the client over stdio. With `--mcp-transport http` the same process serves Streamable HTTP at http://127.0.0.1:3000/mcp, and `--http-host`, `--http-port` and `--http-path` move it. In the source, each HTTP request gets a fresh MCP server with no session id, while the SSH connections are shared across requests.
The README's tool table groups the four job tools in one row, but src/tools/index.ts registers 10 tools in all. A non-zero exit comes back as the output plus `[exit code] N` rather than as an error. Captured output is capped at 10 MiB per command by default (`maxOutputBytes`).
| Runtime | Node.js |
|---|---|
| Runs on | macOS, Windows, Linux |
| SSH | Your own instanceAn SSH account on each target host, reached by password, private key, SSH agent or a ~/.ssh/config alias |
Node.js with npx on the machine running the client (package.json sets no minimum version). An SSH account on each target host, reached with a password, a private key (optionally with a passphrase, also read from SSH_MCP_PASSPHRASE), an SSH agent, or a ~/.ssh/config alias. Background jobs need a POSIX sh on the remote host. For 2FA, the code goes in the SSH_MCP_2FA_CODE environment variable before connecting. For HTTP mode, start the process yourself with `--mcp-transport http` plus the SSH flags, set SSH_MCP_HTTP_TOKEN first, and point the client at the URL with an `Authorization: Bearer` header.
In shell transport mode, upload, download, read-file and write-file are unavailable because SFTP is not used. Commands time out after 30000 ms by default (per-call `timeout` or `commandTimeoutMs` changes it); longer work should go through job-start. read-file returns 65536 bytes per call by default and refuses binary files. Directory transfers stop at 2000 files per call and skip symlinks. Blacklist patterns are case-sensitive regular expressions. The legacy comma-separated --ssh format can break on passwords containing `=`, `,`, `{` or `}`. Its own docs note there is no built-in rate limiting.
Suits developers and operators who want an assistant to inspect or administer servers they already reach over SSH, including through jump hosts, proxies or 2FA, from a local MCP client. It runs on your machine with your SSH credentials and can change anything that account can, so it fits best with a whitelist and allowedRemotePaths set, or with a low-privilege account. Someone who only needs files on the local machine, or a cloud provider's own management API, should look for a server built for that instead.
4 only read, 2 change things, 4 can delete or overwrite. A ? marks an optional parameter.
execute-command deletescmdString: stringdirectory?: stringconnectionName?: stringtimeout?: numberjob-start deletescmdString: stringdirectory?: stringconnectionName?: stringjob-statusjobId: stringoffset?: integertailBytes?: integermaxBytes?: integerconnectionName?: stringjob-kill changesjobId: stringconnectionName?: stringjob-listlimit?: integerconnectionName?: stringupload deleteslocalPath: stringremotePath: stringconnectionName?: stringdownload changesremotePath: stringlocalPath: stringconnectionName?: stringread-fileremotePath: stringoffset?: integerlength?: integerconnectionName?: stringwrite-file deletesremotePath: stringcontent: stringappend?: booleanmode?: integerconnectionName?: stringlist-servers