doc-control

io.github.nuncaeslupus由 GitHub 帳號 nuncaeslupus 發布v0.10.1更新於 Oct 29, 2026

按地址編輯 Word 文件:修訂、批註、錨點,並支援備份和撤銷。

AI 翻譯由 AI 模型根據下方的原文描述翻譯,可能有誤。

原文描述

Edit Word documents by address: tracked changes, comments, anchors, with backups and undo.

已驗證STDIO僅桌面效率與工作流檔案與儲存

概覽

doc-control 的本機 MCP 服務;無需登入;STDIO;68 個工具。

暫無 AI 概覽:以下資訊來自註冊中心條目和服務本身。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 doc-control,將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。

其他 MCP 客戶端

參照 儲存庫 中的啟動說明。

README

[圖片] doc-control

[Built with claude-arsenal]

[PyPI] [CI] [License: MIT] [doc-control MCP server – quality and maintenance score on Glama]

[doc-control: an agent sends one JSON batch that replaces a word and adds a comment; the open Word page shows the replacement as a tracked deletion and insertion with Accept and Reject buttons, and the comment in a margin balloon]

Lets AI agents edit Word documents as tracked changes the writer can accept or reject.

An agent asked to fix a .docx usually rewrites the file and hopes nothing else moved. This tool gives it a deterministic interface instead: read the document by paragraph address, then send edits that land as tracked changes and comments, with a backup, verification and undo on every write. It works on closed files and, through LibreOffice Writer or Microsoft Word on Windows, on the document the writer has open. There is no AI inside: the agent decides, the writer approves.

Not yet stable: the API may still change.

Quick start

Python (the version in pyproject.toml), no other dependencies.

bash
pip install doc-control            # or run without installing: uvx doc-control ...doc-control text.find --json '{"doc": "story.docx", "query": "rain"}'

For live control of Word on Windows install doc-control[live] (adds pywin32); LibreOffice needs no extra package.

Install without Python (Windows)

Every release on the latest release page carries a Claude Desktop extension and an installer. Both install for the current user, need no admin rights, work with any Claude account (the free plan included) and drive Microsoft Word live. Full guide (checksums, updates, uninstalling): docs/install-windows.md.

Claude Desktop extension (.mcpb)

  1. Download doc-control-X.Y.Z-windows-x64.mcpb from the latest release.
  2. Drag the file onto Claude Desktop's Settings > Extensions page and click Install.

Claude Desktop warns that the extension can access everything on your computer; every local extension gets that warning. Double-clicking the file does not install it.

Installer (-setup.exe), for the command line

  1. Download doc-control-X.Y.Z-windows-x64-setup.exe from the latest release.
  2. Double-click it.
  3. If Windows SmartScreen says "Windows protected your PC", click More info, then Run anyway (the installer is not code-signed).
  4. Click Install, Next and Finish, and open a new terminal: doc-control server.capabilities works there.

Example

A two-paragraph story.docx. Find a word, then replace it and comment on the next paragraph in one batch (long envelopes trimmed with ...; the rest is actual output):

console
$ doc-control text.find --json '{"doc": "story.docx", "query": "rain"}'{"api": "1.0", "ok": true, "op": "text.find", ..., "result": {"matches": [{"at": "p1#bfa780f9:4-8", "text": "rain", "context": "The rain had not stopped since Tuesday…"}], "total": 1, ...}}
$ doc-control doc.batch --json '{"doc": "story.docx", "ops": [    {"op": "text.replace", "at": "p1#bfa780f9:4-8", "text": "snow"},    {"op": "comment.add", "at": "p2#f8b24a64", "text": "Which window?"}]}'{"api": "1.0", "ok": true, "op": "doc.batch", ..., "result": {"applied": 2, "dry_run": false, "preview": [{"op": 0, "at": "p1#bfa780f9:4-8", "before": "rain", "after": "snow"},             {"op": 1, "at": "p2#f8b24a64", "before": "Ana closed the window and waited.",              "after": "(comment c0 by Claude: Which window?)"}], ..., "author": "Claude", "backup": ".../.doc-control/story.docx/backups/20261020T091845Z-0ff4dc24.docx", "op_id": "h-000001"}, "warnings": [], "next": null}
$ doc-control review.list --json '{"doc": "story.docx"}'{..., "result": {"totals": {"comments": 1, "insert": 0, "delete": 0, "replace": 1, ...}, "paragraphs": [{"at": "p1#c6f909ff", "text": "The snow had not stopped since Tuesday.",   "changes": [{"type": "replace", "ids": ["1", "2"], "author": "Claude", "old": "rain", "new": "snow", ...}]},  {"at": "p2#f8b24a64", "text": "Ana closed the window and waited.",   "comments": [{"id": "0", "author": "Claude", "text": "Which window?", "quote": "Ana closed the window and waited.", ...}]}]}, ...}

Word shows the replacement as a tracked deletion and insertion and the comment in the margin. Reusing the old address after the edit fails instead of guessing:

console
$ doc-control text.replace --json '{"doc": "story.docx", "at": "p1#bfa780f9:4-8", "text": "fog"}'{"api": "1.0", "ok": false, "op": "text.replace", "error": {"code": "STALE_ADDRESS", "message": "p1#bfa780f9 no longer matches the document", "hint": "Re-read the paragraph (para.get) or resolve its anchor, then retry.", "details": {"address": "p1#bfa780f9", "current": "p1#c6f909ff"}}}

Use with AI agents

MCP server

doc-control mcp serves 60+ operations as MCP tools over stdio, one tool per operation (doc.batch becomes doc_batch), with the same JSON Schemas and response envelope as the CLI. Every tool declares an outputSchema; a successful call also returns the envelope as structuredContent. Standard library only.

json
{  "mcpServers": {    "doc-control": { "command": "uvx", "args": ["doc-control", "mcp"] }  }}

