Skip to main content

MCP server for remote SSH operations -- persistent sessions, structured command execution, SFTP file transfer, and port forwarding for AI agents.

Project description

mcp-remote-ssh

PyPI Python License: MIT

MCP server giving AI agents full SSH access -- persistent sessions, structured command output, SFTP file transfer, port forwarding, and secret-safe environment variable injection with automatic output redaction.

Why this exists

Every other SSH MCP server is missing something: no password auth, no persistent sessions, no SFTP, no port forwarding, or no structured exit codes. This one has all of them -- plus the only MCP-level secret management that prevents AI agents from ever seeing your credentials.

Secret-Safe Environment Variables

The problem: When an AI agent needs to use API tokens, passwords, or keys on a remote server, the standard approach exposes secrets in the LLM's context window. The agent either reads the secret file (now it's in the conversation) or runs echo $TOKEN and sees the value in the output.

The solution: ssh_load_env_file reads secrets from a local file on your machine, injects them into the remote SSH session, and registers them for automatic output redaction. The AI agent can use the variables freely -- every tool response is scrubbed before it reaches the LLM.

# Agent calls this -- file is read from YOUR machine, not the remote host
ssh_load_env_file(session_id="abc", file_path="~/.secrets/prod.env")
→ "Loaded 3 variables from local:~/.secrets/prod.env: API_TOKEN, DB_PASS, SECRET_KEY"

# Agent tries to echo the value -- redacted automatically
ssh_execute(session_id="abc", command="echo $API_TOKEN")
→ {"stdout": "***\n", "exit_code": 0}

# Agent dumps the environment -- all secret values scrubbed
ssh_execute(session_id="abc", command="env | grep API_TOKEN")
→ {"stdout": "API_TOKEN=***\n", "exit_code": 0}

# Agent reads a file containing a secret -- also redacted
ssh_read_remote_file(session_id="abc", remote_path="/etc/app/config")
→ "db_password=***\ndb_host=localhost\n"

# Normal commands work perfectly -- no over-redaction
ssh_execute(session_id="abc", command="uname -a")
→ {"stdout": "Linux server 6.1.0 ...", "exit_code": 0}

How it works

┌─────────┐         ┌──────────────────────────────┐         ┌─────────────┐
│   LLM   │ ←─JSON─ │   MCP Server (your machine)  │ ──SSH─→ │ Remote Host │
│ (Agent) │         │                              │         │             │
└─────────┘         │  1. Reads ~/.secrets/prod.env│         └─────────────┘
                    │  2. Parses KEY=VALUE pairs   │
                    │  3. Stores values in memory  │
                    │  4. Injects into SSH session │
                    │  5. Redacts ALL tool output  │
                    └──────────────────────────────┘
  1. Local file read -- the env file lives on your machine, never on the remote host
  2. Shell injection via builtins -- uses read -r VAR <<< 'value' && export VAR (no process tree exposure)
  3. Stdin-based exec injection -- ssh_execute feeds secrets via stdin to a bash wrapper, so they never appear in /proc/*/cmdline
  4. Automatic redaction -- every tool response (ssh_execute, ssh_shell_send, ssh_shell_read, ssh_read_remote_file) is scrubbed before reaching the LLM
  5. Longest-first matching -- prevents partial-match corruption (e.g., abc123 is replaced before abc)

Security properties

Threat Mitigated? How
Secret in LLM context window Yes Output redaction replaces values with ***
Secret in remote process tree (shell) Yes Shell builtins (read/export) don't fork
Secret in remote process tree (exec) Yes Secrets fed via stdin, never in /proc/*/cmdline
LLM tries cat on the env file N/A File is local-only, doesn't exist on remote
LLM tries echo $VAR Yes Output is redacted
Encoded/transformed secret (base64) No Only literal matches are redacted

Env file format

Standard .env format:

# Comments are ignored
API_TOKEN=your-secret-token
DB_PASSWORD="quoted values work"
SECRET_KEY='single quotes too'
export ALSO_WORKS=yes

Installation

uvx mcp-remote-ssh        # or: pip install mcp-remote-ssh

Configuration

{
  "mcpServers": {
    "remote-ssh": {
      "command": "uvx",
      "args": ["mcp-remote-ssh"]
    }
  }
}

Tools (20)

Connection

Tool Description
ssh_connect Connect with password, key, or agent auth. Returns session_id
ssh_list_sessions List active sessions
ssh_close_session Close a session and release resources

