关于 SSH MCP Server
SSH MCP Server 为 AI Agent 提供结构化工具,用于远程服务器检查、命令执行、文件操作、日志搜索和审计。它使用系统 OpenSSH 客户端,并沿用现有密钥、SSH 配置、跳板主机和 Agent 转发。
主要功能
- 01在远程机器上运行命令
- 02传输和编辑文件
- 03跨路径搜索日志
- 04获取结构化服务器健康检查
- 05审计系统与服务状态
- 06沿用现有 OpenSSH 配置
适用场景
- 01部署后检查服务器健康状况
- 02在多份日志中搜索故障错误
- 03安全替换远程配置文件
- 04审计 VPS 或旧式网络设备
MCP 相关
向 MCP 客户端提供基于 SSH 的命令、文件、日志、健康检查和审计工具。连接使用本机的 OpenSSH 客户端及已配置的配置档案。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
npx -y @hypnosis/ssh-mcp-serverSSH_PROFILES_FILEAbsolute path to the JSON file describing the servers this agent may reach.SSH_MCP_LOG_LEVELLog verbosity: error, warn, info or debug. Defaults to info.本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
18收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| cwd | string | 否 | 要从中开始执行的目录,也适用于分离运行的作业。无法进入该目录时,命令会停止,不会在其他位置运行。 |
| sudo | boolean | 否 | 使用 sudo 执行命令。默认值:false |
| detach | boolean | 否 | 在服务器上运行后台作业:立即返回 ID,作业在此呼叫结束后仍会继续运行,且不受超时限制。使用 ssh_job_status / ssh_job_output 跟进,使用 ssh_job_kill 停止。仅限一条命令。使用 sudo 时,作业以 root 身份运行,之后的每次调用也都以 root 身份执行,前提是配置文件中有密码或 sudo 无需密码。默认值:false |
| command | any | 是 | 一条命令或命令列表:["hostname", "whoami"]。每条命令都在独立的 shell 中运行,不共享变量或当前目录;cwd 对所有命令生效。某条命令以非零状态退出不会停止列表执行。 |
| profile | string | 是 | 机器名称。 |
| timeout | number | 否 | 毫秒;对于命令列表,表示每条命令的超时时间,而非整个列表的超时时间;默认值为 30000。耗时以分钟计的工作应使用 detach,而不是增大此值。 |
详细介绍
SSH MCP Server — 面向 AI Agent 的远程服务工具
|
|
一个 SSH MCP 服务——多功能工具,可帮助你和 AI Agent 节省调试、开发和服务器维护所需的时间与 token。 通过 SSH 运行命令、传输文件、读取日志并审计机器——无论是云 VPS、裸机服务器,还是你壁橱里的 BusyBox 路由器。 |
它使用你机器上已有的 OpenSSH 客户端:你的密钥、你的 ~/.ssh/config、你的跳板机、你的 Agent 转发。不捆绑任何内容,无需编译,也没有原生绑定。
兼容 Claude Code、Codex CLI、Cline、opencode、Gemini CLI、Qwen Code、Hermes 等 MCP 客户端。
安装 · 工具 · 设置 · 安全 · 路线图 · 文档 · 更新日志
30 秒内完成安装
无需全局安装。首次使用时,npx 会下载此软件包:
npx -y @hypnosis/ssh-mcp-server
将其添加到你的 MCP 客户端——例如 Claude Code——即可用于每个项目:
claude mcp add ssh -s user \
-e SSH_PROFILES_FILE="$HOME/.claude/ssh-profiles.json" \
-- npx -y @hypnosis/ssh-mcp-server
也可以手动编写——下面是多数客户端共用的配置格式:
{
"mcpServers": {
"ssh": {
"command": "npx",
"args": ["-y", "@hypnosis/ssh-mcp-server"],
"env": {
"SSH_PROFILES_FILE": "~/.claude/ssh-profiles.json"
}
}
}
}
然后创建 ~/.claude/ssh-profiles.json,至少配置一台机器:
{
"profiles": {
"production": {
"host": "server.example.com",
"username": "admin",
"privateKeyPath": "~/.ssh/your_private_key"
}
}
}
这样就可以连接了。
Codex、opencode、Qwen Code 和其他客户端的配置方法,请参阅 设置 SSH MCP 服务。
以插件形式安装
某些客户端——例如 Claude Code——可以直接将整个工具作为插件使用:
/plugin marketplace add hypnosis/ssh-mcp-server
/plugin install ssh-mcp-server@ssh-mcp-server
除非另行设置 SSH_PROFILES_FILE,否则插件会读取 ~/.claude/ssh-profiles.json。因此请先创建该文件,启动服务时就会自动加载你配置的机器。
要求
需要 Node.js 18+,并确保系统 ssh 客户端位于 PATH 中。在 Windows 上,请使用基于密钥的配置;目前不支持密码和口令配置。
如果你更倾向于使用固定版本、离线工作,或减少每次启动时的一次注册表检查:
先运行 npm install -g @hypnosis/ssh-mcp-server,然后使用 ssh-mcp-server 命令,而不是
npx。
适用人群
- DevOps 和 SRE 从业者:希望更快完成审计、事件检查和日常服务器工作。
- 氛围编程爱好者和独立开发者:借助 AI 助手交付产品,并在自己的服务器上运行所构建的内容。
- 系统管理员和平台工程师:希望使用结构化工具,而不是不受限制的原始 shell。
- 自行运维 VPS 的开发者和小团队:没有专门的运维团队。
- Homelab、NAS 和路由器用户:实用设备的使用寿命已超过其现代协议。
为什么选择 SSH MCP 服务,而不是原始 shell
更少的令牌,更低的 AI 成本
原始 shell 会向 AI Agent 倾倒海量信息:重复命令、ASCII 表格和日志转储。 它会耗费令牌,把这些噪声整理成服务器概况——也就是你的钱。
更快地调试服务器
专用工具会批量执行常规检查、限制嘈杂输出,并返回关键内容。 Agent 花在解读终端输出上的时间更少,能更快找到修复方法。
少些猜测,减少 AI 错误
结构化回答会说明发现了什么、哪些内容无法测量,以及哪些内容被截断。 这样 Agent 就不容易用幻觉填补空白,你也能减少错误修复,让部署更平稳、代码更可靠。
SSH 兼容性:现代服务器、旧式设备和 Windows
使用现有的 OpenSSH 配置
不捆绑 SSH 实现、不需要原生绑定,也无需针对每个平台重新构建。命令通过系统 ssh 客户端运行,因此你的密钥、~/.ssh/config、跳板主机和 Agent 转发都能像在终端中一样正常工作。在支持的情况下,每个目标地址会共用一个多路复用连接,因此只需进行一次身份验证,而不是每条命令都验证一次。
为旧款服务器、路由器和 NAS 设备提供 SSH 支持
使用新版 scp 向路由器发送文件,你会看到:
scp app.conf router:/etc/
# scp: subsystem request failed on channel 0
没有任何东西出故障——当前的 scp 使用新协议,而路由器不支持它。
现在,你只能在终端里去翻论坛帖子,然后带着一个额外的标志回来。在这里,你什么都不用做:系统会先尝试传输,识别出拒绝后改用旧协议,并记住这台设备,让下次发送文件时直接使用旧协议。
兼容旧版 SSH 客户端和缺少工具的设备
旧设备也有备用方案,而不是无路可走。缺少现代功能时,服务器会在可行的情况下改用旧方法:
| 你的设备 | 你会得到什么 |
|---|---|
| 太过老旧,无法使用现代文件传输的路由器或 NAS | 文件仍会成功传输——系统会自动使用旧协议 |
| 十年前的服务器 | 工作流仍可正常运行;只是每条命令都会新建连接,而不是复用连接 |
| 无法对文件进行哈希计算的精简镜像 | 上传结果会显示“无法验证”,而不会声称哈希匹配却无人核验 |
| 未安装某个工具的设备 | 结果会显示“未测量”——绝不会显示容易被误解为“什么都没有”的零 |
为模型上下文协议而构建
基于官方 MCP SDK 构建,全程使用 TypeScript,拥有 2,500 多项单元测试,以及一套针对真实容器而非模拟对象运行的实时测试套件。
原始 SSH 与 SSH MCP 服务:同一项任务,两种方式
SSH 服务器健康检查
**情境:**刚完成一次部署。服务器运行缓慢,你不知道问题出在磁盘、内存、服务、容器还是错误上。
问题:“这台设备运行正常吗?”
原始 SSH
$ uptime
10:42:17 up 18 days, 3:21, 2 users, load average: 0.42, 0.31, 0.28
$ df -hT
Filesystem Type Size Used Avail Use% Mounted on
/dev/sda1 ext4 40G 35G 5.0G 87% /
overlay overlay 40G 35G 5.0G 87% /var/lib/docker/overlay2/...
$ free -h
total used free shared buff/cache available
Mem: 7.7Gi 4.9Gi 612Mi 121Mi 2.2Gi 2.5Gi
$ systemctl --failed
UNIT LOAD ACTIVE SUB DESCRIPTION
● api-worker.service loaded failed failed API background worker
$ docker ps -a
CONTAINER ID IMAGE STATUS PORTS
8e14d0b41c2a api:latest Up 3 minutes 0.0.0.0:8080->8080/tcp
65b894af2430 worker:latest Exited (1) 2 minutes ago
$ ss -tulpn
Netid State Local Address:Port Process
tcp LISTEN 0.0.0.0:22 users:(("sshd",pid=842,fd=3))
tcp LISTEN 0.0.0.0:8080 users:(("docker-proxy",pid=1942,fd=4))
$ journalctl -p err --since -1h | tail -50
Aug 20 10:39:14 prod api-worker[22104]: database connection timed out
Aug 20 10:39:14 prod systemd[1]: api-worker.service: Failed with result 'exit-code'.
这仍然只是经过删减的结果。完整检查还需要更多命令来检查 CPU、服务状态、容器数量和近期错误,而且每条命令的输出格式都不一样。更糟的是,如果设备没有 ss,端口检查就可能根本没有运行,却让人误以为没有监听端口。
结构化 MCP 结果
ssh_snapshot({ "profile": "production" })
{
"disk_pct": 87,
"mem_pct": 64,
"cpu_pct": 12,
"load": "0.42 0.31 0.28",
"containers": 7,
"ports": 14,
"services_running": 3,
"recent_errors": 21,
"unavailable": []
}
Agent 的收获
| 原始 SSH | 结构化 MCP | 你的收获 |
|---|---|---|
| 多条命令和 ASCII 表格 | 一条结果中包含具名字段 | 一次调用即可获得具名字段,减少往返次数 |
| 缺少工具可能看起来像空输出 | unavailable 会指出哪些项目未测量 | 减少猜测和错误修复 |
| 你需要自行梳理磁盘、服务和错误 | 问题信号已直接呈现 | 更快完成调试 |
完整的 ssh_audit_baseline 结果可能比几条原始命令的输出更长——在我们的实验室测量中,约为 1,077 个 token,而原始命令输出为 765 个。节省之处在于完整的工作流,而不是缩短某一条响应。
在实际故障排查过程中,专用工具将 49 次单独的命令调用减少到了 4 次 MCP 调用。每次额外调用都会在累积的对话基础上开启新一轮模型交互。提示词缓存可以降低重复输入的成本,但新命令及其输出仍会占用上下文。减少往返次数意味着整个会话使用的 token 更少、重复分析更少,也能更快找到答案。
需要全面了解,而不只是查看当前状态? ssh_audit_baseline 会批量检查系统、磁盘、内存、端口、sshd、失败的单元、Docker、防火墙和更新。检查结果会标记为 CRITICAL / WARNING / OK;未测量的部分也会明确标出,不会悄悄显示为零。
Linux 服务器日志搜索
**情境:**API 请求超时,但同一条消息可能出现在 nginx、syslog、journald 或你无法以普通用户身份读取的应用日志中。
问题:“这个错误是从哪里来的?”
原始 SSH
$ grep -i "timeout" /var/log/nginx/error.log
2026/08/20 10:38:54 [error] upstream timed out while reading response header
$ grep -i "timeout" /var/log/syslog
Aug 20 10:39:14 prod api-worker[22104]: database connection timed out
$ grep -i "timeout" /var/log/app/*.log 2>/dev/null
$ journalctl -u api --since "1 hour ago" | grep -i timeout
Aug 20 10:39:14 prod api[22104]: database connection timed out after 30000ms
第三条命令看起来没有问题,但 2>/dev/null 也隐藏了权限错误。“没有匹配项”和“没有读取到内容”现在看起来完全一样。繁忙的日志还可能返回数千行内容,把事故处理的其余信息挤出 Agent 的上下文。
结构化 MCP 结果
ssh_log_search({ "profile": "production",
"path": ["/var/log/nginx/error.log", "/var/log/syslog", "/var/log/app/*.log"],
"query": "timeout", "context": 2, "since": "1h" })
{
"matches": 34,
"lines": [
{ "file": "/var/log/nginx/error.log", "line": 4821,
"text": "upstream timed out while reading response header", "context": false },
{ "file": "/var/log/nginx/error.log", "line": 4822,
"text": "client closed connection", "context": true }
],
"files_searched": 6,
"files_unreadable": ["/var/log/app/private"],
"files_skipped": 12,
"files_undated": [],
"limited": false,
"truncated": false
}
Agent 获得的收益
| 原始 SSH | 结构化 MCP | 您的收益 |
|---|---|---|
| 四次搜索和四份输出 | 一次搜索即可遍历文件和 glob 模式 | 减少令牌消耗和往返次数 |
| 权限错误可能悄无声息地消失 | files_unreadable 会列出所有未能读取的路径 | 避免误判为“日志干净” |
| 输出可能无限增长 | limited 和 truncated 会显示每处截断 | 根据不完整结果做出更安全的决策 |
since 使用服务端时钟,namesOnly: true 只返回匹配的路径,而
ssh_log_tail 可在一次调用中读取多个日志的最后 N 行。
安全地编辑远程配置
情境: 您需要在运行中的服务器上替换 nginx 配置。连接中断、 模式错误或未经检查的复制都可能导致服务留下损坏的文件。
问题:“我能替换这个配置,同时避免留下不完整的文件吗?”
原始 SSH
$ sudo sh -c 'cat > /etc/nginx/conf.d/api.conf' <<'EOF'
server {
listen 80;
location / { proxy_pass http://127.0.0.1:8080; }
}
EOF
$ echo $?
0
退出代码为零只表示 shell 已执行完毕,并不能证明实际写入了哪些字节;而且 >
会在新文件的第一个字节到达之前截断旧文件。如果连接在写入过程中断开,
服务就会留下不完整的配置。
结构化 MCP 结果
ssh_file_write({ "profile": "production",
"files": [{ "path": "/etc/nginx/conf.d/api.conf",
"content": "server {\n listen 80;\n location / { proxy_pass http://127.0.0.1:8080; }\n}\n",
"mode": "644", "sudo": true, "verify": true }] })
{
"files": [{ "path": "/etc/nginx/conf.d/api.conf", "written": true,
"verified": "verified", "reason": null, "bytes": 79 }]
}
Agent 获得的收益
| 原始 SSH | 结构化 MCP | 您的收益 |
|---|---|---|
| 复制完成前目标文件就被截断 | 完整的临时文件通过一次重命名替换目标文件 | 不会留下只写入一半的配置 |
| 只有退出代码 | 明确标出字节数和验证结果 | 清楚知道实际写入了什么 |
| 权限设置写在 shell 文本中 | sudo、mode 和 verify 是逐文件设置的字段 | 所有权可预测,也能减少引号错误 |
verified 有三种准确的结果:verified、服务器没有哈希工具时的 unavailable,以及未请求验证时的 skipped。对于读取操作,ssh_file_read 接受路径列表;ssh_file_list 可处理 glob 模式、递归、大小和模式。
使用 sudo 运行批量 SSH 命令
情境: 部署已准备就绪,但在切换流量之前,必须检查 nginx 语法、服务状态和近期错误。任何一项检查失败,都不应被淹没在合并的输出中。
问题:“所有部署前检查都通过了吗?”
原始 SSH
$ ssh [email protected] 'sudo nginx -t'
nginx: configuration file /etc/nginx/nginx.conf test is successful
$ ssh [email protected] 'sudo systemctl is-active nginx'
active
$ ssh [email protected] 'sudo tail -5 /var/log/nginx/error.log'
2026/08/20 10:38:54 [error] upstream timed out while reading response header
三个连接会返回三份互不相关的输出。如果用 ; 连接命令,shell 只会报告最后一条命令的退出代码;如果用 && 连接,第一次失败后,后续检查就不会执行。
结构化 MCP 结果
ssh_exec({ "profile": "production",
"command": ["nginx -t", "systemctl is-active nginx",
"tail -5 /var/log/nginx/error.log"],
"sudo": true })
{
"commands": [
{ "command": "nginx -t", "exit_code": 0, "truncated": false, "clipped_bytes": 0,
"stdout": "", "stderr": "nginx: configuration file /etc/nginx/nginx.conf test is successful\n" },
{ "command": "systemctl is-active nginx", "exit_code": 0, "truncated": false,
"clipped_bytes": 0, "stdout": "active\n", "stderr": "" },
{ "command": "tail -5 /var/log/nginx/error.log", "exit_code": 0, "truncated": false,
"clipped_bytes": 0, "stdout": "2026/08/21 09:14:02 [error] upstream timed out\n", "stderr": "" }
],
"job_id": null
}
Agent 获得的优势
| 原始 SSH | 结构化 MCP | 带来的好处 |
|---|---|---|
| 需要调用 3 次,输出彼此无关 | 一份有序的命令列表 | 减少往返次数 |
| 合并后的 Shell 可能隐藏中间状态 | 每条命令都保留自己的 exit_code | 不会漏掉失败的检查 |
命令文本中反复出现 sudo 和引号 | sudo 应用于整个批次 | 减少引号错误 |
破坏性命令防护会在第一条命令运行前检查完整列表。如果有一项被拒绝,其他所有项都会标记为未运行,且不会向服务端发送任何内容。
每条命令都有自己的 stdout 和 stderr。已运行但没有输出的命令,其对应字段为空字符串;从未运行的命令则完全没有这些字段,因此两者不会混淆。单条命令的输出超过 128 KB 时,会保留两端内容——开头部分用于表格,结尾部分用于日志——并在中间标明省略内容的长度;clipped_bytes 则会说明截断了多少字节。截断会按字节边界进行,并退回到字符边界,因此截断后的答案不会带有替换标记。
sudo 无需终端即可在服务端运行:配置文件中的密码会通过标准输入传给 sudo。使用哪个密码取决于配置文件是否指定了 sudoPassword:如果指定了,就使用它;否则使用 password。使用密钥登录的配置文件根本没有登录密码;如果设备将两种密码分开,那么登录密码就不是正确答案。如果没有可用密码,回复会明确说明,并指出可行的解决方式,而不是留下 sudo 自己关于 -S 和 askpass 辅助程序的提示。对于会读取自身标准输入的命令,则绝不会传入密码,以免密码混入数据。
运行长期 SSH 任务
场景: 备份或迁移的运行时间超过了 Agent 会话时长。连接可能会断开,但你之后仍需要查看任务状态、输出和退出代码。
问题:“这个任务能否在对话结束后继续运行?”
原始 SSH
$ ssh [email protected] 'pg_dump app | gzip > /srv/backups/app.sql.gz'
client_loop: send disconnect: Broken pipe
终端已经消失。你现在必须重新连接、查找进程、检查目标文件,并猜测备份是已完成还是中途停止。
结构化 MCP 结果
ssh_exec({ "profile": "production",
"command": "pg_dump app | gzip > /srv/backups/app.sql.gz",
"detach": true })
{
"commands": [{
"command": "pg_dump app | gzip > /srv/backups/app.sql.gz",
"exit_code": null,
"truncated": false,
"timed_out": false,
"blocked": false,
"blocked_reason": null,
"not_run": false,
"warning": null
}],
"job_id": "mst0f2q1-9ab3c4d5"
}
Agent 获得的优势
| 原始 SSH | 结构化 MCP | 带来的好处 |
|---|---|---|
| 任务绑定到单个 SSH 会话 | 远程任务拥有持久 ID | 可安全断开连接并重启 |
| 重新连接后需要搜索进程和文件 | 状态和退出代码均有明确状态 | 无需猜测任务是否完成 |
| 再次读取输出会重复旧文本 | 输出从字节偏移量处继续 | 长任务可减少 token 用量 |
任务状态保存在远程磁盘上,而非此服务的内存中。ssh_job_status 会区分 running、finished 和 lost;ssh_job_output 会从上次的字节偏移量处继续;ssh_job_kill 会向整个进程组发送信号,而不只是向 Shell 发送信号。
向旧款路由器和 NAS 设备传输文件
场景: 现代 OpenSSH 客户端尝试使用 SFTP,但路由器或 NAS 只支持传统的 scp 协议。文件仍必须完整送达,并安全替换目标文件。
问题:“这台旧设备还能接收经过验证的文件吗?”
原始 SSH
$ scp app.conf operator@router:/etc/app.conf
subsystem request failed on channel 0
scp: Connection closed
通常下一步是想起旧版协议标志、重试复制,然后单独运行哈希命令——前提是设备上有哈希工具。
结构化 MCP 结果
ssh_upload({ "profile": "router", "local_path": "./app.conf",
"remote_path": "/etc/app.conf", "sudo": true,
"mode": "644", "owner": "root:root", "verify": true })
{
"files": [{
"path": "/etc/app.conf",
"written": true,
"verified": "verified",
"reason": null,
"bytes": 1284
}]
}
Agent 获得的能力
| 原始 SSH | 结构化 MCP | 带来的好处 |
|---|---|---|
| 现代 SFTP 模式会在首次出错时停止 | 自动回退到传统 scp,并记住这一选择 | 旧设备仍可正常工作 |
| 复制成功并不能证明文件完整 | SHA-256 校验会给出明确结果 | 不会把损坏误当成成功 |
| 直接替换可能留下不完整的目标文件 | 传输完成后会将临时文件移至目标位置 | 中断时原有文件仍然完好 |
如果设备既没有 sha256sum,也没有 openssl,结果会标明 unavailable,并说明原因,而不是报告错误的匹配结果。整个目录使用 recursive: true,并会批量校验哈希值。
面向 AI Agent 的破坏性命令防护
该防护机制会在本地运行,在命令到达 SSH 之前拦截。它会区分可恢复的操作和会摧毁数据所在容器的操作,并检查命令链和批处理中的操作顺序。
在破坏性命令链执行前将其拦截
安全的备份并替换顺序:
cp -r /srv/app /srv/app.bak && mv /srv/app /srv/app-old && rm -rf /srv/app
相同操作,但顺序错误:
rm -rf /srv/app && cp -r /srv/app /srv/app.bak && mv /srv/app /srv/app-old
# REFUSED before the first command runs
Shell 会先删除目录,然后才发现备份源已不存在。防护机制会发现后续步骤读取了先前步骤已销毁的目标,因此整个调用都会留在你的计算机上。同样的检查也能捕获
dropdb app && pg_dump app > backup.sql。
拒绝不可逆的数据丢失,警告可恢复的更改
| 拒绝执行 — 容器本身 | 仅发出警告 — 容器内容 |
|---|---|
DROP DATABASE、dropdb | DROP TABLE、TRUNCATE、DELETE FROM |
docker volume rm、docker compose down -v | docker rm -f <name> |
crontab -r | 编辑单个任务 |
mkfs、wipefs -a、lvremove、zfs destroy | chmod 777 |
reboot、shutdown、halt | git reset --hard |
拒绝执行 docker compose down -v,因为 -v 会移除具名 Docker 卷,其中可能包括数据库卷。不带 -v 时,停止服务不会被视为同样的不可逆操作。
递归删除文件系统根目录、主目录或 /etc、/var 和 /usr 等系统目录也会被拒绝,即使路径经由符号链接指向这些位置也不例外。无法解析的目标(例如 rm -rf "$DIR"/*)同样会被拒绝:“无法检查”不等于“安全”。
明确指出要停止的对象
命令若通过查找而不是明确指定目标,不会被发送。服务端会展开目标,并说明该目标实际指向什么:
docker kill $(docker ps -q --filter ancestor=web)
# BLOCKED — would stop:
# edge — web:latest, Up 34 days, 0.0.0.0:8443->8443/tcp
对于进程,回复还会给出表明其正在使用的信号:运行时长、监听连接的端口,以及当前承载的连接数。明确指定目标无需额外操作,可直接静默执行——docker kill web-1、kill 4871、systemctl stop app。
若要继续,请明确指出要停止的对象。系统会将名称与命令实际匹配到的对象进行核对,因此,如果匹配模式已偏移并匹配到其他对象,系统会拒绝执行,而不是要求确认:
docker kill $(docker ps -q --filter ancestor=web) # CONFIRMED-KILL: edge
匹配命令行的模式需要单独处理。它会匹配包含该模式的命令本身,因此 Shell 会在目标之前收到信号,回复也会中途截断。此类操作不会要求确认,而是改写为按编号指定,或将一个字符写成字符类,以免模式匹配到自身:
pkill -f relay
# BLOCKED — two ways through:
# kill 4871
# pkill -f '[r]elay' # CONFIRMED-KILL: 4871
三种结果会分别处理:找到目标、展开后没有匹配结果,以及没有可供查询的内容——机器上没有对应引擎、回复被截断或连接失败。后两种情况也会被拒绝:不了解情况并不是继续执行的理由。
确认有意执行的破坏性命令
没有任何操作会被永久禁止。将 # CONFIRMED-DESTRUCTIVE 添加到经过审核的命令中,即可允许执行。如果守卫在批次中拒绝一项操作,整个批次会在执行前停止,因此服务不会因操作只执行了一半而处于不完整状态。
守卫仅在单次调用中生效。它无法将一次调用中的删除操作与下一次调用中的读取操作关联起来,也无法推断它不认识的工具。这是安全带,而非策略引擎:是否执行可恢复操作,由你决定。路径限制和引号规则见 docs/security.md。
工具
用于服务器操作的 18 个 SSH MCP 工具。完整参数和示例见 docs/tools.md。
| 工具 | 功能 |
|---|---|
ssh_exec | 执行单条命令或批量命令,带破坏性命令守卫和可选的分离运行功能 |
ssh_file_read | 读取一个或多个文本或二进制文件 |
ssh_file_write | 写入文件,支持原子重命名和可选的 SHA-256 校验 |
ssh_file_list | 列出目录,支持可选的 glob 模式和递归 |
ssh_upload | 通过 SSH 上传文件或目录,支持二进制安全传输和完整性校验;目录会替换目标目录或与其合并 |
ssh_download | 通过 SSH 下载文件或目录,支持二进制安全传输和完整性校验 |
ssh_job_status | 查看后台任务状态:运行中、已完成或丢失 |
ssh_job_output | 从指定字节偏移处读取累计输出 |
ssh_job_list | 列出任务,并清理超过 TTL 的已完成任务 |
ssh_job_kill | 向任务的整个进程组发送信号 |
ssh_log_tail | 查看一个或多个日志的最后 N 行,支持 glob;也可按名称指定容器 |
ssh_log_search | 在日志中搜索模式,或搜索容器日志 |
ssh_snapshot | 一次性健康快照:服务、资源、Docker、网络、错误 |
ssh_monitor | 传输控制:统计信息、重新加载、测试、列出、关闭 |
ssh_audit_baseline | 系统、磁盘、内存、网络、ssh、服务、Docker、防火墙、更新 |
ssh_tls_check | 检查域名的证书到期时间、SAN、证书链和续期钩子 |
ssh_disk_breakdown | 查看磁盘空间去向:du 占用排名前 N 项、Docker、journald、缓存 |
ssh_service_status | 查看 systemctl status,并显示指定单元的 journalctl 尾部日志 |
MCP 工具安全注解
标准 MCP 注解会告知客户端哪些工具是只读、具有破坏性、幂等或开放世界的。请参阅完整表格。
运行 SSH 命令并管理远程文件
执行命令、读写文件、列出目录——机器上的日常操作,且每项操作的结果都已解析。
监控长时间运行的 SSH 任务
耗时操作会分离运行并持续跟踪,而非等待完成:每次查看都会说明任务进展。
搜索日志并检查服务器健康状况
查看文件和容器的日志,以及机器的一次性状态概览;输出设有上限,避免尾部日志占满上下文窗口。
通过 SSH 上传和下载文件
支持二进制安全传输和完整性校验。详情见 docs/transfer.md。
二进制文件和大文件请使用
ssh_upload/ssh_download——base64 分块和 heredoc 不具备二进制安全性,也不支持原子操作。
通过 SSH 审计 Linux 服务器
只读操作,并合并为一次往返。详情见 docs/audit.md。
Windows SSH 兼容模式
Windows 会自动使用兼容模式。无法使用连接复用时,服务会切换为每条命令使用一个连接。基于密钥的 SSH 仍可使用相同工具,无需单独设置或 Windows 专用实现。
破坏性命令守卫详见面向 AI Agent 的破坏性命令防护。
设置 SSH MCP 服务
先按照30 秒内完成安装运行软件包,然后创建配置文件。
创建 SSH 连接配置
放在任意位置即可——通常会放在 Agent 自己的配置文件旁边。以下示例使用 ~/.claude/ssh-profiles.json;其他 Agent 则替换目录(~/.codex/、~/.qwen/、~/.config/opencode/):
{
"profiles": {
"production": {
"host": "server.example.com",
"username": "admin",
"port": 22,
"privateKeyPath": "~/.ssh/your_private_key"
}
}
}
显式选择 SSH 配置
服务不会默认回退到任何配置:每个配置都对应一台不同的机器,而发送到错误机器的命令,事后无法通过错误消息挽回。若不指定名称,服务会列出可供选择的名称:
ssh_exec({ command: "uptime" })
→ No profile specified. Name one explicitly: production
服务无法通过 SSH 使用的配置——缺少 host、缺少 username 或 mode: "local"——会被静默跳过;服务无法识别的字段也会保留原样,因此该文件可以与其他工具共享。如果配置中有无效字段,则会列出配置名称、字段和字段值,而其他正常配置仍可使用。
每个配置都可以选择添加 pathSecurity 块,用于将文件工具可操作的路径列入允许列表或拒绝列表——参见 docs/security.md。
如果配置使用密钥登录,但需要在远端使用 sudo,则应设置 sudoPassword——即用于响应 sudo 提示的密码。在许多机器上,这个密码并非登录密码。请将其保存在机密文件中,而不是此处。
不要在配置中保存 SSH 密码和密码短语
优先使用密钥。如果无法避免使用密码或加密密钥密码短语,请将其保存在单独的机密文件中,绝不要放在配置本身中:
{
"secretsFile": "~/.config/ssh-mcp/secrets.json",
"profiles": {
"production": {
"host": "server.example.com",
"username": "admin"
}
}
}
机密文件以配置名称为键——参见 secrets.json.example:
{
"production": { "password": "..." },
"buildbox": { "sudoPassword": "..." }
}
sudoPassword 是在该机器上用于响应 sudo 提示的密码。使用密钥登录的配置没有可提供的登录密码;如果两者不同,登录密码就不适用。未设置 sudoPassword 时,将使用 password。
机密文件必须仅对你本人可读(chmod 600)。相对路径以配置文件所在目录为基准解析;机密不会出现在 argv 中,并且会在日志中脱敏。参见凭据安全。
配置 Claude Code、Codex 和其他 MCP 客户端
选择你使用的客户端,并让它指向同一个配置文件。
Claude Code
只需一条命令;-s user 会让所有项目都能使用该服务:
claude mcp add ssh -s user \
-e SSH_PROFILES_FILE="$HOME/.claude/ssh-profiles.json" \
-- npx -y @hypnosis/ssh-mcp-server
Codex CLI
codex mcp add ssh \
--env SSH_PROFILES_FILE="$HOME/.codex/ssh-profiles.json" \
-- npx -y @hypnosis/ssh-mcp-server
opencode
将其放入 ~/.config/opencode/opencode.json:
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"ssh": {
"type": "local",
"command": ["npx", "-y", "@hypnosis/ssh-mcp-server"],
"enabled": true,
"environment": {
"SSH_PROFILES_FILE": "~/.config/opencode/ssh-profiles.json"
}
}
}
}
Qwen Code
只需一条命令,与其他客户端相同:
qwen mcp add ssh \
-e SSH_PROFILES_FILE="$HOME/.qwen/ssh-profiles.json" \
npx -y @hypnosis/ssh-mcp-server
其他 MCP 客户端
Gemini CLI、Hermes、Cline、编辑器插件或你自己的 Agent,配置方式都相同。它们只需要一条要运行的命令和一个环境变量。
重启 MCP 客户端
重启客户端,然后运行 ssh_monitor({ action: "list" }),确认配置已加载。
SSH MCP 服务配置
| 变量 | 作用 | 默认值 |
|---|---|---|
SSH_PROFILES_FILE | 配置 JSON 文件的路径——必填 | — |
SSH_MCP_LOG_LEVEL | debug、info、warn、error | info |
LOG_LEVEL | 回退选项,仅在未设置 SSH_MCP_LOG_LEVEL 时使用 | info |
SSH_MCP_LOG_TIMESTAMP | 在日志行中显示时间戳 | true |
SSH_MCP_CONTROL_PERSIST | 最后一条命令结束后,共享连接继续保持的秒数;0 表示立即关闭 | 600 |
SSH_MCP_CONTROL_DIR | 控制套接字的存放位置 | ~/.ssh/ssh-mcp |
SSH_MCP_PROFILES_CACHE_TTL | 配置缓存 TTL,单位为毫秒 | 60000 |
SSH_MCP_PROFILES_WATCH | 配置文件发生变化时重新加载 | true |
共享连接会有意保持运行,不随此进程退出而关闭:若在进程退出时关闭连接,会切断同一台机器上另一个窗口正在使用的通道。
SSH MCP 服务的限制
每项限制都会告诉你如何绕过它。 如果某个工具无法执行某项操作,它会明确说明,并指出 ssh_exec,后者可直接在机器上运行命令——例如,不受支持的日志驱动程序、机器上没有的实用工具,或此服务不支持的引擎。你不必事先知道工具的能力边界:在需要时,拒绝信息会明确告知。
有三种拒绝会刻意不提 Shell,因为在这些情况下,Shell 并不是正确的解决办法:个人配置文件禁止访问的路径(违反自己的规则并不能解决问题)、格式错误的调用(应修正调用),以及 ssh_exec 自身的拒绝。
- 取消: 现在,取消调用也会停止服务端上的命令,通过同一连接发送第二次调用。如果服务端没有
/proc,则会改用ps查找命令。FreeBSD 尚未验证,无法保证行为正确。文件传输和ssh_snapshot不支持取消。 - 原子写入: BSD 和 macOS 无法预先检查跨文件系统重命名。
SSH MCP 服务路线图
-
针对 macOS SSH 主机进行完整测试
-
在 Windows 上进行端到端兼容性测试
-
多主机审计——在一次调用中比较多个 SSH 配置的运行状况
-
从现有的
~/.ssh/config导入配置 -
支持大文件和不稳定连接的可续传
-
远程操作时间线——在一份审计记录中呈现命令、传输和防护决策
-
现成的 SSH 故障排查手册
-
无需切换到 Shell 即可查看容器日志— 已完成:ssh_log_tail和ssh_log_search接收容器名称,询问 docker 日志写入位置,并使用与其他日志相同的机制读取文件 -
拒绝后让你束手无策— 已完成: 每项限制现在都会指出可用ssh_exec来解决,因此触及工具边界时,只需一句说明,而不必猜测 -
让模型获取答案— 已完成: 命令输出、匹配的日志行、机器名称和快照章节会通过字段传递,而不只是出现在文本中 -
更精简的 MCP 工具架构— 已完成: 工具列表精简了 10%,分离运行的作业现在会显示其写入的最后几行,而不是让人盲目轮询 -
以 root 身份执行长时间任务— 已完成: 分离运行的作业可通过sudo运行,并以 root 身份跟踪;仅使用密钥的配置会通过自身的sudoPassword响应sudo
开发和测试 SSH MCP 服务
npm install
npm run build # tsc
npx tsc --noEmit # types, plus dead declarations
npm run test:unit # unit tests
npm run lab:up # start the two test containers
npm run test:live # live suite against those containers
实时测试套件会在真实容器上运行——一个 BusyBox 容器和一个 coreutils 容器——因为两者的行为存在不易察觉的差异,而 Mock 会迎合编写它的人。布局请参阅 docs/architecture.md。
喜欢 SSH MCP Server 吗?⭐
如果你喜欢这个工具,请在 GitHub 上点个星,帮助更多人发现这个项目。
为 SSH MCP 服务做贡献
欢迎在 github.com/hypnosis/ssh-mcp-server 提交问题和拉取请求。
许可证
MIT——参见 LICENSE。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。