本地 editor_sdk MCP 应用
面向本机磁盘文档,通过本地
editor_sdk进程的 MCP 服务读写。支持编辑的格式按三个品类归类:
- doc:
.doc.dot.wps.wpt.docx.dotx.docm.dotm- sheet:
.xls.xlt.xlsx.xltx.xlsm.xltm.csv.tsv- slide:
.ppt.pps.pot.pptx.ppsx.potx.pptm.ppsm.potm打开时按文件内容(magic bytes)自动识别品类,扩展名仅作兜底。 ⚠️ 这是本地 Office/WPS 文件操作通道,只操作本机磁盘上的文件。
格式限制
- 新建文件:
create_doc/create_sheet/create_slide使用内置空白模板,分别新建.docx/.xlsx/.pptx。 - 含宏格式:
.docm.dotm.xlsm.xltm.pptm.ppsm.potm可按对应品类打开和编辑文档结构,但宏不执行,剥离宏后可能导致数据或样式差异。 - 不支持的 WPS 专属格式:
.et.ett.dps.dpt当前不支持;需要先用 WPS 客户端另存为.xlsx或.pptx后再处理。 - 不属于本 Skill 的编辑范围:
.xmind有独立 Mind Editor 但本目录未暴露 mind MCP 工具;.pdf/.ofd仅查看,不提供编辑工具。
前置
python3- 本地
editor_sdk服务已运行(默认从端口 39099 起探测) - 所有调用都通过本目录的
edsdk.py封装脚本完成(HTTP 直连 MCP 端点):python3 edsdk.py list # 验通:能列出工具即 OK未设置
editor_sdk_port时,按顺序探测39099到39108共 10 个端口;命中后复用该端点。 如需固定端口:editor_sdk_port=40001 python3 edsdk.py list(设置后只访问指定端口)。 如服务启用鉴权,设置editor_sdk_token。
🚧 强制流程(违反将导致参数错误 / 调用失败)
对任何 doc_* / sheet_* / slide_* 编辑工具:渐进式按需查询,不要一次性拉全量 schema。流程:
- 读子文档:如果是Word类任务,必须先读取doc.md。
- 选工具:从子文档(doc.md / sheet.md / slide.md)的一句话清单里挑出要用的工具——这一步不需要命令,清单就是索引。
- 只查该工具 schema:
python3 edsdk.py schema <工具名>,直接得到必填项与各字段说明(内部只取这一个工具,不会全量打印):python3 edsdk.py schema doc_insert_text - 调用:确认
[✓]必填项与各字段语义后再调。
用到一个才查一个,没用到的工具不要预先查。未经
schema查询直接拼参数调用,属于流程违规。
调用前自检(缺一不可):
- ☐ 已对本次要用的工具跑过
edsdk.py schema <工具名> - ☐ 已确认全部
[✓]必填字段 - ☐ 已确认位置 / 坐标 / 单位约定(如 doc 的 UTF-16 偏移、slide 的磅(pt))
- ☐ 【仅
sheet_*写值工具】 调sheet_set_range_value/sheet_set_range_value_by_csv/sheet_set_cell_value前,已用sheet_get_used_range探明真实已用区域边界 —— 不得用肉眼观察、文件预览或上一次读取的行数去猜边界 - ☐ 【仅
sheet_*】 已按用户意图选对分支(二者选一,见sheet.mdHARD RULE):- 新增(用户表达「新增 / 添加 / 再加 / 插入 / 追加」行或列)→
已先调
sheet_insert_dimension腾出空间,确认目标区域为空后再写,而不是直接覆盖已用区域 - 修改(用户明确表达「修改 / 替换 / 更正 XX」)→
已先
sheet_get_cell_data读回目标区域,确认要覆盖的内容与用户意图一致后再写
- 新增(用户表达「新增 / 添加 / 再加 / 插入 / 追加」行或列)→
已先调
若你不确定某工具的必填参数,默认你没查过——先
schema再调,不要凭子文档表里的一句话摘要猜参数。
约定 —— edsdk.py 三条命令
| 命令 | 作用 |
|---|---|
python3 edsdk.py list [doc|sheet|slide|common] |
列出工具(工具名 + 一句话描述);可按品类过滤 |
python3 edsdk.py schema <工具名> |
按需查单个工具的完整参数(required + 各字段说明)。加 --raw 附带原始 inputSchema |
python3 edsdk.py call <工具名> [k=v ...] [--json '{...}'] [--json-file args.json] |
调用工具 |
入参写法:key=value(值优先按 JSON 解析:数字 / 布尔 / 数组 / 对象 / null,失败则当字符串);
复杂 / 嵌套参数用 --json '{...}' 或 --json-file args.json(与 key=value 合并,同名键以后者为准)。
file_id是一切编辑的入口:先尝试get_pool_status/create_*拿到工具返回的真实file_id,再调品类工具(见下方强制流程)。file_id是 pool 里实例的 key,不一定是路径——见下方「打开文档必须用present_files」说明。- 品类由文档类型决定:
doc_*只能作用于 doc 文档,依此类推;类型不匹配会报错。 - 编辑后重新读取:编辑后若要基于文档现状继续操作,先重新读取最新数据;不要沿用编辑前缓存的内容、位置或版本。
- 保存后不要主动关闭:保存用
save_file;不要主动close_file,除非用户明确要求关闭或确实需要释放资源。 - 源码 1:1 对应:
doc_*→server/mcp/doc/,sheet_*→server/mcp/sheet/,slide_*→server/mcp/slide/。
工作流:先打开文件,再编辑,最后保存
⚠️ 本套操作是实时、可视化的——文件打开后用户正在实时查看编辑效果。 不要主动调
close_file:保存用save_file即可,关闭会关掉用户正在看的视图、释放编辑器。 仅在用户明确要求关闭、或确实需要释放资源时才调close_file。
⚠️ 打开文档必须用
present_files工具:要让用户在预览面板中看到文档,必须调用present_files把文件呈现给用户——不能只在回复里说「已打开」。edsdk.py call open_file不会在预览面板里打开文档:open_file只在后台 editor_sdk 里把文件读进内存,供 Agent 后续doc_*等工具编辑,用户界面不会出现这份文档(与present_files相反)。
- 前端 /
present_files→ HTTP/api/open:未指定file_id时服务端生成 UUID(如5dca3e56-389b-461e-88c6-...)。之后get_pool_status、以及编辑工具只传file_path时ResolveFileId,都会反查到这个 UUID。create_*:返回new_doc_.../new_sheet_...等可读 id。
open_file与doc_*的区别:doc_*在file_id缺省时会ResolveFileId(file_path),路径已注册则 复用已有实例(多为 UUID)。open_file默认不从 pool 找已有实例(open_with_existing未传或false):未显式指定file_id时 强制用file_path字符串本身作 key(如/abs/foo.docx),从磁盘 fresh open(OpenByChunks重读);纯后台 MCP、无用户预览时,返回的file_id往往就是路径字符串。用户已在看文档时 不要默认open_file——会再开一个与前端 SSE 订阅脱节的实例,看不到实时改动。
open_with_existing(仅open_file可选):默认false= 不管 pool 里有没有同路径文档,都按上文 fresh open。传true= 先问 pool「这个路径是否已打开」:有则 沿用已有file_id(多为 UUID) 和内存里的文档,不再从磁盘重开;没有则与默认行为相同。找不到已有实例、或磁盘文件已更新时,服务端会清掉旧实例再重开。仅在get_pool_status查不到、但你确信该路径已在 pool 里时,才用open_file file_path=... open_with_existing=true。⚠️
present_files之后不要open_file(尤其默认 fresh open):present_files会在 editor_sdk 注册实例(UUIDfile_id)。此时应get_pool_status按路径拿file_id,或从用户选区取file_id,再直接doc_*。重复默认open_file会再开实例——上传后用户中途点开侧边栏时尤其常见:模型先open_file(key=路径)在改,用户点开生成 UUID 实例并订阅 UUID stream,模型若仍只用file_path可能命中「孤儿」路径实例,revision 推到 path stream,页面绑 UUID stream → 看不到实时改动;改完save_file落盘至少能兜底。⚠️
present_files返回的是文件地址,不是file_id:不能直接当file_id传给doc_*/sheet_*/slide_*。file_id来自get_pool_status(已展示文档)、create_*的返回,或仅后台、无需用户预览时open_file的返回值。传file_path给编辑工具时须与注册时 字符串完全一致(./x.docx≠/abs/x.docx);get_pool_status的file_path过滤会做规范化,编辑工具反查 不会。
# 1. 在预览面板呈现给用户(硬约束:必须调用 present_files)
present_files 工具:把 /abs/a.docx 在预览面板中打开给用户看
# 2. 从 pool 拿真实 file_id(不要 open_file)
python3 edsdk.py call get_pool_status --json '{"file_path":"<同上路径>"}'
# → 在 editors 里取 file_id(多为 UUID);后续编辑一律用这个 file_id,不再拼路径
# 3. 编辑(按品类调用,见下方子文档;调用前先 schema 查参数)
python3 edsdk.py schema doc_insert_text
python3 edsdk.py call doc_insert_text file_id=<上一步返回的 file_id> idx=0 text="hi"
# 4. 保存(file_path 省略则覆盖原文件)—— 到此即可,不要主动 close
python3 edsdk.py call save_file file_id=<上一步返回的 file_id>
get_pool_status未命中时(present_files后偶发游离实例或尚未注册):
- 先再次尝试
present_files,然后用与present_files相同的路径字符串再查一次;- 仍无则
open_file file_path=... open_with_existing=true(不要默认 freshopen_file);- 仍失败再与用户确认路径。
通用工具(文件 / 会话管理,无品类前缀)
这些是入口工具,所有品类共用;具体编辑接口见各子文档。
| 工具 | 必填 | 说明 |
|---|---|---|
open_file |
file_path;可选 open_with_existing(默认 false)、file_type(doc/sheet/slide,可省略自动识别)、password、image_dir |
后台打开本地文件(仅 editor_sdk 内存,预览面板不展示),返回 file_id。默认 fresh open(返回的 file_id 多为 file_path 本身);open_with_existing=true 时复用 pool 里同路径已有实例(file_id 多为 UUID)。 |
get_pool_status |
— | 查看 pool;present_files 之后用它拿 UUID file_id(file_path 过滤支持规范化匹配,比编辑工具的路径反查更宽容) |
save_file |
— | 保存到本地;file_path 省略则覆盖原文件 |
close_file |
file_id |
关闭编辑器、释放资源。⚠️ 实时可视化场景下不要主动调——会关掉用户正在看的视图;仅在用户明确要求时调 |
create_doc / create_sheet / create_slide |
— | 从空白模板新建对应类型文档,返回 file_id |
shutdown |
— | 关停 editor_sdk 服务 |
品类编辑接口(按文档类型进入子文档)
file_id 拿到后,按文档类型调对应品类的工具(工具名带品类前缀):
| 品类 | 文档类型 | 工具数 | 子文档 |
|---|---|---|---|
| doc | Word/WPS 文档类(doc/docx/dot/wps/wpt…) | 37 | doc.md |
| sheet | Excel 表格类(xls/xlsx/xlt/csv/tsv…) | 51 | sheet.md |
| slide | PPT 演示类(ppt/pptx/pps/pot…) | 77 | slide.md |