Audited and hardened SSH Model Context Protocol server
  • TypeScript 51.1%
  • JavaScript 48.9%
Find a file
2026-07-25 01:07:47 +02:00
images Initial audited release 2026-07-25 01:07:47 +02:00
scripts Initial audited release 2026-07-25 01:07:47 +02:00
skills/ssh-mcp-helper Initial audited release 2026-07-25 01:07:47 +02:00
src Initial audited release 2026-07-25 01:07:47 +02:00
test Initial audited release 2026-07-25 01:07:47 +02:00
.gitignore Initial audited release 2026-07-25 01:07:47 +02:00
LICENSE Initial audited release 2026-07-25 01:07:47 +02:00
package-lock.json Initial audited release 2026-07-25 01:07:47 +02:00
package.json Initial audited release 2026-07-25 01:07:47 +02:00
README.md Initial audited release 2026-07-25 01:07:47 +02:00
tsconfig.json Initial audited release 2026-07-25 01:07:47 +02:00

🔐 ssh-mcp-server

NPM Version GitHub forks GitHub Repo stars GitHub Issues or Pull Requests GitHub Issues or Pull Requests GitHub Issues or Pull Requests GitHub Issues or Pull Requests

SSH-based MCP (Model Context Protocol) server that allows remote execution of SSH commands via the MCP protocol.

📝 Project Overview

ssh-mcp-server lets MCP clients execute commands and transfer files over SSH. It runs locally over stdio and does not include telemetry or an HTTP listener. Because it grants an AI client the permissions of an SSH account, treat it as privileged software and use the restrictions described below.

Key Features

  • 🔒 SSH Authentication: Supports SSH agents, private keys, passwords, keyboard-interactive authentication, and optional SHA-256 host-key pinning
  • 🛡️ Command Security Control: Precisely control the range of allowed commands through flexible blacklist and whitelist mechanisms to prevent dangerous operations
  • 🔄 Standardized Interface: Complies with MCP protocol specifications for seamless integration with AI assistants supporting the protocol
  • 🚇 Dual Transport Modes: Supports both exec and shell transport modes for direct SSH hosts and bastion or jump-host scenarios
  • 📂 File Transfer: Supports bidirectional file transfers, uploading local files to servers or downloading files from servers
  • 🔑 Credential Isolation: SSH credentials are managed entirely locally and never exposed to AI models, enhancing security
  • 🚀 Ready to Use: Can be run directly using NPX without global installation, making it convenient and quick to deploy

📦 Open Source Repository

GitHub: https://github.com/classfang/ssh-mcp-server

NPM: https://www.npmjs.com/package/@fangjunjie/ssh-mcp-server

🛠️ Tools List

Tool Name Description
execute-command Command Execution Tool Execute SSH commands on remote servers and get results
upload File Upload Tool Upload local files to specified locations on remote servers
download File Download Tool Download files from remote servers to local specified locations
list-servers List Servers Tool List all available SSH server configurations

📚 Usage

0. 🤖 Setup via the included AI skill

If you are using an AI coding assistant that supports skills (such as Claude Code), you can use the built-in ssh-mcp-helper skill to complete the installation and configuration interactively — no need to manually edit JSON files.

How to use:

  1. Install the skill from this repository's skills/ directory
  2. Tell your AI assistant: "Help me set up ssh-mcp-server" or "Configure SSH MCP for my remote server"
  3. The skill will guide you step by step: check Node.js environment → choose MCP client → select authentication method → collect connection parameters → generate and write configuration

The skill supports the scenarios below and applies safer defaults: least-privileged accounts, key or agent authentication, host-key pinning, command allowlists, and restricted transfer paths.


The sections below cover common connection scenarios. Prefer SSH agent or private-key authentication. Passwords and passphrases supplied as command-line arguments are visible to local process-inspection tools and are usually stored in plaintext by MCP clients.

⚠️ Important: In MCP configuration files, each command line argument and its value must be separate elements in the args array. Do NOT combine them with spaces. For example, use "--host", "192.168.1.1" instead of "--host 192.168.1.1".

