关于 remote-ssh-mcp
remote-ssh-mcp 将持久化的远程 Bash 会话封装为 MCP 工具,面向需要通过 SSH 执行多步远程任务的 AI Agent。会话保留目录和环境状态,并使用本机 OpenSSH 配置连接主机。
主要功能
- 01持久会话保留目录与环境
- 02通过主机别名发现并连接
- 03轮询长时间运行的命令
- 04中断命令并管理会话
- 05输出截断与审计日志
适用场景
- 01在远程主机上执行多步调试
- 02运行构建、部署或下载任务
- 03查看命令输出并恢复卡住的会话
MCP 相关
通过 stdio 向 MCP 客户端提供 SSH 主机发现、会话打开、命令执行、输出查看、中断和关闭等工具。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
7收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| reload | boolean | 否 |
详细介绍
Remote SSH MCP
为 AI Agent 提供持久、有状态的远程 Bash 会话
基于 Model Context Protocol
为什么选择 · 功能 · 工作原理 · 工具 · 快速开始 · 配置 · 安全性 · 简体中文
为什么选择 Remote SSH MCP?
大多数 Agent 访问远程机器的方式如下:
bash → ssh host "cmd" → disconnect → repeat
每次调用都要付出同样的代价:
| 痛点 | 发生的情况 |
|---|---|
| 🔁 浪费 Token | Banner、MOTD、登录杂音以及 pwd / whoami 探测会塞满上下文 |
| 🧊 状态丢失 | cwd、export、venv 激活状态及 Shell 副作用都会消失 |
| 🔌 不稳定 | 每次新建连接都可能遇到超时、主机密钥提示、ProxyJump 和认证抖动 |
| 🌀 错误循环 | 模型用更长的探测命令来补救 → 消耗更多 Token |
Remote SSH MCP 将长期运行的远程 Bash 会话变为一流的 MCP 工具。一个会话 ID 可保留工作目录、环境变量和 Shell 副作用。需要干净的环境时,可以开启新会话。
为什么选择它,而不是在 bash 中一次性执行 ssh?
面向 Agent 工作流(多步骤远程操作:部署、调试、构建、检查日志)的实际优势:
| 维度 | 一次性 ssh host "…" | Remote SSH MCP |
|---|---|---|
| 💰 Token 消耗 | 每一步都要重新支付连接开销并探测状态;模型经常重新执行 cd / pwd | 在 ssh_open 时只支付一次;之后 ssh_run 主要返回命令输出。结构化工具和 head/tail 上限可减少工具结果冗余。在多步骤会话中,与每次都重新连接相比,远程工具上下文通常可减少约 50–80%(具体节省取决于 MOTD 大小以及模型输出的冗长程度)。 |
| ✅ 成功率 | N 个步骤 ≈ N 次握手 → N 次失败机会(超时、跳板、Agent、主机密钥) | 每个会话只需一次握手;后续命令通过活动 shell 执行。长时间运行的任务使用 running + ssh_peek,而不是终止工具调用后重新启动。减少重新连接 → 任务中途出现“SSH 失败”循环的情况大幅减少。 |
| 🧳 可移植性 | 远程端无需额外安装任何东西,但每台 Agent 机器都要重新实现同一套脆弱的 ssh … 模式 | 只需在运行 Claude / Cursor / Grok / 等的机器上安装一次。远程主机无需安装任何东西(无需 Node、MCP 守护进程或 Agent)。只需要一个普通 shell 账户,以及 SSH 所需的工具(bash、base64、stty、…)。密钥和跳板主机保留在本地 ~/.ssh/config 中。 |
| 🧠 模型易用性 | 模型需要自行拼接 ssh 字符串、处理转义和故障恢复 | 稳定的工具:open → run → peek → close。会话 ID 是唯一句柄。 |
| 🔐 信任边界 | 容易过度暴露密钥,或在带外提示输入密码 | 仅使用 OpenSSH 客户端;工具从不接受密码或私钥内容 |
一句话说明可移植性:在你的开发机 / AI 主机上部署 MCP;SSH 配置中的所有服务器都可以访问——远程服务器群无需安装任何软件包。
┌─────────────────────────┐ SSH (OpenSSH) ┌──────────────────┐
│ Your laptop / CI agent │ ───────────────────────────► │ prod / staging │
│ Claude · Cursor · Grok │ ~/.ssh/config · agent │ no MCP install │
│ + remote-ssh-mcp │ │ plain Bash OK │
└─────────────────────────┘ └──────────────────┘
Token 消耗示意(多步骤远程调试示例):
One-shot path (per step × 8):
ssh wrapper + banner/MOTD + pwd/whoami + re-cd + command output
→ noise dominates; context fills with reconnect junk
Session path:
ssh_open → once (handshake + READY)
ssh_run × 8 → mostly the real stdout/stderr (truncated head+tail)
→ context stays on the work product, not the transport
它不会重新实现 SSH。系统 OpenSSH 客户端仍负责处理所有工作,因此 ~/.ssh/config、已知主机、SSH Agent、ProxyJump 路由和硬件密钥都能像以前一样正常工作。
ssh_hosts() → discover allowed Host aliases
ssh_open(host) → session id
ssh_run(id, command) → same cwd + env as last time
ssh_peek / ssh_interrupt → observe or recover long / stuck work
ssh_close(id) → release the shell and connection
功能
🧠 持久化远程会话
- 一个稳定的会话 ID对应一个长期运行的远程 Bash
cwd和环境变量会在多次ssh_run调用之间保留- 需要全新环境时,可打开一个新会话
- 多个会话可以连接到同一主机或不同主机(最多
maxSessions个)
🔧 原生 OpenSSH 集成
- 启动真正的
ssh二进制程序——不使用自定义加密栈 - 遵循
~/.ssh/config、Include、Agent 套接字和 ProxyJump 配置 - 强制使用
BatchMode=yes和StrictHostKeyChecking=yes - 从不接受模型提供的密码、私钥文本或任意 SSH 选项参数
📡 适用于长时间运行的命令
ssh_run最多等待wait_sec(默认 10s),然后返回status: "running",同时远程命令继续运行- 使用
ssh_peek(wait_sec=...)长轮询,而不是忙等循环 - 可选硬性
timeout_sec,超时后发送 Ctrl-C;默认不会自动终止 - 适用于
docker pull、构建、下载和部署等不应无限期阻塞工具调用的任务
🛡️ 安全与控制平面
- 基于
ssh_config的精确主机别名允许列表,并支持可选的配置 / 环境变量覆盖 - 忽略包含
*、?或!的模式 - 故障时关闭中断:如果 Ctrl-C 后无法确认 shell 已恢复,则关闭会话
- 内置拒绝列表,拦截少数明显具有破坏性的模式(并非完整的策略引擎)
- 空闲会话清理、会话数量上限,以及带有哈希命令的 JSONL 审计日志(
0600)
📦 为模型清理工具结果
- 分离
stdout/stderr流 - 按字节截取输出的开头和结尾,并确保截取位置符合 UTF-8 边界
- 在模型看到输出前剥除 ANSI / PTY 噪声(颜色、CSI、括号粘贴标记、仅含控制字符的空行)
- 精简的打开帧:
TERM=dumb、NO_COLOR、关闭括号粘贴 — 从源头减少杂项 - 精简 JSON 负载:省略空的
stderr、值为false的截断标记和请求回显字段,让同时包含content+structuredContent的响应保持精简 ssh_hosts仅返回安全元数据:alias、hostname、user、port、proxy_jump- 绝不泄露
IdentityFile、证书、Agent 套接字或ProxyCommand
🔌 MCP 原生支持
- 为 Claude Desktop、Cursor 和其他 MCP 宿主提供 stdio 传输
- 兼容旧版和当前 MCP 握手
- 父进程 / stdio 退出时会关闭所有已跟踪的 SSH 连接
工作原理
flowchart LR
A[AI Agent] -->|MCP tools| B[Remote SSH MCP]
B -->|spawn| C[OpenSSH client]
C -->|SSH + PTY| D[Remote Bash]
D --> E[(cwd / env / side effects)]
subgraph Local machine
B
C
F[~/.ssh/config<br/>agent / keys]
C -.-> F
end
subgraph Remote host
D
E
end
典型 Agent 工作流
1. ssh_hosts() # pick an alias from the allowlist
2. ssh_open(host="prod") # get session id "s_…"
3. ssh_run(id, "cd app && …") # state sticks to this id
4. ssh_run(id, "npm test") # still in app/, env preserved
5. ssh_peek(id, wait_sec=20) # long-poll a slow job
6. ssh_close(id) # clean up when done
MCP 工具
| 工具 | 功能 |
|---|---|
🗂️ ssh_hosts | 列出允许使用的 Host 别名(仅含安全元数据)。编辑 ~/.ssh/config 后传入 reload=true |
🔓 ssh_open | 为允许使用的 Host 别名打开一个干净的持久 Shell → 返回会话 id |
▶️ ssh_run | 在现有会话中运行非交互式命令 |
👀 ssh_peek | 查看最新的 N 行输出及状态;运行期间可选用 wait_sec 长轮询 |
⛔ ssh_interrupt | 发送 Ctrl-C 并等待确认 Shell 已恢复 |
📋 ssh_list | 列出会话、当前工作目录、状态、空闲倒计时和容量 |
🔒 ssh_close | 清理远程临时状态并关闭连接 |
工具参数(必需项)
| 工具 | 关键参数 |
|---|---|
ssh_open | host(必需的 Host 别名),可选的 name 标签 |
ssh_run | id、command,可选的 wait_sec、可选的 timeout_sec |
ssh_peek | id、可选的 lines(默认值为 50,最大值为 1000)、可选的 wait_sec |
ssh_interrupt / ssh_close | id |
ssh_hosts | 可选的布尔值 reload |
快速开始
要求
| 要求 | 说明 |
|---|---|
| Node.js | 20 或更高版本 |
| OpenSSH 客户端 | PATH 中的系统 ssh(或设置 sshPath) |
| 远程主机 | Bash + base64、stty、mkdir、cat、rm |
| SSH 设置 | ~/.ssh/config 中的 Host 别名,且主机密钥已受信任 |
⚠️ 首次主机密钥确认和身份验证必须在普通终端中完成。MCP 服务绝不会显示密码或信任提示。
安装
git clone https://github.com/the-nine-nation/remote-ssh-mcp.git
cd remote-ssh-mcp
npm install
npm run build
npm test
运行服务:
node /absolute/path/to/remote-ssh-mcp/dist/index.js
或从 npm 安装(发布后):
npx @zyluo/remote-ssh-mcp
# or
npm install -g @zyluo/remote-ssh-mcp
remote-ssh-mcp
或者,从此仓库本地安装软件包后,使用 remote-ssh-mcp 可执行文件。
MCP 宿主配置
大多数 stdio 宿主接受如下格式(外层键可能因产品而异):
{
"mcpServers": {
"remote-ssh": {
"command": "node",
"args": [
"/absolute/path/to/remote-ssh-mcp/dist/index.js"
],
"env": {
"SSH_MCP_ALLOWED_HOSTS": "prod,staging"
}
}
}
}
Cursor · Claude Desktop · Claude Code · 其他支持 MCP 的宿主:将 command / args 指向构建后的 dist/index.js,并设置 SSH_MCP_ALLOWED_HOSTS(或依靠从 ~/.ssh/config 自动发现)。
SSH_MCP_ALLOWED_HOSTS 会向允许列表添加别名。默认情况下,服务还会从 ~/.ssh/config 及其 Include 文件中发现精确的 Host 条目。工具输入仅接受安全别名 — 不接受 user@host、端口或额外的 SSH 选项。
编辑 ~/.ssh/config 后,请调用 ssh_hosts(reload=true),而不是重启服务。
凭据边界
身份验证始终由本地 OpenSSH 客户端处理:
- 工具不接受密码或私钥材料
ssh_hosts绝不返回密钥路径、证书、Agent 套接字或ProxyCommand- Agent 应使用 Host 别名调用
ssh_open,不得从磁盘读取~/.ssh中的私钥
配置
可选配置文件(默认路径):
~/.config/remote-ssh-mcp/config.json
{
"allowedHosts": ["prod", "staging"],
"sshConfigPath": "~/.ssh/config",
"sshPath": "ssh",
"maxTimeoutSec": 1800,
"defaultWaitSec": 10,
"maxWaitSec": 30,
"openTimeoutSec": 20,
"idleTimeoutSec": 1800,
"interruptGraceSec": 5,
"maxSessions": 8,
"outputMaxBytes": 32768,
"outputHeadBytes": 4096,
"auditLogPath": "~/.local/state/remote-ssh-mcp/audit.jsonl"
}
环境变量
| 变量 | 用途 |
|---|---|
SSH_MCP_CONFIG | 配置文件路径 |
SSH_MCP_ALLOWED_HOSTS | 以逗号分隔的附加 Host 别名 |
SSH_MCP_SSH_CONFIG | SSH 配置路径 |
SSH_MCP_SSH_PATH | OpenSSH 可执行文件 |
SSH_MCP_MAX_TIMEOUT_SEC | 显式命令超时上限 |
SSH_MCP_DEFAULT_WAIT_SEC | ssh_run 在返回 running 前等待的时长 |
SSH_MCP_MAX_WAIT_SEC | ssh_run / ssh_peek 的 wait_sec 上限 |
SSH_MCP_OPEN_TIMEOUT_SEC | 连接 / 握手超时 |
SSH_MCP_IDLE_TIMEOUT_SEC | 空闲会话的存续时间 |
SSH_MCP_INTERRUPT_GRACE_SEC | 按下 Ctrl-C 后用于标记恢复的宽限时间 |
SSH_MCP_MAX_SESSIONS | 活跃会话数上限 |
SSH_MCP_OUTPUT_MAX_BYTES | 每个流保留的输出上限 |
SSH_MCP_OUTPUT_HEAD_BYTES | 截断时保留的开头字节数 |
SSH_MCP_AUDIT_LOG | JSONL 审计日志路径 |
环境变量会覆盖文件中的设置。审计日志以 0600 权限创建,并记录会话、主机、结果、持续时间、命令长度、命令名称和 SHA-256——不记录完整参数字符串(以减少密钥泄露)。
执行语义
Agent(以及你)应该了解的详细规则:
| 主题 | 行为 |
|---|---|
| 并发 | 一个会话一次只运行一条前台命令;额外调用 ssh_run 会返回 busy |
wait_sec | 仅限制 MCP 调用的等待时长。超时后:status: "running",远程任务会继续运行 |
| 不要重试 | 返回 running 后,切勿重新发起同一条长时间运行的命令——使用 ssh_peek 轮询 |
| 硬超时 | 只有显式指定 timeout_sec 才会创建截止时间并发送 Ctrl-C |
ssh_peek | 默认返回最后 50 行(最多 1000 行);仍受字节数上限限制;可选长轮询 wait_sec |
| stdin | 用户命令的输入为 /dev/null——不要运行 vim、top 或交互式安装程序 |
| 中断恢复 | 按下 Ctrl-C 后等待协议标记恢复;若恢复失败 → 关闭会话(失败时关闭) |
| 输出 | stdout / stderr 分别保留开头和结尾;始终保持有效的 UTF-8 边界 |
| 拒绝列表 | 仅拦截少数高风险模式——并非完整的策略引擎 |
| 信任模型 | 本地可信的开发者工具——不是多租户远程执行服务 |
| 主机退出 | MCP 主机 / stdio 退出会关闭所有 SSH 连接;nohup / setsid 启动的任务可能仍会运行 |
示例: 使用 wait_sec: 10 且不指定 timeout_sec 启动 docker pull。返回 running 表示原始拉取任务仍在运行——不要再启动一个。调用 ssh_peek 并设置正数 wait_sec,直到任务变为 idle、中断该任务,或另开一个会话并行处理。
开发
npm run typecheck
npm test
npm run build
npm audit --omit=dev
测试套件涵盖 MCP stdio 发现与调用、持久化 cwd / 环境状态、流分离、任意分块边界下的帧处理、超时失败时关闭、Shell 退出、允许列表发现、输出截断以及安全拒绝列表。
设计说明和线缆协议:远程SSH-MCP设计.md。
安全
请不要通过公开的 GitHub issues 报告安全漏洞。在配置好私密公告流程之前,请通过维护者 GitHub 个人资料中的邮箱联系维护者。
即使 MCP 传输正常,远程命令也可能产生不可逆的副作用。请使用最小权限账户、将允许列表范围控制得尽可能小,并仔细检查主机权限。
项目状态
| 项目 | 状态 |
|---|---|
| 版本 | 0.2.2 |
| 许可证 | MIT |
| 语言 | TypeScript(Node ≥ 20) |
| 协议 | 基于 stdio 的 MCP |
| 到主机的传输 | 系统 OpenSSH |
更新日志
未发布 — 连接与进度反馈
- 自定义
sshConfigPath/SSH_MCP_SSH_CONFIG现在不仅控制主机发现,也会通过 OpenSSH-F控制实际连接。默认的~/.ssh/config保持原生 OpenSSH 行为;自定义文件遵循-F语义(跳过系统范围的配置)。 - 主机发现现在也支持
Host=prod、HostName = example.com和Include=...语法。 ssh_open失败时,除了原始错误,还会附带reason和可执行的hint,涵盖主机信任、身份验证、DNS、网络、超时和本地配置问题。- 除非短进度输出的后缀可能是不完整的协议标记,否则会立即发送。
- 已完成的
ssh_peek响应包含command_status和duration_ms,将 shell 状态(idle)与命令结果区分开。会话终止时仍在等待的peek会保留已有输出以及超时/中断详情。之后对已移除会话的调用仍会返回session_gone。
0.2.2 — 减少远程输出噪声与令牌消耗
由 PTY 支持的交互式 bash 经常注入转义序列,这些序列在 JSON 转义后看起来像“二进制”内容(\u001b[?2004h、彩色 CSI、光标代码)。这些噪声会在每次 ssh_peek / ssh_run 时消耗上下文。
| 更改 | 功能 |
|---|---|
| 即时清理 | 去除 ANSI/OSC/CSI,处理回车符覆盖(进度条),丢弃仅含控制字符的空行,然后应用 lines 窗口 |
| 静默打开会话 | 导出 TERM=dumb / NO_COLOR / CLICOLOR=0,禁用括号粘贴,并在打开时发送一次 \033[?2004l |
| 精简工具负载 | 丢弃空的 stderr、值为 false 的标志(truncated、interrupted 等)和回显的 lines;保留空的 stdout,以明确表示无输出 |
| 测试 | 覆盖清理、打开帧静默处理、会话即时路径和精简 JSON |
升级:运行 npm i -g @zyluo/[email protected](或在 MCP 配置中升级软件包),然后重启 MCP 进程,以加载新的服务二进制文件。
0.2.1
- 修复打开帧被 PTY 回显时的 READY 标记解析问题
0.2.0
- 在 npm / GitHub 上首次公开发布
Star 历史
如果这个项目帮你节省了令牌并减少了不稳定的重新连接,欢迎在 GitHub 上点亮 ⭐,让更多人发现它。
专为需要真正远程 shell 的 Agent 打造,而不是又一个一次性 ssh host "…"。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。