Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

claude-ssh-mcp

A tiny Python proxy that lets Claude Desktop on Windows reach an MCP server running on a remote machine over SSH.

It exists to work around a hard Windows-only blocker: Microsoft's C:\Windows\System32\OpenSSH\ssh.exe cannot launch inside an Electron grandchild process tree. Even ssh -V returns exit 255 with no stderr, which means the standard MCP stdio configuration

{
  "mcpServers": {
    "remote": {
      "command": "ssh",
      "args": ["user@host", "python3", "/remote/server.py"]
    }
  }
}

silently fails on Claude Desktop with MCP server: Server disconnected.

This project replaces ssh.exe with paramiko, a pure-Python SSH client. paramiko runs entirely inside the proxy's own Python process, so it has none of the console / handle-inheritance / sandbox constraints that kill ssh.exe in this context.

Status

  • Tested on Windows 11 + Claude Desktop
  • Used to expose a remote MCP server hosted on Debian 12 over SSH
  • Drop-in compatible with the usual ssh-style argument list

How it works

Claude Desktop (Electron)
        │  stdio
        ▼
  python.exe  ──►  mcp_ssh_proxy.py
                          │  paramiko (pure Python SSH)
                          ▼
                  remote MCP server (any language)
                  launched via `exec_command`

The proxy:

  1. Reads ssh-style argv (-i KEY -p PORT user@host remote-cmd …).
  2. Opens a paramiko SSH connection — no ssh.exe, no subprocess.
  3. Calls channel.exec_command(remote_cmd) to spawn the MCP server on the remote host.
  4. Pumps raw bytes between Claude Desktop's stdio and the SSH channel using os.read/os.write (zero Python-level buffering).

Install

Requirements

  • Windows 10/11
  • Python 3.9+ on PATH (any distribution: python.org, Microsoft Store, …)
  • Claude Desktop with MCP support

Automatic

Run from PowerShell:

iwr -useb https://raw.githubusercontent.com/KondrashovDenis/claude-ssh-mcp/main/install.ps1 | iex

This will:

  1. pip install paramiko
  2. Copy mcp_ssh_proxy.py to %LOCALAPPDATA%\Programs\claude-ssh-mcp\
  3. Print the JSON snippet to paste into your claude_desktop_config.json

Manual

pip install paramiko
mkdir "$env:LOCALAPPDATA\Programs\claude-ssh-mcp"
# Save mcp_ssh_proxy.py there.

Configure Claude Desktop

Edit %APPDATA%\Claude\claude_desktop_config.json. Add (or merge with) a mcpServers block:

{
  "mcpServers": {
    "myserver": {
      "command": "python",
      "args": [
        "C:\\Users\\YOU\\AppData\\Local\\Programs\\claude-ssh-mcp\\mcp_ssh_proxy.py",
        "-i", "C:\\Users\\YOU\\.ssh\\my_key",
        "-p", "22",
        "user@host.example.com",
        "python3", "/remote/path/to/your/mcp_server.py"
      ]
    }
  }
}

Replace myserver, the key path, port, host and remote command with your own. Restart Claude Desktop completely (close from system tray) and the server should attach within a few seconds.

A copy of this snippet is also in config.example.json.

Supported argv

The proxy parses an ssh-compatible subset of arguments. Anything else is silently ignored, so you can keep an existing ssh ... command line:

Flag Behaviour
-i KEYFILE private key path
-p PORT remote port (default 22)
-l USER remote username
user@host username + hostname
-T, -v, -vvv, -q, -N, -f, -n, -x, -X, -Y, -A, -a ignored
-o KEY=VALUE ignored (BatchMode, StrictHostKeyChecking, etc. don't apply to paramiko)
everything after host remote command, joined with spaces and run via exec_command

Debugging

The proxy writes a small log to %LOCALAPPDATA%\claude-ssh-mcp.log:

[15:48:18] === proxy start (paramiko) ===
[15:48:18] argv=['-i', 'C:\\Users\\you\\.ssh\\key', '-p', '2233', 'user@host', 'python3', '/srv/server.py']
[15:48:18] parsed host=host port=2233 user=user key=C:\Users\you\.ssh\key cmd='python3 /srv/server.py'
[15:48:18] connecting to user@host:2233 key=C:\Users\you\.ssh\key
[15:48:19] connected
[15:48:19] channel opened, command exec'd
[15:48:19] client->ssh -> 147B
[15:48:19] ssh->client <- 185B

Remote MCP server's stderr is also captured under the [remote-stderr] prefix in the same file.

Security notes

  • The proxy uses AutoAddPolicy for host keys. It accepts whatever public key the server presents on first contact (and on every contact). MCP context cannot prompt for confirmation, so this is the best we can do inside Claude Desktop. If your threat model needs strict host-key pinning, hard-code the expected key inside the script instead of using AutoAddPolicy.
  • look_for_keys=False and allow_agent=False are set explicitly so the proxy never authenticates with anything other than the -i key path you give it.
  • The private key path is read from disk on every connection. Make sure the key file's NTFS ACL allows only your user to read it.

Why not just use the WSL ssh?

You can, but it adds a WSL boot to every Claude session and forces you to keep your key inside WSL. paramiko is one pip install and runs in the same Python process as the proxy, so there is no extra subsystem in the chain.

License

MIT — see LICENSE.

About

Pure-Python SSH proxy that lets Claude Desktop on Windows use a remote MCP server. Works around ssh.exe failing inside Electron grandchild processes

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages