grp-docx-mcp
This MCP server lets an AI agent create, read, and edit Word (.docx) documents directly at the OOXML level, with tracked changes, comments, and structural validation.
Document lifecycle: open, create blank/from-template/from-markdown, save, close, and inspect document info
Reading & search: headings, paragraphs, full body text, text/regex search across body, footnotes, and comments
Tracked changes: insert, delete, replace text, edit header/footer, and modify cells as revisions; accept/reject individually or all, filtered by author
Comments: add, reply, update, delete, and resolve comments
Tables: create, modify cells, add/delete rows and columns, merge cells, set widths/heights, alignment, borders, shading, styles, header rows, and CSV import/export
Styles: list, create, update, delete, copy, and apply styles to paragraph ranges
Footnotes & endnotes: add, list, update, delete, validate, and add repeated references
Images: list, insert inline or floating (cm/EMU sizing, alignment, wrap), replace, resize, set alt text, borders, and delete
Sections & layout: page/section breaks, page size, orientation, margins, columns, and different first-page/odd-even headers
Headers/footers: read, edit (tracked or direct), and delete
Cross-references: internal links between paragraphs with bookmarks
Properties: core and custom document properties
Protection & merge: set document protection (tracked-changes/comments/read-only/forms) and merge DOCX files
Watermarks: insert and remove VML watermarks
Validation & audit: comprehensive structural audit, paraId uniqueness, and footnote/endnote cross-reference checks
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@grp-docx-mcp Open contract.docx and change all 'Net 30' to 'Net 60' with tracked changes"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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.
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-customThat'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-customWith pip:
pip install git+https://github.com/Arvindh95Censof/docx-mcp-customCapabilities
Capability | What your agent can do |
Create documents | Start blank, from a |
Track changes | Insert, delete, and replace text as tracked revisions — underlined insertions, red strikethrough deletions. Pass |
Change log | After editing with tracked changes, call |
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 inlineproposal_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 deliveryYour 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 sharingAlways 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 a .docx file for reading and editing |
| Create a new blank .docx (or from a .dotx template) |
| Create a .docx from GitHub-Flavored Markdown |
| Close the current document and clean up |
| Get overview stats (paragraphs, headings, footnotes, comments) |
| Save changes back to .docx (can overwrite or save to new path) |
Reading
Tool | Description |
| Get heading structure with levels and text |
| Search across body, footnotes, and comments (text or regex) |
| Get full text and style of a paragraph |
Track Changes
Tool | Description |
| Insert text as tracked revision (underlined in Word) — or directly with |
| Mark text deleted (red strikethrough in Word) — or remove directly with |
| Replace text as a tracked del+ins pair — or directly with |
| Modify a table cell — tracked by default (only the changed words are marked), |
| Apply several edits in one call — all of them take effect, or none do (a failure restores the document) |
| Edit header/footer text — tracked by default, |
| Apply bold/italic/underline/color with tracked-change markup |
| Enable or disable Word's built-in track-changes recording flag |
| List all pending tracked changes (author, date, type, text) |
| Accept tracked changes, optionally filtered by author |
| Reject tracked changes, optionally filtered by author |
Change Log
Tool | Description |
| Export all tracked changes in the open document as a numbered .txt file — insertions, deletions, and adjacent del+ins pairs grouped as replacements |
| Diff two DOCX files and produce a tracked-change DOCX (Word-openable) |
| Diff two DOCX files and produce both a tracked-change DOCX and a plain-text summary .txt |
Tables
Tool | Description |
| Get all tables with row/column counts and cell content |
| Insert a new table after a paragraph |
| Modify a table cell with tracked changes (only the changed words are marked) |
| Add a row to a table |
| Delete a table row with tracked changes |
Lists
Tool | Description |
| Apply bullet or numbered list formatting to paragraphs |
Comments
Tool | Description |
| List all comments with ID, author, date, and text |
| Add a comment anchored to a paragraph, or to a phrase inside it ( |
| Reply to an existing comment (threaded) |
Footnotes & Endnotes
Tool | Description |
| List all footnotes with ID and text |
| Add a footnote with superscript reference |
| Validate footnote cross-references |
| List all endnotes with ID and text |
| Add an endnote with superscript reference |
| Validate endnote cross-references |
Headers, Footers & Styles
Tool | Description |
| Get all headers and footers with text content |
| Edit header/footer text with tracked changes |
| Get all defined styles |
Properties & Images
Tool | Description |
| Get core document properties (title, creator, dates) |
| Set core document properties |
| Get all embedded images with dimensions |
| Insert an image after a paragraph |
Sections & Cross-References
Tool | Description |
| Insert a page break after a paragraph |
| Add a section break (nextPage, continuous, evenPage, oddPage) |
| Set page size, orientation, and margins |
| Add a cross-reference link between paragraphs |
Protection & Merge
Tool | Description |
| Set document protection with optional password |
| Merge content from another DOCX |
Validation & Audit
Tool | Description |
| Check internal ID uniqueness across all document parts |
| Remove VML watermarks from document headers |
| 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 toolsaccept_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_hyperlinkA
Append an external hyperlink at the end of a paragraph.
Creates a relationship entry in word/_rels/document.xml.rels and a w:hyperlink element with a Hyperlink-styled run in word/document.xml.
Args: para_id: w14:paraId of the target paragraph. text: Display text for the hyperlink. url: The URL the hyperlink points to.
Inputs: url (required), text (required), para_id (required). Returns: result
add_internal_linkA
Append an internal anchor hyperlink (w:anchor) at the end of a paragraph.
Internal links reference bookmarks by name and do NOT add a relationship.
Args: para_id: w14:paraId of the target paragraph. text: Display text for the hyperlink. bookmark: Name of the bookmark to link to.
Inputs: text (required), para_id (required), bookmark (required). 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:
A DOCX with tracked-change markup (deletions in red, insertions underlined).
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_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_hyperlinksA
List all hyperlinks in the document.
Returns a list of dicts with keys: id, url_or_anchor, text, para_id, type. type is "external" (r:id based) or "internal" (w:anchor based).
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_hyperlinkA
Remove a hyperlink wrapper, preserving the text runs inside.
The paragraph text remains; only the w:hyperlink element is unwrapped.
Args: para_id: w14:paraId of the paragraph containing the hyperlink. url_or_anchor: URL (for external) or bookmark name (for internal) to match.
Inputs: para_id (required), url_or_anchor (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_hyperlinkA
Update the target URL of an existing external hyperlink relationship.
Args: r_id: The relationship ID (e.g. "rId7") to update. new_url: The new target URL.
Inputs: r_id (required), new_url (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
Scored across 227 tools
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.
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.
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.
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
Related MCP Connectors
Convert, create and edit Word, PowerPoint, Excel and PDF documents from any AI agent.
Create Word .docx files from sections or markdown, fill templates, and read text from a .docx.
Real Word and Excel for agents: open your .docx/.xlsm, read, propose, apply, get it back intact.
Related MCP Servers
- AlicenseBqualityNot gradedmaintenanceEnables 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.32MIT
- AlicenseBqualityDmaintenanceEnables AI assistants to create and manipulate Microsoft Word documents programmatically with support for rich text formatting, tables, lists, headings, and find-and-replace operations.1018 npmMIT
- AlicenseNot gradedqualityDmaintenanceEnables AI agents to read, edit, and create Microsoft Word documents (.docx) with support for rich text, tables, and images, deployable locally or via SSE.3MIT
- AlicenseNot gradedqualityFmaintenanceEnables 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.1MIT