Skip to main content
Glama

docx-mcp-custom

Fork of SecurityRonin/docx-mcp (MIT). Tracks upstream and adds a layout/template-map toolset (get_all_paragraphs, fill_paragraph_by_index, inject_para_ids, bulk_replace_text, get_template_map, apply_table_cell_style, copy_table_style, extract_template_structure) plus cm/align sizing for inline images. All credit for the base project to the original author; see LICENSE.

License: MIT CI Coverage Sponsor SafeSkill 91/100

Give your AI coding agent the ability to create, read, and edit Word documents. Edits appear as tracked changes in Microsoft Word — red strikethrough for deletions, green underline for insertions — and after any revision session your agent can produce an email-ready change log listing every insertion, deletion, and replacement. Or compare two separate files to get the same output automatically.

Who This Is For

Professionals who produce Word deliverables and want their AI agent to handle the document work directly:

  • Legal — contract review with tracked redlines, batch clause replacement across templates, comment annotations explaining each change, footnote management

  • Security & Penetration Testing — generate pentest reports from markdown findings, merge appendices from multiple engagements, add executive-summary comments, remove DRAFT watermarks before delivery

  • Consulting — build proposals and SOWs from templates, convert meeting notes to formatted deliverables, bulk-update payment terms across document sets

  • Compliance & Audit — structural validation of document integrity, cross-reference checking, heading-level audits, protection enforcement

Related MCP server: Word Document MCP Server

What You Can Ask Your Agent To Do

Review a contract:

"Open contract.docx, find every instance of 'Net 30', change it to 'Net 60', and add a comment on each explaining it was updated per Amendment 3. Save as contract_revised.docx."

Generate a report from markdown:

"Convert my pentest-findings.md to a Word document using the client's report template. Add footnotes for each CVE reference."

Batch-edit a template library:

"Open the MSA template, replace 'ACME Corp' with 'GlobalTech Inc' everywhere, update the effective date in the header, and set document protection to track-changes-only."

Audit a document before sending:

"Open the final deliverable, run a structural audit, check for any DRAFT or TODO markers, validate all footnote cross-references, and remove any watermarks."

Your agent handles the entire workflow — opening the file, navigating the structure, making precise edits with full revision history, validating integrity, and saving — while you focus on the substance.

Installation

Claude Code (recommended):

claude mcp add docx-mcp -- uvx --from git+https://github.com/Arvindh95Censof/docx-mcp-custom docx-mcp-custom

That's it. A companion skill auto-installs the first time the server starts, teaching Claude the document editing workflow, OOXML pitfalls, and audit checklist. It auto-updates on every upgrade.

Claude Desktop:

Add to your MCP settings:

{
  "mcpServers": {
    "docx-mcp": {
      "command": "uvx",
      "args": ["--from", "git+https://github.com/Arvindh95Censof/docx-mcp-custom", "docx-mcp-custom"]
    }
  }
}

Cursor / Windsurf / VS Code:

Add to your MCP configuration file:

{
  "mcpServers": {
    "docx-mcp": {
      "command": "uvx",
      "args": ["--from", "git+https://github.com/Arvindh95Censof/docx-mcp-custom", "docx-mcp-custom"]
    }
  }
}

OpenClaw:

mcpServers:
  docx-mcp:
    command: uvx
    args:
      - --from
      - git+https://github.com/Arvindh95Censof/docx-mcp-custom
      - docx-mcp-custom

With pip:

pip install git+https://github.com/Arvindh95Censof/docx-mcp-custom

Capabilities

Capability

What your agent can do

Create documents

Start blank, from a .dotx template, or from markdown — headings, tables, lists, images, footnotes, code blocks, smart typography

Track changes

Insert, delete, and replace text as tracked revisions — underlined insertions, red strikethrough deletions. Pass tracked=False to any editing tool to write the change directly with no revision markup. Accept or reject changes by author.

Change log

After editing with tracked changes, call generate_change_summary for a numbered .txt listing every insertion, deletion, and replacement. Or use diff_to_text to compare two separate files and get the same output automatically — useful for document versions you didn't edit yourself.

Comments

Add comments anchored to specific paragraphs, reply to comment threads

Find and replace

Search by text or regex across body, footnotes, and comments — then make targeted edits

Tables

Create tables, modify cells, add or delete rows — all with revision tracking

Footnotes & endnotes

Add, list, and validate cross-references

Formatting

Bold, italic, underline, color — with revision tracking so formatting changes are visible

Headers & footers

Read and edit header/footer content with tracked changes

Images

List embedded images, insert new ones with specified dimensions

Sections & layout

Page breaks, section breaks, page size, orientation, margins

Cross-references

Internal hyperlinks between paragraphs with bookmarks

Document merge

Combine content from multiple DOCX files

Protection

Lock documents for tracked-changes-only, read-only, or comments-only with passwords

Structural audit

Validate footnotes, headings, bookmarks, images, and internal consistency before delivery

Watermark removal

Detect and strip DRAFT watermarks from headers

PII scrubbing (experimental)

Detect names, emails, phone numbers, SSNs, and more using Presidio + spaCy NER. Produces true XML redaction — original text permanently deleted, replaced with a solid black rectangle. Not for production use — see limitations.

Example: Contract Review with Redlines

1. open_document("services-agreement.docx")
2. get_headings()                              → see document structure
3. search_text("30 days")                      → find the payment clause
4. delete_text(para_id, "30 days")             → tracked deletion  (red strikethrough)
5. insert_text(para_id, "60 days")             → tracked insertion (green underline)
6. add_comment(para_id, "Extended per client request — see Amendment 3")
7. audit_document()                            → verify structural integrity
8. save_document("services-agreement_redlined.docx")

Open the output in Word and you see exactly what a human reviewer would produce — revision marks, comments in the margin, clean document structure.

Example: Change Log After a Revision Session

1. open_document("contract.docx")
2. replace_text(para_id, "30 days", "60 days")           → tracked revision (del + ins)
3. add_comment(para_id, "Extended per Amendment 3")
4. save_document("contract_redlined.docx")
5. generate_change_summary("contract_changes.txt")

The .txt output reads like:

1. REPLACEMENT by Claude on 2026-05-25
   Deleted:  "30 days"
   Inserted: "60 days"

Paste it into an email, a PR description, or a handover note.

Example: Compare Two Separate Document Versions

No editing session required — point it at any two files:

1. diff_to_text("proposal_v1.docx", "proposal_v2.docx")

Returns two files:

  • proposal_v1_diff.docx — open in Word to see tracked changes inline

  • proposal_v1_diff.txt — plain-text summary, ready to paste anywhere

Example: Pentest Report from Markdown

1. create_from_markdown("pentest-report.docx",
       md_path="findings.md",
       template_path="client-template.dotx")
2. audit_document()                            → verify integrity
3. save_document()                             → ready for delivery

Your markdown findings — headings, tables of affected hosts, code blocks with proof-of-concept output, severity ratings — become a formatted Word document matching the client's template. Smart typography is applied automatically (curly quotes, em dashes, proper ellipses).

PII Scrubbing (Experimental)

This feature is experimental and NOT suitable for production use or as the sole control for privileged, regulated, or legally sensitive documents.

scrub_pii uses Microsoft Presidio with a spaCy NER model (en_core_web_lg, ~560 MB, downloaded on first call) to detect and permanently redact personal information. Redacted spans are replaced with solid black DrawingML rectangles — the original text is deleted from the XML, not merely hidden.

What it detects reliably (via regex/pattern matching, high confidence):

  • Email addresses, phone numbers, credit card numbers, SSNs, IBANs, IP addresses, US passport numbers

What it detects statistically (via NER, will have misses):

  • Person names, organisation names, locations, dates

Known NER gaps — these will be missed:

  • Names in ALL-CAPS (common in ledgers, table headers, legal party designations)

  • Single-token names with no surrounding context

  • Non-English names (Arabic, CJK, African name patterns have low recall on this English model)

  • Names embedded in legal boilerplate (Lender: Jane Doe, Authorized Signatory: John Smith)

Recommended workflow:

1. open_document("sensitive.docx")
2. scrub_pii(dry_run=True)             → review detected entities — check for misses
3. scrub_pii("sensitive_redacted.docx") → produce redacted copy
4. open the output in Word and verify manually before sharing

Always treat the output as a first pass requiring human review, not a finished redaction.

How It Works

A .docx file is a ZIP archive of XML files. This server unpacks the archive, edits the XML directly, and repacks it. This is what gives it the ability to produce real tracked changes, comments, and footnotes — things that higher-level document libraries can't do.

Every edit is validated against the OOXML specification before saving, catching issues like orphaned footnotes, duplicate internal IDs, and broken cross-references that would otherwise cause Word to "repair" (and silently rewrite) your document.