{
  "mcpServers": {
    "ssh-mcp-server": {
      "command": "npx",
      "args": [
        "-y",
        "@fangjunjie/ssh-mcp-server",
        "--host", "192.168.1.1",
        "--port", "22",
        "--username", "root",
        "--password", "pwd123456"
      ]
    }
  }
}

2. 🔐 Username + Private Key

{
  "mcpServers": {
    "ssh-mcp-server": {
      "command": "npx",
      "args": [
        "-y",
        "@fangjunjie/ssh-mcp-server",
        "--host", "192.168.1.1",
        "--port", "22",
        "--username", "root",
        "--privateKey", "~/.ssh/id_rsa"
      ]
    }
  }
}

3. 🔏 Private Key with Passphrase

{
  "mcpServers": {
    "ssh-mcp-server": {
      "command": "npx",
      "args": [
        "-y",
        "@fangjunjie/ssh-mcp-server",
        "--host", "192.168.1.1",
        "--port", "22",
        "--username", "root",
        "--privateKey", "~/.ssh/id_rsa",
        "--passphrase", "pwd123456"
      ]
    }
  }
}

4. 📋 Reuse ~/.ssh/config

If you already have a host alias in ~/.ssh/config, the server reads connection parameters directly from it — no need to repeat them in mcp.json.

{
  "mcpServers": {
    "ssh-mcp-server": {
      "command": "npx",
      "args": [
        "-y",
        "@fangjunjie/ssh-mcp-server",
        "--host", "myserver"
      ]
    }
  }
}

Assuming your ~/.ssh/config contains:

Host myserver
    HostName 192.168.1.1
    Port 22
    User root
    IdentityFile ~/.ssh/id_rsa

You can also specify a custom SSH config file path:

{
  "mcpServers": {
    "ssh-mcp-server": {
      "command": "npx",
      "args": [
        "-y",
        "@fangjunjie/ssh-mcp-server",
        "--host", "myserver",
        "--ssh-config-file", "/path/to/custom/ssh_config"
      ]
    }
  }
}

Note: Command-line parameters take precedence over SSH config values. For example, if you specify --port 2222, it will override the port from SSH config.

5. 🌐 Connecting Through a SOCKS Proxy

When the target host is only reachable through a SOCKS proxy:

{
  "mcpServers": {
    "ssh-mcp-server": {
      "command": "npx",
      "args": [
        "-y",
        "@fangjunjie/ssh-mcp-server",
        "--host", "192.168.1.1",
        "--port", "22",
        "--username", "root",
        "--password", "pwd123456",
        "--socksProxy", "socks://username:password@proxy-host:proxy-port"
      ]
    }
  }
}

6. 📝 Restricting Commands With Whitelist / Blacklist

Use --whitelist and --blacklist to limit which commands the server is allowed to run. Patterns are comma-separated regular expressions. Strongly recommended for any production use.

These checks apply to the full command string supplied by the MCP client. Prefer anchored allowlist expressions and a dedicated SSH account; a shell allowlist is not a complete sandbox.

Whitelist example (only allow read-only inspection commands):

{
  "mcpServers": {
    "ssh-mcp-server": {
      "command": "npx",
      "args": [
        "-y",
        "@fangjunjie/ssh-mcp-server",
        "--host", "192.168.1.1",
        "--port", "22",
        "--username", "root",
        "--password", "pwd123456",
        "--whitelist", "^ls( .*)?,^cat .*,^df.*"
      ]
    }
  }
}

Blacklist example (block destructive commands):

{
  "mcpServers": {
    "ssh-mcp-server": {
      "command": "npx",
      "args": [
        "-y",
        "@fangjunjie/ssh-mcp-server",
        "--host", "192.168.1.1",
        "--port", "22",
        "--username", "root",
        "--password", "pwd123456",
        "--blacklist", "^rm .*,^shutdown.*,^reboot.*"
      ]
    }
  }
}

