mcpField
O
OpenWrt MCP

OpenWrt MCP

本地服务
github.com云与基础设施03 天前更新

关于 OpenWrt MCP

OpenWrt MCP 运行在标准 OpenWrt 路由器上,供 Claude Code 等 MCP 客户端检查和管理路由器设置。它结合默认拒绝授权、审计日志、秘密信息遮蔽,以及带回滚保护的配置更改。

主要功能

  1. 01检查路由器健康、日志和网络状态
  2. 02应用带检查与回滚的 UCI 更改
  3. 03管理软件包、服务并查看防火墙
  4. 04管理 WireGuard 对端
  5. 05按客户端授权并记录审计日志

适用场景

  1. 01查看路由器健康状况和安全发现
  2. 02暂存配置更改并确认或回滚
  3. 03检查网络客户端和防火墙规则
  4. 04管理软件包与 WireGuard 对端

MCP 相关

运行在路由器上的服务通过 SSH 上的 stdio 或 loopback HTTP 向 MCP 客户端提供 23 个工具。工具访问由授权控制,可能发起出站连接的操作会在 MCP 注释中标明。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

暂未收录安装方式

我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。

代码仓库

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

openwrt-mcp——适用于 OpenWrt 24.10 和 25.12 的 MCP 服务

英语 · 俄语 · 简体中文

CI Release License: MIT OpenWrt 25.12+ M8ven Score

一个运行于 OpenWrt 路由器上的 MCP 服务,因此 Claude Code(或任何 MCP 客户端)都可以检查和更改路由器——并由默认拒绝策略、 审计日志以及配置更改自动回滚机制提供保护。

本地且私密。 服务本身不会建立任何出站连接,也不会发送遥测数据;它只监听 环回地址和仅限 root 访问的套接字,并通过你设置的 SSH 会话与客户端通信。能够让路由器 本身主动连接外部的工具(net_diag、 带 refresh 的 pkg_query、pkg_change、sysupgrade check、uci_apply 探测、 ubus_call、exec)会在其 MCP 注释(openWorldHint)中说明这一点,且每个工具都需要 获得你的授权。工具输出中的机密值默认会被屏蔽;除非你提出要求,否则新的 WireGuard 密钥不会进入对话。

占用空间。 一个无依赖的静态二进制文件:适用于路由器架构的文件大小为 9.1 至 11.3 MB (arm64 上为 9.6 MB,在发布归档中压缩后约为 3.7 MB);在 GL-MT6000 上刚启动后常驻内存约 10 MB;23 个工具的描述约占 22 KB。每个发布文件都经过校验和验证,并附带构建证明。

基于 GlassOnTin/openwrt-mcp,作者 Ian Williams,该项目面向 GL.iNet 固件 4.x(OpenWrt 21.02,opkg)。这是一个移植版本,面向 原版 OpenWrt 25.12(apk、fw4/nftables、procd、netifd、LuCI JS)。安全核心来自 上游;来源详情请参阅致谢。

要求

  • **路由器:**原版 OpenWrt 25.12(apk)或 24.10(opkg),两者均使用 fw4/nftables。路由器无需预先安装任何内容。软件包工具会根据路由器上的二进制文件选择包管理器(先检查 apk,再检查 opkg);system_status 会说明找到的是哪种包管理器和防火墙。
    • 24.10 的差异:pkg_query 没有 policy 和 audit(opkg 两者都不支持;工具会说明这一点)。pkg_change 使用 opkg --noaction 进行模拟。新软件包的默认配置会以 <file>-opkg 而非 <file>.apk-new 的形式出现,pkg_config_diff / pkg_config_resolve 可处理这两种形式(旧副本会保留为 .pre-opkg-new;对于 /etc/config/NAME,则保留为隐藏文件 .NAME.pre-opkg-new,reload_config 会跳过该文件)。添加或移除 WireGuard 对等节点时,会针对该接口重新运行 ifup(24.10 的处理程序无法就地同步对等节点),因此每条隧道的会话都会短暂重启。world 会列出 opkg 标记为“用户安装”的软件包。system_status mode=doctor 会报告 opkg-new-pending;mode=audit 会跳过软件包审计并说明原因。
    • **fw3(OpenWrt 21.02 及更早版本,或基于这些版本构建的固件):**尽力支持。firewall_show 支持 ruleset(iptables-save,IPv4)和 rendered(fw3 -q print);不支持 check、table 和 chain;应用防火墙配置前不会进行预检查。
    • **测试环境:**在 GL-MT6000(硬件)上使用 25.12.5 测试;单元测试会在模拟环境中运行两种包管理器;CI 会在 24.10 和 25.12 的 openwrt/rootfs 容器中运行软件包和 uci 工具(容器中没有 ubus 或 netifd,因此未覆盖网络、无线和防火墙路径)。基于 OpenWrt 衍生的固件(GL.iNet、ImmortalWrt)尽力支持。
  • **你的 PC:**OpenSSH(Windows 10 及更高版本、macOS 和 Linux 均已提供)。从发行版本安装无需其他内容。从源代码构建需要 Go 1.26+、ssh 和 tar(Linux、macOS 或 Windows 上的 Git Bash)。
  • **适用于任意 CPU 架构:**OpenWrt 和 Go 都支持:发行版本为每种架构提供了二进制文件(arm64、armv5/v7、mips/mipsle softfloat、mips64/mips64le、x86、x86-64、riscv64);install.sh 会读取路由器的 DISTRIB_ARCH 并选择对应版本,或者在从源代码构建时进行交叉编译。

已在 GL.iNet GL-MT6000(mediatek/filogic,aarch64_cortex-a53)上使用 OpenWrt 25.12.5 测试。 代码中没有任何内容专门针对该板卡。


与其他 OpenWrt MCP 服务的区别

根据各项目在 2026-10-07 发布的 README;“未说明”表示 README 中没有提及。欢迎提出更正。

本项目openwrt_ssh_mcppaulomac1000/openwrt-mcpopenwrt-luci-mcp
运行位置路由器PC 上,通过 Docker 运行PC 上,通过 Docker 或 Python 运行PC 上,通过 Node.js 运行
连接路由器的方式通过 SSH 连接到绑定密钥的强制命令,或通过环回 HTTPSSHSSHLuCI 的 HTTP /ubus
是否更改配置是,通过一个会先检查并启用回滚的工具是,包括软件包和固件刷写只读,除非设置 ENABLE_WRITE_OPERATIONS=1否
错误更改后是否自动回滚是,且重启后仍有效未说明未说明不适用
访问控制和审计按客户端授权,支持范围、有效期和速率限制;提供审计日志,可选 TOTP命令白名单,审计日志审计日志隐藏机密信息,无审计日志
包管理器apk(25.12)或 opkg(24.10)opkgopkg列出已安装和可用的软件包
固件刷写从不是未说明否
路由器上的依赖二进制文件SSHSSHuhttpd-mod-ubus、rpcd

与上游版本的变化