Document Lifecycle

Tool

Description

open_document

Open a .docx file for reading and editing

create_document

Create a new blank .docx (or from a .dotx template)

create_from_markdown

Create a .docx from GitHub-Flavored Markdown

close_document

Close the current document and clean up

get_document_info

Get overview stats (paragraphs, headings, footnotes, comments)

save_document

Save changes back to .docx (can overwrite or save to new path)

Reading

Tool

Description

get_headings

Get heading structure with levels and text

search_text

Search across body, footnotes, and comments (text or regex)

get_paragraph

Get full text and style of a paragraph

Track Changes

Tool

Description

insert_text

Insert text as tracked revision (underlined in Word) — or directly with tracked=False

delete_text

Mark text deleted (red strikethrough in Word) — or remove directly with tracked=False

replace_text

Replace text as a tracked del+ins pair — or directly with tracked=False

modify_cell

Modify a table cell — tracked by default (only the changed words are marked), tracked=False for direct writes

batch_edit

Apply several edits in one call — all of them take effect, or none do (a failure restores the document)

edit_header_footer

Edit header/footer text — tracked by default, tracked=False for direct writes

set_formatting

Apply bold/italic/underline/color with tracked-change markup

set_track_changes

Enable or disable Word's built-in track-changes recording flag

get_tracked_changes

List all pending tracked changes (author, date, type, text)

accept_changes

Accept tracked changes, optionally filtered by author

reject_changes

Reject tracked changes, optionally filtered by author

Change Log

Tool

Description

generate_change_summary

Export all tracked changes in the open document as a numbered .txt file — insertions, deletions, and adjacent del+ins pairs grouped as replacements

compare_documents

Diff two DOCX files and produce a tracked-change DOCX (Word-openable)

diff_to_text

Diff two DOCX files and produce both a tracked-change DOCX and a plain-text summary .txt

Tables

Tool

Description

get_tables

Get all tables with row/column counts and cell content

add_table

Insert a new table after a paragraph

modify_cell

Modify a table cell with tracked changes (only the changed words are marked)

add_table_row

Add a row to a table

delete_table_row

Delete a table row with tracked changes

Lists

Tool

Description

add_list

Apply bullet or numbered list formatting to paragraphs

Comments

Tool

Description

get_comments

List all comments with ID, author, date, and text

add_comment

Add a comment anchored to a paragraph, or to a phrase inside it (anchor_text)

reply_to_comment

Reply to an existing comment (threaded)

Footnotes & Endnotes

Tool

Description

get_footnotes

List all footnotes with ID and text

add_footnote

Add a footnote with superscript reference

validate_footnotes

Validate footnote cross-references

get_endnotes

List all endnotes with ID and text

add_endnote

Add an endnote with superscript reference

validate_endnotes

Validate endnote cross-references

Headers, Footers & Styles

Tool

Description

get_headers_footers

Get all headers and footers with text content

edit_header_footer

Edit header/footer text with tracked changes

get_styles

Get all defined styles

Properties & Images

Tool

Description

get_properties

Get core document properties (title, creator, dates)

set_properties

Set core document properties

get_images

Get all embedded images with dimensions

insert_image

Insert an image after a paragraph

Sections & Cross-References

Tool

Description

add_page_break

Insert a page break after a paragraph

add_section_break

Add a section break (nextPage, continuous, evenPage, oddPage)

set_section_properties

Set page size, orientation, and margins

add_cross_reference

Add a cross-reference link between paragraphs

Protection & Merge

Tool

Description

set_document_protection

Set document protection with optional password

merge_documents

Merge content from another DOCX

Validation & Audit

Tool

Description

validate_paraids

Check internal ID uniqueness across all document parts

remove_watermark

Remove VML watermarks from document headers

audit_document

Comprehensive structural audit

Requirements

  • Python 3.10+

  • Works on macOS, Linux, and Windows


Privacy Policy · Terms of Service · © 2026 Security Ronin Ltd

Available Tools

227 tools
accept_all_changesA

Accept all tracked changes in document order.

Returns a JSON object with the count of accepted changes: {"accepted": int}.

Inputs: no named parameters. Returns: result

accept_changeA

Accept a single tracked change by its change_id.

For insertions: keeps the inserted text (unwraps w:ins). For deletions: discards the deleted text (removes w:del).

Args: change_id: The integer id attribute of the w:ins or w:del element.

Inputs: change_id (required). Returns: result

accept_changesA

Accept tracked changes — keep insertions, remove deletions. Empty author = all.

Inputs: author, document_handle. Returns: result

add_bookmarkB

Add a named bookmark wrapping the specified paragraph.

Inputs: name (required), para_id (required). Returns: result

add_column_to_tableA

Add a new column to every row of a table. First row gets header_text.

Inputs: table_idx (required), header_text. Returns: result

add_commentC

Add a comment anchored to a paragraph.

Inputs: text (required), para_id (required), author, document_handle. Returns: result

add_content_controlA

Wrap a paragraph in an SDT content control.

Args: para_id: paraId of the paragraph to wrap. tag: Unique tag name for the control. control_type: One of text|checkbox|dropdown|date. label: Optional display label (w:alias). options: List of option strings for dropdown controls. default: Default display text (or date string for date controls).

Inputs: tag (required), para_id (required), control_type (required), label, +2 more. Returns: result

add_cross_referenceC

Add a cross-reference link from one paragraph to another.

Inputs: text (required), source_para_id (required), target_para_id (required), document_handle. Returns: result

add_endnoteC

Add an endnote to a paragraph.

Inputs: text (required), para_id (required), document_handle. Returns: result

add_equationA

Insert a LaTeX equation as OMML. Requires: pip install latex2mathml.

Args: para_id: paraId of the paragraph after which the equation is inserted. latex: LaTeX source string (e.g. r"\frac{1}{2}").

Inputs: latex (required), para_id (required). Returns: result

add_fieldB

Insert a Word field at end of paragraph.

Common field codes: PAGE, NUMPAGES, DATE, SEQ Figure, REF MyBookmark, STYLEREF Heading.

Args: para_id: w14:paraId of the target paragraph. field_code: The field instruction text (e.g. "PAGE"). cached_value: Optional display text cached in the document.

Inputs: para_id (required), field_code (required), cached_value. Returns: result

add_footnoteC

Add a footnote to a paragraph. url, if provided, is rendered as a hotlink.

Inputs: text (required), para_id (required), url, document_handle. Returns: result

add_footnote_refA

Add a subsequent reference to an existing footnote without creating a new definition.

Use when the same source must be cited again in a different paragraph. Inserts a hyperlink-wrapped footnoteReference that navigates to the same footnote as the original citation. Does not duplicate the footnote definition in footnotes.xml.

Inputs: para_id (required), footnote_id (required), document_handle. Returns: result

add_listC

Apply list formatting to paragraphs (bullet or numbered).

Inputs: para_ids (required), style, document_handle. Returns: result

add_page_breakC

Insert a page break after a paragraph.

Inputs: para_id (required), document_handle. Returns: result

add_section_breakB

Add a section break at a paragraph. break_type: nextPage/continuous/evenPage/oddPage.

Inputs: para_id (required), break_type, document_handle. Returns: result

add_tableC

Insert a new table after a paragraph with tracked insertion.

Inputs: cols (required), rows (required), para_id (required), author, +1 more. Returns: result

add_table_rowB

Add a row to a table with tracked insertion. row_idx=-1 appends.

Inputs: table_idx (required), cells, author, row_idx, +1 more. Returns: result

apply_style_to_rangeC

Apply a style to a list of paragraphs by their paraIds.

Args: para_ids: List of paragraph paraIds to update. style_name_or_id: Style name or styleId to apply.

Returns: {"applied": int, "style_id": str, "para_ids": list[str]}

Inputs: para_ids (required), style_name_or_id (required). Returns: result

apply_table_cell_styleB

Apply a paragraph style to every cell in a table (e.g. "TableText", "Table-Text"). Use after add_table + copy_table_style to match template formatting.

Inputs: style_id (required), table_idx (required), document_handle. Returns: result

audit_documentC

Run a comprehensive structural audit of the document.

Inputs: document_handle. Returns: result

bates_numberB

Add Bates numbering stamp to document footer.

Args: prefix: Bates prefix string (e.g. "ACME-"). start: Starting Bates number. digits: Zero-padding width for the number. position: Hint for stamp position (currently "footer-right").

Inputs: prefix (required), start, digits, position. Returns: result

bulk_replace_textA

Find and replace text across all body paragraphs, handling text split across runs. replacements maps search_text -> replacement_text (case-sensitive).

Inputs: replacements (required), document_handle. Returns: result

check_accessibilityA