Note: If both whitelist and blacklist are specified, the command must pass both checks (whitelist first, then blacklist) to be executed.

6a. Verify the SSH Host Key

Pin the server's SHA-256 host-key fingerprint to prevent machine-in-the-middle attacks. Obtain the fingerprint through a trusted channel, then pass it with:

"--host-key-sha256", "SHA256:base64-fingerprint"

Connections without a pinned fingerprint remain supported for compatibility, but the server prints a warning.

7. 🧩 Wrapping Commands With a Template

commandTemplate wraps every executed command in a template — useful for switching user via su, running inside a container, or jumping through another host. Use <quotedCommand> when the command is passed as a shell argument, or <command> for raw insertion. The template is applied after the working-directory cd is prepended, so the entire cd ... && <actual command> chain gets wrapped.

{
  "mcpServers": {
    "ssh-mcp-server": {
      "command": "npx",
      "args": [
        "-y",
        "@fangjunjie/ssh-mcp-server",
        "--host", "10.0.0.1",
        "--port", "22",
        "--username", "deploy",
        "--password", "xxx",
        "--command-template", "su root -c <quotedCommand>"
      ]
    }
  }
}

Executing ls /app with directory /data actually sends:

su root -c 'cd -- '\''/data'\'' && ls /app'

Other useful templates:

sudo bash -c <quotedCommand>
docker exec -i mycontainer sh -c <quotedCommand>
ssh jumphost <quotedCommand>

8. 🚇 Bastion / Jump Host (transportMode: shell)

transportMode defaults to exec. Switch to shell when:

  • SSH login succeeds but exec command execution fails
  • The remote side requires shell startup scripts, banners, or environment initialization first
  • The target effectively exposes only an interactive shell (bastion hosts, jump hosts, network devices)

Behavior differences:

  • exec: supports execute-command, upload, and download
  • shell: runs commands through a persistent shell session with an internal command queue, but does not support upload / download because SFTP is unavailable in this mode
{
  "mcpServers": {
    "ssh-mcp-server": {
      "command": "npx",
      "args": [
        "-y",
        "@fangjunjie/ssh-mcp-server",
        "--host", "bastion.example.com",
        "--port", "22",
        "--username", "ops",
        "--password", "pwd123456",
        "--transport-mode", "shell",
        "--shell-ready-timeout", "15000"
      ]
    }
  }
}

In JSON config files you can also set shellCommandTimeoutMs to override the default per-command timeout for shell-backed connections.

9. 🔐 Multi-Factor Authentication (2FA / MFA)

When the SSH server requires multi-factor authentication (password + private key + 2FA verification code), enable tryKeyboard. The password and private key are auto-supplied. For non-password prompts, set SSH_MCP_2FA_CODE in the server environment before connecting.

{
  "mcpServers": {
    "ssh-mcp-server": {
      "command": "npx",
      "args": [
        "-y",
        "@fangjunjie/ssh-mcp-server",
        "--host", "example.com",
        "--port", "22",
        "--username", "user",
        "--password", "your_password",
        "--privateKey", "/path/to/key",
        "--try-keyboard"
      ]
    }
  }
}

Authentication flow:

  1. Private key authentication (if provided)
  2. Password authentication (if provided)
  3. Keyboard-interactive for 2FA code via SSH_MCP_2FA_CODE

10. 🧩 Managing Multiple SSH Connections

When you need to expose more than one SSH target through the same MCP server, register them under unique connection names and select the target at call time via connectionName. There are three ways to configure them:

Create a JSON configuration file (e.g., ssh-config.json):

Array Format:

