docx-mcp-server
# docx-mcp-server
[](https://github.com/knorq-ai/docx-mcp-server/actions/workflows/ci.yml)
A local [MCP](https://modelcontextprotocol.io/) server for reading and editing Word (.docx) documents. Works with Claude Code, Cursor, and any MCP-compatible client.
**40 tools** for document content, formatting, comments, page layout, and track changes — all running locally via stdio with no file uploads.
## Features
| Category | Tools |
|---|---|
| **Read** | `read_document`, `get_document_info`, `search_text`, `list_images`, `get_paragraph_format`, `ensure_anchors` |
| **Edit** | `replace_texts`, `edit_paragraphs`, `insert_paragraphs`, `delete_paragraphs` |
| **Format** | `format_text`, `set_paragraph_formats`, `highlight_text`, `set_headings` |
| **Structure** | `insert_table`, `create_document`, `apply_document_preset` |
| **Review** | `add_comment`, `add_comments`, `read_comments`, `reply_to_comment`, `delete_comment` |
| **Track changes** | `accept_all_changes`, `reject_all_changes` |
| **Page layout** | `get_page_layout`, `set_page_layout` |
| **Headers/footers** | `read_header_footer` |
| **Tables** | `read_table_structure`, `read_table_cell`, `edit_table_cells`, `edit_table_paragraphs`, `delete_table_paragraphs`, `insert_table_paragraphs` |
| **Footnotes** | `read_footnotes` |
### Track changes
The editing tools (`replace_texts`, `edit_paragraphs`, `insert_paragraphs`, `delete_paragraphs`) support **tracked changes** — edits are recorded as Word revisions (`w:ins`/`w:del`) with author and timestamp, so reviewers can accept or reject them in Word.
Track changes is **on by default**. Pass `track_changes: false` to make direct edits.
Use `read_document` with `show_revisions: true` to see tracked changes annotated as `[-deleted-]` and `[+inserted+]`. The default view shows accepted text only.
Use `accept_all_changes` / `reject_all_changes` to finalize or revert all pending revisions.
### Stable anchors
Paragraphs are normally addressed by integer block index, but every insert/delete shifts the indices of later blocks — so a multi-step edit has to re-read after each change. **Anchors** fix this: an anchor is a stable id (Word's `w14:paraId`) that stays attached to its paragraph across index shifts.
- Run **`ensure_anchors`** once to assign anchors to every paragraph and get the full index→anchor map (most Word-authored documents already carry anchors; the call is idempotent).
- `search_text` returns each match's `anchor`, and `read_document` with `show_anchors: true` prints them inline.
- The edit tools (`edit_paragraphs`, `delete_paragraphs`, `set_paragraph_formats`, `set_headings`, `insert_paragraphs`) accept an `anchor` (or `anchors`) as an alternative to `paragraph_index`. Editing also auto-assigns an anchor to each touched/inserted paragraph and `insert_paragraphs` returns the new anchors, so a pipeline can keep editing without re-reading.
v1 anchors cover top-level (direct-body) paragraphs; paragraphs inside tables or content controls are not anchored.
### Page layout
`get_page_layout` / `set_page_layout` support:
- **Page size presets**: A3, A4, A5, B4, B5, Letter, Legal
- **Margin presets**: Normal, Narrow, Wide, JP Court 25mm, JP Court 30/20mm
- **Custom values** in millimeters for page size and individual margins
- **Orientation** (portrait / landscape)
## Quick start
### Option 1: Install from npm
```bash
npm install -g @knorq/docx-mcp-server
```
Then add to your MCP config (see [Configuration](#configuration) below).
### Option 2: Use npx (no install)
Just add the config — `npx` downloads and runs it automatically:
```json
{
"mcpServers": {
"docx-editor": {
"command": "npx",
"args": ["-y", "@knorq/docx-mcp-server"]
}
}
}
```
### Option 3: Build from source
```bash
git clone https://github.com/knorq-ai/docx-mcp-server.git
cd docx-mcp-server
npm install
npm run build
npm link # makes `docx-mcp-server` available globally
```
## Configuration
### Claude Code
Add to your project's `.mcp.json` (per-project) or `~/.claude/settings.json` (global):
```json
{
"mcpServers": {
"docx-editor": {
"command": "npx",
"args": ["-y", "@knorq/docx-mcp-server"]
}
}
}
```
### Cursor
Add to your MCP server configuration in Cursor settings:
```json
{
"mcpServers": {
"docx-editor": {
"command": "npx",
"args": ["-y", "@knorq/docx-mcp-server"]
}
}
}
```
### Using a local build (without npm)
If you built from source and ran `npm link`:
```json
{
"mcpServers": {
"docx-editor": {
"command": "docx-mcp-server"
}
}
}
```
Or reference the built file directly:
```json
{
"mcpServers": {
"docx-editor": {
"command": "node",
"args": ["/absolute/path/to/docx-mcp-server/dist/index.js"]
}
}
}
```
## Distributing to others
### Via npm (recommended)
```bash
npm publish
```
Recipients install with:
```bash
npm install -g @knorq/docx-mcp-server
```
Or skip the install entirely — just share the `.mcp.json` config with the `npx` setup above and it works out of the box.
### Via zip / git
Share the repository. Recipients run:
```bash
git clone https://github.com/knorq-ai/docx-mcp-server.git
cd docx-mcp-server
npm install
npm run build
npm link
```
Then add the config above.
## Tool reference
### Reading
**`read_document`** — Read content with block indices, styles, and formatting hints. Use `show_revisions` to see tracked changes.
```
file_path, start_paragraph?, end_paragraph?, show_revisions?
```
**`get_document_info`** — Paragraph count, heading outline, table count, comment status.
```
file_path
```
**`search_text`** — Search with context snippets.
```
file_path, query, case_sensitive?
```
**`list_images`** — List all embedded images with filenames, dimensions, alt text, and block indices.
```
file_path
```
**`get_paragraph_format`** — Introspect a paragraph's formatting (style, heading level, alignment, numbering, indentation in twips, spacing in points). Use it to find a `copy_format_from` source or debug why two paragraphs render differently. Values match the units `set_paragraph_formats` accepts.
```
file_path, paragraph_index
```
**`ensure_anchors`** — Assign a stable anchor (`w14:paraId`) to every top-level paragraph that lacks one and return the index→anchor map. Idempotent. See [Stable anchors](#stable-anchors).
```
file_path
```
### Editing
All editing tools accept `track_changes` (default `true`) and `author` (default `"Claude"`). The paragraph tools below accept an `anchor` (or `anchors`) in place of `paragraph_index` for index-shift-proof targeting — see [Stable anchors](#stable-anchors).
**`replace_texts`** — Apply one or more find/replace operations in a single open/save cycle. Handles text spanning multiple runs.
- Under `track_changes: false`, items are applied sequentially: a later item can match text produced by an earlier item.
- Under `track_changes: true` (default), the engine rejects overlapping items where item N's `search` shares text with any earlier item M's `replace` (in either direction). Tracked sequential replacement cannot safely chain overlapping items — nested `w:ins`/`w:del` does not round-trip through `reject_all_changes`. Workaround: issue separate `replace_texts` calls (one per item) or use `track_changes: false` with `allow_untracked_edit: true`.
```
file_path, items (array of {search, replace, case_sensitive?}), track_changes?, author?, include_headers_footers?
```
**`edit_paragraphs`** — Replace the text content of one or more paragraphs in a single open/save cycle. Target each by `paragraph_index` or `anchor`. A `\n` in `new_text` is a paragraph break: untracked edits split it into separate paragraphs (each inheriting the original numbering/indentation), tracked edits keep one paragraph and render `\n` as a soft line break.
```
file_path, edits (array of {paragraph_index? | anchor?, new_text}), track_changes?, author?
```
**`insert_paragraphs`** — Insert one or more paragraphs in one operation. Place each by `position` (block index) or `anchor` + `placement` ("before"/"after"); returns the new paragraphs' anchors. A `\n` in `text` is a paragraph break (untracked: one paragraph per line; tracked: soft line break). When several paragraphs share the same `position`, they land in the document in the reverse of array order — list them back-to-front or use separate calls (anchor placement preserves array order).
```
file_path, paragraphs (array of {text, position? | (anchor + placement), style?, num_id?, num_level?, copy_format_from?, copy_format_from_anchor?}), track_changes?, author?
```
**`delete_paragraphs`** — Delete one or more paragraphs or table blocks in one operation. Target by `paragraph_indices` (paragraph or table) and/or `anchors` (paragraph only).
```
file_path, paragraph_indices?, anchors?, track_changes?, author?
```
### Formatting
**`format_text`** — Apply bold, italic, underline, font, size, color, highlight to matching text.
```
file_path, search, bold?, italic?, underline?, strikethrough?, highlight_color?, font_name?, font_size?, font_color?, case_sensitive?
```
**`set_paragraph_formats`** — Apply alignment, spacing, indentation to one or more paragraphs in one operation. Each group targets paragraphs by `indices` and/or `anchors` and bundles the formatting to apply to them.
```
file_path, groups (array of {indices?, anchors?, alignment?, space_before?, space_after?, line_spacing?, indent_left?, indent_right?, first_line_indent?, hanging_indent?})
```
**`highlight_text`** — Highlight matching text with a color.
```
file_path, search, color?, case_sensitive?
```
**`set_headings`** — Convert one or more paragraphs to headings (level 1-9) in one operation. Target each by `paragraph_index` or `anchor`.
```
file_path, headings (array of {paragraph_index? | anchor?, level})
```
### Structure
**`insert_table`** — Insert a table with optional cell data.
```
file_path, position, rows, cols, data?
```
**`create_document`** — Create a new .docx file with optional title, content, and style preset.
```
file_path, title?, content?, preset?
```
By default `create_document` keeps the generated document generic. If you want a Japanese business-document starting point, pass `preset: "ja-business"` to seed `styles.xml` with Yu Gothic body text, 11pt sizing, roomier paragraph spacing, and less cramped heading spacing.
**`apply_document_preset`** — Apply a document-wide style preset in one pass by updating `styles.xml`.
```
file_path, preset
```
Use this when you want to restyle an existing document without repeated `format_text` calls per paragraph. The preset rewrites `docDefaults` and the `Heading 1`–`Heading 3` styles; an existing `Normal` style and other custom styles are preserved.
### Review
**`add_comment`** — Anchor a comment to specific text.
```
file_path, anchor_text, comment_text, author?
```
**`add_comments`** — Add multiple comments in one operation. Supports partial success.
```
file_path, comments (array of {anchor_text, comment_text, author?}), default_author?
```
**`read_comments`** — List all comments with IDs, authors, text, and threaded replies.
```
file_path
```
**`reply_to_comment`** — Reply to an existing comment, creating a threaded conversation.
```
file_path, parent_comment_id, comment_text, author?
```
**`delete_comment`** — Remove a comment by ID.
```
file_path, comment_id
```
### Track changes
**`accept_all_changes`** — Accept all tracked changes. Insertions become permanent, deletions are removed.
```
file_path
```
**`reject_all_changes`** — Reject all tracked changes. Insertions are removed, deleted text is restored.
```
file_path
```
### Page layout
**`get_page_layout`** — Read page size, margins, orientation.
```
file_path
```
**`set_page_layout`** — Set page size, margins, orientation by preset or custom mm values.
```
file_path, page_size_preset?, orientation?, width_mm?, height_mm?, margin_preset?, top_mm?, right_mm?, bottom_mm?, left_mm?, header_mm?, footer_mm?, gutter_mm?
```
### Headers and footers
**`read_header_footer`** — Read the text content of all headers and footers.
```
file_path
```
### Tables
**`read_table_structure`** — Inspect a table without reading the whole document: row/column dimensions and a short preview of every cell, plus each cell's merge info (`gridSpan` / `vMerge`). Indices are physical `w:tc` positions, matching `read_table_cell` / `edit_table_cells`.
```
file_path, block_index
```
**`read_table_cell`** — Read a single cell's paragraphs (text + style/alignment/numbering) and merge info, without reading the whole document.
```
file_path, block_index, row_index, col_index
```
**`edit_table_cells`** — Replace the text content of one or more table cells in a single open/save cycle. Cells can span different tables. A `\n` in `new_text` is a paragraph break: untracked edits replace the **whole cell**, turning each line into its own paragraph (so re-editing leaves no stale lines); tracked edits diff-replace the cell's first paragraph and render `\n` as a soft line break.
```
file_path, edits (array of {block_index, row_index, col_index, new_text}), track_changes?, author?
```
**`edit_table_paragraphs`** — Edit one specific paragraph inside a cell (cell-local `paragraph_index`) without replacing the whole cell. For surgically changing a single line of a multi-paragraph cell (e.g. one numbered-list item).
```
file_path, edits (array of {block_index, row_index, col_index, paragraph_index, new_text}), track_changes?, author?
```
**`delete_table_paragraphs`** — Delete one specific paragraph inside a cell. Keeps a blank paragraph if the deleted one was the cell's last (so the cell stays valid); real Word numbering renumbers the rest automatically.
```
file_path, targets (array of {block_index, row_index, col_index, paragraph_index}), track_changes?, author?
```
**`insert_table_paragraphs`** — Insert a paragraph inside a cell at a cell-local position (`-1`/out-of-range appends). Supports `num_id`/`num_level` and `copy_format_from` (a paragraph index within the same cell).
```
file_path, inserts (array of {block_index, row_index, col_index, position, text, style?, num_id?, num_level?, copy_format_from?}), track_changes?, author?
```
### Footnotes
**`read_footnotes`** — Read all footnotes with their IDs and text content.
```
file_path
```
## Why MCP tools instead of raw Python?
AI agents can manipulate DOCX via raw Python (python-docx), but MCP tools are significantly more token-efficient:
| Metric | MCP tools | Raw Python |
|--------|-----------|------------|
| Output tokens per operation | **65–95% less** | Baseline (agent must generate full code) |
| Cost per operation | **55–90% less** | Baseline |
| Break-even | **3–5 operations** | — |
| Debug iterations | None (validated inputs) | ~1.5 retries/task on average |
### Scenario comparison (output tokens, measured from actual code)
| Task | MCP | Python (python-docx) | Savings |
|------|-----|---------------------|---------|
| Read paragraphs 0–20 | ~18 | ~52 (open, iterate, print) | **65%** |
| Search and replace text | ~16 | ~67 (iterate paragraphs, run traversal) | **76%** |
| Add tracked change (insert) | ~32 | ~326 (build w:ins/w:del XML, datetime, author, run properties) | **90%** |
| Add comment anchored to text | ~26 | ~575 (comment part, anchor markers, relationship, XML manipulation) | **95%** |
| Format text (bold + color) | ~20 | ~478 (find runs, split at boundaries, apply rPr) | **96%** |
| Set paragraph format (3 paragraphs) | ~30 | ~80 (load, resolve indices, set properties, save) | **63%** |
| Composite: read → search → edit → comment | ~120 | ~1,000 | **88%** |
The savings are especially large for **tracked changes**, **comments**, and **run-level formatting** — python-docx has no built-in API for track changes or comments, and text formatting requires complex run-splitting logic. The agent must generate raw OOXML manipulation code (~300–575 output tokens per operation). MCP tools handle this internally with a simple parameter call.
Simple read and paragraph-format operations see smaller savings (~63–76%) since python-docx has clean APIs for these.
Output tokens cost 5× more than input tokens, so eliminating code generation has an outsized cost impact. The one-time schema overhead (~2,500 tokens for 40 tools) pays for itself in 3–5 operations.
## Requirements
- Node.js 20+
## License
MIT
TDQS
Scored across 27 tools
Each tool targets a distinct operation on the document—creating, reading, editing paragraphs/tables, formatting, comments, changes, footnotes, headers/footers, and images. There is no functional overlap; even similarly named tools like format_text and highlight_text apply different types of formatting.
All tools follow a consistent verb_noun pattern in snake_case (e.g., create_document, edit_paragraphs, set_page_layout). Minor plural/singular variation (add_comment vs. add_comments) is clear and intentional, not a deviation.
With 27 tools, the server is on the high end for an MCP server. While each tool serves a distinct purpose for a comprehensive document editor, the count exceeds the typical 3-15 range and falls into the 'heavy' category, warranting a borderline score.
The server covers major areas: document creation, reading, editing paragraphs/tables, formatting, comments (CRUD plus threading), changes (accept/reject), search/replace, images, footnotes, and headers/footers. Minor gaps exist: footnotes and headers/footers are read-only, and there is no image insertion or deletion.