Scan the document for accessibility issues.

Checks images for missing alt text and tables for missing header rows.

Returns: {"issue_count": int, "issues": list[dict]}

Inputs: no named parameters. Returns: result

clear_run_formattingA

Remove all character formatting from a run, causing it to inherit paragraph/style defaults.

Args: para_id: paraId of the target paragraph. run_idx: Zero-based index of the run.

Inputs: para_id (required), run_idx (required). Returns: result

close_documentB

Close a document and clean up temporary files.

Args: document_handle: Handle of the document to close. Empty = __default__ slot.

Inputs: document_handle. Returns: result

compare_contractsC

Clause-aware diff between the open contract and another .docx file.

Inputs: other_path (required), align_by, output_path. Returns: result

compare_documentsA

Diff two DOCX files and produce a tracked-change document.

Paragraph-level LCS diff:

  • Unchanged paragraphs copied verbatim.

  • Deleted paragraphs (in base, absent in revised) wrapped in w:del.

  • Inserted paragraphs (in revised, absent in base) wrapped in w:ins.

  • Modified paragraphs (1:1 replacement) get word-level del+ins inline.

The output is a valid DOCX readable in Word/LibreOffice showing the changes as tracked revisions.

Args: base_path: Path to the original DOCX. revised_path: Path to the revised DOCX. output_path: Destination path. Auto-generated if empty.

Inputs: base_path (required), revised_path (required), output_path. Returns: result

convert_to_pdfA

Convert the open document to PDF using LibreOffice headless.

Requires LibreOffice to be installed ('libreoffice' or 'soffice' on PATH).

Args: output_path: Destination path for the output PDF file.

Returns: {"pdf_path": str}

Inputs: output_path (required). Returns: result

copy_documentA

Save a complete snapshot of the open document to a new path.

The active session and source path are unchanged.

Args: output_path: Destination file path (must end in .docx).

Returns: {"copied_to": output_path}

Inputs: output_path (required). Returns: result

copy_styleA

Deep-copy an existing style under a new name.

Args: source_name_or_id: Name or styleId of the style to copy. new_name: Name for the new style (spaces stripped for styleId).

Returns: {"style_id": str, "name": str, "type": str}

Inputs: new_name (required), source_name_or_id (required). Returns: result

copy_tableA

Deep-copy a table and insert the copy immediately after the original.

Args: table_idx: 0-based table index.

Returns: {"source_index": int, "new_index": int}

Inputs: table_idx (required). Returns: result

copy_table_styleA

Copy borders, width, and style from one table to another. Use after add_table to make new tables match existing template tables.

Inputs: source_idx (required), target_idx (required), document_handle. Returns: result

create_documentA

Create a new blank .docx document (or from a .dotx template).

The document is automatically opened for editing under the given handle. Use save_document to save changes, or start editing immediately with insert_text, add_table, etc.

Args: output_path: Path for the new .docx file. template_path: Optional path to a .dotx template file. document_handle: Optional handle to store this document under.

Inputs: output_path (required), template_path, document_handle. Returns: result

create_from_markdownA

Create a new .docx document from markdown content.

Supports full GitHub-Flavored Markdown: headings, bold/italic/strikethrough, links, images, bullet/numbered/nested lists, code blocks, blockquotes, tables, footnotes, and task lists. Smart typography (curly quotes, em/en dashes, ellipses) is applied automatically.

Provide exactly one of md_path or markdown. The document is automatically opened for editing under the given handle.

Args: output_path: Path for the new .docx file. md_path: Path to a .md file. Mutually exclusive with markdown. markdown: Raw markdown text. Mutually exclusive with md_path. template_path: Optional path to a .dotx template file. document_handle: Optional handle to store this document under.

Inputs: output_path (required), md_path, markdown, template_path, +1 more. Returns: result

create_multilevel_listB

Create a multilevel list in numbering.xml. Each level dict: {num_fmt, lvl_text, indent, hanging, style?}.

Inputs: name (required), levels (required). Returns: result

create_styleA

Create a new style in the document.

Args: name: Style name (used as styleId after removing spaces). style_type: "paragraph", "character", "table", or "numbering". based_on: Optional styleId this style inherits from. next_style: Optional styleId applied to the next paragraph.

Inputs: name (required), style_type (required), based_on, next_style. Returns: result

csv_to_tableC

Insert a table from CSV text.

Inputs: para_id (required), csv_text (required), header_row. Returns: result

delete_column_from_tableB

Delete a column (0-based) from every row of a table.

Inputs: col_idx (required), table_idx (required). Returns: result

delete_commentB

Delete a comment and remove its range markers from the document.

Args: comment_id: ID of the comment to delete.

Inputs: comment_id (required). Returns: result

delete_content_controlA

Remove an SDT content control wrapper, keeping its content in place.

Args: control_id: The w:id value of the content control to unwrap.

Inputs: control_id (required). Returns: result

delete_custom_propertyC

Delete a custom document property by name.

Args: name: Property name to delete.

Inputs: name (required). Returns: result

delete_endnoteA

Delete an endnote and its in-body reference.

Removes the endnote definition from endnotes.xml and removes the endnoteReference run from the document body.

Args: endnote_id: The numeric ID of the endnote to delete.

Inputs: endnote_id (required). Returns: result

delete_fieldA

Remove a complete complex field (begin through end runs) from the document.

Args: field_id: Positional identifier returned by list_fields (e.g. "field_0").

Inputs: field_id (required). Returns: result

delete_footnoteB

Delete a footnote and its in-body reference.

Removes the footnote definition from footnotes.xml and removes the footnoteReference run from the document body.

Args: footnote_id: The numeric ID of the footnote to delete.

Inputs: footnote_id (required). Returns: result

delete_headerA

Delete a header by location: default, first, or even.

Inputs: location. Returns: result

delete_imageA

Remove the drawing containing the image with the given rId from the document.

Also removes the relationship entry from word/_rels/document.xml.rels.

Args: rId: The relationship ID of the image to delete (e.g. 'rId5').

Inputs: rId (required). Returns: result

delete_paragraphB

Delete the paragraph with the given paraId.

Args: para_id: paraId of the paragraph to remove.

Inputs: para_id (required). Returns: result

delete_section_breakA

Remove a section break from a paragraph.

Removes the w:sectPr from the paragraph's w:pPr. After removal the paragraph's content flows into the next section rather than ending one.

Args: para_id: paraId of the paragraph that holds the section break.

Raises: ValueError: If the paragraph has no section break.

Inputs: para_id (required). Returns: result

delete_styleA

Delete a style from the document.

Args: name: Style name or styleId (case-insensitive).

Inputs: name (required). Returns: result

delete_tableA

Delete a table by index (0-based). Raises IndexError if out of range.

Inputs: table_idx (required). Returns: result

delete_table_rowC

Delete a table row with tracked changes.

Inputs: row_idx (required), table_idx (required), author, document_handle. Returns: result

delete_textA

Delete text from a paragraph.

Finds the text within the paragraph (across run boundaries if needed). With tracked=True (default) the deleted text stays visible as red strikethrough in Word's Track Changes view — the human reviewer must accept the deletion to remove it permanently. With tracked=False the text is removed immediately.

Provide context_before/context_after to disambiguate when the same text appears multiple times, or when it contains smart quotes / special whitespace.

Args: para_id: paraId of the target paragraph. text: Text to delete (ASCII quotes/dashes/spaces match their Unicode equivalents). author: Author name shown in Word's review pane (tracked=True only). context_before: Text immediately before the target (for precise anchoring). context_after: Text immediately after the target (for precise anchoring). ignore_case: If True, match text and context case-insensitively. tracked: True (default) = red strikethrough the human accepts/rejects. False = text removed immediately, no markup. document_handle: Optional handle for concurrent session isolation.

Inputs: text (required), para_id (required), author, tracked, +4 more. Returns: result

demote_list_itemA

Increase the list indentation level (ilvl) of a paragraph by 1, maximum 8.

Returns {para_id, ilvl}. Raises ValueError if paragraph is not a list item.

Inputs: para_id (required). Returns: result

diff_to_textA

Compare two separate DOCX files and produce a tracked-change DOCX plus a plain-text summary.

Use this when you have two distinct files (e.g. an original and a revised copy) and want to show what changed between them. Produces:

  1. A DOCX with tracked-change markup (deletions in red, insertions underlined).

  2. A .txt summary suitable for pasting into an email or pull-request description.

If you have already made tracked edits to the currently open document and just want to summarise those changes, use generate_change_summary instead.

Args: base_path: Path to the original DOCX. revised_path: Path to the revised DOCX. docx_output: Output path for the tracked-change DOCX (auto-generated if empty). text_output: Output path for the plain-text summary .txt (auto-generated if empty).

