Skip to main content

MCP SSH Gateway

Use your MCP client to work with several SSH hosts through one local connection. Run diagnostics on a VPS, inspect a NAS or query a Keenetic router by name. Sessions preserve terminal state between calls; long output can be read a page at a time.

For people who already use SSH and want an assistant to help with routine diagnostics and administration. It is not an SSH daemon, a hosted proxy or a replacement for access controls on your servers.

CI Python 3.11+ License: MIT

Try it with one host

You need Python 3.11+, an SSH account on a host you control and an MCP client that can launch a local stdio server. PyPI publication of version 6.0.1 is pending. Until it is published, start from this repository.

  1. Clone the project and install its dependencies:

    git clone https://github.com/d00mus/MCP-SSH.git
    cd MCP-SSH
    python -m pip install -r requirements.txt
    
  2. Create servers.json in that directory (replace the address, user and key path with your own):

    {
      "servers": {
        "lab": {
          "host": "192.168.1.10",
          "user": "your-ssh-user",
          "key_path": "~/.ssh/id_ed25519"
        }
      }
    }
    

    Host-key verification is enabled by default and uses the machine’s system host-key store. Ensure the host key is already trusted there, and verify its fingerprint independently before adding it. For password authentication, use "password": "${LAB_SSH_PASSWORD}" and provide LAB_SSH_PASSWORD to the MCP server process. Do not commit real credentials or your servers.json. See the security policy.

  3. Add this to a client that uses the mcpServers config format. Replace both absolute paths: clients do not necessarily start in the cloned directory.

    {
      "mcpServers": {
        "ssh-gateway": {
          "command": "python",
          "args": [
            "/absolute/path/to/MCP-SSH/mcp-server.py",
            "--servers-config", "/absolute/path/to/MCP-SSH/servers.json"
          ]
        }
      }
    }
    

    On Windows, point command to your Python executable if needed and use escaped backslashes in JSON paths (for example C:\\work\\MCP-SSH\\mcp-server.py). Restart the MCP client after updating its config. Running the script directly is not an interactive SSH terminal: it communicates with the client over stdio.

  4. In the client, ask: “List my SSH hosts, then run uname -a on lab.” If the host is missing, check the config path and the client's MCP server logs. If SSH fails, check credentials and host-key verification.

Add more hosts under servers in the same file. servers.json.example shows a multi-host configuration; check its host-key and credential choices before copying it.

What using it looks like

A Linux host and a router can share one MCP connection. Your client makes calls like these (they are not terminal commands):

server_list()                                          # find configured hosts
run(server="lab", command="df -h")                  # inspect disk space
run(server="keenetic", command="show interface", shell=false)  # router CLI

run returns a session_id; pass it to later calls if you need the same terminal state. Without it an idle session may be reused with unknown state; new_session: true forces a clean session. A command still running after the initial wait (5 seconds by default) reports still_running: true. Use read(session_id="...") for later output, or whenever has_more indicates unread lines. signal(action="ctrl_c") interrupts a stuck command. Non-zero exits report completed_nonzero and exit_status, not silent success.

The file tool can inspect and edit remote files through SFTP (with shell fallback). Review edits and give an assistant only the SSH permissions it needs.

When to use it

  • Multiple hosts: one MCP server configuration routes calls to named targets. For just one host, this matters less.
  • Multi-step troubleshooting: persistent sessions keep shell state, while line-based output windows avoid dumping an entire log into the conversation at once.
  • A Keenetic alongside Linux hosts: shell: false sends device CLI commands without a POSIX shell; common pagers such as --More-- are handled. Keenetic NDM is a supported use case, but other vendor CLIs are not guaranteed. Keep NDM CLI and Linux shell operations in separate sessions.

Security boundary: read_only and command blacklists are best-effort guardrails against mistakes, not a sandbox. Shell expansion and interpreters can bypass checks on command text. Use restricted SSH users and server-side permissions for sensitive hosts. Host-key verification is on by default; avoid turning it off casually.

The SSH connection originates from the machine running the gateway. This project works with MCP clients that can start a stdio server; it does not add SSH access to a chat app without MCP integration.

Other ways to run it

Docker (build from this clone):

docker build -t mcp-ssh-server .
docker run -i --rm \
  -v /absolute/path/to/servers.json:/app/servers.json:ro \
  -v /absolute/path/to/your/.ssh:/root/.ssh:ro \
  mcp-ssh-server --servers-config /app/servers.json

Use absolute mount paths and pass required environment variables with -e NAME. This example exposes SSH keys to the container; mount only what it needs. For an MCP client using Docker, set command to docker and put the same run arguments in args.

PyPI / MCP Registry: PyPI publication of version 6.0.1 and the MCP Registry listing are pending. Until published, pip install mcp-ssh-gateway and uvx --from mcp-ssh-gateway ... will not work. See the release process. No publication badge is shown until there is a listing.

Configuration and tools

  • Each target has an alias, host, user, optional port (default 22) and a key_path or password. verify_host defaults to true. password and key_passphrase support environment references (${NAME}); missing references fail at startup.
  • The default full profile exposes server_list, server_add, run, read, signal, file, session_list, session_update, session_close and last_command_details. server_add accepts an alias and only appends new targets. --tool-profile lean exposes six everyday tools for a smaller catalog.
  • Changes to servers.json are checked periodically (every 30 seconds); server_list(reload=true) checks immediately. Unchanged hosts keep their sessions; removing a host or changing its address, login or host-key settings closes that host’s active sessions.
  • --log-output meta (the default) records lifecycle information and command text. full also records raw output; off disables logging. Consider what secrets might appear in commands and output.

For contributions or vulnerabilities, see CONTRIBUTING.md and SECURITY.md.

Development

python -m unittest discover -s tests -t .

See the changelog. MIT-licensed; see LICENSE.

Release files for mcp-ssh-gateway 6.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mcp-ssh-gateway 6.0.1
File Size Uploaded
mcp_ssh_gateway-6.0.1.tar.gz 180.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for mcp-ssh-gateway 6.0.1
File Interpreter ABI Platform
mcp_ssh_gateway-6.0.1-py3-none-any.whl Python 3 none any Details

Total release size: 272.3 kB

Release files / mcp_ssh_gateway-6.0.1.tar.gz

Download URL mcp_ssh_gateway-6.0.1.tar.gz
Size 180.2 kB
Tags Source
SHA-256 checksum
How to use checksums
2f62a4dac9845bbf9e804a0b6caa4b5732afe2ed628b581047582d3a6d405720
BLAKE2b-256 checksum
How to use checksums
bf12340c7c7c82ea38fd38217f6be605a16107588ad16bbea8d19afae0d6d6a4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.5

Release files / mcp_ssh_gateway-6.0.1-py3-none-any.whl

Download URL mcp_ssh_gateway-6.0.1-py3-none-any.whl
Size 92.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d29827c45472f91c4343fe572c4844d4dc8397ef8d9c7250f61264d83fdef2b4
BLAKE2b-256 checksum
How to use checksums
26d327acf0362984b95532c768b6e1b9190a1e52e0e71ae86fce96b3ed2c43b8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.5

Release history Release notifications | RSS feed

6.0.2

2 release files

This release

6.0.1 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page