😾 每天的日常
AI:“我已经帮你修改好代码了喵。”
我:“好,那你去远程服务器跑一下实验。”
AI:“🤖……”
我:“?”
然后我自己打开终端:
ssh xxx@server
cd project
git pull
conda activate xxx
python train.py
不是,这 SSH 怎么还得我自己连喵???
于是最近折腾了一个东西:handfree-ssh-mcp。
🧬 项目来源
最开始基于 classfang/ssh-mcp-server 折腾,之后持续补了 SSH 连接管理、后台任务、文件传输、Remote Workspace / Run、安全策略等,慢慢变成现在这个样子。
它已经不只是“让 Agent 执行一条 SSH 命令”,而是一个给 Agent 用的远程服务器工作台喵。
✨ 能力一览
| 能力 | 说明 | |
|---|---|---|
| 🔌 | Connect | 复用~/.ssh/config,支持 Jump Host 链、SOCKS、keepalive、配置热加载、连接池 |
| ⚡ | Execute | 按服务器配置黑名单 / 白名单策略,后台流式执行 + 轮询,完整输出落盘 |
| 📦 | Transfer | 多连接上传 / 下载 / 中继,A→B 直连拷贝,批量、递归、归档、原子替换 |
| 🚀 | Run | workspace-run:推代码 → 运行 → 收集 artifacts,状态存在远端,取消 / 重试都安全 |
| 🔄 | Sync (规划中) | 单向 / 双向同步,每次 run 前有 flush barrier |
| 🖥️ | Web UI (开发中) | 与 MCP 并行的 run / 传输 / 服务器面板 |
🧩 架构
flowchart LR
agent["AI Agent<br/>Claude Code · Cursor · …"] -- "MCP (stdio)" --> hf
ui["Web UI<br/>(开发中)"]:::planned -.-> hf
hf["<b>Handfree</b><br/>策略 · 执行 · 传输连接池<br/>workspace-run · sync (规划中)"]
hf -- SSH / SFTP --> a[("Server A")]
hf -- SSH / SFTP --> b[("Server B")]
a -. "直连拷贝" .-> b
classDef planned stroke-dasharray: 5 5
🐱 核心特性
后台任务
Agent 启动一个长时间运行的命令,拿 runId 随时查询状态和新增输出。像这样的事情可以直接交给它:
“帮我在 GPU 服务器上跑这个实验,跑完之后告诉我结果。”
文件传输
- 上传、下载、目录递归、批量、归档、跳过相同文件
- 单个文件可通过
connections: N走 N 条独立 SSH 连接,突破单连接流控窗口的限制 - 服务器 A → 服务器 B 尽量走远端直连,数据不必绕回 MCP 主机
Remote Workspace / Run
Agent 可以在远程机器上启动一个完整的 run,然后查询状态、读日志、拿 artifacts、取消或重试。状态本身存在服务器上,MCP 进程重启后也能继续查询之前的 run。
安全策略
凭据不会到达模型:Agent 只说“哪台服务器”,密钥由 Handfree 持有。
默认是黑名单模式:内置规则会拦截关机、递归强删等明显危险的命令,其余放行。生产机器请设置 commandMode: whitelist。
- SFTP 传输可限制在
allowedRemoteDirectories/allowedLocalDirectories内 - 服务器间直连拷贝不会传递私钥,也不会自动接受未知 host key
run-cancel发信号前会再次校验进程身份(boot id、pid、启动时间、wrapper token)
📈 性能
单条 SSH 连接受通道流控窗口限制,高延迟链路上跑不满带宽。多连接传输同一个文件的下载吞吐(tc netem 实验链路实测):
| 连接数 | 吞吐 (MiB/s) | 重复调用(连接池已预热) |
|---|---|---|
| 1 | 17.5 | — |
| 2 | 28.5 | 35.2 |
| 4 | 45.6 | 63.2 |
| 8 | 53.8 | 82.8 |
实际收益取决于你自己链路的 RTT 和带宽,快速局域网下没有提升。
🚀 快速开始
1. 先确保 ~/.ssh/config 里有主机,不需要别的:
Host dev
HostName 192.168.1.100
User root
IdentityFile ~/.ssh/id_ed25519
2. 把 Handfree 加到 MCP 客户端:
<details open>
<summary><b>Claude Code</b></summary>
claude mcp add ssh -- npx -y @aaarc/handfree-ssh-mcp --enable-servers dev
</details>
<details>
<summary><b>Claude Desktop / Cursor 等 JSON 配置的客户端</b></summary>
{
"mcpServers": {
"ssh": {
"command": "npx",
"args": ["-y", "@aaarc/handfree-ssh-mcp", "--enable-servers", "dev"]
}
}
}
</details>
3. (可选)用 --config servers.yaml 加策略:
servers:
prod:
commandMode: whitelist
whitelist: ["^ls.*$", "^cat .*$", "^tail .*$"]
allowedRemoteDirectories: [/srv/app, /tmp]
然后就可以让 Agent 去 dev 看日志、部署到 prod、或者在 GPU 机器上训模型了喵。
🧰 Agent 拿到的工具
| 类别 | 工具 |
|---|---|
| Discover | list-servers、show-whitelist、help |
| Execute | execute-command(后台流式,用 command-status 轮询)、close-connection |
| Transfer | upload、download、transfer(上传 / 下载 / 中继,递归、批量、归档) |
| Run | workspace-run、run-status、run-logs、run-list、run-cancel、run-retry |
🗺️ Roadmap
| 状态 | 项目 |
|---|---|
| ✅ | 命令策略、流式执行、完整输出日志 |
| ✅ | 多连接传输 + 按服务器的连接池 |
| ✅ | 服务器间直连传输(strategy: direct / auto) |
| ✅ | workspace-run:推送、运行、收集、取消、重试 |
| 🚧 | Web UI:run / 传输 / 服务器面板 |
| 🗓️ | Sync 引擎:单向 / 双向,run barrier |
| 🗓️ | SFTP 传输断线重连重试 |
| 🗓️ | Conda / module / Slurm 运行环境、GPU 检查 |
🙌 最后
目前还有很多地方可以继续完善。如果你也是 AI Agent + SSH + 多台服务器 这个组合的受害者,欢迎来试试;发现奇怪的地方,直接提 Issue / PR。
毕竟……
能让 AI 自己 SSH,就不要让我 SSH 喵。 🐱
好东西,bd
没绷住,又重复造轮子了:https://www.nodeseek.com/post-967184-1