Inputs: base_path (required), revised_path (required), docx_output, text_output. Returns: result

duplicate_table_rowA

Deep-copy a table row and insert the copy immediately after it.

Args: table_idx: 0-based table index. row_index: 0-based index of the row to duplicate.

Returns: {"row_index": int, "new_row_index": int}

Inputs: row_index (required), table_idx (required). Returns: result

export_markdownA

Export the open document as Markdown.

Converts the document body (headings, bold/italic runs, lists, tables, plain paragraphs) to GitHub-Flavoured Markdown and writes it to a file.

Args: output_path: Destination path for the .md file. Defaults to /export.md when empty.

Returns: {"output_path": str, "paragraphs": int, "tables": int}

Inputs: output_path. Returns: result

export_session_scriptC

Write session operations as a Python replay script.

Returns: {"output_path": str, "operations": int}

Inputs: output_path (required). Returns: result

extract_template_structureA

Extract full structure: all paragraphs (index, style, text, paraId) and tables (index, dimensions, cell content). Use to map a template before filling it.

Inputs: document_handle. Returns: result

fill_paragraph_by_indexA

Replace all text in a paragraph by its zero-based index, preserving the paragraph style and first run's formatting. Use get_all_paragraphs first to find the index.

Inputs: text (required), index (required), document_handle. Returns: result

fill_templateA

Fill SDT content controls from data dict. Keys match w:tag values.

Args: data: Mapping of tag names to values. Use list[str] for repeating sections. remove_empty: If True, remove SDTs with no matching key in data.

Inputs: data (required), remove_empty. Returns: result

find_replace_formattedA

Find all occurrences of a string and replace with formatted text.

Replaces every occurrence of find across all paragraphs in the document with replace, applying the specified character formatting to the replacement run only.

Args: find: Text to search for (must be non-empty). replace: Replacement text. bold: True to bold, False to explicitly un-bold, None to leave unchanged. italic: True to italicise, False to explicitly un-italicise, None to leave unchanged. color: Font color as 6-digit hex (e.g. "FF0000"). None leaves color unchanged. size_pt: Font size in points (e.g. 12.0). None leaves size unchanged.

Inputs: find (required), replace (required), bold, color, +2 more. Returns: result

flatten_documentA

Accept all tracked changes and remove all revision markup.

Accepts every w:ins / w:del and strips all w:rPrChange and w:pPrChange elements, leaving plain text with no tracked-change metadata.

Returns: {"changes_accepted": int, "formatting_changes_removed": int}

Inputs: no named parameters. Returns: result

generate_change_summaryA

Summarise tracked changes already present in the open document as an email-ready .txt.

Use this after making edits with tracked=True (the default) and saving, to produce a human-readable change log of what was modified. Reads the document's existing w:ins / w:del elements, groups adjacent deletion+insertion pairs as REPLACEMENT entries, and writes a numbered list with author, date, and text per change.

Typical workflow: open_document → edit with tracked=True → save_document → generate_change_summary

If you have two separate files to compare rather than an already-edited document, use diff_to_text instead.

Args: output_path: Destination .txt path. Auto-generated from the document stem if empty. document_handle: Optional handle for concurrent session isolation.

Inputs: output_path, document_handle. Returns: result

generate_list_of_figuresA

Insert a List of Figures field (requires SEQ Figure captions).

Inputs: no named parameters. Returns: result

generate_list_of_tablesA

Insert a List of Tables field (requires SEQ Table captions).

Inputs: no named parameters. Returns: result

generate_privilege_logA

Generate a privilege log DOCX from document metadata.

Args: output_path: Destination path. If empty, writes to a temp file.

Inputs: output_path. Returns: result

generate_redaction_logA

Write a DOCX table of all redactions made this session.

Args: output_path: Destination path. If empty, writes to a temp file.

Inputs: output_path. Returns: result

generate_tocC

Generate a Table of Contents from document headings.

Inputs: title, max_level. Returns: result

generate_tofC

Insert a Table of Figures field block after the paragraph with para_id.

Collects caption-styled paragraphs starting with 'Figure' as entries.

Inputs: para_id (required), title. Returns: result

generate_totB

Insert a Table of Tables field block after the paragraph with para_id.

Collects caption-styled paragraphs starting with 'Table' as entries.

Inputs: para_id (required), title. Returns: result

get_all_paragraphsA

Return all body paragraphs with index, style, text, and paraId. Use to discover indices for fill_paragraph_by_index or paraIds after inject_para_ids.

Inputs: document_handle. Returns: result

get_alt_textA

Get the alt text and title for an image by 0-based index.

Args: image_index: 0-based index across all wp:docPr elements in document.xml.

Returns: {"image_index": int, "alt_text": str, "title": str}

Inputs: image_index (required). Returns: result

get_body_textA

Return the full accepted-view text of the document.

Accepted view: w:ins text included, w:del text excluded. Includes text inside w:hyperlink runs. Paragraphs are joined by newline. Footnote text is returned separately.

Returns JSON: {"body": str, "footnotes": str}

Inputs: document_handle. Returns: result

get_bookmarked_textB

Get the text content within a named bookmark.

Inputs: name (required). Returns: result

get_cell_textA

Return text content of a specific cell.

Args: table_idx: 0-based table index. row_idx: 0-based row index. col_idx: 0-based column index.

Returns: {"table_index": int, "row_index": int, "col_index": int, "text": str}

Inputs: col_idx (required), row_idx (required), table_idx (required). Returns: result

get_commentsC

List all comments with their ID, author, date, and text.

Inputs: document_handle. Returns: result

get_content_controlA

Return details of a single content control by its w:id.

Args: control_id: The w:id value of the content control to retrieve.

Inputs: control_id (required). Returns: result

get_content_controlsA

List all SDT content controls in the document.

Inputs: no named parameters. Returns: result

get_custom_propertiesA

Get custom document properties from docProps/custom.xml.

Inputs: no named parameters. Returns: result

get_document_infoA

Get overview stats: paragraph count, headings, footnotes, comments, images.

Inputs: document_handle. Returns: result

get_document_outlineB

Return a flat list of headings as a document outline.

Walks the document body and returns every heading paragraph up to the specified level.

Args: max_level: Maximum heading level to include (1–6, default 6).

Returns: JSON list of dicts with keys: level (int), text (str), para_id (str).

Inputs: max_level. Returns: result

get_endnotesC

Get all endnotes with their ID and text content.

Inputs: document_handle. Returns: result

get_equationsA

Return all equations in the document as OMML XML strings.

Inputs: no named parameters. Returns: result

get_fieldA

Return details of a single field by field_id.

Args: field_id: Positional identifier returned by list_fields (e.g. "field_0").

Inputs: field_id (required). Returns: result

get_footnotesC

List all footnotes with their ID and text content.

Inputs: document_handle. Returns: result

get_headers_footersC

Get all headers and footers with their text content.

Inputs: document_handle. Returns: result

get_headingsB

Get the document heading structure with levels, text, and paraIds.

Returns a list of headings in document order, each with:

  • level (1-9)

  • text (heading content)

  • style (e.g., "Heading1")

  • paraId (unique paragraph identifier for targeting edits)

Inputs: document_handle. Returns: result

get_imagesB

Get all embedded images with rId, filename, content type, and dimensions.

Inputs: document_handle. Returns: result

get_listsA

Return all list definitions from numbering.xml.

Each entry: {abstract_num_id, num_format, levels}. Returns [] if no numbering.xml exists.

Inputs: no named parameters. Returns: result

get_paragraphA

Get the full text and style of a specific paragraph by its paraId.

Args: para_id: The 8-character hex paraId (e.g., "1A2B3C4D").

Inputs: para_id (required), document_handle. Returns: result

get_paragraph_formatA

Read all formatting attributes of a paragraph.

Returns style, alignment, indentation, line spacing, border, shading, and list info.

Args: para_id: paraId of the target paragraph.

Inputs: para_id (required). Returns: result

get_propertiesB

Get core document properties (title, creator, subject, dates, revision).

Inputs: document_handle. Returns: result

get_reading_timeA

Estimate reading time for the open document.

Args: words_per_minute: Assumed reading speed (default 200 wpm).

Returns: {"word_count": int, "words_per_minute": int, "minutes": float, "seconds": int}

Inputs: words_per_minute. Returns: result

get_runsA

Get all runs in a paragraph with their formatting properties.

Args: para_id: paraId of the target paragraph.

Inputs: para_id (required). Returns: result

get_sectionsA

List all sections in the document with their properties.

Returns a JSON array of section objects, each containing: index, break_type, page_width, page_height, orientation, columns, margin_top, margin_bottom (all sizes in twips/DXA). The final section always has break_type="".