[
  {
    "name": "dev",
    "host": "1.2.3.4",
    "port": 22,
    "username": "alice",
    "password": "{abc=P100s0}",
    "socksProxy": "socks://127.0.0.1:10808"
  },
  {
    "name": "bastion",
    "host": "9.9.9.9",
    "port": 22,
    "username": "ops",
    "password": "pwd123456",
    "transportMode": "shell",
    "shellReadyTimeoutMs": 15000,
    "shellCommandTimeoutMs": 45000,
    "connectionTimeoutMs": 30000,
    "keepaliveIntervalMs": 10000,
    "keepaliveCountMax": 3
  },
  {
    "name": "prod",
    "host": "5.6.7.8",
    "port": 22,
    "username": "bob",
    "password": "yyy",
    "socksProxy": "socks://127.0.0.1:10808"
  },
  {
    "name": "secure-server",
    "host": "secure.example.com",
    "port": 22,
    "username": "admin",
    "password": "your_password",
    "privateKey": "/path/to/private/key",
    "tryKeyboard": true
  }
]

Object Format:

{
  "dev": {
    "host": "1.2.3.4",
    "port": 22,
    "username": "alice",
    "password": "{abc=P100s0}",
    "socksProxy": "socks://127.0.0.1:10808"
  },
  "bastion": {
    "host": "9.9.9.9",
    "port": 22,
    "username": "ops",
    "password": "pwd123456",
    "transportMode": "shell",
    "shellReadyTimeoutMs": 15000,
    "shellCommandTimeoutMs": 45000
  },
  "prod": {
    "host": "5.6.7.8",
    "port": 22,
    "username": "bob",
    "password": "yyy",
    "socksProxy": "socks://127.0.0.1:10808"
  }
}

Then use the --config-file parameter:

{
  "mcpServers": {
    "ssh-mcp-server": {
      "command": "npx",
      "args": [
        "-y",
        "@fangjunjie/ssh-mcp-server",
        "--config-file", "ssh-config.json"
      ]
    }
  }
}

🔧 Method 2: Using JSON Format with --ssh Parameter

You can pass JSON-formatted configuration strings directly:

{
  "mcpServers": {
    "ssh-mcp-server": {
      "command": "npx",
      "args": [
        "-y",
        "@fangjunjie/ssh-mcp-server",
        "--ssh", "{\"name\":\"dev\",\"host\":\"1.2.3.4\",\"port\":22,\"username\":\"alice\",\"password\":\"{abc=P100s0}\",\"socksProxy\":\"socks://127.0.0.1:10808\"}",
        "--ssh", "{\"name\":\"bastion\",\"host\":\"9.9.9.9\",\"port\":22,\"username\":\"ops\",\"password\":\"pwd123456\",\"transportMode\":\"shell\",\"shellReadyTimeoutMs\":15000}",
        "--ssh", "{\"name\":\"prod\",\"host\":\"5.6.7.8\",\"port\":22,\"username\":\"bob\",\"password\":\"yyy\",\"socksProxy\":\"socks://127.0.0.1:10808\"}"
      ]
    }
  }
}

📝 Method 3: Legacy Comma-Separated Format (Backward Compatible)

For simple cases without special characters in passwords, you can still use the legacy format:

npx @fangjunjie/ssh-mcp-server \
  --ssh "name=dev,host=1.2.3.4,port=22,user=alice,password=xxx" \
  --ssh "name=prod,host=5.6.7.8,port=22,user=bob,password=yyy"

⚠️ Note: The legacy format may have issues with passwords containing special characters like =, ,, {, }. Use Method 1 or Method 2 for passwords with special characters.

In MCP tool calls, specify the connection name via the connectionName parameter. If omitted, the default connection is used.

Example (execute command on 'prod' connection):

{
  "tool": "execute-command",
  "params": {
    "cmdString": "ls -al",
    "connectionName": "prod"
  }
}

Example (execute command with timeout options):

{
  "tool": "execute-command",
  "params": {
    "cmdString": "ping -c 10 127.0.0.1",
    "connectionName": "prod",
    "timeout": 5000
  }
}

⏱️ Command Execution Timeout

The execute-command tool supports timeout options to prevent commands from hanging indefinitely:

  • timeout: Command execution timeout in milliseconds (optional, default is 30000ms)
  • In shell mode, you can also set shellCommandTimeoutMs per connection in the JSON config file
  • Connections use SSH keepalives by default (keepaliveIntervalMs: 10000, keepaliveCountMax: 3) and respect connectionTimeoutMs for connection setup
  • SFTP open and transfer operations respect sftpTimeoutMs (default 300000ms)
  • Error responses include stable code, message, and retriable fields for easier agent-side handling