上游 0.5(21.02 / GL.iNet)此移植版(25.12)
打包供 opkg 使用的 .ipkinstall.sh 文件安装 + keep.d(25.12 的 apk 没有 mkpkg;见下文)
Web UIGL.iNet oui-httpd 页面LuCI 页面,Services -> MCP Server:查看状态,以及批准或拒绝请求
传输通过 ssh -L 隧道使用 HTTP相同,另加通过 SSH 的 stdio,使用绑定密钥的强制命令——无需保持隧道连接
回滚快照/tmp(重启后丢失)保存在闪存中;未确认的更改会在下次启动时回滚,包括断电重启后
uci_apply设置 / 创建 / 删除+ add_list、del_list、set_list、dry_run,显示暂存的 uci changes,严格验证名称。试运行结束时会给出建议:warnings from this change。
重新加载ubus call uci reload_config(异步)/sbin/reload_config;若没有校验和,还会显式发送 config.change 事件(启动后的首次运行)
WireGuardGL wireguard_server / gl_ddns标准的 network.<iface> + wireguard_<iface> 对等端,来自 ddns 的 DDNS、CGNAT 警告、PSK、列出和移除
软件包--pkg_query、pkg_change(先模拟),通过设置级差异审查 .apk-new
防火墙--firewall_show:nft 规则集、fw4 print、fw4 check
服务--通过 rpcd rc 使用 service_list / service_control
范围 glob[0] 曾是一个字符类方括号按字面处理:firewall.@rule[3].* 即表示其字面含义
预设--`openwrt-mcp allow @readonly

工具

工具范围
system_status工具型号、版本、运行时间、负载、内存、存储、温度、conntrack、每个接口(状态、地址、默认路由、错误)、无线电/SSID,以及待处理事项。请先调用。mode=doctor 返回按优先级排列的健康问题(无线电关闭、接口没有地址、已启用的服务已停止、conntrack 和存储即将满、待处理的 .apk-new、NTP 或时钟未设置、已安装的内核比正在运行的内核更新)。mode=audit 返回按优先级排列的安全问题(防火墙允许从互联网进入的内容、SSH 和 LuCI 的暴露情况、密码登录、UPnP、WPS、开放或安全性较弱的 Wi-Fi、root 密码为空、软件包文件已修改、WireGuard 对等节点过期)。两者仅供参考,每条问题都会指出下一项只读调用和一个 OpenWrt wiki 页面,并列出无法检查的内容。它们的范围相互独立:openwrt-mcp allow <client> system_status doctor。
logread工具在行数限制之前筛选整个日志缓冲区(子字符串、RE2、最近 N 分钟);offset 从最新行开始向前翻页。mode=summary 会将日志折叠为不同的消息(数字、地址和 ID 替换为占位符),并提供数量、首次和最近时间以及最高严重性。baseline=save 返回一个令牌;baseline=<token> 仅显示当时日志中没有的消息。基线保存的是指纹,而不是日志文本,并驻留在内存中。
network_clients工具DHCP 租约 + 静态主机 + 邻居表 + 每个 AP 的关联列表,按 MAC 合并:名称、IP、SSID、信号、速率、连接时间、每个客户端占用其无线电空中时间的比例(来自 hostapd)、租约;每页 100 行,使用 offset 查看下一页。
firewall_show工具nft list ruleset、fw4 print、fw4 check、单个表或链。
net_diag<action>[.<target>]ping / traceroute / nslookup(可指定接口)、路由、策略规则、邻居。wifi_survey 按无线电报告其所在信道的繁忙程度(繁忙时间、接收和发送空中时间、噪声);不会测量其他信道,因此不会推荐信道。traffic 会在几秒内采样接口速率,并按来源排列 conntrack 表。usage 读取 nlbwmon 按设备统计的计费周期总量(默认按月)。这三项都只读取数据。
uci_get<config>[.<section>[.<option>]];按名称搜索使用 refsuci show,可选择使用稳定的 cfgXXXXXX ID。末尾显示配置的修订版本。history=list / diff:<id> 显示每次确认更改之前保留的版本。refs=NAME 可在各配置中查找定义和使用某个名称的位置。
uci_apply每项更改使用 <config>.<section>[.<option>];还原使用 <config>;每项探测使用 probe.<kind>.<target>暂存、检查、提交、重新加载,并启用回滚保护。dry_run 会准确显示将发生的更改,以及服务自身检查器的检查结果。若配置在此期间发生变化,expected_revisions 会拒绝操作;probe 会在之后检查路由器;restore=<id> 会还原到过去的版本。提交后会重新读取配置;如果路由器没有保留某项更改,则返回 NOT_APPLIED,且不会重新加载。
uci_confirm / uci_rollback工具将待处理的更改永久生效 / 立即撤销。
service_list / service_control工具,<service>.detail / <service>.<action>procd 服务;service_list detail=NAME 返回已安装附加组件自身的状态(tailscale、AdGuard Home、podkop;普通列表会列出此处已安装的服务);拒绝停止或禁用 dropbear、network、rpcd 或 openwrt-mcp。service_control 会等待状态稳定(wait),并说明状态是否未稳定;如果最终状态与操作相矛盾,则返回 NOT_APPLIED。
pkg_query工具已安装、可升级、搜索、信息、文件、所有者、策略、apk audit、world;每页 200 行,使用 offset 查看下一页。
pkg_change<action>.<pkg> / upgradeapk add/del/upgrade;除非 commit=true,否则仅模拟操作;报告新建的 .apk-new 文件;之后检查 /etc/apk/world。
pkg_config_diff工具将每个 .apk-new 与当前文件进行差异对比——对于 /etc/config/*,按设置进行对比(uci show),因此引号/缩进差异不会造成干扰。
pkg_config_resolve当前路径keep_current(丢弃新的默认配置)或 use_new(安装新配置;UCI 配置启用回滚保护)。
sysupgrade<action>list(保留的文件)、test(验证 /tmp 中的映像)、check(owut)、backup(模式 0600;删除此工具创建的上一个归档)。绝不会刷写固件。
wg_list_clients工具对等节点的握手时长、端点、流量、配置/内核不匹配情况、重复名称。
wg_new_clientwireguard.<iface>;reveal=true 时使用 wireguard.<iface>.reveal密钥对、下一个可用地址、保存到网络配置中的对等节点;uci_confirm 会保留此更改。私钥和配置会写入仅 root 可访问的文件;使用 openwrt-mcp wg-show <name> 获取(配置和 QR 码会显示在你自己的终端中)。reveal=true 则会在结果中返回它们。
wg_remove_clientwireguard.<iface>.<name>按名称、密钥或区段从配置和接口中移除;启用回滚保护(uci_confirm 会保留此更改)。如果某个对等节点在最近 3 分钟内连接过,或是当前会话自己的对等节点,则拒绝移除,除非 force=true。
ubus_list(不受门控)发现:所有对象、方法和参数签名。
ubus_call<object>.<method>总线上其他任何内容。超过 8 KB 的回复会删减较长数组。
execargv[0]单个程序,不使用 shell。对 sh、find、awk、env、ssh 等的授权相当于 root shell:没有 --shell-equivalent 时,allow 会拒绝授权。
mfa_unlock(不受门控)打开 MFA 门控工具的 TOTP 时间窗口。
提示词。 显示 MCP 提示词的客户端(通常作为斜杠命令)可获取五个操作指南:
router-health、who-is-online、secure-my-router、wifi-doctor 和 upgrade-plan。每个指南都用几行文字列出要调用的工具,并以报告或提案结尾,绝不执行任何更改。它们只使用 @readonly 预设授予的权限,因此只读客户端可以按指南操作到底,而且只有在使用时才会产生费用。列出或获取指南不会在路由器上运行任何操作,因此不适用任何策略,也不会留下审计记录。

警告、引用、对等端和附加组件

试运行中的警告。 uci_apply dry_run 会在末尾显示 warnings from this change:列出此次更改会新增、但路由器当前尚不存在的问题,按与审计相同的方式排序,每项都附有后续步骤和 OpenWrt wiki 页面。这些只是建议,不会拒绝操作。问题包括:向互联网开放端口或端口转发、将 WAN 区域的输入或转发设为 ACCEPT、启用 SSH 密码登录、SSID 未加密、某项更改移除或拼错名称但其他区段仍在使用该名称(ref-removed、ref-unknown;大小写不匹配时会提示“是否想找”),以及关闭你当前会话所连接的 Wi-Fi 网络(radio-in-use)。更改前就已存在的问题不会再次报告。实际应用更改时不会重复这些警告,因此请先运行试运行。

管理路径跟随你的会话。 除 LAN、其网桥、SSH 监听器以及 SSH 所用的区域和规则外,你自己的 SSH 连接所经由的接口也属于管理路径(守护进程会使用 ip route get 解析客户端地址),因此,对你当前连接所经的 WireGuard 隧道进行更改时,也需要探测或设置 force=true,与更改 LAN 一样。

重命名或删除接口、区域或设备之前。 uci_get refs=lan 会列出定义该名称的对象(网络接口、防火墙区域、设备、无线电、mwan3 成员或策略),以及使用该名称的所有引用字段:区域 network、规则/转发/重定向的 src 和 dest、dhcp interface、无线 network 和 device、网络 device、ports、route.interface、以及安装后可用的 sqm 和 mwan3。“defined as: nothing” 表示没有区段使用该名称(可能是拼写错误或大小写不匹配)。这里只会读取这些字段,因此不能用此搜索查找键。它需要 refs 作用域,@readonly 和 @operator 都包含该作用域;config=firewall 可将范围缩小到单个配置。

添加和移除 WireGuard 对等端属于常规的、带回滚保护的更改。 对等端会写入 /etc/config/network,并由 netifd 加载,因此其他对等端的会话会保持连接。两个工具都会等待对等端出现在运行中的接口上(或从中移除),并说明验证结果。只有执行 uci_confirm 后更改才会永久生效;否则,超时或重启后,对等端会再次被移除,新客户端的密钥文件也会被删除。一次只能有一项更改等待确认。若要移除你自己的会话所经由的对等端,需要设置 force=true。

附加组件状态。 service_list detail=NAME 会读取已安装的附加组件(其 init 脚本存在),并以只读方式输出一组固定字段:它绝不会回显配置文件或命令的原始输出,因为其中可能包含登录哈希、代理链接、节点密钥和令牌。授权时需要该服务的作用域(tailscale.detail,或像 @readonly 中那样使用 *);普通列表不需要任何作用域。由他人设置的文本(例如 tailnet 对等端的主机名)会被截断、清理并标记为不可信。

附加组件报告内容已验证
tailscale状态、tailnet DNS 后缀、本节点地址、通告的路由、出口节点、运行状况、对等端(在线状态、直连或中继、流量)GL-MT6000,25.12
AdGuard Home运行状态、监听地址、防护和过滤开关、上游(仅方案和主机)、引导服务器、过滤列表和规则数量、dnsmasq 是否转发至该服务GL-MT6000,25.12
podkop版本、sing-box 运行状态、nft 表、dnsmasq 路径、设置、每个区段一行(类型、接口、社区列表、动态列表大小)GL-MT6000,25.12

有意不读取:AdGuard Home 的 Web API(需要 UI 登录)、podkop 的 proxy_string、选择器和出站设置,以及用户的域名和子网列表。

安装

在路由器上从发布版本安装(除默认的 25.12 镜像外,无需其他内容):

wget -O /tmp/install-router.sh https://github.com/stanislav-testhub/openwrt-mcp/releases/latest/download/install-router.sh
sh /tmp/install-router.sh

它会根据路由器的 DISTRIB_ARCH 选择归档文件,根据发布版本的 SHA256SUMS 校验文件(校验不匹配时拒绝安装),然后进行安装。设置 VERSION=v1.2.0 可选择最新版本以外的发布版本。每个发布文件还附带构建证明: gh attestation verify <file> --repo stanislav-testhub/openwrt-mcp。

在 PC 上从源代码构建(需要 Go、ssh 和 tar;Windows 上可使用 Git Bash):

git clone https://github.com/stanislav-testhub/openwrt-mcp
cd openwrt-mcp
[email protected] SSH_PORT=22 SSH_KEY=~/.ssh/id_ed25519 ./install.sh install

ROUTER、SSH_PORT 和 SSH_KEY 是路由器的 root 登录信息(默认值:[email protected]、 22,以及 ssh 的默认设置)。./install.sh install --release [vX.Y.Z] 会跳过构建,让路由器改为获取经过验证的发布版本;未找到 Go 工具链时也会采用这种方式。

无论哪种方式,都会在路由器上运行同一个安装程序。

  • 它会安装:
    • /usr/bin/openwrt-mcp;
    • init 脚本;
    • 默认配置,不会覆盖现有配置;
    • keep.d 条目;
    • LuCI 页面。
  • 随后启用并启动服务。
  • 如果 uci_apply 正在等待确认,它会拒绝重启守护进程,因为这会导致配置回滚;除非设置 FORCE=1。
  • 它会先检查二进制文件是否能放入 overlay。

./install.sh uninstall [--purge] 可撤销安装。

从 LuCI 批准

安装后,LuCI 中的 Services -> MCP Server 会显示守护进程(运行或已停止、版本、HTTP 监听器、stdio 套接字)、待处理的 uci_apply 及其自动回滚截止时间、已配对的 HTTP 客户端、生效中的策略和审计日志。你也可以在此处回复 Agent,因此无需打开 shell 粘贴 allow 命令。

  1. Agent 调用了一项尚未获授权的工具。该调用会被拒绝,并记录一条请求:客户端、工具、未通过检查的确切范围以及经过脱敏的参数。这些信息均由守护进程生成,不包含来自 Agent 的文本。
  2. 请求会显示在 MCP 页面顶部,也会显示在 Status -> Overview 中的小组件内;仅在有待处理请求时才会显示该组件。
  3. Allow 会添加授权,Deny 会拒绝请求。默认授权范围最窄(仅限此工具在此确切范围内使用,时长 1 小时)。通过下拉选项可扩大范围(该范围所属的类别、所有范围、@operator 预设),并将时长设为 15 分钟至 7 天。对于请求 root-shell 工具的请求,页面会显示警告,并要求再次确认。
  4. 设置 option request_wait '300'(单位为秒,范围 0 至 600,默认值为 300;0 表示立即拒绝)后,被拒绝的调用会进入等待状态;只要有授权覆盖该调用,它就会自动继续,Agent 无需重试。若未设置此选项,或等待超时,Agent 会收到带有请求 ID 的 POLICY_DENIED;获批后重试即可。allow 命令仍会显示出来,CLI 也会像按钮一样释放等待中的调用。

请求会去重,数量有限(每个客户端最多 5 条未处理请求,总计最多 50 条),并在 request_ttl 指定的时长后过期(默认 10 分钟)。请求保存在 RAM 中,因此重启后会清除。只有读取 ACL 权限的 LuCI 用户可以查看请求,但看不到按钮;写入操作需要 luci-app-openwrt-mcp-write。

其他标签页:Permissions(预设、按组排列并带有风险徽章的工具、Revoke)、Activity(带筛选器的审计日志)、Settings(请求等待时间和 TTL、可选通道、二次验证状态)。

允许全部权限。 Permissions 标签页底部有一个红色区域,可授予客户端对所有范围内所有工具的使用权限,包括 exec:也就是 root shell。最长为 24 小时(不提供“永不”选项),需要勾选复选框并输入客户端名称;已启用二次验证的客户端无法使用此选项。启用期间,两个页面都会显示红色横幅,并提供 End now 按钮。通过此方式授权的调用会在审计日志中标记为 "via":"permit-all"。对应的 CLI 命令为 openwrt-mcp allow <client> @all 1h --shell-equivalent。建议逐条批准请求。

可选通道安装后默认关闭;每个通道都可通过 Settings 中的开关启用,旁边会显示相关风险提示,每次切换都会记入审计日志。

通道选项功能可能造成的危害
从 LuCI 配对pairing一个 客户端 选项卡:粘贴 openwrt-mcp connect 输出的密钥,为客户端命名,并选择一个起始预设(默认为只读)。写入与 authorize-key 相同的强制命令行。任何拥有 LuCI 写入权限的人都可以添加一个通过 SSH 访问路由器的密钥。
推送钩子push_hook每个新请求都会触发 ubus 事件 openwrt-mcp.request,并运行 /etc/openwrt-mcp/hooks.d/ 中的可执行文件,将已脱敏的请求作为 JSON 写入标准输入(限时 10 秒;跳过组或全局可写、不可执行以及点文件)。适用于 Telegram、ntfy 或 Home Assistant。通知不会批准请求;它会指向 LuCI。请求详情会离开路由器,而且钩子以 root 身份运行。
在聊天中批准chat_approve、list chat_approve_client支持协商 elicitation 的客户端也会在自己的提示词中显示请求。仅限确切范围,可授权 15 分钟、1 小时或 8 小时,绝不适用于 root shell 工具或 全部允许。客户端会转达你的回答。会自动回答提示词的客户端或 Agent 可能会自行授权。

Agent 无法做什么。 LuCI 管理员本身已是 root,因此按钮不会赋予任何人更多权限。必须防止的是 Agent 扩大自己的授权,因此内置的 拒绝底线 无论授权内容如何(包括 @operator 和 @all)都会拒绝:对对象 openwrt-mcp、session、file 和 rpc-sys 执行 ubus_call,以及写入 openwrt-mcp 和 rpcd 配置的 uci 操作;对二进制文件及其 rpcd 插件,或策略、rpcd 配置、/etc/openwrt-mcp 和请求状态执行 exec;以及对这两个配置执行 uci_apply、历史记录 restore 和 pkg_config_resolve。已经拥有 root shell 的客户端不在此底线的保障范围内:请参阅 SECURITY.md。设计说明:docs/luci-approvals.md。

在真实的 OpenWrt 25.12.5 路由器(包括 LuCI 手机布局)和 24.10.8 虚拟机上运行测试:LuCI 页面在无头 Chromium 中的渲染、批准、拒绝和 root shell 确认流程、rpcd 拒绝只读登录的写入调用,以及 Overview 小组件仅在有待处理请求时显示。稍后会添加截图。

为什么不用 .apk: 25.12 软件包是 apk-tools v3 ADB 归档。构建软件包需要 OpenWrt SDK,或带有 mkpkg 的主机 apk-tools(路由器上的 apk 没有 mkpkg),而未签名的软件包无论如何都需要使用 --allow-untrusted。安装为文件的静态二进制不会损失任何功能:/etc/config 会在 sysupgrade 中自行保留,而 /lib/upgrade/keep.d/openwrt-mcp 会保留二进制文件、init 脚本、rc.d 链接、状态和 LuCI 页面。(owut/ASU 镜像会重新构建软件包,而不是散装文件——keep.d 条目正是用来确保这些文件能在一次升级中保留的。)


连接

openwrt-mcp connect(推荐)

此版本也提供适用于 PC 的二进制文件(openwrt-mcp_<version>_windows_amd64.zip、darwin_arm64.tar.gz 等;Linux PC 可使用 linux_ 压缩包)。将其解压到 PATH 中的任意位置;它需要 OpenSSH,Windows 10 及更高版本、macOS 和 Linux 均已提供 OpenSSH。然后,在 PC 上运行:

openwrt-mcp connect --client claude-code --host 192.168.1.1          # or claude-desktop, codex, cursor, gemini, vscode

它会创建专用的 ed25519 密钥(~/.ssh/openwrt_mcp,绝不会覆盖现有密钥),打印需要在路由器上运行一次的两条命令,并显示要添加到客户端的配置项。添加 --write 后,它会完成最后一步:对于 Claude Code 和 Codex,调用它们各自的 mcp add;对于 Claude Desktop、Cursor、Gemini CLI 和 VS Code,则会将一个 openwrt 配置项合并到它们的 JSON 文件中(保留其他服务;原文件只备份一次,文件名为 <file>.before-openwrt-mcp;如果文件带有注释,则不修改文件,而是打印配置片段)。配置项会将 ssh 及其参数存为列表,因此密钥路径中含有空格时无需加引号,并且会设置保活选项,以便检测路由器重启。

openwrt-mcp connect doctor --host 192.168.1.1

会按顺序检查整个链路,遇到第一个故障时停止,并提供相应的修复方法:

[ ok ] ssh reachable   192.168.1.1:22
[ ok ] host key
[ ok ] key accepted    /home/you/.ssh/openwrt_mcp
[FAIL] forced command  the key logged in, but nothing answered as an MCP server (...)
       fix: the key is probably authorized without its forced command ...
[skip] daemon socket

检查步骤依次为:SSH 可访问、主机密钥、密钥已接受、强制命令、守护进程套接字、initialize、tools/list。密钥错误、缺少强制命令、守护进程已停止以及套接字已禁用,都会分别指出对应的步骤。

重启或守护进程重启后。 重启、install.sh 升级或崩溃都会结束所有打开的会话:openwrt-mcp stdio 桥接进程会退出,并显示 daemon closed the connection,客户端会显示服务已断开连接。MCP 客户端并非都会自动重新连接,因此请从客户端重新连接(使用其 MCP 菜单,或重启客户端)。启动后,SSH 会比守护进程开始监听早几秒响应;桥接进程会等待最多 10 秒,等待守护进程套接字就绪,之后才会报错 daemon not reachable ... after waiting 10s(以及 [code: TIMEOUT; ...])。因此,路由器恢复运行后立即重新连接通常就能正常工作;如果不行,请等待几秒后再试。仍在等待 uci_confirm 的更改会在下次启动时回滚。openwrt-mcp status 会显示最近一次断开连接的情况及原因(client closed the connection、connection error: ... 或 the daemon restarted (router reboot, upgrade or crash));这些信息来自审计日志,日志中还会为每个会话记录一行 stdio session closed after ...,并在每次启动时记录一行 daemon started。

更精简的会话,以及 Shell

请求的权限少于密钥允许的权限。 策略决定客户端可以做什么;客户端也可以为某个会话请求更少的权限。

openwrt-mcp connect --client claude-code --host 192.168.1.1 --read-only            # nothing that changes the router
openwrt-mcp connect --client claude-code --host 192.168.1.1 --toolset diag,pkg      # only those tools

工具集包括 diag(logread、network_clients、firewall_show、net_diag、service_list、ubus_list、ubus_call)、 config(uci_get、uci_apply、uci_confirm、uci_rollback、service_control)、pkg(pkg_query、pkg_change、 pkg_config_diff、pkg_config_resolve、sysupgrade)和 wg(三个 WireGuard 工具)。system_status 始终可用; exec 不属于任何工具集。--read-only 是与客户端授权取交集后的 @readonly 预设,因此它只能移除权限;调用隐藏工具时会被拒绝,而不仅仅是不显示在列表中。配置文件会作为强制命令后的参数传递(SSH_ORIGINAL_COMMAND),由守护进程按照固定语法解析:任何其他内容都会被拒绝并记入审计日志,而且其中没有任何参数可以扩大会话权限。

从 Shell 或脚本调用,无需 MCP 客户端。 同一个 SSH 密钥每次运行一个工具:

ssh -T [email protected] call --list
ssh -T [email protected] call uci_get --help
ssh -T [email protected] call uci_get '{"config":"network"}'
ssh -T [email protected] -- --read-only call uci_get '{"config":"network"}'   # after a profile word, `--` ends ssh's own options

结果会输出到 stdout;拒绝或错误会连同 allow 行输出到 stderr,并以状态码 1 退出。调用会经过与 MCP 会话相同的策略、审计和脱敏流程。对于偏好使用 Shell 而非 MCP 的 Agent,skills/openwrt-mcp/SKILL.md 是一个可直接使用的技能文件。

限制。 每个工具都有一个高于其自身等待时间的截止时间,同时最多运行 8 个调用:其余调用最多等待 30 秒,之后会收到 busy 拒绝(TIMEOUT)。客户端取消调用时,会终止该调用启动的命令。uci_confirm、uci_rollback 和 mfa_unlock 无需等待空闲名额。

手动通过 SSH 使用 stdio

在路由器上绑定到 MCP 桥接程序且不授予其他权限的专用密钥:

ssh-keygen -t ed25519 -N '' -f ~/.ssh/openwrt_mcp
ssh root@router "openwrt-mcp authorize-key claude-code '$(cat ~/.ssh/openwrt_mcp.pub)'"
ssh root@router "openwrt-mcp allow claude-code @readonly 30d"
claude mcp add openwrt -- ssh -T -i ~/.ssh/openwrt_mcp -o BatchMode=yes root@router

authorize-key 会在 /etc/dropbear/authorized_keys 中写入类似 command="/usr/bin/openwrt-mcp stdio --client claude-code",no-port-forwarding,no-agent-forwarding,no-X11-forwarding,no-pty ssh-ed25519 ... 的一行:该密钥会以客户端 claude-code 的身份使用 MCP 服务,且无法打开 Shell 或转发端口。如果密钥已用于 root 登录,则会拒绝处理。

桥接程序会将会话转发到仅限 root 访问的 Unix 套接字(/var/run/openwrt-mcp/mcp.sock,位于权限为 0700 的目录中,套接字权限为 0600)。工具由守护进程运行,因此在一个会话中启动的回滚操作会在该会话结束后继续生效。套接字上的客户端名称是经过断言的,而非经过验证的——这是安全的,因为只有 root 才能连接,而 root 本就可以修改策略文件;SSH 密钥才是真正的身份验证方式。

在 Windows 上,内置的 OpenSSH 客户端(ssh.exe)可用于此操作;密钥请使用完整路径。

通过隧道使用 HTTP

与上游保持一致:openwrt-mcp pair <client> 会输出一次承载令牌; 然后执行 ssh -N -L 8730:127.0.0.1:8730 root@router; 接着执行 claude mcp add --transport http openwrt http://127.0.0.1:8730/mcp --header "Authorization: Bearer $TOK"。 守护进程拒绝绑定除回环地址以外的任何地址。

让策略真正发挥作用

如果 Agent 还能通过 Shell 工具运行 ssh root@router ...,那么策略就只是建议。请在客户端中禁止此操作(Claude Code:添加针对 Bash(ssh * root@<router>*) 的 permissions.deny 规则),或者将 root 密钥保存在 Agent 无法读取的位置。


策略

默认拒绝。策略会向一个客户端授予一组工具、范围 glob(* 和 ? 是通配符;方括号按字面匹配)、每分钟调用上限和有效期。对 /etc/config/openwrt-mcp 的更改无需重启即可生效。

openwrt-mcp allow claude-code @readonly 30d          # every read tool; ubus_call on an allow-list of read methods
openwrt-mcp allow claude-code @operator 7d           # + uci_apply/confirm/rollback, service_control, pkg_change,
                                                      #   pkg_config_resolve, wg_new/remove_client, sysupgrade backup
openwrt-mcp allow claude-code uci_apply 'dhcp.* wireless.*.disabled' 30d
openwrt-mcp allow claude-code service_control 'dnsmasq.restart adguardhome.*' 30d
openwrt-mcp revoke claude-code                        # remove every policy for the client
openwrt-mcp prune --older-than 7d                     # delete grants that expired over a week ago (audited)
openwrt-mcp policies | status [--all] | clients

再次授予相同的工具和范围时,会替换客户端已过期的授权,而不是新增一条授权。status 会用一行统计过期授权;--all 会列出它们。

两个预设都不包含 exec 或不受限制的 ubus_call;这两者都会绕过安全防护。只读 ubus 列表明确列出 object.method 对,而非 *.list 这类 glob,因为 session.list 会返回 LuCI 会话 ID。uci_get '*' 会读取所有配置,但机密选项(Wi-Fi 密钥、WireGuard 密钥、密码、令牌)的值会以 <redacted> 返回;参见 安全模型。exec 不会隐藏敏感内容。

拒绝信息会指出未覆盖的范围,并打印能够覆盖它的确切 allow 行。

exec 范围按字面取 argv[0],因此授予 find 或 awk 看似无害,实则相当于授予 root shell:这两个命令都会运行其他程序,shell、env、nice、flock、timeout、xargs、tar、ssh、lua、ucode、apk 和 opkg 也一样。allow 会拒绝此类授权,除非你添加 --shell-equivalent;判断依据是基本名称,因此 /usr/bin/../bin/sh 和 /bin/* 也算在内;能够访问 file.exec 的 ubus_call 授权也会受到同样处理。openwrt-mcp policies 和 status 会标记此类授权,也包括手动编写的策略。此列表只是最低限度,并非安全保证:能够写入文件的授权(wget、cp、dd),或启动未列入其中的解释器,仍可能被滥用。

第二因素

与上游保持一致:openwrt-mcp mfa enrol <client> 会打印一个 otpauth:// URI;然后在策略中设置 list mfa_tools 'uci_apply' 和 option mfa_window '15m'。一个验证码可开启限时窗口;验证码只能使用一次;解锁状态仅保存在内存中。

仅注册不会启用任何限制。只有 /etc/config/openwrt-mcp 中带有 mfa_tools 的 config policy 段会启用限制,而且 mfa_tools 只能指定同一策略授予的工具。通过 openwrt-mcp allow 或 LuCI 页面授予的权限从不需要验证码。openwrt-mcp mfa status 会提示已注册客户端何时没有启用限制的策略。

错误验证码会受到限制:在 config server 段中,连续输错 option mfa_max_failures '5' 次后,该客户端将无法使用 mfa_unlock,锁定时间为 option mfa_lockout '300' 秒('5m' 也有效);每次再次触发时,锁定时间都会翻倍,最长为一小时。锁定期间输入正确验证码也会被拒绝,且不会消耗验证码。没有密钥的客户端也按相同方式计数,因此该限制不会泄露客户端是否已注册;计数器保存在内存中(重启、执行 mfa enrol 或轮换密钥都会清除计数器)。每次输错验证码都会记录为 ERROR,锁定则记录为 DENIED;验证码本身绝不会被记录。代价是:任何持有客户端令牌的人都可以让该客户端最长被锁定一小时。


安全模型

  • 回滚机制能应对最可能发生的情况。 uci_apply 会将修改过的配置快照保存到 /etc/openwrt-mcp/rollback/<token>/(并执行 fsync),在提交前写入待处理记录,随后提交、重新加载并启动计时器(默认 90 s,最长 600 s)。如果没有调用 uci_confirm,文件会被原子恢复,暂存的更改会被撤销,服务会重新加载。如果路由器在时间窗口内断电重启,procd 会在网络启动后(S95)启动守护进程,由其在启动时执行回滚,并为每个恢复的配置强制触发一次 config.change。
  • 不碰其他人的编辑。 只要要提交的配置存在其他会话尚未提交的更改,或有尚未确认的应用操作待处理,所有会提交配置的工具都会拒绝运行。
  • 重新加载前先检查。 只要服务提供了检查器,试运行就会用它检查暂存配置(防火墙使用 fw4 check),并且只列出此次更改新增的问题。除非设置 force=true,否则实际应用会拒绝这些问题;原本就有问题的配置仍可修复。dnsmasq、dropbear 等没有能检查暂存更改的检查器:它们会标记为“未检查”,而非“通过”。
  • 写入后再读回。 uci_apply 会在提交后、重新加载前重新读取每个配置;wg_new_client、wg_remove_client 和 pkg_change 会检查之后的对等端表、配置和 /etc/apk/world;service_control 会将最终状态与操作进行对比。路由器接受了写入却未保留时,会返回 NOT_APPLIED,而 uci_apply 的回滚仍处于待命状态。无法执行读回检查时,会显示“未验证”。位置段(@rule[3])无法进行对比,系统会统计其数量。
  • 不静默覆盖。 uci_get 会在末尾返回配置的修订版本。将其作为 expected_revisions 传回,如果期间配置发生更改(例如通过 LuCI 或其他客户端),应用操作会以 CONFLICT 拒绝。
  • 管理路径受保护。 除非调用指定了 probe(ping 网关、解析域名)或设置了 force=true,否则会拒绝更改 LAN 接口或其网桥、SSH 监听器,或允许 SSH 进入的防火墙区域和规则。重新加载后会运行探测,并报告结果。此类更改的回滚窗口默认为 180 s。规则集是静态的(LAN 加上 dropbear 指定的任何内容),因此适用于常见布局,但并非所有网络拓扑。
  • 历史记录与恢复。 确认更改后,系统会保留更改前的配置版本(每个配置保留最新的 history_keep 个版本,默认为 5,存放于 /etc/openwrt-mcp/history/)。 uci_get history=list 会显示历史版本,history=diff:<id> 会将某个版本与当前版本比较,uci_apply restore=<id> 会通过相同的快照、检查和回滚计时器恢复某个版本。历史记录和快照一样包含密钥:权限为 0600,显示时会遮蔽,并包含在 sysupgrade 备份中。设置 option history_keep '0' 可关闭此功能。
  • 禁止自行提升权限。 无论授予何种权限,uci_apply 和 pkg_config_resolve 都会拒绝修改 openwrt-mcp 的策略配置,因此客户端无法为自己写入更宽泛的策略。策略只能通过路由器上的 openwrt-mcp allow / revoke 修改。
  • @operator 权限很强。 对 * 使用 uci_apply 可访问所有 UCI 配置,包括 dropbear、rpcd 和 uhttpd;pkg_change 则会以 root 身份安装软件包。请仅为工作会话授予权限(2h),并将长期授权限制在较窄的范围内,例如 'dhcp.* wireless.*.disabled'。
  • 名称会根据 UCI 语法进行验证,因此像 lan.ipaddr=x 这样的段名称无法让 uci 操作策略批准范围之外的其他键。
  • 任何地方都不使用 shell。 所有命令都以 argv 形式执行;可能被解释为选项的目标和名称(例如 -f)都会被拒绝。
  • 生命线保护。 service_control 不会停止或禁用 dropbear、network、rpcd 或 openwrt-mcp。sysupgrade 不提供刷写操作——可用 test 验证镜像,再手动刷写。除非强制执行,否则 wg_remove_client 不会断开最近 3 分钟内完成过握手的隧道,也不会断开当前会话所用的隧道。
  • 凭据不会进入对话。 wg_new_client 会将新客户端的配置(包含私钥)写入套接字旁仅 root 可读的 RAM 文件,并返回公钥和要运行的命令。在路由器上运行 openwrt-mcp wg-show <name>,即可在终端打印配置和二维码并删除该文件;未读取的文件会在 24 小时后或下次重启时删除。只有设置 reveal=true 才会将密钥放入结果,进而进入模型上下文和提供商日志。写入 sysupgrade 备份前会先创建权限为 0600 的备份,并且只保留最新的一份。审计日志会记录参数和摘要,不会记录工具输出,并会遮蔽看起来像密钥的字段。服务器私钥会从 wg show dump 读取后立即丢弃。
  • 工具输出中的密钥会被遮蔽。 uci_get、uci_apply(差异和错误)、pkg_config_diff、pkg_config_resolve、uci_confirm、uci_rollback、system_status 和 ubus_call 会保留行的结构,并将密钥选项(key、key1..key4、psk、password、sae_password、passphrase、private_key、preshared_key、token、pwd、任何包含 secret 的内容等)的值替换为 <redacted>。审计日志使用相同的规则。exec、logread 和诊断信息不会遮蔽,wg_new_client 则是有意豁免的。在 config server 区段中:list redact_extra 'vendor_blob' 可添加名称;option redact_output '0' 可关闭遮蔽,并会记录在审计日志中。经过遮蔽的差异不会显示密钥的具体变化。
  • 路由器输出不可信。 主机名、SSID、日志行和 DNS 应答都由其他设备决定。所有结果都会移除终端转义序列、控制字符、双向文本和零宽字符以及无效 UTF-8,并将行截断至 1024 字节(exec、ubus_call、wg_new_client 和配置工具除外)。network_clients、logread 和 net_diag 的开头会显示一行 [untrusted text: ...]。这能降低模型服从此类文本的可能性,但无法彻底消除风险,因此请勿在读取这些内容的会话中开放写入权限。
  • 状态不会暴露到 Web。 如果状态目录、审计日志或套接字位于 /www、cgi-bin 目录或 uhttpd 的 home 目录之下(包括通过符号链接指向这些位置),系统会拒绝使用。
  • SECURITY.md 中提供了威胁模型。
  • 上游的发现仍然适用: rpcd ACL 不会约束本地 ubus 套接字上的 root 进程,因此不会依赖它;rpcd 自带的应用/回滚功能会将状态保存在 rpcd 内存和 tmpfs 中,所以快照由我们自行管理并保存在闪存上。

已验证

在 GL-MT6000 上,使用 install.sh 中的步骤安装原版 OpenWrt 25.12.5,并完全按照 Claude Code 的方式从 Windows 驱动(使用带强制命令密钥的 ssh.exe 和 stdio 桥接):

  • **安装:**通过 procd 服务运行并启用 respawn,设置 S95/K10 链接,在 0700 目录中创建权限为 0600 的套接字,仅在 127.0.0.1 上启用 HTTP,在 dropbear 的 authorized_keys 中添加强制命令行;@readonly 和限时 @operator 策略无需重启即可生效。
  • **读取实时数据:**列出全部 23 个工具及其架构和服务端说明;system_status、network_clients、wg_list_clients(重复名称警告)、service_list、pkg_query upgradable、pkg_config_diff(11 个真实 .apk-new 文件、设置级别差异)、firewall_show check、从 br-WAN 执行的 net_diag ping、logread 过滤、uci_get、sysupgrade list、ubus_call。
  • 可回滚的写入(在临时 UCI 配置上):uci_apply(包括 set_list)-> 在截止时间自动回滚,文件恢复为逐字节一致,并移除 pending.json;有待处理操作时拒绝第二次 apply;uci_confirm;按需执行 uci_rollback;dry_run。
  • **WireGuard:**使用预共享密钥执行 wg_new_client(下一个可用地址,将对等端提交到 UCI 并热添加到内核,CGNAT 端点警告),列出后再执行 wg_remove_client——/etc/config/network 恢复为逐字节一致,内核中的对等端已移除。1.3.0 的交接流程(第二次运行,在通过 uci_apply 为测试启用、之后又禁用的接口上运行,配置修订版本与之前完全一致):结果不包含私钥;配置位于 /var/run 下的 0700 目录中,是仅 root 可读写的 0600 文件;在操作员终端运行 openwrt-mcp wg-show 会打印配置和 QR 码,并删除该文件。1.4.0 的回读会针对 uci_apply、wg_new_client 和 wg_remove_client 打印 Verified: ...,每项操作都在启用回滚后应用并确认。
  • **崩溃恢复:**守护进程在未确认 apply 时被 SIGKILL;procd 在 6 s 后重新启动它,并在启动时回滚更改。
  • 拒绝情况:exec、ubus_call session.list、超出范围的 uci_apply(每项都会指出授权行)、对策略配置执行的任何 uci_apply、未经身份验证的 HTTP(已审计)。
  • 输出安全(1.1.0),在同一台设备上:uci_get wireless 和 ubus_call network.wireless status 对每个 Wi-Fi key 都返回 <redacted>,而 ssid、encryption 和类似名称(如 wpa_disable_eapol_key_retries)不受影响(JSON 回复中的数字、布尔值和数组也一样)。设置 key 的 uci_apply dry run 会对其进行脱敏,且不会留下暂存内容。network_clients 和 logread 的输出以不可信文本标记开头。没有密钥的客户端输入 5 个错误的 mfa_unlock 代码后会被锁定 5 分钟(第 6 次会被拒绝并提示重试时间);审计日志会记录 4 条 ERROR,随后是 DENIED,且不会出现任何提交的代码。通过 ubus call log write 写入的 syslog 行包含 CSI 和 OSC 序列、颜色代码、BEL 及双向文本覆盖字符(这些内容在路由器自己的日志中都存在),经 logread 返回时只剩文本和制表符;超长行则会截断到 1024 字节,并附上 [+N bytes] 说明。路由器上捕获了 uci changes 的语法(+=、-=、'\''、用于删除的裸 -path),并将其作为测试固定数据。
  • 可靠更改(1.2.0),在同一台设备上通过守护进程的 stdio 桥接驱动:fw4 check 会识别暂存在 uci 中的更改(对于无效值,它会以 0 退出并打印 [!] 行,验证逻辑会读取这些行):设置错误区域值的 dry run 会将其报告为新的问题,实际 apply 会被拒绝,且不会留下暂存或快照内容。过时的 expected_revisions 会导致 CONFLICT;当前值可通过检查;调用未更改的配置对应的值会被拒绝。对 LAN 接口执行 dry run 会指出管理路径;未进行探测就实际 apply 会被拒绝,且不会留下暂存内容。若 system.ntp.server 中已存在某个元素,则会跳过对该元素执行 add_list。在 luci 的临时选项上:执行带有 ping 和 resolve 探测的 apply 后,重载完成时两项都会报告 OK,并启用回滚;确认后会留下历史记录(0700 目录,0600 文件);history=diff 会显示更改添加的内容;restore 会将文件恢复为逐字节一致并保留原权限,执行探测并留下自己的记录;探测失败的 apply 会报告 PROBE FAILED,同时仍启用回滚,而 uci_rollback 会将文件恢复为逐字节一致。守护进程的 service_control restart 会报告其状态“Settled after”。
  • **接入与凭据(1.3.0),在同一台设备上:**使用 install.sh 安装构建版本,openwrt-mcp version 报告 1.3.0。Windows PC 上使用真实 OpenSSH 执行 connect doctor,七个步骤全部通过并列出 23 个工具;使用路由器不认识的密钥时会停在 key accepted,端口关闭时则停在 ssh reachable,两种情况都会给出修复方法。为 Cursor 执行 connect --write 时,使用 PC 自带的 ssh-keygen 生成密钥,将条目合并到 JSON 文件中;再次运行时会报告条目未更改。diag 会打印版本、设备板型、策略结构和审计行,将 SSH 客户端地址显示为 ip-1,且不包含审计参数;--detail 会额外显示脱敏后的摘要。sysupgrade backup 创建了权限为 -rw------- 的归档(4.3 MB,因此 sysupgrade -b 会保留已存在文件的权限);第二次备份会删除第一次备份。重启后守护进程的常驻内存约为 10 MB。
  • **会话清理(1.3.0),在同一台设备上:**守护进程重启后,在新构建版本上启动的 stdio 桥接会立即退出;客户端的下一次调用会打开新会话,且不会报错。仍运行旧构建版本的桥接会继续运行,直到客户端发出下一个请求。status 会将 45 个过期授权合并为一行;prune 会将其移除,写入一条 prune 审计记录,守护进程重新加载后仅保留有效授权。
  • 诊断(1.4.0),在同一台设备上,由 @readonly 客户端执行:system_status mode=doctor 列出一项低严重性发现:有三个等待合并的 .apk-new 文件;这与实际情况一致,且没有误报。mode=audit 列出允许从 WAN 接入 WireGuard 端口的防火墙规则(确实存在且符合预期)和监听所有地址的 uhttpd(确实如此),并为每项提供 wiki 链接和下一步调用。logread mode=summary 将一千多行日志合并成约 50 条不同消息。先保存基线,再在定时任务运行后进行比较,每次都会将一条监控日志计为新增,因为其中的 uptime 字段发生了变化;因此将时长替换为 <dur>,修复后同样的往返比较不再报告新增内容。net_diag wifi_survey 会给出各无线电当前信道的 busy、rx、tx 和 noise 数值,与当天稍早读取的原始 iwinfo survey 数据相差不超过 1 个点;traffic 会采样接口 3 秒,并按字节数对 conntrack 来源排序;usage 会列出 nlbwmon 中当前月度周期的设备;network_clients 会显示 AIR 列,其占比在每个无线电上相加约为 100%。
  • **连接范围与韧性(1.5.0),在同一台运行 25.12.5 的设备上:**按安装程序的步骤部署构建版本。service_list detail= 读取 tailscale、AdGuard Home 和 podkop。dry-run 警告和 WireGuard 写入操作都在为测试创建的临时接口上运行(绝不使用 LAN 或真实隧道),之后删除该接口:通过启用回滚的路径添加并移除对等端,以及将对等端恢复回来的回滚操作。通过 SSH 从 PC 执行 openwrt-mcp call,以及使用 --toolset diag --read-only 的会话。硬件测试发现,移除一个并非最后一个的对等端后会错误地报告 NOT_APPLIED(UCI 匿名节 ID 按位置排列,因此回读时检查了错误的节);此问题已修复,测试用的假实现现在会像 UCI 一样重新编号。 单元测试和端到端测试(通过内存传输和桥接握手运行真实 MCP 客户端)可在任何操作系统上针对模拟路由器运行:apply/confirm/rollback/timeout、在时间窗口内重启、dry run、拒绝外部暂存的编辑、列出操作、针对捕获的 25.12 配置执行 WireGuard 添加/列出/移除、客户端加入、日志筛选、apk 模拟、.apk-new 回滚解析、预设内容、范围 glob 语义。

尚未在硬件上验证(仅由模拟路由器测试覆盖):带 commit 的 pkg_change、LuCI 页面渲染、真实 /www 路径或符号链接上的 Web 根目录保护、真实断电重启(恢复路径与 SIGKILL 测试所测试的相同),以及真实 sysupgrade 期间的 keep.d。对于 1.2.0,还包括:应用管理路径变更时带有探测(仅运行了不带探测时的拒绝测试)、探测失败后等待回滚计时器触发而非执行 uci_rollback,以及 sysupgrade 后的历史记录。对于 1.3.0,还包括:使用真实 Claude Code、Codex、Claude Desktop、Gemini 或 VS Code 执行 connect --write(仅测试了文件合并和命令行)、macOS 上的任何操作,以及路由器上的安装程序下载路径(发布工作流已运行并发布了 1.2.0 和 1.3.0,但尚未对这些版本运行 install-router.sh)。对于 1.4.0,还包括:硬件上仅对 uci_apply、wg_new_client 和 wg_remove_client 执行了写入后的回读;service_control 和 pkg_change 是针对模拟路由器运行的,日志摘要也只查看过一台路由器的日志。WireGuard 移交的文件模式已在 CI 中通过 Linux 断言。欢迎提交其他板卡的报告。


致谢

  • GlassOnTin/openwrt-mcp(Ian Williams,MIT)——本项目的基础。沿用了其中的内容:运行在路由器上的静态 Go 守护进程(由 procd 管理)、默认拒绝的策略(带范围 glob)、速率限制和过期机制、以摘要形式存储的承载令牌、TOTP 时间窗口、带机密信息脱敏的 JSONL 审计日志、仅限 loopback 的 HTTP、ubus 回复裁剪,以及 uci_apply 的确认或回滚机制。上游项目的 git 历史已保留在本仓库中。

  • jsebgiraldo/openwrt_ssh_mcp——一个路由器外运行的 Python 服务(每次调用都通过 SSH,使用正则表达式进行命令白名单过滤),已作为思路参考。以下四项经过改造后在此重新实现:

    1. 刷写前验证固件映像(sysupgrade -T -> sysupgrade 的 test 操作);
    2. 固件和板卡信息(system_status 的一部分);
    3. 软件包管理(针对 apk 重写:pkg_query、pkg_change);
    4. ping / traceroute / nslookup(net_diag)。

    未采用的内容:OpenThread 边界路由器工具(依赖特定硬件)、opkg 和 iptables 命令(在 25.12 中已移除)、刷写固件工具(远程操作不可逆且无法恢复),以及对 shell 字符串进行正则表达式白名单过滤(改用 argv + 策略范围)。

贡献和安全

欢迎提交 Bug 报告和拉取请求——请参阅 CONTRIBUTING.md。请按照 SECURITY.md 中的说明私下报告漏洞,不要在公开议题中报告。计划中的工作请参阅 ROADMAP.md。

**报告 Bug:**在路由器上运行 openwrt-mcp diag,并将输出粘贴到议题中。它会打印版本、板卡、守护进程状态、策略概要和最近 20 条审计记录(--audit N);其中所有 IPv4、IPv6 和 MAC 地址、主机名、DHCP 名称、SSID、域名和 WireGuard 对等端名称都会替换为占位符(ip-1、mac-1、host-1、ssid-1),且同一值始终替换为相同占位符。它绝不会打印审计参数;只有使用 --detail 时才会打印条目的范围、摘要和错误文本,并以相同方式脱敏。名称会根据路由器自身对名称的描述进行匹配,因此粘贴前请先查看一次输出:仅出现在自由文本中、而未出现在路由器配置中的名称无法识别。

Licence

MIT -- see LICENSE. Copyright (c) 2026 Ian Williams (upstream) and Stanislav Chupin (OpenWrt 25.12 port).

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。