Inputs: no named parameters. Returns: result

get_session_logA

Return all operations performed this session as replayable JSON.

Inputs: no named parameters. Returns: result

get_statisticsA

Return document statistics for the open document.

Returns a dict with keys: word_count, character_count, paragraph_count, table_count, image_count, section_count.

Inputs: no named parameters. Returns: result

get_styleA

Get details of a single style by name or styleId (case-insensitive).

Args: name_or_id: Style name or styleId to look up.

Returns: {"style_id": str, "name": str, "type": str, "base_style": str, "next_style": str}

Inputs: name_or_id (required). Returns: result

get_stylesB

Get all defined styles with ID, name, type, and base style.

Inputs: document_handle. Returns: result

get_tableA

Get structured info for a single table by zero-based index.

Args: table_idx: 0-based table index.

Returns: {"index": int, "row_count": int, "col_count": int, "cells": list[list[str]]}

Inputs: table_idx (required). Returns: result

get_tablesB

Get all tables with row/column counts and cell text content.

Inputs: document_handle. Returns: result

get_template_mapA

Return the template style map: default font/size, named paragraph styles, table styles, and page margins. Use before filling to match formatting.

Inputs: document_handle. Returns: result

get_theme_colorsA

Return the named color slots from word/theme/theme1.xml.

Returns a dict mapping slot names (dk1, lt1, accent1, ...) to 6-digit hex strings. Returns an empty dict if the document has no theme file.

Inputs: no named parameters. Returns: result

get_tracked_changesC

Return all pending tracked changes (insertions and deletions) as a JSON list.

Each entry contains: type, change_id, author, date, para_id, text. Changes are returned in document order.

Inputs: document_handle. Returns: result

get_word_countA

Return the word count of the open document body.

Returns: {"word_count": int}

Inputs: no named parameters. Returns: result

inject_para_idsA

Assign w14:paraId to every paragraph missing one, so paraId-based tools work. Returns the count injected. Run once on templates created without paraIds.

Inputs: document_handle. Returns: result

insert_bar_chartC

Insert a native bar chart (no Excel required).

series: [{"name": str, "values": [float, ...]}]

Inputs: title (required), series (required), para_id (required), categories (required), +2 more. Returns: result

insert_blockquoteA

Insert a blockquote paragraph after the given paragraph.

The new paragraph has 720-twip left indent and italic formatting.

Args: para_id: paraId of the reference paragraph (new para inserted after it). text: Text content of the blockquote.

Inputs: text (required), para_id (required). Returns: result

insert_captionB

Insert a caption paragraph after the specified paragraph.

Args: after_para_id: paraId of the paragraph to insert after. text: Caption description text (without label/number prefix). label: Caption label, e.g. "Figure" or "Table" (default "Figure").

Inputs: text (required), after_para_id (required), label. Returns: result

insert_code_blockA

Insert a code-block paragraph after the given paragraph.

The new paragraph uses Courier New 10pt with light-gray background shading.

Args: para_id: paraId of the reference paragraph (new para inserted after it). text: Code text content. language: Optional language hint (stored in return value only).

Inputs: text (required), para_id (required), language. Returns: result

insert_date_fieldB

Insert a DATE field at the end of a paragraph.

Args: para_id: w14:paraId of the target paragraph. date_format: Date picture switch (default: @ "MMMM d, yyyy").

Inputs: para_id (required), date_format. Returns: result

insert_floating_imageC

Insert a floating (anchored) image. wrap: square|topbottom|none.

Inputs: para_id (required), width_cm (required), height_cm (required), image_path (required), +3 more. Returns: result

insert_if_fieldA

Insert a Word IF conditional field at the end of a paragraph.

Args: para_id: w14:paraId of the target paragraph. condition: The condition expression (e.g. "x > 0"). true_text: Text to display when condition is true. false_text: Text to display when condition is false.

Inputs: para_id (required), condition (required), true_text (required), false_text (required). Returns: result

insert_imageC

Insert an image after a paragraph. Size in cm (preferred) or EMU; cm overrides EMU.

align sets the image paragraph alignment (left/center/right).

Inputs: para_id (required), image_path (required), align, width_cm, +4 more. Returns: result

insert_line_chartC

Insert a native line chart.

series: [{"name": str, "values": [float, ...]}]

Inputs: title (required), series (required), para_id (required), categories (required), +2 more. Returns: result

insert_merge_fieldA

Insert a MERGEFIELD (mail merge) field at the end of a paragraph.

Args: para_id: w14:paraId of the target paragraph. field_name: The merge field name (e.g. "FirstName").

Inputs: para_id (required), field_name (required). Returns: result

insert_page_number_fieldB

Insert a PAGE field at the end of a paragraph.

Args: para_id: w14:paraId of the target paragraph.

Inputs: para_id (required). Returns: result

insert_paragraphA

Insert a new paragraph after the paragraph with the given paraId.

Args: after_para_id: paraId of the paragraph to insert after. text: Text content for the new paragraph. style: Optional paragraph style name (e.g., "Heading1", "Normal").

Inputs: text (required), after_para_id (required), style. Returns: result

insert_pie_chartC

Insert a native pie chart (single series, fixed 14x9 cm).

series: [{"name": str, "values": [float, ...]}]

Inputs: title (required), series (required), para_id (required), categories (required). Returns: result

insert_sequence_fieldA

Insert a SEQ (sequence) field for figure/table numbering.

Args: para_id: w14:paraId of the target paragraph. seq_name: The sequence identifier (e.g. "Figure", "Table"). reset: If True, restarts the sequence counter at 1.

Inputs: para_id (required), seq_name (required), reset. Returns: result

insert_textA

Insert text into a paragraph.

By default (tracked=True) the insertion is marked as a proposed addition that appears underlined in Word's Track Changes view. The human reviewer must accept it in Word before it becomes permanent. Pass tracked=False to write the text directly with no revision markup.

Args: para_id: paraId of the target paragraph. text: Text to insert. position: Where to insert — "start", "end", or a substring to insert after. author: Author name shown in Word's review pane (tracked=True only). context_before: Text immediately before the insertion point (for precise anchoring). context_after: Text immediately after the insertion point (for precise anchoring). ignore_case: If True, match context_before/context_after case-insensitively. tracked: True (default) = revision markup the human accepts/rejects in Word. False = text written directly, no markup. document_handle: Optional handle for concurrent session isolation.

Inputs: text (required), para_id (required), author, tracked, +5 more. Returns: result

insert_text_boxA

Insert an inline text box after the paragraph with para_id.

Args: para_id: paraId of the reference paragraph (insert after this). text: Text content for the text box. width_cm: Width in centimetres (default 5.0). height_cm: Height in centimetres (default 2.0).

Returns: JSON with para_id (new paragraph), text, width_cm, height_cm.

Inputs: text (required), para_id (required), width_cm, height_cm. Returns: result

insert_watermarkA

Insert a VML watermark into the document's default header.

Places a <v:shape> with a <v:textpath> inside the default header, which is the standard Word watermark pattern.

Args: text: Watermark text (e.g. "DRAFT", "CONFIDENTIAL"). diagonal: If True (default), diagonal orientation; if False, horizontal. document_handle: Optional handle for concurrent session isolation.

Inputs: text (required), diagonal, document_handle. Returns: result

list_bookmarksB

List all bookmarks in the document.

Inputs: no named parameters. Returns: result

list_comment_threadsA

List all comment threads (root comments with their replies).

Returns a list of thread dicts: {root: {id, author, date, text}, replies: [...]}.

Inputs: no named parameters. Returns: result

list_fieldsA

List all fields in the document with their codes and cached values.

Inputs: no named parameters. Returns: result

list_partsA

List all XML parts (files) in the open DOCX zip.

Inputs: no named parameters. Returns: result

list_template_fieldsA

List all SDT template fields (tag, label, type) in the document.

Inputs: no named parameters. Returns: result

lock_content_controlB

Lock a content control to prevent editing.

Args: tag: Tag name of the control to lock. lock: Lock type — sdtLocked|contentLocked|sdtContentLocked.

Inputs: tag (required), lock. Returns: result

merge_cellsC

Merge a rectangular range of cells. Horizontal: gridSpan. Vertical: vMerge.

Inputs: end_col (required), end_row (required), start_col (required), start_row (required), +1 more. Returns: result

merge_documentsC

Merge another DOCX document's content into the current document.

Inputs: source_path (required), document_handle. Returns: result

merge_review_roundsC

Merge tracked changes from N reviewer copies into the open document.

Inputs: reviewer_paths (required), base_path. Returns: result

modify_cellA

Modify a table cell.

By default (tracked=True) the old content is marked as a deletion and the new content as an insertion — the human reviewer accepts/rejects in Word's Track Changes view. Pass tracked=False to overwrite the cell directly with no markup.

