router-ssh-mcp
Provides SSH-based service management for generic Linux systems using systemd or sysvinit, including service listing, status checks, log reading, and controlled start/stop/restart with confirmation.
Allows managing services on OpenWrt/ImmortalWrt routers over SSH, including listing services, checking status, reading logs, and performing confirmed start/stop/restart operations, with an optional high-risk shell command capability.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@router-ssh-mcp @router-ssh-mcp show the status of the dropbear SSH service and the last 20 log lines"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
router-ssh-mcp
通过 SSH 管理路由器(OpenWrt / ImmortalWrt / 通用 Linux)上服务的 MCP(Model Context Protocol)服务器。
上层模型可以列出服务、查询状态、读取日志,并在二次确认后执行启动 / 停止 / 重启,全程受命令白名单约束;此外提供一个默认关闭的高危 run_shell 能力,在显式开启并二次确认后可于远端执行任意 shell 命令,用于白名单工具覆盖不到的排障场景。
1. 能力概览
能力 | 说明 |
多 init 系统 | 自动探测 |
连接复用 | 进程内复用单条 SSH 连接,带 keepalive 与断线重连 |
只读 / 写入分离 | 4 个只读工具 + 3 个写入工具 + 1 个高危 shell 工具,通过 MCP annotations 标注 |
写入二次确认 | 写入工具必须传 |
命令白名单 | 所有命令以 argv 序列下发,不经 shell;服务名、路径、行数均做严格校验 |
可选 shell 能力 |
|
结构化输出 | 统一返回 |
结构化错误 | 连接、认证、超时、服务不存在等均映射为稳定错误码 + 修复建议 |
Related MCP server: OpenWRT SSH MCP Server
2. 目录结构
router-ssh-mcp/
├── pyproject.toml # 依赖与 console script 入口
├── config.example.json # 配置文件示例(不含真实口令)
├── .env.example # 环境变量示例
├── src/router_mcp/
│ ├── config.py # 配置加载(默认值 < 配置文件 < 环境变量)
│ ├── errors.py # 错误码与结构化异常
│ ├── commands.py # 命令白名单与 argv 模板(安全核心)
│ ├── ssh_pool.py # SSH 连接复用 / 保活 / 超时重连 / 错误归类
│ ├── controller.py # 编排层:安全策略、二次确认、工具语义
│ ├── models.py # pydantic 返回模型
│ ├── server.py # MCP 服务器与工具注册
│ ├── _compat.py # mcp 1.x / 2.x 兼容层
│ └── backends/
│ ├── base.py # 后端抽象与公共能力(启动时间推算)
│ ├── initd.py # /etc/init.d 通用实现(sysvinit)
│ ├── procd.py # OpenWrt ubus + procd
│ └── systemd.py # systemctl + journalctl
├── scripts/
│ ├── selftest.py # 真实设备自测(只读 + 可选写入演练)
│ └── mcp_smoke.py # stdio 协议级冒烟测试
└── tests/ # 离线单元测试 + MCP 层端到端测试调用链:MCP 工具 → controller(安全策略/确认) → backends(init 适配) → commands(白名单 argv) → ssh_pool(连接复用) → asyncssh。
3. 环境要求与依赖
项目 | 要求 |
Python | >= 3.10(CI 验证 3.11 / 3.12 / 3.13) |
mcp | >= 2.0, < 3(SDK,代码内含 1.x 兼容路径) |
asyncssh | >= 2.14(纯 Python SSH,自带 crypto,不需要系统 ssh 客户端) |
pydantic | >= 2.7(结构化输出模型) |
Linux / macOS
python3 -m venv .venv
source .venv/bin/activate
pip install -e . # 开发模式
# 或:pip install .Windows(PowerShell 7)
python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -e .开发额外依赖:pip install -e ".[dev]"(pytest、pytest-asyncio)。
4. 配置
优先级:默认值 < JSON 配置文件 < 环境变量 < 命令行参数。
连接参数完全可自定义:目标路由器 IP、登录账号、密码或私钥均由你按实际设备填写,三种方式任意组合。本仓库所有示例中的
192.168.1.1仅为占位默认地址,请替换为你自己的路由器地址。密码与令牌切勿提交进仓库(config.json/.env已在.gitignore中忽略)。
配置文件查找顺序:--config → $ROUTER_MCP_CONFIG → ./router-mcp.json → ./config.json → ~/.config/router-mcp/config.json。
4.1 环境变量
变量 | 默认值 | 说明 |
|
| SSH 主机 |
|
| SSH 端口 |
|
| SSH 用户名 |
| — | 密码(与 |
| — | 私钥路径 |
| — | 私钥口令;未设置时会尝试用 |
|
|
|
|
| 连接超时(秒) |
|
| 单条命令超时(秒) |
|
| 保活间隔(秒) |
|
| 连续保活失败次数阈值 |
|
| 命令失败重连重试次数 |
|
| 单次日志读取上限 |
|
| 写入操作是否要求二次确认 |
| 空 | 服务白名单,逗号分隔;为空表示不限制 |
|
| 禁止写入的高危服务 |
|
| 是否校验 known_hosts(路由器重装后建议临时关闭) |
| — | known_hosts 路径(仅在严格模式下生效) |
|
| 是否启用高危的 |
|
|
|
|
| 是否拦截高危命令(rm -rf / mkfs / dd / 写 /dev/* / fork 炸弹 / 管道进 shell) |
|
| asyncssh 日志级别,排障时设 |
4.2 配置文件
复制 config.example.json 为 config.json(已在 .gitignore 中忽略,避免口令入库):
{
"host": "192.168.1.1",
"username": "root",
"password": "",
"init_system": "auto",
"security": {
"require_confirmation": true,
"allowed_services": [],
"denied_services": ["network", "firewall"]
}
}5. 启动
5.1 直接运行
# stdio(默认,供 MCP 客户端拉起)
ROUTER_MCP_HOST=192.168.1.1 ROUTER_MCP_PASSWORD=xxx python -m router_mcp
# 等价的 console script
ROUTER_MCP_HOST=192.168.1.1 ROUTER_MCP_PASSWORD=xxx router-ssh-mcp
# 命令行覆盖参数
router-ssh-mcp --host 192.168.1.1 --user root --key-path ~/.ssh/id_ed25519
# SSE / Streamable HTTP(远程部署时使用)
router-ssh-mcp --transport streamable-http5.2 接入 MCP 客户端
WorkBuddy(~/.workbuddy/mcp.json):
{
"mcpServers": {
"router-ssh": {
"description": "通过 SSH 管理路由器(OpenWrt/ImmortalWrt/systemd)服务的 MCP 连接器;只读工具可直接调用,写入操作需二次确认,run_shell 高危能力默认关闭。",
"command": "python",
"args": ["-m", "router_mcp"],
"env": {
"ROUTER_MCP_HOST": "192.168.1.1",
"ROUTER_MCP_USER": "root",
"ROUTER_MCP_PASSWORD": "你的密码"
}
}
}
}新增服务器后需在 WorkBuddy 的连接器管理页点击「信任」才会启用。
Claude Desktop / 其他支持 MCP 的客户端:claude_desktop_config.json 使用同样的 mcpServers 结构。
6. 工具清单
工具 | 类型 | 参数 | 说明 |
| 只读 | — | 连接健康、init 系统、设备信息、生效的安全策略 |
| 只读 |
| 列出服务及运行状态 |
| 只读 |
| 单服务详情(PID、启动时间、自启状态) |
| 只读 |
| 日志尾部(logread / journalctl) |
| 写入 |
| 启动服务 |
| 写入 |
| 停止服务 |
| 写入 |
| 重启服务(非幂等) |
| 高危 |
| 在路由器上执行任意 shell 命令(默认关闭,需二次确认) |
6.1 成功返回示例
{
"ok": true,
"tool": "service_status",
"host": "192.168.1.1",
"duration_ms": 412,
"data": {
"service": {
"name": "dnsmasq",
"running": true,
"enabled": true,
"pid": 2814,
"started_at": "2026-09-10 09:00:12",
"description": "/usr/sbin/dnsmasq -C /var/etc/dnsmasq.conf",
"source": "procd"
},
"checked_at": "2026-09-10T09:21:03+08:00"
},
"error": null
}6.2 需要二次确认时
{
"ok": false,
"tool": "service_restart",
"host": "192.168.1.1",
"error": {
"code": "CONFIRMATION_REQUIRED",
"message": "写入操作 restart 需要二次确认",
"hint": "确认无误后再次调用,并传入 confirm=\"dnsmasq\"",
"details": {"service": "dnsmasq", "expected_confirm": "dnsmasq"}
}
}6.3 常见用法(场景示例)
识别连接器本质(capability)
router_info 的返回中包含机器可读的 capability 字段(固定为 ssh-router-management),上层软件可据此确认本连接器是「可通过 SSH 操作路由器」的 MCP 连接器,而非普通工具:
{
"ok": true,
"tool": "router_info",
"data": {
"capability": "ssh-router-management",
"init_system": "procd",
"host": "192.168.1.1",
"security": {
"require_confirmation": true,
"allow_shell": false,
"denied_services": ["network", "firewall", "system", "boot", "done"]
}
}
}通过 run_shell 安装插件(OpenWrt / ImmortalWrt)
服务管理类工具只覆盖已注册 init 脚本的服务,安装插件这类操作需走 run_shell 高危通道。先确保已设置 ROUTER_MCP_ALLOW_SHELL=true 启用该能力,再按「命令本身」做二次确认:
# 1) 刷新软件源并安装插件(OpenWrt 包管理器 opkg)
run_shell(
command="opkg update && opkg install luci-app-sqm",
confirm="opkg update && opkg install luci-app-sqm"
)
# 2) 安装后启用并随系统自启
run_shell(
command="/etc/init.d/sqm enable && /etc/init.d/sqm start",
confirm="/etc/init.d/sqm enable && /etc/init.d/sqm start"
)经验证,
opkg update/opkg install <包名>/ 带&&的连写均不会被高危拦截列表误拦;被拦的只有rm -rf/mkfs/dd if=/ 写/dev/*/ fork 炸弹 /curl ... | sh等模式。run_shell以 root 身份在远端登录 shell 中执行,绕过服务白名单,仅应在可信网络内启用;每次执行都会写入审计日志(仅含命令本身,不含任何凭据)。
run_shell 返回示例
{
"ok": true,
"tool": "run_shell",
"data": {
"raw_command": "opkg update && opkg install luci-app-sqm",
"exit_status": 0,
"stdout": "Installing luci-app-sqm (...)",
"stderr": "",
"host": "192.168.1.1",
"reused_connection": true,
"confirmed": true,
"warning": "命令以 root 身份在路由器上执行,已绕过服务白名单;请确认输出不含敏感信息"
}
}7. 安全模型
无 shell 拼接:所有命令以 argv 列表经 exec 通道下发,全程不出现
sh -c。服务名白名单:
^[A-Za-z0-9][A-Za-z0-9_.@+-]{0,63}$,拒绝/、空格、;、|、&、$、反引号、换行与..。路径白名单:可读路径限定为
/proc/1/comm、/proc/uptime、/proc/stat、/etc/os-release、/etc/openwrt_release、/etc/init.d、/etc/rc.d、/run/systemd/system。写入二次确认:
confirm必须与目标服务名完全一致;可用ROUTER_MCP_REQUIRE_CONFIRM=false关闭(不推荐)。高危服务拒绝写入:
network、firewall、system、boot、done默认只可读不可写(重启这些服务会断网或重启设备)。可选服务白名单:设置
ROUTER_MCP_ALLOWED_SERVICES后,白名单外的服务读写均被拒绝。只读 / 写入标注:工具 annotations 标注
readOnlyHint/destructiveHint/idempotentHint,客户端可据此做权限提示。可选 shell 能力(高危,默认关闭):
run_shell必须显式设置ROUTER_MCP_ALLOW_SHELL=true才会出现并可调用;启用后命令仍经validate_shell_command校验,默认拦截rm -rf / mkfs / dd if= / 写入 /dev/* / fork 炸弹 / 管道进 shell等高危模式,且必须把confirm设为命令本身的完整字符串才会真正执行。shell 命令以 root 身份在远端登录 shell 中执行,绕过服务白名单——这是刻意保留的逃生通道,而非默认能力。口令不外泄:
config.redacted()用于输出快照,日志与工具返回值中不含密码。
8. 错误码
错误码 | 触发场景 | 典型修复 |
| 缺少 host / 认证方式,配置文件非法 | 设置 |
| 服务名 / 行数 / 参数非法 | 使用 |
| 路径或参数绕过白名单 | 属于实现缺陷,请提交 issue |
| TCP 不可达、连接被重置 | 检查 host/port、设备 SSH 服务与防火墙 |
| 用户名 / 密码 / 私钥错误 | 核对凭据;OpenWrt 默认用户为 |
| known_hosts 不匹配 | 更新 known_hosts 或临时关闭严格校验 |
| 命令超过 | 调大超时,或检查设备负载 |
| 命令返回非零退出码 | 查看返回中的 |
| 服务不存在 | 用 |
| 命中黑名单 / 白名单外 / polkit 拒绝 | 调整安全策略或以 root 连接 |
| 写入操作缺少正确 confirm | 传入 |
| 未开启 shell 能力却调用 | 设置 |
| 日志通道不可用(无 logread / journald) | 启用 logd 或 journald 持久化 |
| 解析 ubus / systemctl 输出失败 | 检查系统版本兼容性 |
9. 本地自测步骤
共四层,从无依赖到真实设备逐级验证。
第 1 层:离线单元测试(无需设备、无需网络)
pip install -e ".[dev]"
python -m pytest -q覆盖:命令白名单、配置优先级、procd/systemd 解析、确认机制、黑名单、超时映射、错误结构。
第 2 层:MCP 协议冒烟(验证客户端视角)
# 仅握手 + 列工具,不需要设备
python scripts/mcp_smoke.py
# 真实调用一次只读工具
ROUTER_MCP_HOST=192.168.1.1 ROUTER_MCP_USER=root ROUTER_MCP_PASSWORD=xxx \
python scripts/mcp_smoke.py --call router_info
# 带参数调用
python scripts/mcp_smoke.py --call service_status --args '{"name": "dnsmasq"}'预期输出:握手成功、8 个工具、读写标注正确;调用失败时返回 ok=false 与错误码。
第 3 层:真实设备自测(只读)
ROUTER_MCP_HOST=192.168.1.1 ROUTER_MCP_USER=root ROUTER_MCP_PASSWORD=xxx \
python scripts/selftest.py依次验证:SSH 连接与 init 探测 → 服务列表 → 单服务状态 → 日志读取 → 错误路径(服务不存在)→ 写入二次确认拦截,并输出表格化结果。加 --json 可查看原始返回。
第 4 层:写入演练(谨慎)
python scripts/selftest.py --host 192.168.1.1 --password xxx \
--service dnsmasq --do-write会真正执行一次 restart。建议先挑选无状态影响的服务(如 dnsmasq、odhcpd),不要在远程办公时段对 network / firewall 演练。
排障小贴士
现象 | 处理 |
| OpenWrt 默认禁止空密码;确认 dropbear 允许该用户登录 |
| 设备 CPU 繁忙或网络丢包,调大 |
| 确认 |
systemd 主机 | 以 root 连接,或配置 polkit / sudo 规则 |
想看 SSH 握手细节 | 设置 |
10. 已知限制
单条 SSH 连接复用,写入操作串行执行;高并发场景建议为每个设备起独立进程。
OpenWrt 的启动时间依赖
/proc/<pid>/stat与/proc/stat的btime,容器或受限命名空间内可能拿不到。service_list默认不填充started_at(避免对每个服务额外发起命令),需要时传with_start_time=true或调用service_status。systemd 主机上
enabled判定包含static状态(视为自启)。默认
run_shell不可用(刻意的安全设计);仅当用户显式设置ROUTER_MCP_ALLOW_SHELL=true后才提供任意命令执行能力。该通道仍受高危拦截列表与强制二次确认约束,但本质上以 root 身份运行,请仅在可信网络内启用。
11. 许可证
MIT
Available Tools
8 toolsrouter_infoARead-onlyIdempotent
查询 SSH 连接健康状态、探测到的 init 系统、设备系统与生效的安全策略。
返回结构: {"ok": true, "tool": "router_info", "host": "...", "data": { "host", "port", "username", "backend", "ssh": {"connected", "reuse_count", "reconnect_count", "server_version", ...}, "device": {"uname", "release", "release_source"}, "security": {...}, "limits": {...}}}
Inputs: no named parameters. Returns: result
run_shellADestructive
在路由器上执行任意 shell 命令(高危能力,默认关闭,需二次确认)。
安全约束(任意一条不满足都会返回错误而非执行): * 必须设置 ROUTER_MCP_ALLOW_SHELL=true 才会启用,否则返回 error.code = SHELL_DISABLED; * 默认拦截高危命令(rm -rf / mkfs / dd if= / 写入 /dev/* / fork 炸弹 / 管道进 shell 等), 命中时返回 error.code = BLOCKED_COMMAND; * 需把 confirm 设为命令本身的完整字符串进行二次确认,否则返回 CONFIRMATION_REQUIRED; * 命令以 root 身份在远端登录 shell 中执行,会绕过服务白名单,请谨慎使用。
参数: command: 要执行的 shell 命令字符串 confirm: 必须等于 command 的完整内容(含空格与引号)才真正执行
返回结构: {"ok": true, "data": {"raw_command", "exit_status", "stdout", "stderr", "duration_ms", "host", "reused_connection", "denied", "confirmed", "warning"}}
Inputs: command (required), confirm. Returns: result
service_listARead-onlyIdempotent
列出路由器上的服务及其运行状态。
参数: name_filter: 按名称子串过滤(大小写不敏感),可选 running_only: 仅返回运行中的服务 with_start_time: 额外读取每个服务的启动时间(命令数随服务数增加,默认关闭)
返回结构: {"ok": true, "data": {"services": [{"name", "running", "enabled", "pid", "started_at", "description", "source"}], "count", "running_count", "init_system", "host", "warnings"}}
Inputs: name_filter, running_only, with_start_time. Returns: result
service_logsARead-onlyIdempotent
读取服务日志尾部内容(OpenWrt 走 logread,systemd 走 journalctl)。
参数: name: 服务名 lines: 返回行数,1 <= lines <= ROUTER_MCP_MAX_LOG_LINES(默认 500)
返回结构: {"ok": true, "data": {"name", "requested_lines", "returned_lines", "source", "command", "truncated", "lines": [...] }}
Inputs: name (required), lines. Returns: result
service_restartADestructive
重启指定服务(写入操作,需二次确认)。
参数与返回结构同 service_start。restart 非幂等,重复调用会重复中断服务。
Inputs: name (required), confirm. Returns: result
service_startAIdempotent
启动指定服务(写入操作,需二次确认)。
参数: name: 服务名 confirm: 必须等于服务名才真正执行;否则返回 error.code = CONFIRMATION_REQUIRED
返回结构: {"ok": true, "data": {"name", "action", "exit_status", "command", "stdout", "stderr", "duration_ms", "state_before", "state_after"}}
Inputs: name (required), confirm. Returns: result
service_statusARead-onlyIdempotent
查询单个服务的运行状态、PID、启动时间与开机自启状态。
参数: name: 服务名(OpenWrt 为 /etc/init.d 脚本名,systemd 为 unit 名,如 sshd.service)
返回结构: {"ok": true, "data": {"service": {"name", "running", "enabled", "pid", "started_at", "description", "source"}, "checked_at", "init_system"}}
服务不存在时返回 ok=false,error.code = SERVICE_NOT_FOUND,并在 details 中给出可用服务样例。
Inputs: name (required). Returns: result
service_stopADestructiveIdempotent
停止指定服务(写入操作,需二次确认)。
参数与返回结构同 service_start。注意停止 ssh/dropbear 会导致连接中断。
Inputs: name (required), confirm. Returns: result
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 8 tools
Each tool has a clearly distinct role: router-level info, service list/status/logs, three explicit service actions, and shell execution. The service_list/service_status pair is well-differentiated as bulk listing vs single-service lookup.
The service_* family follows a predictable and readable pattern, and all names use snake_case. Minor deviations exist: router_info is noun-style rather than verb_noun, and service_logs uses a noun instead of a verb like service_log.
Eight tools is well-scoped for an SSH-based router management server. The set covers inspection, logs, and service lifecycle operations without bloating the surface.
Core service operations—list, status, logs, start, stop, restart—are covered, plus router health info and shell fallback. A notable minor gap is the lack of first-class service enable/disable operations, since the enabled state is reported but cannot be changed through the main tools.
Maintenance
Related MCP Connectors
Manage servers through SSH.
Linux server administration over OAuth MCP: shell, Docker, systemd, files and monitoring.
Let AI operate servers without SSH. Choose actions, approve risky changes, and audit every step.
Work on your own computers from Claude or ChatGPT: run commands, edit and search files.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables management of ASUS routers running Asuswrt-Merlin firmware via SSH/SCP. Supports system monitoring, device management, WiFi control, service restarts, NVRAM operations, file transfers, VPN management, and custom command execution.13MIT
- AlicenseAqualityDmaintenanceEnables AI agents to manage OpenWRT routers remotely via SSH, supporting system monitoring, network management, OpenThread Border Router configuration, and package management through natural language commands.1917MIT
- AlicenseBqualityDmaintenanceEnables AI assistants to monitor and securely manage AsusWRT and AsusWRT-Merlin routers via SSH with allowlisted commands, supporting read-only monitoring and controlled mutations.473MIT
- AlicenseNot gradedqualityCmaintenanceProvides full administrative control of OpenWRT routers via SSH, with a preview-confirm safety model for mutating operations.17 npmMIT