Execution

Tool Description
ssh_execute Run a command, returns {stdout, stderr, exit_code}
ssh_sudo_execute Run with sudo elevation

Interactive Shell

Tool Description
ssh_shell_open Open persistent shell (preserves cwd, env, processes)
ssh_shell_send Send text (with optional Enter)
ssh_shell_read Read current output buffer
ssh_shell_send_control Send Ctrl+C, Ctrl+D, etc.
ssh_shell_wait Wait for a pattern or output to stabilize

Secrets Management

Tool Description
ssh_load_env_file Load secrets from a local env file; values never returned to the LLM
ssh_clear_secrets Clear redaction registry (values become visible again)

SFTP

Tool Description
ssh_upload_file Upload local file to remote host
ssh_download_file Download remote file to local machine
ssh_read_remote_file Read a remote text file
ssh_write_remote_file Write/append to a remote file
ssh_list_remote_dir List directory with metadata

Port Forwarding

Tool Description
ssh_forward_port Create SSH tunnel (local -> remote)
ssh_list_forwards List active tunnels
ssh_close_forward Close a tunnel

Quick start

ssh_connect(host="server.example.com", username="admin", password="secret")
→ {"session_id": "a1b2c3d4", "connected": true}

ssh_load_env_file(session_id="a1b2c3d4", file_path="~/.secrets/prod.env")
→ "Loaded 2 variables: API_TOKEN, DB_PASS"

ssh_execute(session_id="a1b2c3d4", command="curl -H \"Authorization: Bearer $API_TOKEN\" https://api.example.com")
→ {"stdout": "{\"status\": \"ok\"}", "exit_code": 0}  # token used but never visible

ssh_shell_open(session_id="a1b2c3d4")
ssh_shell_send(session_id="a1b2c3d4", data="cd /opt && make -j$(nproc)")
ssh_shell_wait(session_id="a1b2c3d4", pattern="$ ", timeout=600)

ssh_upload_file(session_id="a1b2c3d4", local_path="config.yaml", remote_path="/etc/app/config.yaml")
ssh_forward_port(session_id="a1b2c3d4", remote_port=5432, local_port=15432)

Design

Built on Paramiko (SSH) + FastMCP (MCP protocol).

  • ssh_execute uses exec_command() for clean structured output with real exit codes
  • When secrets are loaded, ssh_execute feeds exports via stdin to a bash wrapper, then execs the actual command -- secrets never appear in the process tree
  • ssh_shell_* uses invoke_shell() for persistent interactive sessions
  • All blocking Paramiko calls run in run_in_executor to stay async
  • Shell keeps a 500KB rolling buffer for shell_read polling
  • Secret redaction uses longest-first string replacement across all output paths

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mcp_remote_ssh-0.3.0.tar.gz (23.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mcp_remote_ssh-0.3.0-py3-none-any.whl (20.3 kB view details)

Uploaded Python 3

File details

Details for the file mcp_remote_ssh-0.3.0.tar.gz.

File metadata

  • Download URL: mcp_remote_ssh-0.3.0.tar.gz
  • Upload date:
  • Size: 23.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for mcp_remote_ssh-0.3.0.tar.gz
Algorithm Hash digest
SHA256 959e69ff78d8743d61092ad5a79272898dab4d75d4a342740acf7f68251cb3c8
MD5 22deff7b3d56c430defd2ceb999cd7a4
BLAKE2b-256 01ee3562dfc1874f4a3e74fd533f8fb0f9a310743bef7ba9298dce8d9f24c285

See more details on using hashes here.

Provenance

The following attestation bundles were made for mcp_remote_ssh-0.3.0.tar.gz:

Publisher: python-publish.yml on faizbawa/mcp-remote-ssh

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file mcp_remote_ssh-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: mcp_remote_ssh-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 20.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for mcp_remote_ssh-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7615e9e419342dafc6d369a660a3f4ca6783ea42070de9556b7b8b7c505fa61a
MD5 066b0713901f19fe55868cbc641d8562
BLAKE2b-256 bf6530ae320d247e1661a3c021473a226cbfa8437250cc53e0ff0bb3b491c646

See more details on using hashes here.

Provenance

The following attestation bundles were made for mcp_remote_ssh-0.3.0-py3-none-any.whl:

Publisher: python-publish.yml on faizbawa/mcp-remote-ssh

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page