Skip to content

How It Works

Daniel Boles edited this page Nov 6, 2026 · 1 revision

How It Works

The problem inside a DOCX file

A DOCX file is a zip archive, and the text lives in one XML part, word/document.xml, where tracked changes are structure rather than formatting. A deletion wraps its run in w:del, an insertion wraps its run in w:ins, and moved text appears twice, once in w:moveFrom and once in w:moveTo. The difficulty is that a reader written for clean documents has no reason to know about any of this. It may report deleted words as text, or drop pending insertions, and in either case it returns a confident answer about a document that does not exist. The same part holds a second trap, because Word divides a sentence across runs for formatting and spell-check reasons, so a phrase that looks continuous on the page can sit in two separate XML elements. Therefore the server parses the tree and defines one reading of it, which it calls accepted text.

Architecture

flowchart LR
    A[Agent] -->|MCP over stdio| B[server.py]
    B --> C[docx_tools.py]
    C -->|zipfile| D[word/document.xml]
    C -->|zipfile| E[word/comments.xml]
    D --> F[Safe XML parser]
    E --> F
    F --> G[Walk body paragraphs]
    G --> H[Drop del and moveFrom]
    H --> I[Join text per paragraph]
    I -->|structured results| A
Loading

The code divides into two files of 341 and 136 lines, and the boundary between them is enforced. docx_tools.py holds all the document logic and does not import the MCP SDK, which test_the_docx_logic_does_not_import_the_sdk checks, so the same functions work as a plain Python library. server.py is a thin layer that registers six tools and turns expected failures into errors an agent can read.

Main parts

Tool Library function Purpose
read_docx_accepted_text read_accepted_text Paginated paragraphs of accepted text
find_docx_term find_term Regex search over accepted text, with context
check_docx_term_variants check_term_variants Count each spelling of a term and say where it appears
get_docx_structure_stats get_docx_stats Paragraph, word, tracked-change, and comment counts
scan_docx_meta_notes scan_meta_notes Find working notes such as TODO: left at the start
list_docx_paragraph_range list_paragraph_range Read a known range of paragraphs by index

Three behaviors support all six tools. _collect_accepted() walks the children of each paragraph and skips w:del, w:moveFrom, nested paragraphs, and paragraph properties, which means the exclusion holds at any depth, including a deletion nested inside an insertion. _get_body_paragraphs() collects every paragraph in the body, including table cells and text boxes, and leaves out the mc:Fallback copy that Word writes for a text box, so nothing counts twice. Each result carries para_index, a paragraph's position in the document and not in a page of results, so an agent can page, search, and then return to the same place.

Walkthrough: examples/call_over_stdio.py

The example builds an invented document in a temporary folder and calls the server the way a real client would, as a subprocess speaking MCP over stdio. The second paragraph of that document is the interesting one, because it holds a tracked replacement, shown here with the revision attributes shortened:

<w:p>
  <w:r><w:t>The pilot covered twelve sites </w:t></w:r>
  <w:del ...><w:r><w:delText>and failed </w:delText></w:r></w:del>
  <w:ins ...><w:r><w:t>and met its target </w:t></w:r></w:ins>
  <w:r><w:t>in the first year.</w:t></w:r>
</w:p>

An editor changed "and failed" to "and met its target", and the two meanings are opposites. The full output of the example, from a run on 2026-11-06, shows what the agent receives:

tools: check_docx_term_variants, find_docx_term, get_docx_structure_stats, list_docx_paragraph_range, read_docx_accepted_text, scan_docx_meta_notes
stats: {"comment_count": 0, "file_size_kb": 1.1, "has_tracked_changes": true, "paragraph_count": 4, "tracked_change_paragraph_count": 1, "word_count_approx": 31}
accepted: The pilot covered twelve sites and met its target in the first year.
search for deleted word: 0 hits
variants: {"cyber security": 1, "cybersecurity": 1}
notes: TODO: check the figures before this goes out.

Each line answers a question an agent would ask during review. The stats line says the document contains a tracked change, so the agent knows it is reading a revision and not a settled text. The accepted line gives the sentence with the insertion in place and the deletion gone, and the search for "failed" returns nothing, because the word survives only inside w:del. The variants line deserves a closer look. The third paragraph splits "Cyber security" across two runs, and the check still finds it, because text joins per paragraph before any matching. A search that ran on single runs would have reported that spelling as absent and the document as consistent. Finally, notes catches a TODO: left in the first paragraph, which is the kind of thing a document should not carry into its final version.

The sample proves the path works from end to end; however, it remains a small invented file, and Evidence sets out how far the tests go beyond it.

Clone this wiki locally