libreoffice-mcp
v0.1.0
Published
MCP server that drives a local LibreOffice instance over UNO: convert, export, read and edit Writer/Calc/Impress documents.
Maintainers
Readme
LibreOffice MCP
WorkBuddy 连接器:让 AI 直接驱动本机安装的 LibreOffice,完成文档转换、导出、读取与编辑。
- 接入方式:MCP + Skill(本地 stdio 进程,无需 OAuth、无需上传文件)
- 支持平台:Windows 完整支持;Linux 实验性支持(需系统装有
python3-uno);macOS 暂不支持,会返回显式错误 - 工具数量:22
- 依赖:本机已安装 LibreOffice(Windows 无需另装 Python;Linux 需
python3-uno)
版本与环境
开发与全部实测结论对应的环境:
| 项目 | 版本 | |---|---| | 操作系统 | Windows 11 Pro(10.0.26300,x64) | | LibreOffice | 26.2.6.3 | | Python | 3.12.14(CPython,LibreOffice 自带) | | Node.js | 22.22.2(要求 ≥ 18) | | npm | 10.9.7 |
运行期依赖只有 Node.js ≥ 18 与本机 LibreOffice。不需要 uv,也不需要 pip install 任何包——UNO 绑定随 LibreOffice 一起安装。
- PDF 专用参数(
page_range/pdfa/password/quality)需要 LibreOffice 7.4 及以上,更低版本会退回默认导出并在结果里给warning。 - 用户机器上的实际版本由
lo_status返回:os、node、python_version、version(LibreOffice)。排查环境问题时以这几个字段为准,不要用上表的开发版本代替。
它是怎么工作的
WorkBuddy ──stdio/JSON-RPC──▶ Node MCP Server ──UNO socket──▶ headless soffice
│
└──JSON-Lines──▶ UNO Worker(带 uno 绑定的 Python)- Node 侧负责 MCP 协议、参数校验、路径守卫、作业队列。
- UNO Worker 由一个能
import uno的 Python 启动(Windows 用 LibreOffice 自带的python.exe;Linux 用系统的python3,需装python3-uno),直接调用 UNO API 完成文档操作。 - 每个 MCP 会话使用独立的临时 UserInstallation 配置,不会与用户正在使用的 LibreOffice 互踢。
工具一览
| 分组 | 工具 |
|---|---|
| 环境 | lo_status、lo_restart_runtime |
| 转换导出 | lo_convert、lo_convert_batch、lo_job_status、lo_export_pdf、lo_export_images |
| 读取 | lo_read_text、lo_inspect、lo_calc_read、lo_calc_formulas、lo_calc_sheets、lo_writer_paragraphs、lo_impress_read |
| 写入 | lo_create、lo_writer_write、lo_writer_replace、lo_calc_write、lo_calc_recalc、lo_impress_set_text、lo_metadata、lo_merge |
支持的目标格式(lo_convert):pdf docx doc odt rtf txt html xlsx xls ods csv pptx ppt odp fodt fods fodp odg
目标格式必须与源文档同族(文字→文字、表格→表格、演示→演示、绘图→odg)。跨族转换 LibreOffice 不会报错,而是把源格式写进目标文件;连接器会校验产物容器,不一致就返回 FORMAT_MISMATCH。
图片(png / jpg)不是转换目标格式,逐页导出请用 lo_export_images。
本地开发
npm install
npm run build # 编译 TS 并复制 worker.py 到 dist/py
npm run smoke # 端到端冒烟:22 个工具跑真实文档
npm run audit # 发布前审计:97 项,覆盖元数据/协议/功能/异常/安全/文档一致性
npm run scenarios # 场景验证:靠环境模拟验证「未安装/被占用/macOS/超时/超大文件」等
npm run guard # 预算与预检回归:30 秒口径、PDF 拦截、产物与预算的实测耗时
npm run real # 真实文件验证:跑 samples/ 下的业务文件(旧格式升级 + PDF + 预览图)
npm run pkg # 打包产物验证:把待发布的 tarball 装进干净目录再启动npm run audit 通过真实 stdio JSON-RPC 驱动服务,逐条打印 PASS/FAIL,结果写入 docs/audit-report.json。
npm run scenarios 用真实环境模拟覆盖那些"人眼才能判断"的条目:起一个占用默认 profile 的 LibreOffice 验证共存、用 --import 钩子伪造"未安装"与 macOS、把超时压到 400ms 验证报错可读、把大小上限压到 1MB 验证拦截,结果写入 docs/scenario-report.json。
npm run real 需要你把待测文件放进 samples/(不入 npm 包),覆盖旧格式保真升级、8MB 级大文件、真实母版渲染与文本替换。
三套脚本都返回非零退出码,可直接当 CI 门禁。检查项定义见 docs/release-checklist.md,提审前必须全绿。
在 WorkBuddy 里本地挂载(开发调试用)
项目级 mcp.json 用本地路径,替代 npx 方式(<项目根> 换成你本机的实际路径):
{
"mcpServers": {
"libreoffice": {
"type": "stdio",
"command": "node",
"args": ["<项目根>/dist/index.js"],
"timeout": 60000
}
}
}timeout 不要低于 30000:连接器内部单次调用预算上限是 28 秒(含冷启动),声明值更小的话,长任务会被传输层先掐断,看到的就不是可读的 TIMEOUT 了。改完需在 WorkBuddy 界面点一次「信任」才会激活。
环境变量
| 变量 | 作用 | 默认 |
|---|---|---|
| LO_PROGRAM_DIR | 指定 <LibreOffice>/program 目录 | 自动探测(注册表 / 常见路径 / PATH) |
| LO_SOFFICE | 直接指定 soffice 可执行文件 | — |
| LO_MCP_TIMEOUT_MS | 单次调用预算上限(对所有工具生效,上限 55 秒) | 28000 |
| LO_MCP_BOOT_TIMEOUT_MS | 无头进程启动等待上限 | 30000 |
| LO_MCP_MAX_FILE_MB | 单文件大小上限 | 200 |
| LO_MCP_ALLOW_ROOTS | 允许访问的根目录,系统路径分隔符分隔 | 工作目录 + 主目录 + 临时目录 |
已知限制
- macOS 未实现:
lo_status会返回UNSUPPORTED_PLATFORM并说明原因。 - Linux 需要
python3-uno(Debian/Ubuntu)或libreoffice-pyuno(Fedora)。 - 复杂排版(艺术字、VBA 宏、非常见字体)转换保真度取决于 LibreOffice 过滤器,交付前应抽查。
- PDF 专用参数(
page_range/pdfa/password/quality)通过FilterOptionsJSON 传递,需要 LibreOffice 7.4 及以上;更早版本会退回无参数导出,并在返回值的warning字段里说明。注意:这些参数以FilterData属性传入时会被 LibreOffice 静默忽略(不报错、不生效),已在代码注释中标注原因,勿改回。 - 读取 PDF 很慢,且无法优化:LibreOffice 需要先把 PDF 导入成绘图文档再逐形状取文本。实测 7.1MB / 41 页:导入 27.3 秒 + 遍历 21.5 秒 ≈ 48.8 秒,约 6.6 秒/MB;同一份文件在三次实测里出现 48.4s / 48.9s / 55.4s 的波动(取决于机器负载),所以 6MB 以上就已贴近 55 秒上限。因此
lo_read_text/lo_inspect对 >2MB 的 PDF 会直接拦截(毫秒级返回PDF_TEXT_SLOW),要读需显式传allow_slow: true。docx / xlsx / pptx 不走这条路径,7.7MB 演示文稿约 1 秒。 - 单次调用预算是整次调用的总预算,冷启动时间也算在里面(早期版本把启动与操作分开计时,最坏能叠到 80 秒以上)。统一为 28 秒,与平台「建议 30 秒内响应」的口径一致;与
mcp.json声明的 60 秒传输超时留有大量余量,保证用户看到的是带hint的TIMEOUT而不是传输层中断。LO_MCP_TIMEOUT_MS可整体放宽,但会被截到 55 秒 —— 再长只会让宿主先断开。 - 唯一会超过 30 秒的路径是大 PDF 的
allow_slow: true(上限 55 秒),已由调用方显式选择。预期耗时较长的任务请直接走lo_convert_batch+lo_job_status作业模式。 - 作业进行中再调用其他
lo_*工具会排队(LibreOffice 文档对象不是线程安全的),此时只用lo_job_status轮询即可。 - 批量作业的状态只存在内存里:MCP 进程重启后旧
job_id失效(会返回JOB_NOT_FOUND)。 - 默认只允许访问工作目录、用户主目录与临时目录;数据盘上的素材需通过
LO_MCP_ALLOW_ROOTS显式放行,当前允许范围可用lo_status的allowed_roots查看。 - 客户端异常强杀进程时(Windows
TerminateProcess),本次会话的临时 profile 无法自清,会由下一次启动扫描回收。
目录结构
├── src/ Node MCP Server(TypeScript)
│ ├── platform.ts soffice / python 探测
│ ├── runtime.ts 进程生命周期、RPC、自愈重试
│ ├── jobs.ts 批量作业队列
│ ├── paths.ts 路径守卫
│ ├── tools.ts 22 个工具注册
│ └── py/worker.py UNO worker
├── connector/ 提交到连接器市场的包
│ ├── connector-meta.json
│ ├── mcp.json
│ ├── icon.svg
│ ├── SUBMIT.md 提审说明与自检清单
│ └── skills/libreoffice/SKILL.md
├── scripts/copy-assets.mjs 构建后复制 worker.py
├── test/
│ ├── smoke.mjs 端到端冒烟
│ ├── audit.mjs 发布前审计(含格式清单/PDF 参数一致性)
│ ├── scenarios.mjs 场景验证(环境模拟 + 文档指导性)
│ ├── guard.mjs 预算与预检回归(28 秒口径、PDF 拦截、各操作实测耗时)
│ ├── real-samples.mjs 真实业务文件验证
│ ├── pkg-install.mjs 待发布 tarball 的安装态验证
│ ├── sim/ 环境模拟钩子(伪造未安装 / darwin)
│ ├── fixtures/ 合成夹具(含组合形状页)
│ ├── make-fixtures.py 生成夹具
│ ├── probe-*.py/.mjs 实测取证脚本(不参与自动回归,结论见 CHANGELOG)
│ └── demo-real.mjs 真实场景演练(模板填充 → PDF → 预览图)
│ (`debug-*.mjs` 为排障脚本,不在发布范围内)
├── docs/
│ ├── connector-design.md 设计与任务分解
│ ├── release-checklist.md 发布前检查清单
│ ├── npm-publish.md npm 发布操作单
│ ├── audit-report.json 最近一次审计结果
│ ├── scenario-report.json 最近一次场景验证结果
│ └── real-samples-report.json 最近一次真实文件验证结果
├── CHANGELOG.md 版本记录与已知限制License
MIT
