Hermes WPS MCP
by DSMFRP1024
README.md
# Hermes WPS MCP
[](https://github.com/DSMFRP1024/hermes-wps-mcp/actions/workflows/build.yml)
上游 [`lc2panda/wps-skills`](https://github.com/lc2panda/wps-skills) 的**离线部署适配版**,
用于内网 aarch64 Linux(银河麒麟 V10 / 统信 UOS 20 / openEuler)上,
让 Hermes Agent 通过 MCP 直接操作正在运行的 WPS 文档。
- 上游 pin:`a82533662268b3245f93d8685bc45dffede048b6`
- 许可:MIT(与上游一致)
- **不是重写**,是 clone + 环境适配。业务逻辑(243 个工具的实现)一行没动。
- 上游原始说明保留在 [`README.upstream.md`](README.upstream.md)
关于「为什么不直接在 GitHub 上 fork」:本仓库是**独立仓库**,但保留了完整上游历史
(首个提交的父提交就是上游 pin),并且配了 `upstream` remote。这样上游后续修复仍能合并:
```bash
git remote add upstream https://github.com/lc2panda/wps-skills.git # 已配置则跳过
git fetch upstream main
git merge upstream/main # 或 git diff upstream/main 只看我们改了什么
```
GitHub 上的「Forked from」标识是没有的 —— 这是一个内网适配发布仓库,
需要打自己的 release 产物、写自己的 README,独立更清晰。
## 能力
243 个工具:231 个专业工具 + 12 个内置工具,覆盖 Excel 82 / Word 28 / PPT 112 / 通用 21。
能做到「实时」是因为加载项跑在 WPS 自己的 V8 引擎里,直接操作文档对象模型:
**改动立刻上屏、格式自动继承、不与用户的 Ctrl+S 冲突**。
MCP Server 不碰文件,只把命令排进队列,等加载项轮询取走。
```
Hermes Agent
│ MCP (stdio)
▼
wps-office-mcp (node dist/index.js)
│ HTTP 轮询,仅 127.0.0.1:58891
▼
WPS 加载项「WPS助手」(WPS 内嵌 V8)
│ WPS JS API
▼
你正在编辑的文档(内存对象)
```
## 三端
| 平台 | 通道 | 本仓库状态 |
|---|---|---|
| **Linux** | 反向轮询(与 macOS 共用一套) | **适配目标,CI 覆盖** |
| macOS | 反向轮询 | 上游支持,未验证 |
| Windows | PowerShell + COM | 上游支持,未验证 |
## 相对上游改了什么
只有下面这些,逐条列出便于对照回滚。
| # | 文件 | 改动 | 原因 |
|---|---|---|---|
| 1 | `wps-office-mcp/src/client/mac-poll-server.ts` | `switchApp()` 开头加 Linux 分支直接返回 | 原实现调 `wps-auto.sh`,其 `start_app()` 用 macOS 的 `open` 命令(Linux 没有),pptx 分支还依赖 `python-pptx`。内网场景 Hermes 常驻、WPS 用户自己开,无需拉起/杀进程 |
| 2 | `install.sh` | **新增** | 上游 `scripts/install.sh` 硬编码了特定 AI 宿主的 MCP 注册命令与 skills 软链目录,还带联网检查 |
| 3 | `build/test_poll_protocol.mjs` | **新增** | 上游**零测试**:`jest.config.js` 指向的 `src/tests/setup.ts` 根本不存在,`npm test` 必然失败 |
| 4 | `build/verify_dist.mjs` | **新增** | 拦「编译绿了但产物不可用」 |
| 5 | `build/package_offline.sh` | **新增** | 内网无 npm,必须自带 `node_modules` |
| 6 | `build/verify_package.py` | **新增** | CI 与本地同一份校验规则 |
| 7 | `.github/workflows/build.yml` | **新增** | 目标机是 aarch64,x86 上跑绿没意义 |
| 8 | `README.md` / `SKILL.md` | **替换** | 说明来源、改动、部署 |
| 9 | `build/test_install.py` | **新增** | `install.sh` 的语义测试:合并、幂等、卸载不误伤他人配置 |
| 10 | `build/test_verify_package.py` | **新增** | 校验器自身的回归测试(符号链接判定),不依赖宿主符号链接权限 |
| 11 | `wps-assistant/**`、`install.sh`、`wps-addon/**` | **改名 + 去品牌** | 界面/文档不再出现第三方品牌:功能区显示「WPS助手」、注册 ID `wps-assistant`、源码目录 `wps-assistant/`。`install.sh` 会顺手清掉旧 ID 的注册项与目录(防升级后出现两个页签) |
**完全没动**:`tools/**`、`server/**`、`client/wps-client.ts`。
加载项本体除 `manifest.xml` / `ribbon.xml` 的显示名与 id、`main.js` 里 3 处
`console.log` 文案外,业务实现零改动。
> 界面显示名是我方自定义的,与上游品牌无关;上游原文说明存档在
> `README.upstream.md`,那份文件**刻意未做替换**,以保留可核对的原始出处。
## 产出离线包
联网机或 CI:
```bash
cd wps-office-mcp && npm ci && npx tsc -p tsconfig.json && cd ..
node build/verify_dist.mjs
node build/test_poll_protocol.mjs
bash build/package_offline.sh
# -> dist-pkg/wps-mcp-offline-aarch64.tar.gz + .sha256
```
或直接取 CI artifact(workflow `build-wps-mcp`,runner `ubuntu-24.04-arm`):
```
wps-mcp-offline-aarch64.tar.gz 约 4.2 MiB / 约 5000 条目
wps-mcp-offline-aarch64.tar.gz.sha256
```
**字节数与 sha256 每次构建都会变**,所以这里不写死:README 本身也打进了产物,
写死哈希会立刻变成自指的过期值。校验一律以 artifact 里那个 `.sha256` 为准:
```bash
sha256sum -c wps-mcp-offline-aarch64.tar.gz.sha256
```
CI 会在两个 node 版本(22 / 24)上各跑一遍,只有 node 22 那路上传产物。
打包后立刻用 `build/verify_package.py` 校验(28 项),本地下载回来再跑**同一份规则**。
## 内网安装
```bash
tar -xzf wps-mcp-offline-aarch64.tar.gz -C wps-mcp
cd wps-mcp
bash install.sh --dry-run # 先看它要做什么
bash install.sh
```
`install.sh` 依次做:探测 node/WPS → 校验包完整性 → 部署服务与加载项 →
**合并**(非覆盖)`publish.xml`(改前备份)→ 重启 WPS → 打印 MCP 配置片段。
### 加载项要不要手动启用?
**不需要。** 注册进 `publish.xml` 之后,每次 WPS 启动都会自动扫描并加载,
功能区的 `onLoad="OnAddinLoad"` 自动触发,轮询随即开始,用户不用点任何按钮。
只有一个**一次性**动作:**把所有 WPS 进程退干净再打开**(托盘/后台残留也算)。
因为 `publish.xml` 只在进程启动时读一次。`install.sh` 会自动执行
`quickstartoffice restart` 代替你完成;探测不到该脚本时才需要手动重启。
`publish.xml` 里的 `enable="enable_dev"` 是让它默认启用、并跳过 md5 校验,
省掉在「加载项管理器」里手工勾选这一步。
功能区没出现「WPS助手」时,按顺序查:
1. **装到了 root 名下** —— `sudo bash install.sh` 会把加载项写进
`/root/.local/share/Kingsoft/wps/jsaddons/`,你用普通用户开 WPS 自然看不到。
`install.sh` 现在会对这种情况显式告警。用 `--jaddons` 指到正确用户目录。
2. **没真正重启** —— `ps -ef | grep -i wps` 还有残留就得全杀掉再开。
3. **没有图形会话** —— ssh 无 X11/Wayland 转发时加载项不会加载,无报错。
4. 以上都对仍不出现,看 `~/.local/share/Kingsoft/wps/jsaddons/publish.xml`
里有没有 `name="wps-assistant"`,以及目录是否叫 `wps-assistant_`(尾部下划线)。
### 在 Hermes 注册 MCP
```json
{
"wps-office": {
"command": "/path/to/node",
"args": ["/home/<user>/.local/share/hermes-wps-mcp/wps-office-mcp/dist/index.js"],
"env": { "WPS_MCP_PORT": "58891" }
}
}
```
## 启动顺序(重要)
**先起 Hermes(含 MCP),再开 WPS。**
反之在麒麟上有已知问题:WPS 已运行后再起 MCP Server,WPS 进程可能异常退出
(上游 Issue #17)。Hermes 常驻这一点天然满足正确顺序。
## 验证
```bash
# 加载项是否注册成功
cat ~/.local/share/Kingsoft/wps/jsaddons/publish.xml
ls ~/.local/share/Kingsoft/wps/jsaddons/wps-assistant_/
# 服务是否在跑(需 Hermes 已拉起 MCP)
curl -s http://127.0.0.1:58891/status
# 端到端:重启 WPS,功能区应出现「WPS助手」
# 然后在 Hermes 里问「看看当前表格有哪些工作表」
```
日志:`~/.wps-office-mcp/logs/combined.log` / `error.log`。
## 已知限制
- **必须有图形会话**。加载项的宿主是运行中的 WPS 进程;无头服务器上整条链不成立。
- **只能操作活动文档**。WPS JS API 是 `ActiveWorkbook` / `ActiveDocument` 语义。
- **同机**。加载项与 MCP Server 必须同机,走 `127.0.0.1`。
- **加载项注册机制脆弱**。目录名尾部 `_`、`publish.xml` 的 `url` 前缀、
`oem.ini` 的 `JsApiPlugin`,任一不符就**静默不加载**,零报错。
- **ARM64 的 WPS 无公开下载**。需从统信/麒麟 OEM 渠道或已预装整机获取,
注意 OEM 授权与机器绑定。
- **测试覆盖有限**。补的协议测试只覆盖轮询链路,不覆盖 243 个工具的语义。
## 卸载
```bash
bash install.sh --uninstall
```
**什么都不删,全部改名留备份**:
- 安装目录 → `~/.local/share/hermes-wps-mcp.bak.<时间戳>`
- 加载项目录 → `jsaddons/wps-assistant_.bak.<时间戳>`
- `publish.xml` 改前先 `cp` 一份 `.bak.<时间戳>`
卸载只做两件事:从 `publish.xml` 摘掉 `name="wps-assistant"` 那一条(**别人的条目一条不碰**),
以及把上面三个位置改名。想回滚:把两个 `.bak` 改回原名即可。
> 不用 `rm -rf` 是有原因的:部分环境把 `rm` 劫持成回收站 shim,
> 会阻塞或直接失败;而且删了之后回滚只能重装。
## 本地快速跑测试
Windows / 无 `node_modules` 的环境也能跑安装语义测试:
```bash
WPS_TEST_STUB=1 python3 build/test_install.py # 桩包,秒级
python3 build/test_verify_package.py # 校验器回归测试
```
CI 不设 `WPS_TEST_STUB`,始终用真包 —— 防止「桩包绿了真包红」。
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues