ssh-mcp
An MCP server that gives AI agents SSH access to remote machines through your local OpenSSH client, enabling remote command execution, file transfer, persistent shell sessions, and port forwarding.
README
ssh-mcp
An MCP server that gives AI agents SSH access to remote machines through your local OpenSSH client. It wraps ssh, scp, and rsync so agents can run remote commands, transfer files, maintain persistent shell sessions, and set up port forwards — all using your existing SSH config, keys, and credentials.
Why ssh-mcp?
- Uses your local SSH — host aliases,
~/.ssh/config,ProxyJump, agent forwarding, and existing credentials all work naturally. No SSH libraries or key management. - Persistent sessions — agents can keep a shell open across multiple tool calls, just like a human would. Sessions survive context window resets when you give them a
session_name. - Observable — every session records a transcript and optionally launches a detached tmux viewer so you can watch what the agent is doing in real time.
- Permission-gatable — port forwarding is a separate tool from command execution, so MCP clients can allow SSH access without allowing port forwards.
- Pure Python — no third-party runtime dependencies. Runs anywhere Python 3.10+ and OpenSSH are available.
Requirements
- Python 3.10+
sshandscpon PATH (or setSSH_MCP_SSH_BIN/SSH_MCP_SCP_BIN)rsyncon PATH (or setSSH_MCP_RSYNC_BIN) — only needed forssh_synctmux— optional, for live session observation
Installation
With uv (recommended)
uvx --from slepp-ssh-mcp ssh-mcp
Or install persistently:
uv tool install slepp-ssh-mcp
With pip
pip install slepp-ssh-mcp
Setup
Claude Code
claude mcp add --transport stdio --scope user ssh-mcp -- uvx --from slepp-ssh-mcp ssh-mcp
Or commit a .mcp.json to share with your team:
{
"mcpServers": {
"ssh-mcp": {
"type": "stdio",
"command": "uvx",
"args": ["--from", "slepp-ssh-mcp", "ssh-mcp"]
}
}
}
Codex CLI
codex mcp add ssh-mcp -- uvx --from slepp-ssh-mcp ssh-mcp
GitHub Copilot
Add to ~/.copilot/mcp-config.json (or .vscode/mcp.json per-project):
{
"mcpServers": {
"ssh-mcp": {
"type": "stdio",
"command": "uvx",
"args": ["--from", "slepp-ssh-mcp", "ssh-mcp"]
}
}
}
Generic MCP client
Any stdio MCP client works. Point it at uvx --from slepp-ssh-mcp ssh-mcp or at a virtualenv's ssh-mcp entrypoint.
How it works
ssh-mcp runs as a stdio process that your MCP client spawns. It receives JSON-RPC tool calls and translates them into local ssh/scp/rsync commands. Because it uses your local SSH binary, everything in your ~/.ssh/config works — jump hosts, custom ports, key selection, SSH_AUTH_SOCK, proxy commands.
There are three modes of operation:
One-off commands (ssh_exec)
Run a command, get stdout/stderr/exit code back. Works like ssh host 'command'.
{
"target": "prod-web01",
"command": "systemctl status nginx",
"timeout": 10
}
Use cwd to set the working directory, env to export variables, and tty: true for commands that need a terminal (like sudo with a password prompt). Note that tty merges stdout and stderr.
Interactive sessions (ssh_ensure_session + ssh_write_session + ssh_read_session)
For multi-step work, open a persistent shell. The agent writes commands and reads output just like typing in a terminal.
Start or reuse a session:
{
"target": "prod-web01",
"session_name": "deploy-api"
}
Always use a descriptive session_name. It serves three purposes:
- The agent can find the same session across multiple tool calls
- The tmux observer window gets a human-readable name (e.g.,
ssh-mcp-prod-web01-deploy-api) - A different agent or conversation can recover the session by name
Write a command:
{
"session_id": "a1b2c3d4e5f6",
"input": "cd /app && git pull\n",
"wait_seconds": 5
}
Always include \n to press Enter. Use \u0003 for Ctrl-C, \u0004 for Ctrl-D. Set wait_seconds high enough for the command to produce output (default: 1 second).
Read more output:
{
"session_id": "a1b2c3d4e5f6",
"wait_seconds": 10
}
Check pending_output_chars in the response — if non-zero, call again to drain the buffer.
Session lifecycle:
ssh_ensure_sessionis idempotent — call it at the start of each step- Sessions auto-detect dead connections via SSH keepalive (~90 seconds)
- Response includes
uptime_seconds,idle_seconds, andexit_reasonfor health monitoring - Use
auto_close: truefor one-shot commands that should clean up when done - Exited sessions are pruned from memory after 1 hour (5 minutes for
auto_close) cwd,env, andshellonly apply when creating a new session — they are ignored when reusing an existing one
File transfer (ssh_scp, ssh_sync)
Copy files between local and remote machines.
scp — simple file/directory copy:
{
"target": "prod-web01",
"direction": "upload",
"sources": ["/local/path/app.tar.gz"],
"destination": "/tmp/"
}
For upload, sources are local paths and destination is remote. For download, it's reversed. The target parameter specifies the host — don't include the host in sources or destination.
rsync — incremental sync with --delete and --exclude:
{
"target": "prod-web01",
"direction": "upload",
"source": "./dist/",
"destination": "/var/www/app/",
"delete": true,
"exclude": ["*.log", ".git"]
}
Port forwarding (ssh_forward)
Create local or remote port forwards. This is a separate tool from ssh_exec so MCP clients can grant SSH access without granting port forwarding.
Local forward — make a remote service reachable locally:
{
"target": "prod-web01",
"direction": "local",
"local_port": 15432,
"remote_host": "prod-db.internal",
"remote_port": 5432
}
This binds localhost:15432 and tunnels it to prod-db.internal:5432 through prod-web01.
Remote forward — expose a local service on the remote host:
{
"target": "prod-web01",
"direction": "remote",
"local_port": 3000,
"remote_host": "localhost",
"remote_port": 8080
}
Forwards bind to 127.0.0.1 by default. Set bind_address: "0.0.0.0" to expose on all interfaces (use with caution).
Use ssh_list_forwards to see active forwards and ssh_stop_forward to tear them down.
Watching sessions
Every interactive session records a transcript to ~/.local/state/ssh-mcp/<session_id>/transcript.log.
By default, sessions also launch a detached tmux window so you can watch in real time. The tmux session name includes the target and session name for easy identification:
# List ssh-mcp tmux sessions
tmux ls | grep ssh-mcp
# Attach to watch
tmux attach -t ssh-mcp-prod-web01-deploy-api
If you prefer not to use tmux, set observer_mode: "transcript" and tail the transcript file directly — the response includes an observer_command you can copy-paste.
The tmux observer is tied to the session lifecycle: stopping a session closes its tmux window. The MCP server also cleans up tmux on shutdown.
Environment variables
| Variable | Default | Description |
|---|---|---|
SSH_MCP_SSH_BIN |
ssh |
Path to the SSH client |
SSH_MCP_SCP_BIN |
scp |
Path to the SCP client |
SSH_MCP_RSYNC_BIN |
rsync |
Path to rsync |
SSH_MCP_TMUX_BIN |
tmux |
Path to tmux |
SSH_MCP_STATE_DIR |
~/.local/state/ssh-mcp |
Where transcripts are stored |
Security
ssh-mcp is designed for single-developer use on your own machine. It runs SSH commands as your user with your credentials.
What's protected:
- Port forwarding flags (
-L,-R,-D,-W) and dangerous SSH options (ProxyCommand,LocalCommand,LocalForward,RemoteForward,DynamicForward) are blocked inextra_ssh_args. The only way to create forwards is through the explicitssh_forwardtool, which MCP clients can permission-gate. - Transcript files are created with mode
0600and the state directory with0700. - All command arguments use
shlex.quote()to prevent shell injection. Subprocess calls use list arguments, nevershell=True. - Environment variable names are validated against
^[A-Za-z_][A-Za-z0-9_]*$.
What's not protected:
- An agent with
ssh_execaccess can run arbitrary commands on any host your SSH config can reach. The security boundary is SSH itself (keys, known_hosts). - Transcript files persist on disk after sessions end and may contain secrets (passwords typed at sudo prompts, API keys in output). Clean up
SSH_MCP_STATE_DIRwhen you no longer need them. - The
shellparameter lets agents choose any remote executable. This is by design — the tool is for remote execution.
Known limitations
- POSIX-only remotes —
cwd,env, andshellwrapping assumes a POSIX shell on the remote side. Windows SSH targets need commands written for their shell. - PTY output — interactive sessions use a PTY, so output includes terminal formatting (ANSI escape codes, command echo, line wrapping). This is intentional — it matches what a human would see.
- No multiplexing — each
ssh_execcall opens a new SSH connection. If your agent runs many rapid commands to the same host, consider using a session instead, or configureControlMasterin your~/.ssh/config. - Transcript growth — transcripts grow without bound for long-running sessions. The response includes
transcript_size_bytesso you can monitor this. Restart the session if it gets too large. - Forward connections are standalone — each
ssh_forwardopens its own SSH connection. Forwards are not tied to sessions.
Tools reference
| Tool | Description |
|---|---|
ssh_exec |
Run a one-off remote command |
ssh_scp |
Copy files via scp |
ssh_sync |
Incremental sync via rsync |
ssh_start_session |
Start a new interactive session |
ssh_ensure_session |
Reuse or start an interactive session (recommended) |
ssh_read_session |
Read output from a session |
ssh_write_session |
Write input to a session |
ssh_stop_session |
Stop a session |
ssh_list_sessions |
List tracked sessions |
ssh_forward |
Start a port forward |
ssh_list_forwards |
List tracked forwards |
ssh_stop_forward |
Stop a port forward |
All session and forward tools accept standard SSH connection parameters: port, identity_file, known_hosts_file, strict_host_key_checking, and extra_ssh_args.
Development
python3 -m pip install build
python3 -m unittest discover -s tests -v
python3 -m compileall src
python3 -m build
License
MIT. See LICENSE.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。