For live Word on Windows use "args": ["--from", "doc-control[live]", "doc-control", "mcp"], or install the .mcpb extension above. live_places also ships an MCP Apps UI (a ui:// page) whose buttons call live_select.

GroupTools
Readdoc_outline, para_list, para_get, para_locate, text_find, doc_stats, doc_segment, review_list, comment_list, revision_list
Edit (tracked)doc_batch, text_replace, para_insert, para_delete, comment_add, note_add, term_rename, table_row_insert, table_row_delete
Reviewrevision_accept, revision_reject, doc_diff, doc_compare
Layout and fieldslayout_get, layout_apply, field_list, field_update, toc_insert, caption_add, xref_add, index_mark, index_insert
Stylesstyle_*, styleset_*
Safetyhistory_list, history_undo, history_restore, history_search, ...
Live windowlive_status, live_open, live_select, live_places, live_save, live_close, ...
Discoveryserver_capabilities, server_help, recipe_list, recipe_run

server_help with {"op": "<name>"} returns any operation's schema and summary.

From the CLI

An agent can drive the CLI directly. It never prompts and reads stdin only with --json -. It prints exactly one JSON envelope on stdout: {"ok": true, "result": ...} or {"ok": false, "error": {"code", "message", "hint", "details"}}. Exit codes: 0 success, 1 operation error, 2 usage error (unknown operation or invalid request). Error codes are listed in docs/errors.md.

The intended loop: read (doc.outline, para.list, text.find), build one doc.batch, run it with "dry_run": true to get the preview, apply it, and point the writer to Word's Review pane. history.undo reverts a write.

How it works

  • Addresses, not offsets. p12#a7b2a7b2:4-10 is paragraph 12, its text fingerprint and a character range. A changed paragraph gives STALE_ADDRESS with the current address. anchor.create / anchor.resolve find a passage again after edits.
  • Every content change is tracked, authored by DOC_CONTROL_AUTHOR (default Claude). For an edit the writer already approved, the text-editing operations and history.restore take "tracked": false (file engine only).
  • Safety layer: lock-file and on-disk change detection, backup before every write, read-back verification, automatic restore on failure, an operation log with undo.
  • File and live engines, one API. The file engine edits a closed .docx with the Python standard library, rewriting only the parts it touches; reads also accept .txt, .md and .odt. The live engine drives the open document in LibreOffice Writer or Word (Windows, COM).
  • Recipes propose fix batches (Spanish typography, quotes, dashes, spacing, empty paragraphs): recipe.list, then recipe.run returns a batch to review and apply.

Usage

Operations are named resource.action; any single edit operation runs as a one-edit doc.batch. doc-control server.help <op> prints an operation's schema. From Python:

python
from doc_control import api
with api.open("Novela.docx") as doc:    for p in doc.paragraphs(normalized=True, pages=True):        print(p["at"], p["style_name"], p["page"], p["text"][:40])    chapters = doc.run("doc.segment", {"unit": "chapters"})["chapters"]

Notes, headers, footers and tables

Reads cover the body unless stories adds "notes" (footnotes, endnotes) or "headers" (headers, footers). Those paragraphs carry prefixed addresses (fn3/p1#0c9e0c9e, hdr1.default/...) that edits accept.

bash
doc-control para.list --json '{"doc": "Tesis.docx", "stories": ["body", "notes"]}'doc-control text.replace --json '{"doc": "Tesis.docx", "at": "fn3/p1#0c9e0c9e:4-10", "text": "2019"}'doc-control note.add --json '{"doc": "Tesis.docx", "at": "p40#c0ffee00:17", "kind": "footnote", "text": "Ver el cap. 2."}'doc-control table.row.insert --json '{"doc": "Tesis.docx", "table": 1, "after_row": 2, "cells": ["a", "b"]}'

Page layout and fields

layout.get returns the page plan per section (start, break, numbering, header and footer per kind); layout.apply brings the document to a plan in the same shape. This one gives a novel an unnumbered title page, then starts chapter 1 on a right-hand page numbered 1, with the chapter title in odd headers and the book title in even ones:

bash
doc-control layout.apply --json '{"doc": "Novela.docx", "untracked": true, "plan": {  "version": 1, "two_sided": true, "mirror_margins": true,  "sections": [    {"from": "p1#ea7412aa", "break": "nextPage",     "numbering": {"shown": false, "start": 1, "format": "lowerRoman"},     "header": {"default": "", "even": ""}, "footer": {"default": "", "even": ""}},    {"from": "p2#5fac7aad", "break": "oddPage",     "numbering": {"shown": true, "start": 1, "format": "decimal"},     "header": {"default": "{chapter}", "even": "La casa del río"},     "footer": {"default": "{PAGE}", "even": "{PAGE}"}}]}}'

{PAGE}, {NUMPAGES} and {chapter} become fields. Breaks and numbering are tracked; new header parts and two-sided settings cannot be tracked in Word, hence untracked: true. Try "dry_run": true first. field.list, toc.insert, caption.add, xref.add and index.insert handle fields; field.update refreshes them through headless LibreOffice.

Live: the document the writer has open

bash
doc-control live.open --json '{"doc": "Novela.docx"}'doc-control doc.batch --json '{"doc": "Novela.docx", "engine": "live", "ops": [  {"op": "comment.add", "at": "p40#c0ffee00", "text": "¿Seguro?"}]}'doc-control live.close --json '{"doc": "Novela.docx", "save": true}'

Live doc.batch takes comment.add, text.replace and para.insert. Without a usable app it answers ENGINE_UNAVAILABLE with the reason; live.status lists the apps found and, for LibreOffice, the Python its UNO bridge needs. Closing never drops unsaved work.

Environment

VariableEffect
DOC_CONTROL_AUTHORAuthor of tracked changes and comments (default Claude)
DOC_CONTROL_STOREappdata keeps backups and history in the user store instead of .doc-control/ beside the document
DOC_CONTROL_STORE_DIRStore root override
DOC_CONTROL_HOMEUser store and recipes folder
DOC_CONTROL_UPDATE_CHECK1 asks GitHub Releases at most once a day and reports a newer version in server.capabilities; never downloads anything

Everything runs locally. With the update check off (the default), nothing leaves the machine.

What it is not

  • Not an AI: no model calls, no judgment. It executes what the agent sends.
  • No live control on macOS yet; live doc.batch covers only some edit types.
  • An index is built by Word (F9); citation fields are never changed.

Documentation

Development

bash
uv syncmake check     # ruff, mypy, pytest, hygiene script

Work is planned as gated tasks with claude-arsenal; see CLAUDE.md.

Related projects

  • claude-arsenal — spec-driven Claude Code workflow and gated task queue.
  • drawspec — declarative diagram spec → clean SVG/PNG/text, writable by LLMs; MCP server included.
  • literary-critic — LLM manuscript analysis the way a professional editor would.
  • integral-job-search — candidate-centred job search run in Claude Code.

License

MIT — see LICENSE.

來源:README.md,提交 bb6211d

工具

68

anchor_create

Make an anchor for a passage so it can be found again after edits shift or change addresses: the quoted text, `context` characters before and after (default 32, max 200), and the paragraph's number and fingerprint then. `at` is a range in one paragraph (p142#a7b2a7b2:10-15), a whole paragraph (p142#a7b2a7b2) or a sentence (p142#a7b2a7b2.s3). Use it to bookmark passages you will come back to; find them later with anchor.resolve (text.find searches for any text instead). Read-only; STALE_ADDRESS when `at` is outdated, SCHEMA_INVALID for a range across paragraphs.

輸入參數at必填doc必填context

anchor_resolve

Find an anchor's passage in the document as it is now: its current range, a 0-1 score and the match kind (exact, normalized or fuzzy). Pass the `anchor` object anchor.create returned, or just {exact, prefix, suffix}. mode best (default) falls back to a fuzzy match scoring at least 0.80; mode exact accepts only verbatim, normalised or folded (match_level, default `default`: case, accents, punctuation) matches. Fails with ANCHOR_NOT_FOUND, or ANCHOR_AMBIGUOUS listing up to 5 tied candidates. Use it to re-address a passage after edits; doc.diff maps addresses between two files instead. Read-only.

輸入參數doc必填modeanchor必填match_level

caption_add

Add a numbered caption next to a body paragraph (a table, figure or equation), as tracked insertions: label, a SEQ field with the number, and `text` (e.g. label Table, text "Results" gives "Table 3 Results"). position defaults to above for Table, below for Figure and Equation. A hidden bookmark wraps label and number so xref.add can point at it; field.update renumbers after later inserts. Backed up and undoable (history.undo); dry_run previews; expect.sha256 refuses a changed file. Returns the caption's address; UNSUPPORTED_CONTENT for a paragraph in a text box.

輸入參數at必填doc必填tagtext必填label必填authorexpectdry_runposition

comment_add

Add one comment on a range: `at` is a paragraph (p3#a1b2c3d4), part of one (p3#a1b2c3d4:10-15) or a span across paragraphs (p3#a1b2c3d4:10..p5#e5f6a7b8:2); a newline in `text` starts a new comment paragraph. Author defaults to DOC_CONTROL_AUTHOR. File engine: backed up first, dry_run previews, expect.sha256 refuses a file changed since, logged for history.undo. engine live adds it in the open Word/LibreOffice window (after live.open). Use doc.batch to add several, or with comment.reply/comment.resolve, all or nothing. Returns the new comment id.

輸入參數at必填doc必填tagtext必填authorengineexpectdry_run

comment_list

List the comments in thread order (each root comment, then its replies) with id, author, date, text (clipped at 1,000 characters), quote (the text the comment covers, clipped at 200), resolved state and the address where each starts, including comments in notes and headers. Filter with `resolved` (true or false) and `author` (exact). Use the ids to reply or resolve with doc.batch (comment.reply, comment.resolve); use revision.list for tracked changes instead, review.list for comments and changes together by paragraph. Read-only; paged: a reply is capped at about 8,000 bytes (limit up to 1,000), continue with `cursor` set to the reply's `next`.

輸入參數doc必填limitauthorcursorresolved

doc_batch

The main editing tool: apply several edits in one verified write, as tracked changes, all or nothing. One family per batch: text.replace, para.insert, para.delete, para.move, style.apply, format.apply, comment.add/reply/resolve; or table.row.insert/delete; or style.create/update/rename/merge; or revision.accept/reject. Example ops: [{"op": "text.replace", "at": "p3#a1b2c3d4:10-15", "text": "new"}]. Every address is as read; overlapping edits are BATCH_CONFLICT, outdated ones STALE_ADDRESS. File engine: backed up first, dry_run previews, expect.sha256 refuses a changed file, history.undo takes the batch back. tracked: false writes approved text plainly, on either engine. engine live edits the open app (comment.add, text.replace, para.insert only; no dry_run or rollback). server.help <kind> gives each kind's fields.

輸入參數doc必填ops必填tagauthorengineexpectdry_runtracked

doc_compare

Write a redline: a new .docx at `output` (which must not exist) holding the `new` version with every difference from `old` as a tracked change (author default Claude, `date` ISO 8601 with time zone, default now), so a writer reviews and accepts or rejects them in Word. The input files are never modified; the redline is verified (accepting every change gives the new text) or removed (VERIFY_FAILED_RESTORED). Use doc.diff for the alignment as data without writing a file. Returns the output path and hash, per-kind paragraph counts and the revision count.

輸入參數new必填old必填tagdateauthoroutput必填

doc_diff

Align the paragraphs of two versions (`old`, `new`; .docx, or .txt/.md/.odt): which are unchanged, modified, moved, split, merged, inserted or deleted, with a map from old addresses to new ones (null for deleted). Use it to carry addresses read from an older version over to the current one, or to see what changed; doc.compare writes the same differences as a redline .docx instead. collapse_runs folds runs of shifted unchanged paragraphs into one record. Read-only; paged at about 8,000 bytes with limit and `cursor` (the `next` of the previous reply).

輸入參數new必填old必填limitcursorcollapse_runs

doc_info

Cheap first look at a file: size, sha256 (pass it as expect.sha256 to a later write so it is refused if the file changed meanwhile), core properties (title, author, dates), whether Track Changes is on, whether Word holds it open (a ~$ lock file) and which parts exist (comments, notes, headers). The envelope's `doc` gives the paragraph count. Use doc.stats for word, heading and revision counts, doc.outline for the headings. Read-only; DOC_NOT_FOUND or NOT_A_DOCX for a bad path.

輸入參數doc必填

doc_open

Start a session on a document: back it up into its store (.doc-control/<file name>/ next to it; the newest 20 backups or 30 days are kept) and note it in the undo log if it changed since the last logged write. Call it once before editing; the document itself is not changed. If the document has no history yet, `adoptable` lists orphaned stores (it was moved or renamed) that history.adopt can re-attach. Returns the backup path, the log entry and the store folder.

輸入參數doc必填

doc_outline

List the headings (levels 1-9, by style or outline level) in document order, each with its address, level, style and text: the table-of-contents view. max_level 2 keeps levels 1-2; stories ["body", "notes"] adds headings in notes. Use doc.segment for chapters that are not styled as headings, para.list for every paragraph, para.get to read one heading in full. Read-only; paged: about 8,000 bytes per reply (limit up to 1,000), continue with `cursor` set to the reply's `next`.

輸入參數doc必填limitcursorstoriesmax_level

doc_segment

Split a manuscript into units with stable ids: unit chapters (default), parts or scenes (detected from headings, numerals, centring, scene markers; signals say which), or sentences or dialogue lines within `range` (an address, p24#a1b2c3d4..p60#e5f6a7b8, or a unit id such as ch-d5a3930d). `chapter` limits scenes to one chapter; language (default es) sets sentence rules; profile and overrides tune detection. Use it to work chapter by chapter or to get sentence addresses (p142#a7b2a7b2.s3); doc.outline lists only styled headings. Read-only; paged at about 8,000 bytes with `cursor`.

輸入參數doc必填unitlimitrangecursorchapterprofilelanguageoverrides

doc_stats

Count what the body holds: paragraphs (and empty ones), words, characters, headings, tables, comments, footnotes, tracked changes by type, styles in use and the 20 most used fonts. stories ["notes", "headers"] adds separate counts for notes and for headers/footers (the body counts stay the body's). Use it to size a document or check for leftover revisions; doc.info gives file facts and the hash, revision.list the changes themselves. Read-only; one reply, not paged.

輸入參數doc必填stories

field_list

List every field (PAGE, TOC, REF, SEQ, XE, citations such as ADDIN ZOTERO_ITEM...) with its address, story, code, type, shown result and whether it is stale (needs updating). stories defaults to all: body, notes, headers and comments; `type` keeps one type ("TOC"). Covers nested, split and simple fields and fields in text boxes. Use it before field.update to see what is stale, or to find citations; it never changes anything. Read-only; one reply, not paged.

輸入參數doc必填typestories

field_update

Recompute the body's TOC, PAGE, NUMPAGES, SEQ and REF/PAGEREF results with headless LibreOffice and write only those result runs, as tracked changes (old result struck, new inserted). Needs LibreOffice (soffice): without it, ENGINE_UNAVAILABLE names the stale fields; set_update_on_open instead sets w:updateFields so Word updates them on opening. Citations are never changed; INDEX, SECTIONPAGES, SECTION, NOTEREF and unresolvable REFs are left and listed under `skipped`. Run after toc.insert, caption.add or xref.add. Backed up and undoable; dry_run previews; slow (seconds).

輸入參數doc必填tagauthorexpectdry_runset_update_on_open

history_adopt

Re-attach a moved or renamed document to its old undo history, so history.undo and history.restore work again. It finds the orphaned store whose last recorded state is this file's sha256 (doc.open lists them as `adoptable`, history.orphans lists all) and moves it, backups and log, to where this document's store lives now. Pass `store` when several match (the reply lists `candidates`); `force` adopts a file edited since; a document that already has history is never overwritten (BATCH_CONFLICT). dry_run moves nothing.

輸入參數doc必填forcestoredry_run

history_list

List the document's undo log, newest first: each write's op_id, kind, time, author, the operations it ran, whether it was undone, and the deleted paragraphs history.restore can bring back (change numbers). Filter with `kind` (batch, restore, undo, open, purge, adopt); `store` reads an orphaned store instead. Use it to find the op_id for history.undo or history.restore; history.search finds a quote in the log. Read-only; paged with limit (default 20) and `cursor` (the reply's `next`).

輸入參數dockindlimitstorecursor

history_orphans

List undo stores whose document is gone (moved, renamed or deleted): the app-data stores, plus `dir`/.doc-control when `dir` is given, each with its folder, last document path, entry count and last time; `unknown` holds stores that name no document. Their history is never deleted automatically. Use history.adopt to re-attach one to a moved document, history.list or history.restore with `store` to read from it, history.purge with `store` to delete it. Read-only.

輸入參數dir

history_purge

Delete undo history for privacy: the whole log (`all`) or entries before a date (`before`, ISO 8601, UTC), of the document or of an orphaned `store`. Two steps: run with dry_run to see what would go and get a `confirm` token, then repeat with `confirm`; a log that changed in between is refused. Leaves a tombstone entry; backups are not removed by it. Destructive: purged entries can no longer be undone or restored. Use history.list first to check what you remove.

輸入參數alldocstorebeforeconfirmdry_run

history_restore

Bring back paragraphs a logged write deleted or changed, as a tracked insertion (tracked: false: plainly, for a change the writer approved). Give the `op_id` (from history.list or history.search); without `change`, every paragraph that op deleted comes back after the paragraph now holding its old place; `change` picks one (also an edited one) and `after` (p12#a1b2c3d4) places it when that place was edited (BATCH_CONFLICT otherwise). Use history.undo to revert a whole write instead. Backed up, logged, undoable; dry_run previews.

輸入參數doc必填tagafterop_id必填storeauthorchangeexpectdry_runtracked

history_undo

Take back one logged write (doc.batch, text.replace and the other direct writes, comment and style writes, styleset.install, term.rename, field and note inserts, layout.apply, history.restore), returning its paragraphs and parts to their state before it: by default the latest one still in effect, or the `op_id` from history.list. Refused with BATCH_CONFLICT if a paragraph or part it changed was edited since (undo the later write first). Use history.restore to bring back single deleted paragraphs, revision.reject to drop tracked changes by author or id. The undo is itself logged; dry_run checks without writing; expect.sha256 refuses a changed file.

輸入參數doc必填op_idexpectdry_run

index_insert

Insert an index (an INDEX field) after a body paragraph `after` (p40#a1b2c3d4), as tracked insertions: a title (default "Index", "" for none), the field with a placeholder result, and a closing paragraph. Mark entries first with index.mark; Word builds the entries and page numbers when the field is updated there (F9), since field.update leaves indexes alone. Backed up and undoable (history.undo); dry_run previews; expect.sha256 refuses a changed file. Returns the field paragraph's address.

輸入參數doc必填tagafter必填titleauthorexpectdry_run

index_mark

Mark an index entry: insert an XE field, as a tracked insertion, right after a range (p8#a1b2c3d4:10-15) so that word or phrase is indexed on its page. `entry` is "Term", or "Term:Sub" / "Term:Sub:Detail" for sub-entries. Mark every occurrence you want listed, then insert the index once with index.insert. Backed up and undoable (history.undo); dry_run previews; expect.sha256 refuses a changed file. Returns the paragraph address and field code.

輸入參數at必填doc必填tagentry必填authorexpectdry_run

layout_apply

Bring the document to a page plan, the shape layout.get returns: per section its start (`from` address), break, page-number start and format, first-page difference, and header/footer text per kind ({PAGE}, {NUMPAGES}, {chapter} become fields; null inherits). Keys left out stay as they are. Edit the layout.get result and send it back. Breaks, numbering and header text are tracked changes; a new header part, a dropped reference, two_sided or mirror_margins need untracked: true. All or nothing: UNSUPPORTED_CONTENT if the result would differ from the plan or a section has a pending setup change. dry_run lists the changes.

輸入參數doc必填tagplan必填authorexpectdry_rununtracked

layout_get

Read the page plan: per section where it starts (`from` address), its break, page numbering (shown, start, format), first-page difference, and header/footer text per kind (default, first, even) with fields as {PAGE}, {NUMPAGES} and {chapter}; null means inherited from the previous section. Edit it and pass it to layout.apply to change the layout; section.list gives the same setup with header/footer part names instead of text. Read-only.

輸入參數doc必填

live_close

Close the document in its Word or LibreOffice window (attached with live.open). With unsaved changes it is refused (UNSAVED_CHANGES) unless `save` (save, then close) or `discard` (drop them) is given; nothing unsaved is ever lost silently. Use live.save to keep the window open. Returns whether it saved.

輸入參數doc必填saveenginediscard

live_get_selection

Read what the user has selected in Word or LibreOffice right now, and the paragraph the selection starts in with its address (p12#a1b2c3d4), to edit with doc.batch or text.replace. Use it when the user points at text ("this paragraph", "here"); live.select moves the selection instead. The document must be attached with live.open (ENGINE_UNAVAILABLE otherwise). Read-only.

輸入參數doc必填engine

live_open

Attach to Word or LibreOffice for live editing: find the window already holding the document, or open it in `app` (word or libreoffice; default the one reachable, see live.status) and bring it to the front (front: false keeps it behind). Call it before live.select, live.get_selection, live.save, live.close and doc.batch with engine live. Opens or focuses a window; never changes the document. ENGINE_UNAVAILABLE when no app can be driven, DOC_AMBIGUOUS when a file name matches several open documents.

輸入參數appdoc必填frontengine

live_places

Show the user a list of places as clickable buttons: in a client that renders MCP Apps, a click moves Word's or LibreOffice's cursor there (mode cursor) or selects the paragraph (mode select) by calling live.select, with no assistant turn. Each place has a `label` and an `at` address and/or a `find_phrase` from para.locate. Use it to walk a writer through findings; live.select jumps to one place directly. Other clients just get the list back. Read-only.

輸入參數doc必填modeengineplaces必填

live_save

Save the document open in Word or LibreOffice (attached with live.open) to its file, from inside the app. Call it after live edits (doc.batch with engine live) to keep them; live.close with save: true saves and closes in one call. Overwrites the file on disk with the app's version; no backup is taken.

輸入參數doc必填engine

live_select

Show the user a place in Word or LibreOffice: select a paragraph, or with mode cursor only put the cursor at its start, by address (`at`, p142#a7b2a7b2) or by a `find_phrase` from para.locate (matched loosely per match_level). paragraphs_after moves on that many paragraphs. Use it to point at a passage you are discussing; live.places offers several as buttons. Changes only the selection; needs live.open. STALE_ADDRESS when `at` no longer matches, ANCHOR_NOT_FOUND or ANCHOR_AMBIGUOUS for a phrase.

輸入參數atdoc必填modefrontenginefind_phrasematch_levelparagraphs_afterfind_phrase_across

live_status

Check the live apps: which of Word and LibreOffice are installed and reachable, and which documents they have open; with `doc`, which app holds it, whether that app is driven by this server or only a lock file says so, its full path and whether it has unsaved changes. Call it before live.open, or to decide between live editing and the file engine (a file open in Word should be edited live or closed first). Read-only; never fails for a missing app.

輸入參數docengine

note_add

Add a footnote or endnote: the reference mark goes at a body point (p3#a1b2c3d4:10-10, after offset 10), after a range, or at the end of a paragraph (p3#a1b2c3d4); `text` is the note's one line. Mark and note are tracked insertions, so rejecting them removes the note; the notes part is created when missing. Backed up and undoable (history.undo); dry_run previews; expect.sha256 refuses a changed file. Returns the note's id, displayed number and address (fn5/p1#…).

輸入參數at必填doc必填tagkind必填text必填authorexpectdry_run

page_estimate

Estimate the page each paragraph starts on (and ends on when it spans pages): from Word's rendered page marks and explicit breaks where present, else a words-per-page model; always flagged `estimated`. `at` (p142#a7b2a7b2) gives one paragraph; by "page" lists pages with their first and last paragraph instead. Use it to tell a user roughly where text is; para.locate adds chapter, scene and a find phrase. Read-only; paged at about 8,000 bytes with `cursor`.

輸入參數atbydoc必填limitcursor

para_delete

Delete a paragraph (p3#a1b2c3d4) or a run of paragraphs (p3#a1b2c3d4..p5#e5f6a7b8), as a tracked deletion the writer can reject. File engine: backed up first, logged for history.undo, dry_run previews, expect.sha256 refuses a file changed since; tracked: false deletes plainly (a change the writer approved). STALE_ADDRESS if the paragraph changed since it was read. Use text.replace to delete part of a paragraph, doc.batch for several edits all or nothing. Returns the address_map of shifted paragraphs.

輸入參數at必填doc必填tagauthorexpectdry_runtracked

para_get

Read one paragraph in full by address (p142#a7b2a7b2, or fn3/p1#… in a note): text, style, direct paragraph formatting, character-formatted spans (bold, italic, font...) and layout facts (heading_level is null for a body paragraph, 1-9 for a heading). Use it before editing a paragraph or to check its formatting; para.list reads many paragraphs, style.get a style. Read-only; a very long text is paged (limit characters, `cursor` from the reply's `next`). STALE_ADDRESS if it changed since read.

輸入參數at必填doc必填limitcursor

para_insert

Insert one new paragraph with `text` after or before an existing one (give `after` or `before`, e.g. p3#a1b2c3d4), as a tracked insertion (style it with style.apply in doc.batch). File engine: backed up first, logged for history.undo, dry_run previews, expect.sha256 refuses a changed file. tracked: false inserts plainly, on either engine. engine live inserts in the open Word/LibreOffice window (after live.open). Use doc.batch for several edits all or nothing. Returns the address_map of shifted paragraphs.

輸入參數doc必填tagtext必填afterauthorbeforeengineexpectdry_runtracked

para_list

List every paragraph in document order with its address, text, style, heading level and layout facts (centred, page break before, blank paragraphs before, empty, table cell); heading_level is null for a body paragraph. normalized adds normalised text, pages the estimated page, stories notes or headers. Use it to index or scan a whole document; para.get reads one paragraph with formatting, doc.outline only headings, text.find searches. Read-only; paged: about 8,000 bytes per reply (limit up to 1,000), continue with `cursor` set to the reply's `next`.

輸入參數doc必填limitpagescursorstoriesnormalized

para_locate

Say where paragraphs are, in terms a person can follow: for one paragraph, a range (p40#a1b2c3d4:0..p45#e5f6a7b8:0, offsets ignored) or the whole document, each one's chapter, scene, estimated page, shortest unique first … last words phrase, and a phrase Word's Find meets exactly once (for Ctrl+F or live.select). A phrase can be null with a `reason` (empty, duplicate). structure: false skips chapter detection for speed. Use it to tell a writer where a passage is; page.estimate gives only pages. Read-only; paged at about 8,000 bytes with `cursor`.

輸入參數atdoc必填limitcursorprofileoverridesstructure

recipe_list

List the cleanup recipes recipe.run can apply: the bundled ones and the user's own, each with name, version, description and source, plus any recipe files that failed validation and why. Call it to pick a recipe before recipe.run. Read-only; needs no document.

無需輸入參數。

recipe_run

Run a cleanup recipe (a name from recipe.list, or an inline `definition`) over the document and return the edits it proposes as a ready doc.batch request (`batch`, guarded by expect.sha256), repeating until the text is stable. Writes nothing itself: review the proposal, then apply it with doc.batch (dry_run there to preview). RECIPE_INVALID for an unknown or invalid recipe; edits that would touch unsupported content are listed under skipped. engine live reads the document open in the app (text-only recipes) and returns a live batch without expect, plus the text of each paragraph it edits. Refused for .txt/.md/.odt sources, which cannot be written.

輸入參數doc必填enginerecipedry_rundefinition

review_list

Read a reviewed document in one go: the body's comments and tracked insertions and deletions grouped by paragraph, in document order. Each paragraph gives its address, its text as it reads now, its comment threads (id, author, date, text, quote: the text the comment covers, resolved, replies) and its changes: a deletion and an insertion by one author right next to each other are one `replace` with `old`, `new` and both `ids`, others a lone `insert` or `delete`. `totals` counts them by kind and by author. `author` keeps that author's changes and the threads they wrote in. Use the ids with revision.accept / revision.reject (both of a replace) and doc.batch comment.reply / comment.resolve; revision.list adds formatting, moves, table changes and notes. Read-only; paged: about 8,000 bytes per reply (limit up to 1,000 paragraphs), continue with `cursor` set to the reply's `next`.

輸入參數doc必填limitauthorcursor

revision_accept

Accept tracked changes, making them plain text: by `ids` (from revision.list), by revision `author`, in a paragraph or run (`at`, p3#a1b2c3d4..p9#e5f6a7b8), or `all`. Not itself a tracked change: on the file engine the file is backed up first and the write is logged for history.undo; dry_run previews counts; expect.sha256 refuses a changed file. Use revision.reject to drop changes instead, doc.batch to accept and reject in one write. Returns counts and the revisions resolved.

輸入參數atalldoc必填idsauthorexpectdry_run

revision_list

List the tracked changes (insertions, deletions, moves, formatting, table rows and cells) in document order, each with id, type, author, date, paragraph address and text (clipped at 200 characters). A deletion and an insertion by one author right next to each other are one replacement: the insertion names the deletion in `replaces`, the deletion the insertion in `replaced_by`. Filter by `author` or `type`; stories notes or headers adds their changes. Use the ids with revision.accept or revision.reject (both halves to resolve a replacement); doc.stats counts changes by type, review.list groups changes and comments by paragraph. Read-only; paged: about 8,000 bytes per reply (limit up to 1,000), continue with `cursor` set to the reply's `next`.

輸入參數doc必填typelimitauthorcursorstories

revision_reject

Reject tracked changes, undoing them in the text (insertions removed, deletions restored): by `ids` (from revision.list), by revision `author`, in a paragraph or run (`at`, p3#a1b2c3d4..p9#e5f6a7b8), or `all`. Not itself a tracked change: on the file engine the file is backed up first and the write is logged for history.undo; dry_run previews counts; expect.sha256 refuses a changed file. Use revision.accept to keep changes instead, doc.batch to accept and reject in one write. Returns counts and the revisions resolved.

輸入參數atalldoc必填idsauthorexpectdry_run

section_list

List each section's page setup: break type, title page, even/odd headers, page-number start and format, and which header and footer story (hdr1.default, ftr2.first...) it uses per kind; null means inherited. Use the story keys to read header text with para.list (stories headers) or para.get; layout.get gives the same setup with the header and footer text, in the form layout.apply takes. Read-only.

輸入參數doc必填

server_capabilities

Describe this doc-control server: API and package version, engines (file, live), which live apps are reachable, feature support per engine, every operation with its kind and summary, every error code, and whether it is a frozen build. Call it once at the start of a session; server.help gives one operation's full request and result schemas. Read-only; needs no document.

無需輸入參數。

server_help

Get the JSON Schemas of one operation (`op`, e.g. "para.get": its request schema and its result's output_schema), or of every operation when `op` is left out. A doc.batch kind ("comment.reply", "para.move"...) gives the fields it takes inside `ops`, under batch_operations; a name that is both gets both. Old names (before 0.7.0) give the renamed operation. Call it before using an operation whose fields you are unsure of; server.capabilities lists the operations. Read-only.

輸入參數op

style_create

Add a new paragraph, character, table or numbering style with its parent (based_on), next and linked styles and run and paragraph formatting (e.g. run {"bold": true, "size_pt": 14}; paragraph in twips). `id` defaults from `name`. Refused (STYLE_CONFLICT) when the id or name exists: use style.update to change a style, style.rename to rename one, styleset.install to add many. Not a tracked change, but history.undo takes it back: the file is backed up first (`backup`); dry_run previews.

輸入參數iddoc必填runlinkname必填nexttypequickcustomexpectdefaultdry_run另有 2 個

style_get

Read one style by id or name ("Heading1" or "heading 1"): its type, parent chain, next and linked styles, how often it is used, heading level, the formatting it sets itself and its effective formatting after inheritance. Use it before style.update, or to see why text looks as it does; style.list lists all styles, para.get a paragraph's direct formatting. Read-only; STYLE_NOT_FOUND for an unknown style.

輸入參數doc必填style必填

style_list

List the styles defined in the document with id, name, type, parent and how many times each is used in the body. `type` keeps one kind (paragraph, character, table, numbering); used_only drops unused ones. Use it to find styles to clean up or reuse; style.get gives one style's full and inherited formatting. Read-only; paged: about 8,000 bytes per reply (limit up to 1,000), continue with `cursor` set to the reply's `next`.

輸入參數doc必填typelimitcursorused_only

style_merge

Merge styles: re-point every use of the `from` style or styles (ids or names) to `into` in the body, notes, headers and other styles, then delete the `from` styles. Use it to clean up duplicate styles; style.rename only renames, style.update changes formatting. Not a tracked change, but history.undo takes it back; it removes style definitions, so the file is backed up first (`backup`); check with dry_run first. STYLE_NOT_FOUND for an unknown style.

輸入參數doc必填from必填into必填expectdry_run

style_rename

Rename a style's display `name` and/or `id` (id defaults from the name); every reference follows: paragraphs, runs, headers, notes, numbering, and based_on, next and link in other styles. STYLE_CONFLICT if the new name or id is taken. Use style.merge to fold one style into another instead. Not a tracked change, but history.undo takes it back: the file is backed up first (`backup`); dry_run previews.

輸入參數iddoc必填namestyle必填expectdry_run

style_update

Change an existing style: its parent (based_on), next or linked style, quick-gallery flag, or run and paragraph formatting (e.g. run {"italic": true, "font": "Garamond"}; null removes a key; other keys stay). Every paragraph using the style changes with it; to format one paragraph use format.apply in doc.batch. Not a tracked change, but history.undo takes it back: the file is backed up first (`backup`); dry_run previews. STYLE_NOT_FOUND for an unknown style.

輸入參數doc必填runlinknextquickstyle必填expectdry_runbased_onparagraph

styleset_export

Export the document's style definitions (all, the `styles` named, or custom_only) as a JSON style set, returned and also written to `out` when given (never overwrites an existing file). Feed it to styleset.install on another document to copy the look; styleset.export_dotx writes a Word template instead. The document is not changed.

輸入參數doc必填outnamestylescustom_only

styleset_export_dotx

Write the document's style definitions (all, the `styles` named, or custom_only) to a new .dotx Word template at `out` (must not exist), for Word users to attach or base documents on. The document itself is never changed. styleset.import_dotx brings a template's styles into a document; styleset.export gives a JSON style set instead. Returns the path, style count and size.

輸入參數doc必填out必填namestylescustom_only

styleset_import_dotx

Install the styles of a .dotx Word template (`path`) into the document: new styles are added, existing ids replaced (on_conflict replace, default), kept (skip) or refused (error: STYLE_CONFLICT). Use it to apply a house template; styleset.install takes a JSON style set, style.create adds one style. Not a tracked change, but history.undo takes it back: the file is backed up first (`backup`); dry_run previews.

輸入參數doc必填path必填expectdry_runon_conflict

styleset_install

Install a JSON style set (inline `styleset`, or a file at `path`, as styleset.export writes it) into the document: new styles are added, existing ids replaced (on_conflict replace, default), kept (skip) or refused (error: STYLE_CONFLICT). Use it to copy styles between documents; styleset.import_dotx reads a Word template, style.create adds one style. Not a tracked change, but history.undo takes it back: the file is backed up first (`backup`); dry_run previews.

輸入參數doc必填pathexpectdry_runstyleseton_conflict

table_get

Read one table's grid: rows of cells, each with its first grid column, span, vertical merge (null, restart, continue) and the addresses of its paragraphs (read their text with para.get or para.list). Name it by `table` (1-based, body only, the `cell.table` para.list shows) or by `at`, any paragraph address in it, also in notes and headers. Use it before table.row.insert/delete or to edit cells with text.replace. Read-only; ANCHOR_NOT_FOUND for no such table.

輸入參數atdoc必填table

table_row_delete

Delete one row of a body table (`table` as table.get numbers it, `row` 1-based) as a tracked change the writer can reject. Refused (UNSUPPORTED_CONTENT) for a row with a vertically merged cell. Backed up and logged for history.undo; dry_run previews; expect.sha256 refuses a changed file. Use doc.batch to change several rows at once, para.delete for paragraphs outside tables.

輸入參數doc必填row必填tagtable必填authorexpectdry_run

table_row_insert

Insert a row into a body table as a tracked change: after row `after_row` (0: before the first), with one text per cell in `cells` (e.g. ["2024", "12.5"]), matching the row it follows (SCHEMA_INVALID otherwise). Refused (UNSUPPORTED_CONTENT) between two rows a vertical merge spans. Backed up and logged for history.undo; dry_run previews; expect.sha256 refuses a changed file. Use doc.batch for several rows at once.

輸入參數doc必填tagcells必填table必填authorexpectdry_runafter_row必填

term_rename

Rename a term or a character's name everywhere (Lucía to Elena) as one tracked batch: whole words by default, case-insensitive with case preserved (LUCÍA becomes ELENA), with optional inflections (["s"] also renames Lucías) and ignore_accents. match_level default/loose folds case, accents and punctuation. Matches inside fields or links are left and counted. Run dry_run first to check counts and the preview, then the same request without it. Use it instead of many text.replace edits. Backed up, undoable; tracked: false writes plainly.

輸入參數to必填doc必填tagfrom必填authorexpectdry_runtrackedwhole_wordinflectionsmatch_levelpreserve_case另有 3 個

text_find

Find text or a Python regex (`regex`) in the document; each match comes back as a range (p8#b72009e4:0-8) with context, ready for text.replace or anchor.create. ignore_case, ignore_accents (maria finds María), whole_word and match_level (default: also ignores punctuation) loosen it; stories adds notes or headers. The first page gives `total`, and query_word, the query as typed in Word's Find box. Use para.locate to describe where a paragraph is, history.search for removed text. Read-only; limit (default 20) per page, `cursor` continues; stops after 10 s with `more`.

輸入參數doc必填limitquery必填regexcursorcontextstorieswhole_wordignore_casematch_levelignore_accents

text_normalize

Normalise text (NFC, soft hyphens and zero-width characters removed, whitespace collapsed) and return a two-way offset map to the original code points, for the whole document, one paragraph or range (`at`, p142#a7b2a7b2:10-15), or a `text` string (give `doc` or `text`). Use it to compare or search text robustly and map found offsets back to addresses for text.replace; para.list normalized adds just the text. Read-only; paged at about 8,000 bytes with `cursor`; a very long paragraph comes in parts.

輸入參數atdoctextlimitcursor

text_replace

Replace one range with new text, as a tracked change: part of a paragraph (p3#a1b2c3d4:10-15, code-point offsets), a whole paragraph (p3#a1b2c3d4) or a span across paragraphs (p3#a1b2c3d4:10..p5#e5f6a7b8:2); empty `text` deletes it. Get ranges from text.find or para.get. File engine: backed up first, logged for history.undo, dry_run previews, expect.sha256 refuses a changed file. tracked: false writes plainly, on either engine. engine live edits the open app. STALE_ADDRESS if the paragraph changed since read. Use term.rename for a name everywhere, doc.batch for several edits.

輸入參數at必填doc必填tagtext必填authorengineexpectdry_runtracked

toc_insert

Insert a table of contents (a TOC field) after a body paragraph `after` (p1#a1b2c3d4), as tracked insertions: a title (default "Contents", "" for none), the field listing heading levels `levels` (default "1-3") with a placeholder result, and a closing paragraph. Run field.update next to fill in the entries and page numbers (until then field.list shows it stale). Backed up and undoable (history.undo); dry_run previews. Returns the field paragraph's address.

輸入參數doc必填tagafter必填titleauthorexpectlevelsdry_run

xref_add

Insert a cross-reference field at an insertion point (`at`: p14#a1b2c3d4:20-20, or a paragraph address for its end), pointing `to` a caption or another body paragraph. show number (default: "Table 3" for a caption), page (a PAGEREF) or text. The target gets a hidden bookmark when it has none; run field.update afterwards to fill in the shown value. A tracked insertion; backed up and undoable (history.undo); dry_run previews. Add captions with caption.add first.

輸入參數at必填to必填doc必填tagshowauthorexpectdry_run

版本歷史

10
  1. v0.10.1最新Oct 20, 2026
  2. v0.10.0Oct 20, 2026
  3. v0.9.0Oct 19, 2026
  4. v0.8.4Oct 18, 2026
  5. v0.8.3Oct 16, 2026
  6. v0.8.2Oct 16, 2026
  7. v0.8.1Oct 16, 2026
  8. v0.8.0Oct 15, 2026
  9. v0.7.0Oct 15, 2026
  10. v0.6.4Oct 15, 2026

評分

已滿足適用檢查項的 90%

該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。

  • 近 30 天內可連通25 分
  • 已宣告認證方式10 分

    請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。

  • 已列出工具必需15 分
  • 描述必需15 分
  • 圖示10 分
  • 文件15 分
  • 授權條款10 分