Args: table_idx: Table index (0-based). row: Row index (0-based). col: Column index (0-based). text: New cell text. author: Author name shown in Word's review pane (tracked=True only). tracked: True (default) = tracked del+ins. False = direct overwrite, no markup. document_handle: Optional handle for concurrent session isolation.

Inputs: col (required), row (required), text (required), table_idx (required), +3 more. Returns: result

open_documentA

Open a .docx file for reading and editing.

Unpacks the DOCX archive, parses all XML parts, and caches them in memory under a handle. Multiple documents may be open concurrently, each under its own handle.

Args: path: Absolute path to the .docx file. document_handle: Optional handle to store this document under. Empty string uses the shared __default__ slot (legacy behavior); pass a unique value (e.g. a UUID) per concurrent session for isolation.

Inputs: path (required), document_handle. Returns: result

promote_list_itemA

Decrease the list indentation level (ilvl) of a paragraph by 1, minimum 0.

Returns {para_id, ilvl}. Raises ValueError if paragraph is not a list item.

Inputs: para_id (required). Returns: result

read_partA

Read raw XML of any DOCX part (e.g. 'word/document.xml'). Use list_parts() to discover available parts.

Inputs: part_path (required). Returns: result

redact_textA

True redaction: remove text and replace with black rectangle. Use exact_text or pattern.

Args: pattern: Regex pattern to match run text. para_ids: Optional list of paragraph paraId values to limit scope. exact_text: Exact string to match against run text. reason: Reason for redaction (stored in log).

Inputs: reason, pattern, para_ids, exact_text. Returns: result

reject_all_changesA

Reject all tracked changes in document order.

Returns a JSON object with the count of rejected changes: {"rejected": int}.

Inputs: no named parameters. Returns: result

reject_changeA

Reject a single tracked change by its change_id.

For insertions: discards the inserted text (removes w:ins). For deletions: keeps the deleted text (unwraps w:del, restoring text).

Args: change_id: The integer id attribute of the w:ins or w:del element.

Inputs: change_id (required). Returns: result

reject_changesC

Reject tracked changes — remove insertions, restore deleted text.

Inputs: author, document_handle. Returns: result

remove_bookmarkA

Remove a bookmark by name (keeps paragraph content).

Inputs: name (required). Returns: result

remove_watermarkB

Remove VML watermarks (e.g., DRAFT) from all document headers.

Inputs: document_handle. Returns: result

replace_textA

Replace text in a paragraph.

With tracked=True (default) the old text is shown as red strikethrough and the new text as an underlined insertion — the human reviewer accepts/rejects in Word. Only the actually-changed portion is marked; common leading/trailing text is left as plain runs. With tracked=False the replacement is applied immediately without any visible markup.

Args: para_id: paraId of the target paragraph. find: Text to find and replace (may span run boundaries). replace: Replacement text. author: Author name shown in Word's review pane (tracked=True only). context_before: Text immediately before the target (for precise anchoring). context_after: Text immediately after the target (for precise anchoring). tracked: True (default) = strikethrough + underline the human accepts/rejects. False = replaced immediately, no markup. document_handle: Optional handle for concurrent session isolation.

Inputs: find (required), para_id (required), replace (required), author, +5 more. Returns: result

reply_to_commentC

Reply to an existing comment (creates a threaded reply).

Inputs: text (required), parent_id (required), author, document_handle. Returns: result

resolve_commentA

Mark a comment as resolved (sets w15:done='1' in commentsExtended.xml).

Args: comment_id: ID of the comment to resolve.

Inputs: comment_id (required). Returns: result

restart_numberingB

Restart list numbering at a paragraph. Adds lvlOverride with startOverride.

Inputs: para_id (required), level, start. Returns: result

sanitize_metadataA

Write a sanitized copy of the open document to output_path.

Level 1: Remove rsid session-fingerprint attributes from document.xml. Level 2: + Replace tracked-change author names (w:author on w:ins/w:del). Level 3: + Clear creator/lastModifiedBy/revision in docProps/core.xml + Clear Company in docProps/app.xml + Remove attachedTemplate reference from word/settings.xml

Args: output_path: Destination path for the sanitized DOCX. Must be non-empty. level: Sanitization depth (1, 2, or 3). Default 1. redact_authors_as: Replacement author string for level 2+. Default "Anonymous".

Inputs: output_path (required), level, redact_authors_as. Returns: result

save_documentA

Save all changes back to a .docx file.

Args: output_path: Path for the output file. If empty, overwrites the original source path of the document under this handle. document_handle: Handle of the document to save. Empty = __default__.

Inputs: output_path, document_handle. Returns: result

scrub_piiA

[EXPERIMENTAL] Detect and redact PII from the open document using Presidio + spaCy NER.

WARNING: This tool WILL miss PII. It is experimental and NOT suitable for production use or as the sole control for privileged, regulated, or legally sensitive documents. Always run with dry_run=True first and manually review every detected entity before committing a redacted file.

Known limitations (statistical NER gaps):

  • Names in ALL-CAPS (ledger headers, table cells) are frequently missed.

  • Single-token names with no surrounding context are unreliable.

  • Non-English names (Arabic, CJK, African) have low recall on this English model.

  • Names embedded in legal boilerplate ("Borrower: Jane Doe") are often skipped.

NER model (en_core_web_lg, ~560MB) downloads automatically on first use.

Detects: PERSON, EMAIL_ADDRESS, PHONE_NUMBER, CREDIT_CARD, SSN, IP_ADDRESS, IBAN_CODE, US_BANK_NUMBER, US_PASSPORT, and more via Presidio.

Redacted text is replaced with a solid black DrawingML rectangle — true XML redaction where the original text is deleted from the OOXML entirely, not merely hidden by formatting.

Args: output_path: Destination path. Required when dry_run=False. entities: Presidio entity types to redact. None = all detected types. confidence_threshold: Presidio score floor (default 0.35). dry_run: If True, detect only — return entity list, write no file. also_sanitize_metadata: Apply level-3 metadata sanitization (default True). redact_authors_as: Replacement author string for metadata pass.

Inputs: dry_run, entities, output_path, redact_authors_as, +2 more. Returns: result

search_textA

Search for text across the document body, footnotes, and comments.

Args: query: Text to search for (case-insensitive), or a regex pattern. regex: If true, treat query as a Python regular expression.

Returns matching paragraphs with their paraId, source part, and context.

Inputs: query (required), regex, document_handle. Returns: result

set_alt_textB

Set the alt text (and optionally title) on an image by 0-based index.

Args: image_index: 0-based index across all wp:docPr elements in document.xml. alt_text: Accessibility description to set on the image. title: Optional title attribute; removed from XML if empty.

Returns: {"image_index": int, "alt_text": str}

Inputs: alt_text (required), image_index (required), title. Returns: result

set_cell_shadingC

Set background shading fill color on a table cell.

Inputs: col_idx (required), row_idx (required), table_idx (required), fill_color (required), +1 more. Returns: result

set_cell_vertical_alignmentC

Set vertical alignment of a table cell: top, center, or bottom.

Inputs: col_idx (required), row_idx (required), alignment (required), table_idx (required). Returns: result

set_cell_widthB

Set the width of a table cell in millimetres (stored as DXA).

Inputs: col_idx (required), row_idx (required), width_mm (required), table_idx (required). Returns: result

set_character_positionA

Set vertical character position (raised/lowered) for a specific run.

Args: para_id: paraId of the target paragraph. run_idx: Zero-based index of the run. position_pt: Offset in points (positive = raised, negative = lowered).

Inputs: para_id (required), run_idx (required), position_pt (required). Returns: result

set_character_spacingA

Set character spacing (tracking) for a specific run in a paragraph.

Args: para_id: paraId of the target paragraph. run_idx: Zero-based index of the run. spacing_pt: Spacing in points (positive = expanded, negative = condensed).

Inputs: para_id (required), run_idx (required), spacing_pt (required). Returns: result

set_column_widthsA

Set column widths in cm. len(widths_cm) must match column count.

Inputs: widths_cm (required), table_index (required). Returns: result

set_content_control_valueB

Update the value/text of a content control by its tag.

Args: tag: Tag name of the control to update. value: New value. For checkbox: 'true'/'1' = checked, 'false'/'0' = unchecked.

Inputs: tag (required), value (required). Returns: result

set_custom_propertyC

Set (upsert) a custom document property.

Args: name: Property name. value: Property value as a string. vt_type: VT type element name (lpwstr, i4, bool, etc.).

Inputs: name (required), value (required), vt_type. Returns: result

set_different_first_pageA

Enable or disable a different first-page header/footer for a section.

