关于 SSH Host Storage & Remote Exec
这是一个用于保存 SSH 主机并执行远程命令的独立 stdio MCP 服务及配套 CLI。它在本地加密凭据、复用 SSH 会话,并提供连接池监控。
主要功能
- 01加密保存本地主机信息
- 02SSH 主机管理工具
- 03执行命令、交互命令和剧本
- 04复用 SSH 连接
- 05脱敏监控连接池
- 06交互式与批处理 CLI
适用场景
- 01管理 MCP 运维所用的主机
- 02通过 MCP 客户端执行远程命令
- 03自动运行多步骤 SSH 剧本
- 04批量导入主机并查看连接状态
MCP 相关
通过 stdio MCP 工具提供 SSH 主机管理、远程命令执行、剧本运行和连接池状态。MCP 客户端可在本地启动该服务。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
npx -y @xlbbb-cn/mcp_ssh_exec_cmdMCP_SSH_CONFIG_DIRDirectory holding hosts.json and master.key (default: ~/.mcp-ssh-server).MCP_SSH_MASTER_KEY密钥64-hex-character AES-256-GCM master key; auto-generated into <config dir>/master.key when unset.MCP_SSH_POOL_IDLE_MSIdle SSH connections are closed after this many milliseconds (default: 60000, 0 disables pooling).MCP_SSH_POOL_MAX_HOSTSMaximum number of pooled SSH connections (default: 32).本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
9收录的工具列表 · 更新于 6 天前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| topic | string | 否 | 可选的帮助主题筛选条件:'install'、'list'、'get'、'create'、'update'、'delete'、'import'、'monitor'、'tui';排查问题时可选 'windows'。省略则获取完整帮助文本。 |
详细介绍
mcp-ssh-server
一个独立的 stdio MCP 服务,可将 SSH 主机管理和远程命令执行转化为 MCP 工具。 此外,它还附带一个交互式/批处理命令行工具(
remote-ssh-cli),用于管理本机上的 SSH 主机。简体中文文档见 README.zh-CN.md。
这是一个 Node.js 实现,重写了 FastAPI 项目中 SSH 主机相关的部分
(app/services/llm_tools.py + app/utils/ssh_helper.py + app/models/host.py)——
不依赖后端、数据库或网络服务。任何 MCP 客户端(Claude Desktop、Continue、自定义 Agent 等)都可以通过 npx
启动它,并通过 JSON-RPC 与其通信。
亮点
- 完全独立——使用自己的加密 JSON 存储(
~/.mcp-ssh-server/hosts.json);不需要 FastAPI 后端或数据库。 - 无原生依赖——使用 Node 内置的
crypto实现 AES-256-GCM,使用纯 JavaScript 的ssh2实现 SSH;可在 Windows/macOS/Linux 上运行。 - 符合标准的 stdio MCP——使用 Model Context Protocol stdio 传输协议,可通过
npx启动。 - 凭据不会离开服务端——主机工具的响应不会包含密码或私钥;SSH 工具会在内部解密凭据以建立连接,绝不会将其回传。
- 连接池——每台主机保留一个活动 SSH 会话,并在多条命令和剧本步骤之间复用。
- 连接池监控——
pool_status会将活动连接池显示为脱敏表格:uid、名称、连接状态、已执行/失败的命令以及最后一条命令(机密信息和 IP 地址均已屏蔽)。 - 配套命令行工具——
remote-ssh-cli管理同一个存储(交互式 TUI、批处理命令、CSV 批量导入),还可以实时监控连接池(每秒刷新一帧,按q退出)。
MCP 工具
共发布九个工具:
| 工具 | 描述 |
|---|---|
cli_help | 提供 remote-ssh-cli 的安装、使用和故障排除说明;可使用 topic 指定主题 |
host_list | 列出已知主机——仅返回 sys_name 和 uid(不包含地址、用户名或凭据);uid 是其他工具所需的 host_id |
host_create | 添加主机(密码/私钥/口令短语均会在静态存储时加密) |
host_update | 更新主机字段(未提供的凭据字段保留当前值) |
host_delete | 删除主机 |
ssh_run_command | 在主机上运行一条非交互式命令 |
ssh_run_interactive | 通过 expect/answer 对运行会响应提示的命令(sudo 等) |
ssh_run_playbook | 运行多步骤剧本(结构化的 # @… 指令或旧版 expect:/answer: 行) |
pool_status | 监控活动连接池——每台主机对应一行脱敏信息(uid、名称、状态、已执行/失败的命令、最后一条命令) |
主机标识符约定
host_list是入口:每台主机返回的字段恰好为两个——sys_name(名称)和uid(唯一标识符)。- 其他所有工具都使用
host_id这一名称接收同一个值。原样传回;绝不要自行拼接或猜测。 - 如果找不到对应 ID,工具会返回可供采取操作的错误:
Host '<id>' not found. host_id must be the exact 'uid' value returned by host_list — call host_list to obtain the valid identifiers.请重新调用host_list,并使用其返回的值重试。 host_get不会发布给 MCP 客户端:模型始终只能看到sys_name+uid,不会看到任何主机的地址、用户名或凭据。命令行工具为人工操作人员保留了自己的get子命令。
参数一览
| 工具 | 参数 |
|---|---|
cli_help | topic? — 以下之一:install、list、get、create、update、delete、import、monitor、tui、windows |
host_list | enabled_only?(bool) |
host_create | ip_address(必填)、system_name?、ssh_port?(1-65535,默认 22)、ssh_username?(默认 root)、ssh_auth_method?(password | pubkey)、ssh_password?、ssh_private_key?、ssh_private_key_passphrase?、is_enable?(默认 true) |
host_update | host_id(必填)以及上述任意字段 — 未提供的字段将保留当前值 |
host_delete | host_id |
ssh_run_command | host_id、command、timeout_seconds(1-600,必填) |
ssh_run_interactive | host_id、command、expects?([{ expect, answer }])、timeout_seconds |
ssh_run_playbook | host_id、content(多行 playbook 正文)、timeout_seconds |
pool_status | host_id?(仅一个主机)、format?(table | json)、include_history?(bool)、include_idle_hosts?(bool) |
ip_address 在存储中必须唯一 — 创建重复项会失败。
命令结果以纯文本块的形式返回:
[stdout]
hello
[stderr]
[exit_code=0 success=true duration_ms=42]
快速开始
git clone https://github.com/xlbbb-cn/mcp_ssh_exec_cmd.git
cd mcp_ssh_exec_cmd
npm install
npm run build
构建输出位于 dist/,并提供两个二进制文件:
| 二进制文件 | 入口点 | 用途 |
|---|---|---|
remote-ssh-server | dist/index.js | stdio MCP 服务(由 MCP 客户端启动) |
remote-ssh-cli | dist/cli.js | 交互式 / 批量主机管理命令行工具 |
安装
该软件包以同一名称发布在两个注册表中:
@xlbbb-cn/mcp_ssh_exec_cmd:
| 注册表 | 安装命令 | 身份验证 |
|---|---|---|
| npmjs.org(推荐) | npm install -g @xlbbb-cn/mcp_ssh_exec_cmd | 无 — 公共注册表无需令牌 |
| GitHub Packages | npm install -g @xlbbb-cn/mcp_ssh_exec_cmd --registry=https://npm.pkg.github.com | 具有 read:packages 权限的个人访问令牌(classic) |
GitHub Packages 安装时也需要身份验证 — 即使是公共软件包也不例外, 因此请先为 npm 配置令牌:
npm login --scope=@xlbbb-cn --auth-type=legacy --registry=https://npm.pkg.github.com
# Username: <your GitHub username>
# Password: <your PAT>
也可以将相同内容写入 ~/.npmrc:
//npm.pkg.github.com/:_authToken=<PAT>
@xlbbb-cn:registry=https://npm.pkg.github.com
全局安装(命令行工具)
在本地检出目录中运行:
npm run build
npm install -g --allow-scripts=ssh2,cpu-features .
或者直接从注册表安装:
npm install -g @xlbbb-cn/mcp_ssh_exec_cmd # npmjs.org
npm install -g @xlbbb-cn/mcp_ssh_exec_cmd --registry=https://npm.pkg.github.com # GitHub Packages
之后,remote-ssh-cli 就会位于你的 PATH 中:
remote-ssh-cli # interactive TUI
remote-ssh-cli list # JSON list of every host
remote-ssh-cli get <host_id>
remote-ssh-cli create <json-file>
remote-ssh-cli update <host_id> <json-file>
remote-ssh-cli delete <host_id>
remote-ssh-cli import <csv-file> [--dry-run]
remote-ssh-cli monitor # live connection-pool monitor (q quits)
create / update 接受 JSON 文件,例如:
{
"ip_address": "192.168.1.100",
"system_name": "web-01",
"ssh_port": 22,
"ssh_username": "root",
"ssh_auth_method": "password",
"ssh_password": "your-password"
}
CSV 批量导入
remote-ssh-cli import hosts.csv 会将文件中的每一行转换为一个主机。
第一行必须是表头行,并且至少包含 ip_address 列:
ip_address,system_name,ssh_port,ssh_username,ssh_auth_method,ssh_password
192.168.1.100,web-01,22,root,password,secret
192.168.1.101,db-01,2222,deploy,password,secret
| 列 | 说明 | 默认值 |
|---|---|---|
ip_address | 必填,主机地址,在表中唯一 | — |
system_name | 显示名称 | 空 |
ssh_port | SSH 端口 | 22 |
ssh_username | SSH 用户名 | root |
ssh_auth_method | password / pubkey | password |
ssh_password | 明文密码(写入磁盘前会加密) | — |
ssh_private_key | 明文私钥(写入磁盘前会加密) | — |
ssh_private_key_path | 密钥文件路径(可替代 ssh_private_key) | — |
ssh_private_key_passphrase | 保护密钥的密码短语(写入磁盘前会加密) | — |
is_enable | true/false/1/0/yes/no | true |
- 列名不区分大小写,并接受别名(
ip、host、name、port、user、password等)。 - 按照 RFC 4180 解析带引号的逗号、内嵌换行符和
""转义,因此私钥内容不会遭到破坏。 - 错误行不会中止批量导入:每一行都会被报告为已创建/已跳过(IP 重复)/失败(数据无效)。命令会打印摘要;只要有一行失败,退出码就是
1。 --dry-run只验证并报告,不会写入任何内容。- CSV 模板(表头 + 示例行)会显示在
remote-ssh-cli横幅中;不带文件参数运行remote-ssh-cli import也会打印相同内容。
**Windows 注意事项:**如果遇到
JScript runtime error (800A03EA),请确保 npm 版本不低于 10.8.0,或者改用 cmd.exe 而非 PowerShell 运行命令——在较旧的 npm 版本中,npm 生成的.ps1包装脚本可能不会显式调用node。两个入口都包含#!/usr/bin/env nodeshebang。
监控连接池
remote-ssh-cli monitor 会打开 SSH 连接池的实时视图。屏幕每秒重绘一次,按 q 键即可退出(也可以按 Ctrl+C):
SSH connection pool monitor · 15:05:31 · tick 42 · refresh 1000ms · last tick 41ms
pool 2/32 connections · idle timeout 1m00s · probe timeout 5s · press q to quit
HOST ADDRESS USER STATUS LATENCY LAST OK POOL
------------ ------------ ----- -------- ------- ------- ----
web-01 192.168.1.100 root up 12ms 15:05:30 1m05s
db-01 10.0.0.5:2222 deploy down 8ms - -
problems (1):
db-01 (10.0.0.5:2222): connect ECONNREFUSED 10.0.0.5:2222
| 列 | 含义 |
|---|---|
STATUS | up(探测成功)、down(连接失败)或 disabled(主机已关闭——会列出,但不会探测) |
LATENCY | 上次探测的端到端耗时 |
LAST OK | 上次探测成功的时间 |
POOL | 连接池中连接的存续时间——会话在未重新建立的情况下被复用的时长 |
- 监控器会让连接池保持热状态:每台主机都会通过 SSH 工具使用的同一个
ConnectionPool进行探测,因此显示的连接都是真实且可复用的会话。 - CLI 运行自己的连接池——MCP 服务是独立进程,因此无法从终端检查它的连接。
- 默认探测命令为
echo(POSIX shell 和cmd.exe中都存在);每次间隔都会运行一次探测,因此主机列表较大时,请合理设置--interval。 - 如果没有终端(例如管道或 CI 作业),监控器只会打印一帧,而不会循环——因为没有按键可以停止它。
| 选项 | 说明 | 默认值 |
|---|---|---|
--interval <ms> | 重绘间隔 | 1000 |
--timeout <s> | 每台主机的探测超时时间(1-600 s) | 5 |
--probe-cmd <cmd> | 用于探测主机的命令 | echo |
--host <host_id> | 只监控此主机(可重复指定) | 所有主机 |
--all | 同时列出已禁用的主机(不会探测) | 关闭 |
--once | 打印一帧后退出 | 关闭 |
交互式 TUI 中也提供相同视图(7) Monitor connection pool)。
在 MCP 客户端中注册
~/Library/Application Support/Claude/claude_desktop_config.json(macOS)或
%APPDATA%\Claude\claude_desktop_config.json(Windows):
{
"mcpServers": {
"ssh": {
"command": "npx",
"args": ["-y", "-p", "@xlbbb-cn/mcp_ssh_exec_cmd", "remote-ssh-server"]
}
}
}
使用 GitHub Packages 时,请在同一代码片段中添加注册表:
"args": [
"--registry=https://npm.pkg.github.com",
"-y",
"-p",
"@xlbbb-cn/mcp_ssh_exec_cmd",
"remote-ssh-server"
]
file: 形式会让 npx 直接指向本地检出目录——开发时很方便,也无需任何注册表:
"args": ["-y", "-p", "file:./mcp_ssh_exec_cmd", "remote-ssh-server"]
配置
所有配置均通过环境变量完成:
| 变量 | 说明 | 默认值 |
|---|---|---|
MCP_SSH_MASTER_KEY | 以十六进制字符串表示的 32 字节密钥(64 个十六进制字符)。请备份此密钥:更改密钥或迁移到另一台机器,都会导致 hosts.json 中的所有凭据无法解密 | 首次启动时生成并存储在 <config_dir>/master.key |
MCP_SSH_CONFIG_DIR | 覆盖配置目录 | ~/.mcp-ssh-server |
MCP_SSH_POOL_IDLE_MS | 空闲连接在连接池中保留的时长(毫秒)。0 会完全禁用连接池,因此每条命令都会建立新连接 | 60000 |
MCP_SSH_POOL_MAX_HOSTS | 连接池保持活动状态的主机数上限(超过上限时,会驱逐最近最少使用的空闲连接) | 32 |
SSH 连接池
- 每台主机保留 一个 SSH 连接;连续执行的命令和操作手册中的各个步骤都会复用该连接,而不是重新连接。
- 连接池的键由主机 ID 和凭据指纹组成。更改地址、端口、用户名、身份验证方法、密码或密钥会立即使缓存连接失效;
host_update和host_delete会显式关闭对应连接。 - 空闲连接会在
MCP_SSH_POOL_IDLE_MS指定的时长后关闭。如果对端在此期间断开连接,下次使用时会自动重新建立连接(重试一次)。 - 进程退出时(即 MCP 客户端断开连接时),所有连接池中的连接都会关闭。
remote-ssh-cli monitor可实时查看连接池(参见监控连接池);它遵循相同的两个环境变量。
从 MCP 监控连接池
pool_status 会将 MCP 服务自身 连接池的状态以表格形式呈现 —
每台有连接池连接或命令流量的主机占一行:
SSH connection pool · 1/32 connections · idle timeout 1m00s · 2 commands executed · 1 failed · updated 15:24:16
Commands and error messages are desensitized (secrets masked, IP addresses replaced).
UID SYS_NAME STATE EXECUTED FAILED LAST CMD LAST RESULT LAST ERROR LAST RUN POOL AGE
------------------------------------ -------- ------ -------- ------ ----------------------- ----------- ----------------------------- -------- --------
9bcb7682-537b-4837-8b9e-6e37b4c61cdc web-01 pooled 3 1 systemctl restart nginx exit=1 connect to *** port 22 failed 12s ago 1m05s
| 列 | 含义 |
|---|---|
UID | 主机 ID,与 host_list 返回的 uid 值相同 |
SYS_NAME | 主机名称 |
STATE | pooled(连接存活且空闲)、busy(正在运行命令)、down(连接已断开)、idle(运行过命令,但当前没有打开连接)、new(从未使用) |
EXECUTED / FAILED | 主机上运行的命令数,以及其中失败的命令数 |
LAST CMD / LAST RESULT | 上一条命令及其结果(ok、exit=1、timeout、error) |
LAST ERROR | 上次失败时经过脱敏的错误消息 |
LAST RUN | 上次运行命令距今的时长 |
POOL AGE | 连接池中的连接被复用的时长 |
- 表格已脱敏:凭据会被屏蔽(
mysql -pS3cret→mysql -p***),IP 地址会被替换(10.0.0.5→***),并且绝不会显示主机的地址、端口、用户名或凭据。 format: "json"会以 JSON 文档返回相同信息;include_history: true会添加每台主机最近运行的命令;include_idle_hosts: true还会列出没有流量的主机;host_id可将视图缩小到单台主机。- 计数器保存在服务进程中:它们记录的是此 MCP 服务运行的命令,并会在服务重启时重置。CLI 监控器是独立进程,显示的是它自身连接池的状态。
超时语义
timeout_seconds 是一个硬性上限:达到上限时,命令会被终止,
结果会报告 success=false、exit_code=null,并在
stderr 中附带说明。对于 ssh_run_playbook,该值是整个操作手册的时间预算 —
每个步骤只能使用剩余时间。
Playbook 格式
ssh_run_playbook 接受多行内容。支持两种格式。
结构化 —— 指令以 # @ 开头:
# @section "Restart nginx"
# @desc "Reload the reverse proxy after the config change"
systemctl stop nginx
# @on "systemctl start nginx"
expect: [sudo] password for deploy:
answer: secret
# @note "the site is back up"
# @danger "this drops active connections"
| 指令 | 含义 |
|---|---|
# @section "Title" | 将后续步骤归入一个章节 |
# @desc "…" | 当前章节的描述 |
# @note "…" | 附加到当前步骤的注释 |
# @danger "…" | 附加到当前步骤的危险警告 |
# @on "command" | 以交互方式运行此命令(为后续的 expect:/answer: 配对提供回答) |
旧格式 —— 每行一个命令,可选地跟上 expect: / answer: 配对:
sudo systemctl restart nginx
expect: [sudo] password for deploy:
answer: secret
普通的 # 注释会被忽略。Playbook 会在首次遇到非零退出码时停止,并返回所有已执行步骤的记录。
存储与安全
hosts.json— 所有主机记录均以纯 JSON 格式存储。元数据字段以明文存储;ssh_password、ssh_private_key和ssh_private_key_passphrase保存的是 base64 密文。master.key— 32 字节的主密钥(POSIX 上的模式为0o600)。- 写入操作会先写入
.tmp文件,再通过重命名完成,因此即使发生崩溃,也不会留下只写了一部分的文件。
密钥使用 AES-256-GCM 加密,密钥由主密钥通过 PBKDF2-SHA-256 派生(200k 次迭代,16 字节随机盐)。数据布局为 [16-byte salt][12-byte IV][16-byte tag][ciphertext];每次加密都会生成新的盐和 IV,因此相同明文不会产生相同的密文。
请确保整个目录不纳入版本控制:
# mcp-ssh-server state
.mcp-ssh-server/
项目结构
src/
index.ts MCP entry point — stdio transport, tool registration, dispatch
cli.ts remote-ssh-cli — interactive TUI + batch subcommands
monitor.ts live connection-pool monitor (one frame per second, q quits)
pool.ts ConnectionPool — one live SSH connection per host + the command counters
ssh.ts ssh2 execution primitives + playbook parser/runner
storage.ts HostStore — hosts.json load/save with an atomic full-file rewrite
csv.ts RFC 4180 CSV reader + host-row mapping
importer.ts bulk-import orchestration
crypto.ts AES-256-GCM encryption of secrets at rest
master_key.ts master-key resolution (env var or key file)
paths.ts configuration-directory paths
redact.ts masks secrets and IP addresses in commands and error messages
tools/
_types.ts shared MCP tool shape + the host-identifier wording
hosts.ts host_list / host_create / host_update / host_delete
ssh_exec.ts ssh_run_command / ssh_run_interactive / ssh_run_playbook
pool_status.ts pool_status — the desensitized pool table
help.ts cli_help
test/
smoke.mjs encryption round-trip, HostStore CRUD, playbook parsing
features_smoke.mjs CSV parsing/import, connection pool behaviour
monitor_smoke.mjs monitor frames, the refresh loop, the 'q' shortcut
mcp_smoke.mjs end-to-end JSON-RPC handshake against a spawned server
开发
npm run dev # tsc --watch
npm run build # tsc (the only typecheck)
npm test # all four smoke tests
npm run build 是验证步骤——没有单独的 lint 或格式配置。测试会执行 dist/,因此测试前请先构建。
冒烟测试
npm test 会运行四个纯 Node 脚本;它们都不需要真实的 SSH 服务:
test/smoke.mjs— 加密/解密往返测试、完整的 HostStore CRUD 流程(写入时加密、读回、列出、更新、删除),以及两种格式的 Playbook 解析。test/features_smoke.mjs— CSV 解析和主机行映射、批量导入(包括--dry-run和跳过重复 IP)、连接池复用 / 凭据变更时失效 / 空闲时逐出、Playbook 解析边缘用例、命令和错误的脱敏,以及pool_status表格。test/monitor_smoke.mjs— 连接池监视器:帧渲染、每个时间间隔循环一帧、q快捷键、SSH 探测(针对模拟客户端)以及连接池快照。test/mcp_smoke.mjs— 将 MCP 服务作为子进程启动,执行 JSON-RPC 握手,并断言所有 9 个工具均已注册、host_list仅公开sys_name+uid、host_get未被公开、pool_status返回脱敏表格,且响应中绝不会出现密码。
使用 node test/<file>.mjs 运行单个文件。每项测试都会将状态隔离到临时的 MCP_SSH_CONFIG_DIR 中,因此不会触及你的真实 hosts.json。
发布
发布流程通过 GitHub Actions 工作流(.github/workflows/publish.yml)将版本发布到 npmjs.org、GitHub Packages 和官方 MCP Registry(registry.modelcontextprotocol.io)。两个软件包任务会进行构建、运行冒烟测试,并发布带有 npm 来源证明的压缩包;随后第三个任务会将 server.json 发布到 MCP Registry,镜像该 Registry 的目录(Glama、PulseMCP 等)会从中列出此服务。
MCP Registry 任务会在 npmjs.org 任务之后运行,因为 Registry 会读取已发布软件包中的 mcpName 标记,以验证 npm 归属:package.json 中的 mcpName 必须始终与 server.json 的 name 相等(由 test/features_smoke.mjs 断言),并且 CI 会在发布前将 server.json 中的版本与 package.json 同步。
# 1. bump the version in package.json, commit
git commit -am "chore: release v0.1.1" && git push github master
# 2. tag it — the tag triggers the publish workflow
git tag v0.1.1
git push github v0.1.1
| Registry | 凭据 | 来源 |
|---|---|---|
| GitHub Packages | GITHUB_TOKEN | 工作流内置,无需配置 |
| npmjs.org | 可信发布(OIDC) | 在 npmjs.org 上一次性配置:Packages → @xlbbb-cn/mcp_ssh_exec_cmd → Settings → Trusted publisher → 用户 xlbbb-cn、仓库 mcp_ssh_exec_cmd、工作流文件名 publish.yml。无需密钥——工作流会将短期 GitHub OIDC 令牌交换为发布令牌。 |
| npmjs.org(备用) | NPM_TOKEN | 仓库密钥(Settings → Secrets and variables → Actions)。必须是具有 @xlbbb-cn 作用域的 Read and write 权限,并启用 Bypass 2FA 的细粒度 npm 令牌——普通用户令牌会导致 npm 返回 EOTP(需要一次性密码)。仅在配置可信发布之前需要。 |
| MCP Registry | GitHub OIDC | 工作流内置,无需密钥:publish-mcp-registry 任务会在 npmjs.org 任务之后运行 mcp-publisher login github-oidc 和 mcp-publisher publish。mcp-publisher 二进制文件的版本固定在 publish.yml 中——请谨慎升级。 |
如果既没有 NPM_TOKEN 密钥,也没有配置可信发布,npmjs.org 任务会失败并显示可操作的错误消息。
可以通过命令行创建备用令牌(需要输入一次 2FA 代码):
npm token create --scopes=xlbbb-cn --packages-and-scopes-permission=read-write \
--bypass-2fa --otp=<your-2fa-code>
--bypass-2fa 可避免 npm 在每次发布时都要求输入一次性密码;如果没有此选项,Registry 会返回 EOTP。
可信发布生效后,可以删除 NPM_TOKEN 密钥,并将软件包的 Publishing access 设置为 “Require two-factor authentication and disallow tokens”。
也可以从 Actions 选项卡手动启动工作流(workflow_dispatch);勾选 dry_run 可在不发布的情况下验证压缩包。
已发布的软件包会显示在 https://github.com/xlbbb-cn/mcp_ssh_exec_cmd/packages 和 https://www.npmjs.com/package/@xlbbb-cn/mcp_ssh_exec_cmd。
发布到 npmjs.org 需要使用拥有
@xlbbb-cn作用域的 npm 账户(名为xlbbb-cn的 npm 用户或组织)。
与 FastAPI 后端的关系
| 方面 | Python 后端 | 此 MCP 服务 |
|---|---|---|
| 存储 | PostgreSQL / SQLite(app.models.host) | 本地 JSON 文件(~/.mcp-ssh-server/hosts.json) |
| 加密 | Fernet(AES-128-CBC + HMAC) | AES-256-GCM + PBKDF2 |
| SSH 客户端 | paramiko | ssh2 |
| 协议 | HTTP REST(/api/v1/hosts 等) | MCP(通过 stdio 传输的 JSON-RPC) |
| 身份验证 / 授权 | RBAC(host:* 权限代码) | 单用户本地进程,由文件系统权限保护 |
我们有意避免共用存储后端:一旦涉及数据库或 API,此服务就无法再独立运行;而让 MCP 服务和后端指向同一个 JSON 文件则会造成一致性问题。
要从 FastAPI 后端迁移主机,请编写一次性脚本:列出
/api/v1/hosts(需要host:secure:read),并将相同的字段结构传入此服务的host_create。
已知限制
- 主机数据存储在单个 JSON 文件中,每次更改时都会完整重写——对于数百台主机来说没问题,但不适用于数千台。
- 不提供并发写入保护:请勿同时运行两个实例并使用相同的
MCP_SSH_CONFIG_DIR。 - 没有审计日志或操作记录。如有需要,请在调用方 Agent 的记录中保存 SSH 输出。
- MCP 接口有意不公开每台主机的元数据,因此模型无法查询主机地址或用户名——必须通过
host_list/host_create操作。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。