🚀 SSH 联络 MCP 服务器
SSH 联络 MCP 服务器借助模型上下文协议(MCP)实现有状态的 SSH 连接和命令执行,为用户提供了一种高效、稳定的 SSH 操作方式。
✨ 主要特性
| 特性 |
描述 |
| 🔄 有状态会话 |
核心特性:在 MCP 工具调用之间保留 shell 状态(当前目录、环境变量、工作目录)。每个命令都在同一个持久 shell 会话中运行,支持多步骤工作流。 |
| 🔌 MCP 服务器模式 |
与 Cursor/Claude Desktop 集成,实现由 AI 辅助的有状态命令执行的 SSH 操作。 |
| ⚙️ SSH 配置支持 |
使用 ~/.ssh/config 进行主机别名和连接参数配置。 |
| 🔐 直接连接 |
无需 SSH 配置,直接使用用户、主机名、密码和端口进行连接。 |
| 💻 独立 CLI 模式 |
提供交互式终端,用于调试和测试(详见下文)。 |
📦 安装指南
预构建二进制文件
从 发布页面 下载适用于您平台的最新版本。
可用平台:
- macOS(Intel x86_64、Apple Silicon aarch64)
- Linux(x86_64、aarch64)
- Windows(x86_64)
curl -LO https://github.com/Citizen4our/ssh-liaison-mcp/releases/latest/download/ssh-liaison-mcp-aarch64-apple-darwin.tar.gz
tar -xzf ssh-liaison-mcp-aarch64-apple-darwin.tar.gz
curl -LO https://github.com/Citizen4our/ssh-liaison-mcp/releases/latest/download/ssh-liaison-mcp-x86_64-apple-darwin.tar.gz
tar -xzf ssh-liaison-mcp-x86_64-apple-darwin.tar.gz
curl -LO https://github.com/Citizen4our/ssh-liaison-mcp/releases/latest/download/ssh-liaison-mcp-x86_64-unknown-linux-gnu.tar.gz
tar -xzf ssh-liaison-mcp-x86_64-unknown-linux-gnu.tar.gz
从源代码构建
git clone https://github.com/Citizen4our/ssh-liaison-mcp.git
cd ssh-liaison-mcp
cargo build --release
二进制文件将位于 target/release/ssh-liaison-mcp。
🚀 快速开始
MCP 服务器模式(主要用例)
此服务器的主要特性是 有状态的 SSH 会话 —— 每个 SSH 连接都维护一个持久的 shell 会话,在 MCP 工具调用之间保留状态(当前目录、环境变量等)。这使得命令可以相互依赖,实现自然的多步骤工作流。
有状态会话的工作原理
当您通过 ssh_connect 连接到主机时,会建立一个持久的 shell 会话。该主机的所有后续 ssh_run_command 调用都在 同一个 shell 会话 中执行,这意味着:
- 当前目录得以保留:如果您在一个命令中执行
cd /var/log,下一个命令将从 /var/log 开始执行。
- 环境变量持久化:使用
export 设置的变量在后续命令中仍然可用。
- shell 状态得以维护:历史记录、别名和其他 shell 状态在调用之间保持不变。
- 高效性:无需为每个命令重新连接或重新建立上下文。
示例工作流:
1. ssh_connect("production") → 建立持久 shell 会话
2. ssh_run_command("production", "cd /var/log") → 更改目录
3. ssh_run_command("production", "pwd") → 返回 "/var/log"(状态已保留!)
4. ssh_run_command("production", "ls -la") → 列出 /var/log 中的文件
对于 Cursor IDE
- 构建二进制文件:
cargo build --release
- 添加到 Cursor 设置(
~/.cursor/mcp.json 或 Cursor 设置界面):
{
"mcpServers": {
"ssh-liaison": {
"command": "/absolute/path/to/ssh-liaison-mcp",
"args": ["serve"]
}
}
}
- 重启 Cursor
对于 Claude Desktop
- 构建二进制文件:
cargo build --release
- 添加到 Claude Desktop 配置(macOS 上为
~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"ssh-liaison": {
"command": "/absolute/path/to/ssh-liaison-mcp",
"args": ["serve"]
}
}
}
- 重启 Claude Desktop
旧版直接连接模式
为了向后兼容:
cargo run -- connect <user> <host> [--port <port>]
🔧 技术细节
SSH 配置
服务器从 ~/.ssh/config 读取主机别名。这是推荐的连接方式,因为它集中管理了连接设置。
示例 SSH 配置
# 简单主机别名
Host rpi
HostName 192.168.1.100
User pi
Port 22
IdentityFile ~/.ssh/id_ed25519
# 带有自定义端口的生产服务器
Host production
HostName prod.example.com
User deploy
Port 2222
IdentityFile ~/.ssh/deploy_key
# 开发服务器
Host dev
HostName dev.example.com
User developer
IdentityFile ~/.ssh/id_rsa
MCP 工具
作为 MCP 服务器运行时,可使用以下工具:
| 工具 |
描述 |
参数 |
| ssh_connect |
连接到远程 SSH 服务器并建立一个 持久的 shell 会话。该会话在后续命令调用之间保持状态。 |
host_alias(字符串) - SSH 配置中定义的主机别名 |
| ssh_connect_direct |
直接使用用户、主机名/IP、可选密码和可选端口连接到远程 SSH 服务器。建立一个 持久的 shell 会话,在后续命令调用之间保持状态。身份验证首先尝试 SSH 密钥,然后在提供密码且 SSH 密钥验证失败时使用密码。 |
host_alias(字符串) - 用于标识此连接的主机别名,user(字符串) - SSH 用户名,hostname(字符串) - 主机名或 IP 地址,password(字符串,可选) - SSH 密码(如果 SSH 密钥验证失败或不可用),port(整数,可选) - SSH 端口(默认:22) |
| ssh_run_command |
在连接的主机上的 同一个持久 shell 会话 中执行命令。当前目录、环境变量和 shell 状态从之前的命令中保留。 |
host(字符串) - 主机别名,command(字符串) - 要执行的命令 |
| ssh_read_log |
使用持久的 shell 会话从日志文件中读取最后 N 行。 |
host(字符串) - 主机别名,file_path(字符串) - 日志文件的路径,lines(整数) - 要读取的行数 |
重要注意事项:
- 有状态:同一主机的所有命令都在同一个持久 shell 会话中运行。
- 连接方法:
- 使用
ssh_connect 进行基于 SSH 配置的连接(需要 ~/.ssh/config)。
- 使用
ssh_connect_direct 进行无需 SSH 配置的直接连接。
- 身份验证顺序(对于
ssh_connect_direct):
- SSH 密钥(SSH 代理,然后是常见密钥文件)
- 密码(如果提供且 SSH 密钥验证失败)
- 会话持久性:一旦连接,会话将保持活动状态,直到 MCP 服务器重启或显式断开连接。
- 安全性:
ssh_connect_direct 中的密码通过 SSH 协议安全传输(加密)。在可用时,首选 SSH 密钥身份验证。
MCP 模式的示例提示
MCP 服务器配置完成后,您可以要求 AI 助手执行 SSH 操作:
示例 1:
用户:连接到 rpi 并显示当前目录和磁盘使用情况
AI 响应:
我将连接到 rpi 并为您检查当前目录和磁盘使用情况。
[正在连接到 rpi...]
当前目录:/home/pi
磁盘使用情况:
Filesystem Size Used Avail Use% Mounted on
/dev/root 15G 5.2G 8.8G 38% /
/dev/mmcblk0p1 253M 49M 204M 20% /boot
示例 2:
用户:连接到 production 并读取 /var/log/nginx/error.log 的最后 50 行
AI 响应:
我将连接到 production 并读取 nginx 错误日志的最后 50 行。
[正在连接到 production...]
/var/log/nginx/error.log 的最后 50 行:
2024-01-15 10:23:45 [error] 12345#0: *123 connect() failed (111: Connection refused)
2024-01-15 10:24:12 [error] 12345#0: *124 upstream timed out
...
示例 3:
用户:以用户 pi 身份直接连接到 192.168.88.63 并显示磁盘使用情况
AI 响应:
我将以用户 pi 身份直接连接到 192.168.88.63 并检查磁盘使用情况。
[正在使用 SSH 密钥连接到 192.168.88.63...]
磁盘使用情况:
Filesystem Size Used Avail Use% Mounted on
/dev/root 15G 5.2G 8.8G 38% /
/dev/mmcblk0p1 253M 49M 204M 20% /boot
示例 3b(带密码):
用户:以用户 admin 身份使用密码 mypass123 连接到 192.168.88.63 并检查系统运行时间
AI 响应:
我将以用户 admin 身份连接到 192.168.88.63 并检查系统运行时间。
[正在连接到 192.168.88.63...]
[SSH 密钥身份验证失败,尝试使用密码...]
系统运行时间:5 天 12 小时 30 分钟
示例 4:
用户:检查 dev 服务器的系统运行时间和内存使用情况
AI 响应:
我将检查 dev 服务器的系统运行时间和内存使用情况。
[正在连接到 dev...]
系统运行时间:15 天 3 小时 22 分钟
内存使用情况:
total used free shared buff/cache available
Mem: 7.8G 2.1G 3.2G 128M 2.5G 5.4G
Swap: 2.0G 0B 2.0G
示例 5:
用户:显示正在运行的进程并检查 production 上的 nginx 服务是否正在运行
AI 响应:
我将检查正在运行的进程并验证 production 上的 nginx 是否正在运行。
[正在连接到 production...]
Nginx 服务状态:active (running)
按 CPU 使用率排序的前几个进程:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1234 nginx 20 0 123456 4567 1234 S 2.3 0.1 0:05:23 nginx
5678 app 20 0 234567 8901 2345 S 1.8 0.2 0:12:45 app
...
有状态会话示例
此示例演示了命令之间状态的保留:
用户:连接到 rpi,导航到 /var/log,并显示 syslog 的最后 10 行
AI 响应:
我将连接到 rpi,导航到 /var/log,并显示 syslog 的最后 10 行。
[正在连接到 rpi...]
[正在更改目录到 /var/log...]
syslog 的最后 10 行:
Jan 15 10:23:45 rpi systemd[1]: Started Network Manager.
Jan 15 10:24:12 rpi sshd[1234]: Accepted publickey for pi from 192.168.1.50
...
幕后发生的事情:
ssh_connect("rpi") - 建立持久的 shell 会话
ssh_run_command("rpi", "cd /var/log") - 更改目录(状态已保存)
ssh_run_command("rpi", "tail -n 10 syslog") - 从 /var/log 执行(状态已保留!)
第二个命令自动从 /var/log 开始执行,因为 shell 状态从上一个命令中得到了保留。
独立 CLI 模式(用于调试)
交互式终端模式,用于测试和调试。对于排查连接问题或手动测试命令很有用。
连接方法
1. 使用 SSH 配置别名
cargo run -- cli --host rpi
./target/release/ssh-liaison-mcp cli --host rpi
2. 通过命令行直接连接
cargo run -- cli --user pi --hostname 192.168.1.100
cargo run -- cli --user pi --hostname 192.168.1.100 --port 2222
cargo run -- cli --user pi --hostname 192.168.1.100 --password mypassword
cargo run -- cli --user pi --hostname 192.168.1.1