When enabled, the section can have a unique header/footer on its first page, separate from the header/footer used on subsequent pages.

Args: section_index: Zero-based section index (use get_sections to find it). enabled: True to enable different first page, False to disable.

Returns: {"section_index": int, "different_first_page": bool}

Inputs: enabled (required), section_index (required). Returns: result

set_document_languageA

Set the default document language.

Writes the BCP-47 language tag into the default paragraph style's run properties (w:rPr/w:lang) in word/styles.xml.

Args: language_code: BCP-47 language tag, e.g. "en-US", "fr-FR", "de-DE".

Returns: {"language": language_code}

Inputs: language_code (required). Returns: result

set_document_protectionC

Set document protection. edit: trackedChanges/comments/readOnly/forms/none.

Inputs: edit (required), password, document_handle. Returns: result

set_formattingC

Apply character formatting to text with tracked-change markup.

Inputs: text (required), para_id (required), bold, color, +4 more. Returns: result

set_header_rowC

Mark the first row as a repeating header row.

Inputs: table_index (required). Returns: result

set_image_alt_textA

Set accessibility alt text and title on an embedded image.

Args: rId: The relationship ID of the image. alt_text: Alt text (descr attribute on wp:docPr). title: Optional title attribute on wp:docPr.

Inputs: rId (required), alt_text (required), title. Returns: result

set_image_borderA

Set or remove a border on an embedded image.

Args: rId: The relationship ID of the image (e.g. 'rId6'). border_pt: Border width in points. Use 0 to remove the border. color: RGB hex color string without '#' (default '000000' = black).

Returns: JSON with rId, border_pt, and color fields.

Inputs: rId (required), border_pt (required), color. Returns: result

set_image_sizeA

Resize an embedded image by updating its EMU extent attributes.

Args: rId: The relationship ID of the image to resize. width_cm: New width in centimetres. height_cm: New height in centimetres.

Inputs: rId (required), width_cm (required), height_cm (required). Returns: result

set_keep_lines_togetherA

Keep all lines of this paragraph on the same page.

Args: para_id: paraId of the target paragraph. enabled: True to enable keep-lines-together, False to remove it.

Inputs: enabled (required), para_id (required). Returns: result

set_keep_with_nextA

Keep this paragraph on the same page as the next paragraph.

Args: para_id: paraId of the target paragraph. enabled: True to enable keep-with-next, False to remove it.

Inputs: enabled (required), para_id (required). Returns: result

set_line_spacingA

Set line spacing and paragraph spacing.

Args: para_id: paraId of the target paragraph. line_rule: "auto" | "exact" | "atLeast". None leaves unchanged. line_value: For "auto": 240=single, 360=1.5x, 480=double. For "exact"/"atLeast": twips. space_before_pt: Space before paragraph in points (None = unchanged). space_after_pt: Space after paragraph in points (None = unchanged).

Inputs: para_id (required), line_rule, line_value, space_after_pt, +1 more. Returns: result

set_odd_even_headersA

Enable or disable different odd/even page headers globally.

This is a document-level setting stored in word/settings.xml. When enabled, even-numbered pages can use a different header/footer from odd-numbered pages.

Args: enabled: True to enable different odd/even headers, False to disable.

Returns: {"odd_even_headers": bool}

Inputs: enabled (required). Returns: result

set_page_break_beforeA

Force a page break before this paragraph.

Args: para_id: paraId of the target paragraph. enabled: True to force page break before, False to remove it.

Inputs: enabled (required), para_id (required). Returns: result

set_page_marginsA

Set page margins from millimetre values.

Args: top_mm: Top margin in mm. None = unchanged. bottom_mm: Bottom margin in mm. None = unchanged. left_mm: Left margin in mm. None = unchanged. right_mm: Right margin in mm. None = unchanged. para_id: paraId of paragraph with section break. None = body section.

Inputs: top_mm, left_mm, para_id, right_mm, +1 more. Returns: result

set_page_orientationA

Set page orientation, swapping width/height dimensions if needed.

Args: orientation: "portrait" or "landscape". para_id: paraId of paragraph with section break. None = body section.

Inputs: orientation (required), para_id. Returns: result

set_page_sizeA

Set page size from millimetre values.

Args: width_mm: Page width in mm (e.g. 210 for A4, 215.9 for Letter). height_mm: Page height in mm (e.g. 297 for A4, 279.4 for Letter). para_id: paraId of paragraph with section break. None = body section.

Inputs: width_mm (required), height_mm (required), para_id. Returns: result

set_paragraph_borderB

Set borders on one or more sides of a paragraph.

Args: para_id: paraId of the target paragraph. sides: List of sides: "top", "bottom", "left", "right", "between". color: Border color as 6-digit hex (default "000000"). size: Border width in eighths of a point (default 4 = 0.5pt).

Inputs: sides (required), para_id (required), size, color. Returns: result

set_paragraph_indentationA

Set indentation on a paragraph.

Args: para_id: paraId of the target paragraph. left_cm: Left indent in centimetres (None = unchanged). right_cm: Right indent in centimetres (None = unchanged). first_line_cm: First-line indent in cm (mutually exclusive with hanging_cm). hanging_cm: Hanging indent in cm (mutually exclusive with first_line_cm).

Inputs: para_id (required), left_cm, right_cm, hanging_cm, +1 more. Returns: result

set_paragraph_shadingB

Set background shading on a paragraph.

Args: para_id: paraId of the target paragraph. fill_color: Fill color as 6-digit hex (e.g., "FFFF00"). pattern: Shading pattern (default "clear").

Inputs: para_id (required), fill_color (required), pattern. Returns: result

set_propertiesB

Set core document properties. Empty string = unchanged.

Inputs: title, creator, subject, description, +1 more. Returns: result

set_row_heightB

Set row height in millimetres. rule: exact, atLeast, or auto.

Inputs: row_idx (required), height_mm (required), table_idx (required), rule. Returns: result

set_run_colorA

Set the font color of a specific run in a paragraph.

Args: para_id: paraId of the target paragraph. run_idx: Zero-based index of the run. color: Hex color without # (e.g., "FF0000").

Inputs: color (required), para_id (required), run_idx (required). Returns: result

set_run_fontC

Set the font of a specific run (zero-based index) in a paragraph.

Args: para_id: paraId of the target paragraph. run_idx: Zero-based index of the run. font_name: Font name (e.g., "Arial", "Times New Roman").

Inputs: para_id (required), run_idx (required), font_name (required). Returns: result

set_run_highlightB

Set highlight color of a specific run in a paragraph.

Args: para_id: paraId of the target paragraph. run_idx: Zero-based index of the run. color: Word highlight color name (e.g., "yellow", "green", "cyan", "none").

Inputs: color (required), para_id (required), run_idx (required). Returns: result

set_run_languageA

Set the language on a run for spell-checking purposes.

Args: para_id: paraId of the target paragraph. run_idx: Zero-based index of the run. language_code: BCP-47 language code (e.g., "en-US", "fr-FR", "de-DE").

Inputs: para_id (required), run_idx (required), language_code (required). Returns: result

set_run_sizeB

Set the font size of a specific run in a paragraph.

Args: para_id: paraId of the target paragraph. run_idx: Zero-based index of the run. size_pt: Font size in points (e.g., 12.0).

Inputs: para_id (required), run_idx (required), size_pt (required). Returns: result

set_run_strikethroughA

Set strikethrough on a specific run in a paragraph.

Args: para_id: paraId of the target paragraph. run_idx: Zero-based index of the run. double: False for single strikethrough, True for double strikethrough.

Inputs: para_id (required), run_idx (required), double. Returns: result

set_run_subscriptA

Set subscript vertical alignment on a specific run in a paragraph.

Args: para_id: paraId of the target paragraph. run_idx: Zero-based index of the run.

Inputs: para_id (required), run_idx (required). Returns: result

set_run_superscriptC

Set superscript vertical alignment on a specific run in a paragraph.

Args: para_id: paraId of the target paragraph. run_idx: Zero-based index of the run.

Inputs: para_id (required), run_idx (required). Returns: result

set_run_underlineB

Set underline style on a specific run in a paragraph.

Args: para_id: paraId of the target paragraph. run_idx: Zero-based index of the run. style: Underline style (e.g., "single", "double", "dotted", "none").

Inputs: para_id (required), run_idx (required), style. Returns: result

set_section_columnsA

Set the number of columns in a section.

Args: section_index: Zero-based section index (use get_sections to find it). num_columns: Number of text columns (1 = single column). equal_width: If True, all columns are equal width. Default True.

Inputs: num_columns (required), section_index (required), equal_width. Returns: result

set_section_propertiesC

Modify section properties (page size, orientation, margins). 0/empty = unchanged.

