sshmcp
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
sshmcp
让 AI 通过 SSH 操作你的服务器,但全程看不到你的凭据。
AI 只能看到主机别名,用别名下发命令和文件操作。私钥、登录密码、私钥口令、sudo 密码 存在本机的加密 vault 里,由你在终端管理;sshmcp 的任何工具都不接受、不返回、不记录这些值。
20 个工具:执行命令、批量执行、持久 shell 会话、命令模板、日志 tail、文件读写、精确替换、目录增删、移动、上传下载
跨平台:Windows / Linux / macOS 同一套加密 vault,Linux 不需要 dbus 或 SecretService
双传输:stdio(客户端拉起本地进程)或 streamable HTTP(带 Bearer 鉴权)
命令完全放开,只做全量审计;不拦
rm -rf(见下方风险提示)341 项测试,其中端到端门禁跑真实 SSH 协议 + 真实 MCP JSON-RPC,CI 覆盖 Windows 与 Linux
密码明文输入:提示直接显示输入内容,粘贴照常可用,绕开 Windows
getpass在 Git Bash 下读不到粘贴的坑
安全边界:AI 能看到什么,看不到什么
AI 可见 | AI 不可见 | |
主机 | 别名、描述、标签;host/port/user(可配置隐藏) | 私钥路径、vault 内容 |
认证 | 配了哪几种方式( | 私钥内容、密码、口令、sudo 密码 |
命令 | 自己下发的命令、exit code、stdout/stderr | 注入 stdin 的 sudo 密码 |
文件 | 远端文件内容(脱敏后) | 其中的凭据与私钥块 → |
本地 |
| state 目录、 |
主机密钥 | 已固定的指纹 | 无「接受新密钥」的工具,密钥变更只能由你处理 |
实现上的四条硬约束:
工具签名里没有任何凭据参数,也不存在名字含 vault / config / credential / secret / key / token / audit 的工具。这两点有专门的测试断言。
凭据管理只有 CLI 一条路,秘密通过明文提示或剪贴板输入,不进命令行参数、不进 shell 历史。
所有出口过脱敏器:stdout/stderr、文件内容、异常消息、审计日志。启动时把 vault 里全部秘密值注册为字面量,另有正则拦截 PEM / OpenSSH / PuTTY 私钥块(含被输出截断后只剩头部的残块)。
主机密钥 TOFU 固定在独立的
known_hosts,指纹变更直接拒绝连接。
审计记录每次连接、命令、文件操作,以及被拒绝的连接(主机密钥变更、认证失败)和所有报错的工具调用。
这个保证的边界在哪(请务必读)
sshmcp 保证的是「它自己不把凭据交给模型」。它不能约束你的 MCP 客户端还给 AI 开了什么别的工具。
如果你的客户端同时给了 AI 本地 shell 或任意文件读取能力,AI 理论上可以读到主密码文件, 再自己解密 vault。要把这条路也堵上,选一个:
最彻底:不用 vault,改用
--identity-file+ ssh-agent。私钥完全不经过 sshmcp, 主密码这种东西根本不存在。把 state 目录放在 AI 工作区之外,并且不给 AI 该目录的读权限。
只给 AI sshmcp 这一组工具,不给本地 shell。
另外几点需要知道:
解密后的凭据在进程内存中。Python 无法可靠擦除字符串,这是语言层面的限制。
Windows 上文件权限继承目录 ACL,不像 POSIX 的
0600那样强制。state 目录请放在你自己的用户目录下。redact_private_keys: false只放开「vault 不认识的」私钥;vault 里自己的凭据始终脱敏。密码短于 4 个字符时,脱敏会替换掉输出里所有同样的片段,可能让输出很难读。CLI 会警告。
命令权限风险
按你的选择,命令面完全放开:AI 可以执行 rm -rf /、mkfs、shutdown。
sshmcp 不拦,只记账。降低风险的做法:
用低权限账号登录,需要提权时才
sudo=true,并单独存 sudo 密码给关键主机设
host_key_policy: "strict"定期看
sshmcp audit tail在远端用
authorized_keys的command=/restrict选项做服务端限制
Related MCP server: SSH MCP Server
安装
需要 Python 3.11+。
git clone https://github.com/boyunxi/sshmcp.git sshmcp && cd sshmcp
python -m venv .venvWindows(Git Bash):
source .venv/Scripts/activate
pip install -e .Linux / macOS:
source .venv/bin/activate
pip install -e .装完有一个 sshmcp 命令,也可以用 python -m sshmcp。
快速开始
1. 初始化
sshmcp init会创建 state 目录、config.json 和加密 vault,并让你设一个主密码(至少 8 位)。
只想用 ssh-agent 或现有私钥文件、不想管主密码:
sshmcp init --no-vault2. 加一台主机
密码登录(--password 是触发交互提示的开关,不是密码本身):
sshmcp host add prod-web --hostname 203.0.113.10 --user deploy --password \
--description "生产 Web" --tag prod用现有私钥文件(只存路径,私钥仍由 ssh 自己读):
sshmcp host add prod-web --hostname 203.0.113.10 --user deploy \
--identity-file ~/.ssh/id_ed25519把私钥导入加密 vault(之后原文件可以不在本机):
sshmcp host add prod-web --hostname 203.0.113.10 --user deploy \
--private-key ~/.ssh/id_ed25519 --passphrase需要 sudo 的话补一条(对已存在的别名是合并更新,只改动你给出的字段,其它保持原样):
sshmcp host add prod-web --sudo-password脚本化场景用 JSON 从 stdin 喂:
echo '{"password":"...","sudo_password":"..."}' | \
sshmcp host add prod-web --hostname 203.0.113.10 --user deploy --secrets-stdin3. 验证连通
sshmcp host test prod-web
sshmcp doctorhost test 首次连接会按 TOFU 固定主机密钥。想显式核对指纹再固定:
sshmcp host trust prod-web --fingerprint SHA256:xxxxx4. 接到 MCP 客户端
sshmcp mcp-config输出可直接贴进客户端配置。stdio 模式不能交互输入主密码(stdin 被 JSON-RPC 占用),
所以生成的配置用 SSHMCP_VAULT_PASSWORD_FILE 指向一个密码文件。按提示创建它:
# state 目录路径见 sshmcp paths
printf '%s' '你的主密码' > ~/.config/sshmcp/vault-password
chmod 600 ~/.config/sshmcp/vault-passwordWindows 上路径是 %APPDATA%\sshmcp\vault-password,请把该文件的权限限制为只有你的账号。
生成的配置形如:
{
"mcpServers": {
"sshmcp": {
"command": "/path/to/.venv/bin/python",
"args": ["-m", "sshmcp", "serve", "--transport", "stdio", "--home", "/path/to/state"],
"env": { "SSHMCP_VAULT_PASSWORD_FILE": "/path/to/state/vault-password" }
}
}
}Qoder / Claude Desktop / Cursor 等支持 mcpServers 格式的客户端都能直接用。
接好后让 AI 先调 ssh_hosts 看有哪些别名。
HTTP 模式
想让多个客户端或远程 IDE 连同一个实例:
sshmcp serve --transport http --http-host 127.0.0.1 --http-port 8765
sshmcp token show # 取 Bearer token
sshmcp mcp-config --transport http默认只绑回环地址。要绑非回环地址,必须在 config.json 的 http.allowed_hosts
里写上客户端会用的域名,否则 DNS rebinding 保护会拒掉请求(返回 421)。
密码输入
密码提示是明文的:直接读一行,输入的字符会显示在屏幕上,粘贴照常可用。
sshmcp init
# vault 主密码:███
# 再次输入主密码:███
sshmcp host add prod-web --hostname 203.0.113.10 --user deploy --password
# 登录密码:███CLI 的所有提示、状态输出和帮助文本都是中文;底层异常(vault / 配置 / SSH 层)保持英文, 因为同一批消息也会作为工具结果返回给 MCP 客户端。
之所以不用隐藏输入:Windows 上 getpass 绑定的是 win_getpass,用 msvcrt.getwch()
从控制台 CONIN$ 逐字符读,而不是从 sys.stdin 读。在 Git Bash / mintty 里 stdin 是管道,
你粘贴的内容进了管道,getwch() 却在等控制台输入——粘贴会丢失,或者直接卡死。
明文读一行在所有终端下都稳。
需要知道的取舍:
密码会显示在屏幕上,并留在终端滚动缓冲里。别在录屏或共享会话里用。
不会进 shell 历史,也不会出现在命令行参数里(
--password只是触发提示的开关)。提示语走 stderr,所以
mcp-config之类输出 JSON 的命令不会被污染。
如果你不想让它出现在滚动缓冲里,用剪贴板:
# 主机密码 / 私钥口令 / sudo 密码,各自独立
sshmcp host add prod-web --hostname 203.0.113.10 --user deploy --password-clipboard
sshmcp host add prod-web --sudo-password-clipboard
sshmcp host add prod-web --passphrase-clipboard
# vault 主密码(--clipboard 是全局选项,适用于任何需要解锁的命令)
sshmcp init --clipboard
sshmcp host show prod-web --clipboard
sshmcp vault passwd --new-clipboard # 新主密码从剪贴板读后端:Windows 用 ctypes 调原生 Win32 剪贴板 API(零新依赖);macOS 用 pbpaste;
Linux 依次尝试 wl-paste(Wayland)、xclip、xsel。都没有时会报错,去掉 --clipboard 直接输入即可。
剪贴板只去掉末尾的换行,不会动密码本身首尾的空格。读到的值不会出现在任何输出或错误信息里, 只告诉你读到了多少个字符,方便你确认复制对了。
注意:读完后密码仍留在剪贴板里。sshmcp 不会替你清空——那会破坏你的剪贴板内容。
什么时候会提示
场景 | 行为 |
解锁已有 vault |
|
| 不读 env(你要设的是新密码)。用 |
| 提示两次(新密码 + 确认),或 |
| 明确指定来源,任何情况下都优先于 env |
JSON 从 stdin 喂(脚本化)
echo '{"password":"...","sudo_password":"..."}' | \
sshmcp host add prod-web --hostname 203.0.113.10 --user deploy --secrets-stdin--secrets-stdin 不能和提示类或剪贴板类开关混用;同一个字段的 --password 和
--password-clipboard 也互斥,都会直接报错而不是静默选一个。
serve 拒绝 --clipboard——服务进程不该交互读秘密,请用密码文件或环境变量。
工具清单
多数工具的第一个参数是 host(别名);ssh_batch_exec 收 hosts 列表,
ssh_session_send / ssh_session_close 收 session_id,ssh_templates 和 ssh_hosts 无参数。
工具 | 说明 |
| 列出别名与非秘密元数据。先调这个 |
| 连通性、远端 uname、已固定的主机密钥指纹 |
| 执行命令。 |
| 一次对多台主机跑同一命令,逐台返回结果 + 成功/失败汇总 |
| 开一个持久 |
| 在会话里跑一条命令,状态(cd/export)保留,返回 |
| 关闭会话,返回执行过的命令数 |
| 列出操作员在 config 里定义的命令模板 |
| 执行模板;参数值会被 shell 转义 |
| 读文本文件。 |
| 读文件最后 N 行(默认 100,最多 5000)。二进制拒绝 |
| 写文件。 |
| 精确字符串替换。命中 0 次报错;命中多次需 |
| 列目录,返回 name/type/size/mode/uid/gid/mtime。可用 |
| 路径是否存在及元信息;符号链接会带 |
| 建目录, |
| 删文件或目录。非空目录需 |
| 移动/重命名。目标已存在需 |
| 本地 → 远端。单文件或 |
| 远端 → 本地。本地侧受 |
几个行为细节:
ssh_exec的max_output_bytes是每条流各自的上限(stdout 和 stderr 分开算)。超限时继续 drain 到命令结束,所以 exit code 仍然有效,只是truncated=true。timeout必须大于 0;省略则用主机的command_timeout。max_output_bytes/ssh_read_file的max_bytes上限 8 MiB,stdin上限 8 MiB——更大的内容请分块读或走ssh_upload。ssh_write_file的content只收 UTF-8 文本;图片、压缩包这类非文本内容用content_base64(两者互斥,解码后上限 8 MiB)。返回里的content_encoding会告诉你是哪种。ssh_batch_exec最多 50 台主机、最多 8 台并发;单台失败不影响其它,失败项带error字段。整体耗时是并行后的墙钟时间。持久会话:
ssh_session_open起一个/bin/sh,cd、export等在后续ssh_session_send里保留。空闲 15 分钟自动回收,累计 500 条命令后拒绝继续,每台主机最多 4 个会话。一条命令超时会直接销毁会话——超时后剩下的输出无法归属,继续复用会把脏数据算到下一条命令头上。ssh_tail从文件尾部读最多 256 KiB,按行切出最后 N 行;文件更早的内容不会加载。命令模板由操作员写在
config.json的templates里,AI 只能执行不能新增。参数值一律shlex.quote,所以模板里不要自己加引号。模板可以限定hosts(支持 glob)、带sudo和timeout。配置热加载:改完
config.json最多 1 秒生效,新增/修改/删除的主机会自动断开旧连接,不影响其它主机;改坏文件时保留旧配置并在审计里记config.reload_failed。vault文件变化会用已派生的密钥重读(host add新增的密码立即生效),但vault passwd换了 salt 之后需要重启服务。ssh_upload的recursive=true上传整个目录:跳过符号链接(不跟随,避免逃出目录树)、尽力保留文件权限位、整棵树上限 10000 个文件,返回files/directories/skipped_symlinks。ssh_upload的resume=true从远端已有字节数继续传(远端比本地大时直接拒绝);verify=true传完比对 sha256,主机没有sha256sum/shasum时返回verified=false+verify_error而不是假成功。resume/verify只用于单文件,目录上传会拒绝。超时会关闭通道(远端命令随之终止),返回
timed_out=true、exit_code=null。传输层故障只在命令尚未发出时重试一次;已经发出后不再重发,避免同一条命令被执行两次。sudo=true时先探测是否需要密码。需要则走sudo -S -p '' -- /bin/sh -c '<整条命令>', 密码只作为 stdin 首行注入,不分配 PTY(避免回显)。用sh -c包裹是为了让 sudo 覆盖整条命令,包括&&和管道。ssh_move的overwrite=true依赖服务端的posix-rename扩展(OpenSSH 支持)。
配置
config.json 在 state 目录里,不含任何秘密。未知字段会直接报错,避免拼错被静默忽略。
{
"version": 1,
"defaults": {
"port": 22,
"connect_timeout": 15.0,
"command_timeout": 60.0,
"keepalive": 30.0,
"max_output_bytes": 262144,
"host_key_policy": "tofu"
},
"security": {
"local_roots": [],
"redact_private_keys": true,
"expose_connection_details": true
},
"http": {
"host": "127.0.0.1",
"port": 8765,
"allowed_hosts": []
},
"audit": {
"enabled": true,
"max_bytes": 5242880,
"keep": 5
},
"templates": {
"svc-restart": {
"command": "systemctl restart {service} && systemctl is-active {service}",
"description": "重启服务并确认状态",
"params": ["service"],
"sudo": true,
"hosts": ["prod-*"],
"timeout": 90
}
},
"hosts": {
"prod-web": {
"hostname": "203.0.113.10",
"user": "deploy",
"port": 22,
"identity_file": "~/.ssh/id_ed25519",
"allow_agent": true,
"host_key_policy": "strict",
"description": "生产 Web",
"tags": ["prod", "web"]
}
}
}defaults里的值会被各主机继承,主机里同名字段覆盖它。security.local_roots为空时用进程当前工作目录。state 目录和~/.ssh始终被禁, 即使显式写进local_roots也不行;路径先resolve()再判断,..和符号链接都逃不出去。security.expose_connection_details: false可以让ssh_hosts只返回别名/描述/标签, 连 host/port/user 都不给 AI。host_key_policy:tofu首次连接自动固定;strict必须先sshmcp host trust。allow_agent: false禁止该主机尝试 ssh-agent。look_for_keys恒为 false—— 绝不会隐式捡起你没为该主机声明的~/.ssh私钥。templates里每个模板必须有command和params;command里用到的占位符必须全部声明, 声明了但没用到也会报错(防拼错)。hosts留空表示所有主机可用,支持 glob。 参数值在渲染时会被shlex.quote,模板里不要再加引号。
state 目录位置
优先级:--home > $SSHMCP_HOME > 平台默认。
平台 | 默认位置 |
Windows |
|
Linux |
|
macOS |
|
里面有 config.json、vault.enc、known_hosts、audit.jsonl、http_token。
sshmcp paths 可以打印实际路径。这些文件都已在 .gitignore 里,CI 也会检查它们没被提交。
环境变量
变量 | 用途 |
| state 目录 |
| 存主密码的文件(推荐) |
| 主密码本身(会出现在进程环境里,不如用文件) |
| HTTP 监听地址,被同名命令行参数覆盖 |
主密码优先级:--vault-password-file > SSHMCP_VAULT_PASSWORD_FILE > SSHMCP_VAULT_PASSWORD > 终端提示。
凭据存储
vault.enc 是一个自包含的加密文件:
KDF:Argon2id,64 MiB 内存、3 轮、并行度 4,随机 16 字节 salt
加密:AES-256-GCM,每次保存用新的随机 nonce
完整性:整个 header(含 KDF 参数)作为 AAD 参与认证,改动工作因子会导致解密失败; 并且在派生密钥之前校验工作因子上下界,防止恶意 vault 文件用超大
memory_cost打爆内存落盘:临时文件 + 原子替换,POSIX 上
0600
支持导入 OpenSSH、PKCS8、PKCS1、DER 格式的 Ed25519 / RSA / ECDSA 私钥。
改主密码:
sshmcp vault passwd # 用新 salt 重新加密
sshmcp vault status # 只看存了哪几类秘密,不显示值主密码忘了怎么办
加密是认真的:主密码丢了,vault 里的秘密就解不出来了,没有后门。
但主机配置不在 vault 里——config.json 原样保留。
sshmcp vault reset它会把旧文件改名为 vault.enc.bak-<时间戳>(不删除),然后用新主密码建一个空 vault,
并列出还需要重新添加密钥的主机。如果中途放弃输入新密码,会自动把原文件还原回去。
重置只影响存进去的密码和私钥;主机、端口、用户、已知主机密钥都不动。
CLI
sshmcp init [--no-vault] [--force] [--clipboard]
sshmcp host add <alias> [--hostname H] [--user U] [--port P]
[--identity-file PATH | --private-key PATH]
[--password] [--passphrase] [--sudo-password]
[--password-clipboard] [--passphrase-clipboard]
[--sudo-password-clipboard] [--secrets-stdin]
[--description D] [--tag T] [--host-key-policy tofu|strict]
[--connect-timeout S] [--command-timeout S] [--no-agent|--agent]
sshmcp host list
sshmcp host show <alias>
sshmcp host remove <alias> [--keep-secrets]
sshmcp host test <alias>
sshmcp host trust <alias> [--fingerprint SHA256:...] [--yes]
sshmcp vault status | passwd [--new-clipboard] | reset [--yes]
sshmcp token show | rotate
sshmcp audit tail [-n N]
sshmcp serve [--transport stdio|http] [--http-host H] [--http-port P]
sshmcp mcp-config [--transport stdio|http]
sshmcp paths
sshmcp doctor全局选项 --home、--vault-password-file、--clipboard 放在子命令前后都有效。
开发
pip install -e ".[dev]"
python -m pytest -q # 全套
python -m pytest -m protocol -q # 只跑真实 MCP 协议端到端测试不依赖外部 sshd:tests/fakeserver.py 用 paramiko 的 ServerInterface +
SFTPServerInterface 起一个进程内真实 SSH 服务端,走真协议,Windows 和 Linux 都能跑。
tests/test_protocol_e2e.py 更进一步,把 sshmcp 跑在子进程里,用真实 MCP 客户端走真实 JSON-RPC。
门禁里包含专门的泄露断言:命令回显密码、读含私钥的文件、异常路径、审计日志、 tool input schema、线上 payload 六处都断言无明文秘密。
设计见 PLAN.md,逐里程碑进度与踩坑记录见 PROGRESS.md。
许可
MIT,见 LICENSE。
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
Related MCP Connectors
Let AI operate servers without SSH. Choose actions, approve risky changes, and audit every step.
Operate Linux, macOS and Windows from your LLM. Every action runs through an auditable allowlist.
Connect AI assistants to explicitly paired computers, servers, and VMs for permitted remote actions.
Work on your own computers from Claude or ChatGPT: run commands, edit and search files.
Related MCP Servers
- AlicenseAqualityDmaintenanceEnables AI assistants to securely execute remote SSH commands, perform file transfers, and monitor system status through a standardized interface. It features robust security controls including command whitelisting, blacklisting, and credential isolation to prevent unauthorized operations.1014 npmMIT
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to securely execute commands, transfer files, and manage port forwarding on remote servers via SSH.158 npm38Apache 2.0
- AlicenseAqualityBmaintenanceEnables AI assistants to manage remote servers via SSH with agentless command execution, file operations, and service management.9MIT
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to manage remote servers via SSH, including command execution, multi-host batch operations, SFTP file transfer, background job handling, DevOps diagnostics, port tunneling, and safety guardrails like high-risk command blocking and read-only mode.MIT