This is particularly useful for commands like ping, tail -f, or other long-running processes that might block execution.

🗂️ List All SSH Servers

You can use the MCP tool list-servers to get all available SSH server configurations:

Example call:

{
  "tool": "list-servers",
  "params": {}
}

Example response:

[
  { "name": "dev", "host": "1.2.3.4", "port": 22, "username": "alice" },
  { "name": "prod", "host": "5.6.7.8", "port": 22, "username": "bob" }
]

⚙️ Command Line Options Reference

Options:
  --config-file       JSON configuration file path (recommended for multiple servers)
  --ssh-config-file   SSH config file path (default: ~/.ssh/config)
  --ssh               SSH connection configuration (can be JSON string or legacy format)
  -h, --host          SSH server host address or alias from SSH config
  -p, --port          SSH server port
  -u, --username      SSH username
  -w, --password      SSH password
  -k, --privateKey    SSH private key file path
  -P, --passphrase    Private key passphrase (if any)
  -a, --agent         SSH agent socket path
  --host-key-sha256   Expected SHA-256 SSH host-key fingerprint
  --try-keyboard      Enable keyboard-interactive authentication for 2FA/MFA (default: false)
  -W, --whitelist     Command whitelist, comma-separated regular expressions
  -B, --blacklist     Command blacklist, comma-separated regular expressions
  -s, --socksProxy    SOCKS proxy server address (e.g., socks://user:password@host:port)
  --allowed-local-paths   Additional allowed local paths for upload/download, comma-separated
  --allowed-remote-paths  Allowed remote (POSIX, absolute) paths for SFTP upload/download, comma-separated
  --transport-mode    SSH transport mode: exec or shell (default: exec)
  --shell-ready-timeout   Shell readiness probe timeout in milliseconds (default: 10000)
  --command-template  Command template, use <quotedCommand> for shell arguments or <command> for raw insertion
  --pty               Allocate pseudo-tty for command execution (default: true)
  --pre-connect       Pre-connect to all configured SSH servers on startup
  --collect-status    Opt in to remote system inventory collection after connecting
  --version, -v       Print package version
  --help              Print this help message

🛡️ Security Considerations

This server can execute commands and transfer files with the full permissions of the configured SSH account. The code has no telemetry or hidden outbound service; its intended outbound connections are the configured SSH destination and optional SOCKS proxy.

  • Least privilege: Use a dedicated non-root SSH account with narrowly scoped server-side permissions.
  • Host verification: Set --host-key-sha256 or hostKeySha256. Without it, the underlying SSH library accepts the presented host key and the server prints a warning.
  • Command allowlisting: Use anchored --whitelist expressions. Without an allowlist, the MCP client can run arbitrary commands permitted to the SSH account.
  • Credentials: Prefer an SSH agent or private-key path. Command-line passwords and passphrases can be exposed through process listings and MCP configuration files.
  • Status collection: Remote inventory collection is disabled by default. --collect-status opts in to running commands that inspect the host name, addresses, OS, CPU, GPU, disks, processes, and services.
  • Local Transfer Scope: By default, local file transfers are restricted to the current working directory. Use --allowed-local-paths or allowedLocalPaths in config only for explicitly trusted directories.
  • Remote Transfer Scope: SFTP upload/download accepts only absolute POSIX paths. If allowedRemotePaths (or --allowed-remote-paths) is not configured, any remote path is accepted and the server prints a startup warning. Configure allowedRemotePaths to whitelist a small set of remote directories; this is strongly recommended to prevent prompt-injection-driven reads or writes of files like ~/.ssh/authorized_keys or /etc/sshd_config.
  • Restriction boundary: allowedRemotePaths limits SFTP only. It cannot constrain paths accessed by remote shell commands.

🌟 Star History

Star History Chart