Inputs: width, height, para_id, margin_top, +5 more. Returns: result

set_table_alignmentC

Set table alignment: left, center, or right.

Inputs: alignment (required), table_idx (required). Returns: result

set_table_bordersC

Set borders on all six sides of a table (top, bottom, left, right, insideH, insideV).

Inputs: table_idx (required), size, color, border_style. Returns: result

set_table_styleA

Apply a named table style (e.g. TableGrid, LightShading-Accent1) to a table.

Inputs: table_idx (required), style_name (required). Returns: result

set_text_caseA

Set text case transformation on a run.

Args: para_id: paraId of the target paragraph. run_idx: Zero-based index of the run. case: "upper" (all caps), "small" (small caps), or "none" (remove case transform).

Inputs: case (required), para_id (required), run_idx (required). Returns: result

set_theme_colorA

Update a named color slot in the document theme.

Args: slot: One of dk1, lt1, dk2, lt2, accent1-accent6, hlink, folHlink. hex_color: 6-character hex string without # (e.g. "FF0000").

Inputs: slot (required), hex_color (required). Returns: result

set_track_changesA

Enable or disable revision tracking in the document.

When enabled, Word/LibreOffice will record future edits as tracked changes.

Args: enabled: True to enable tracking, False to disable. author: Optional author name (stored in response for reference only).

Returns: {"track_changes": bool, "author": str}

Inputs: enabled (required), author. Returns: result

set_widow_controlB

Enable widow/orphan control for this paragraph.

Args: para_id: paraId of the target paragraph. enabled: True to enable widow control, False to remove it.

Inputs: enabled (required), para_id (required). Returns: result

sort_tableA

Sort the non-header rows of a table by the text content of a column.

Header rows (marked with w:tblHeader) remain at the top. Rows with a missing cell at column_index sort as empty string.

Args: table_idx: 0-based table index. column_index: 0-based column to sort by. ascending: True for A→Z (default), False for Z→A.

Returns: {"sorted_rows": int, "column_index": int, "ascending": bool}

Inputs: table_idx (required), column_index (required), ascending. Returns: result

split_documentA

Split the open document into multiple DOCX files, one per heading section.

Args: output_dir: Directory for output files. Defaults to /split_output. at_heading_level: Heading level to split on (default 1).

Returns: {"output_dir": str, "files": list[str], "parts": int}

Inputs: output_dir, at_heading_level. Returns: result

split_tableA

Split a table into two tables at the given row index.

Rows 0..at_row_index-1 remain in the original table; rows at_row_index..end move to a new table inserted immediately after.

Args: table_idx: 0-based table index. at_row_index: 0-based row index to split at (must be > 0 and < row_count).

Returns: {"table1_rows": int, "table2_rows": int}

Inputs: table_idx (required), at_row_index (required). Returns: result

suppress_numberingB

Remove list numbering from a paragraph by setting numId to 0.

Inputs: para_id (required). Returns: result

table_to_csvB

Export a table as CSV string.

Inputs: table_index (required). Returns: result

update_chart_dataA

Replace data series in an existing chart by chart_id.

series: [{"name": str, "values": [float, ...]}]

Inputs: series (required), chart_id (required). Returns: result

update_commentC

Replace the text of an existing comment.

Args: comment_id: ID of the comment to update. text: New comment text.

Inputs: text (required), comment_id (required). Returns: result

update_content_controlA

Modify properties of an existing content control.

Args: control_id: The w:id value of the content control to update. title: New title (w:alias/@w:val). Omit to leave unchanged. tag: New tag (w:tag/@w:val). Omit to leave unchanged. placeholder_text: New placeholder text. Omit to leave unchanged.

Inputs: control_id (required), tag, title, placeholder_text. Returns: result

update_endnoteA

Update the text of an existing endnote.

Args: endnote_id: The numeric ID of the endnote to update (must be >= 1). text: The new text content for the endnote.

Inputs: text (required), endnote_id (required). Returns: result

update_fieldsA

Mark all fields as dirty so Word recalculates on open.

Inputs: no named parameters. Returns: result

update_footnoteA

Update the text of an existing footnote.

Args: footnote_id: The numeric ID of the footnote to update (must be >= 1). text: The new text content for the footnote.

Inputs: text (required), footnote_id (required). Returns: result

update_imageA

Replace the binary for an existing image in-place.

The image dimensions and position in the document are preserved.

Args: rId: The relationship ID of the image to replace. new_image_path: Absolute path to the new image file on disk.

Inputs: rId (required), new_image_path (required). Returns: result

update_paragraphA

Update the text and/or style of an existing paragraph.

Args: para_id: paraId of the paragraph to update. text: New text content. Empty string leaves text unchanged. style: New paragraph style name. Empty string leaves style unchanged.

Inputs: para_id (required), text, style. Returns: result

update_styleB

Update an existing style's basedOn and/or next properties.

Args: name: Style name or styleId (case-insensitive). based_on: New basedOn styleId (replaces existing). next_style: New next styleId (replaces existing).

Inputs: name (required), based_on, next_style. Returns: result

update_tocA

Regenerate ToC entries from current headings.

Inputs: no named parameters. Returns: result

validate_endnotesC

Cross-reference endnote IDs between document.xml and endnotes.xml.

Inputs: document_handle. Returns: result

validate_footnotesC

Cross-reference footnote IDs between document.xml and footnotes.xml.

Inputs: document_handle. Returns: result

validate_paraidsC

Check paraId uniqueness across all document parts.

Inputs: document_handle. Returns: result

validate_template_dataA

Validate data dict covers all template fields. Returns missing and extra keys.

Inputs: data (required). Returns: result

write_partB

Replace a DOCX part with new XML. Validates well-formedness first. WARNING: Direct XML manipulation can corrupt the document if used incorrectly.

Inputs: xml (required), part_path (required). Returns: result

xpath_queryA

Run XPath against any DOCX part. Pre-bound namespaces: w, w14, r, wp, a, mc.

Examples: xpath="//w:p" — all paragraphs xpath="//w:t/text()" — all text content xpath="//w:p[w:pPr/w:pStyle/@w:val='Heading1']" — Heading 1 paragraphs

Inputs: xpath (required), part. Returns: result

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    C2.9/5.0

    Scored across 227 tools

    Disambiguation1/5

    Numerous near-duplicate tools make selection treacherous: accept_change/accept_changes/accept_all_changes, generate_list_of_figures/generate_tof, generate_list_of_tables/generate_tot, get_headings/get_document_outline, compare_documents/diff_to_text, and set_image_alt_text/set_alt_text all perform essentially the same operations with minor parameter differences. The specialized insert_date_field/insert_page_number_field/insert_merge_field tools also duplicate what add_field already covers. An agent cannot reliably distinguish these without reading full argument lists.

    Naming Consistency4/5

    The overwhelming majority of tools follow a consistent verb_noun snake_case pattern (get_, set_, add_, delete_, update_, insert_, create_), which is a strong convention across 227 tools. Minor outliers like xpath_query, bates_number, csv_to_table, and table_to_csv deviate slightly but are still readable and do not undermine the overall pattern.

    Tool Count1/5

    227 tools is an extreme count, far beyond even the 50+ threshold the calibration identifies as a mismatch. Even for a broad domain like DOCX manipulation, this flat namespace is unmanageable and should be split into focused servers (e.g., tracked-changes, tables, formatting, templates) or namespaced submodules. The bloat directly causes the disambiguation failures.

    Completeness5/5

    The domain surface is exhaustively covered: full lifecycle tools for text, paragraphs, runs, tables, images, headers/footers, footnotes/endnotes, comments, tracked changes, styles, fields, bookmarks, hyperlinks, content controls, sections, properties, charts, and equations, plus redaction, sanitization, accessibility checking, PDF conversion, and document comparison. There are no obvious dead ends or missing CRUD operations—if anything, the surface over-covers due to redundancy.

    Maintenance

    ActivityMaintained
    ResponsivenessOutdated data

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      B
      quality
      Not graded
      maintenance
      Enables AI assistants to create, read, and manipulate Microsoft Word documents with comprehensive formatting, table creation, content management, and document protection capabilities. Supports advanced operations like merging documents, PDF conversion, and rich text formatting through a standardized interface.
      32
      MIT
    • A
      license
      Not graded
      quality
      D
      maintenance
      Enables AI agents to read, edit, and create Microsoft Word documents (.docx) with support for rich text, tables, and images, deployable locally or via SSE.
      3
      MIT
    • A
      license
      Not graded
      quality
      F
      maintenance
      Enables AI assistants to create, edit, and extract data from Microsoft Word documents programmatically, supporting document creation, content editing, table manipulation, parameter extraction, and template generation.
      1
      MIT