How to Handle DOCX Files (Outline vs Raw XML) in Desktop Commander MCP

Desktop Commander MCP treats Word documents as virtual text files, exposing either a concise structural outline or paginated raw XML to balance readability with precision editing capabilities.

The repository wonderwhy-er/DesktopCommanderMCP implements a specialized handler in src/utils/files/docx.ts that unzips Office Open XML packages and translates binary DOCX files into editable text representations. This dual-mode approach allows AI agents to quickly grasp document structure or perform surgical XML edits without overwhelming context windows.

Two Read Modes for DOCX Files

The handler exposes every .docx file through two distinct interfaces, selectable via read parameters.

Outline Mode (Default)

When you read a DOCX without specifying pagination parameters, the handler returns a structural outline generated by extractOutline(). This mode parses the <w:body> element and lists every top-level child—paragraphs (<w:p>), tables (<w:tbl>), images, and structured document tags—with their associated text content and style attributes.

The outline includes a summary header showing counts of paragraphs, tables, and images, followed by indexed entries. Each line displays the raw XML tag (e.g., w:p style="Heading1") so you can target specific elements for later editing. This mode is optimal for navigation and understanding document structure without processing thousands of XML lines.

Raw XML Mode (Offset/Length)

For granularity, specify offset and length parameters to receive pretty-printed XML from document.xml. The prettyPrintXml() function splits tags onto individual lines with indentation, making the file searchable and editable. The response includes a status header indicating the line range and remaining content.

Use raw mode when the outline lacks sufficient detail—such as when editing complex drawing attributes, precise style definitions, or nested formatting that requires exact XML fragments.

Core Implementation Architecture

The DOCX workflow relies on several specialized utilities within src/utils/files/docx.ts.

Loading the ZIP Package

The loadDocxZip() function uses pizzip to read every .xml and .rels entry in the DOCX archive (which is a standard ZIP file). It returns the main documentXml string plus a map of all XML parts, including headers and footers. This occurs at lines 74-94 of the source file.

XML Processing Pipeline

Two complementary functions handle XML formatting:

  • prettyPrintXml() (lines 31-51): Splits XML tags and adds indentation, ensuring each tag occupies one line for precise offset-based pagination.
  • compactXml() (lines 55-61): Removes whitespace and indentation after edits, preparing the XML for repackaging into the ZIP container.

Outline Extraction Logic

The extractOutline() function (lines 143-225) walks the children of <w:body> using splitTopLevelElements() to identify paragraphs, tables, and drawings. It also calls extractHeaderFooterOutline() (lines 13-33) to preview content from header and footer parts. The resulting summary provides counts and contextual hints, suggesting when to request raw XML for bulk operations.

Editing DOCX Files Programmatically

Desktop Commander MCP enables surgical edits through the editRange() method, which operates on the same pretty-printed XML returned by raw read mode.

The editing workflow follows these steps:

  1. Load: The handler loads the pretty-printed XML representation of the document and any header/footer parts.
  2. Search: It searches for the exact old_string within the XML.
  3. Validate: It verifies the replacement matches the expected_replacements count to prevent unintended modifications.
  4. Substitute: Performs the string replacement.
  5. Compact: Runs compactXml() to strip indentation.
  6. Repack: Writes the compacted XML back into the ZIP archive.
await fileHandler.editRange(
  'report.docx',
  '',
  {
    old_string: '<w:t>Quarterly Results</w:t>',
    new_string: '<w:t>Annual Results</w:t>',
    expected_replacements: 1,
  }
);

This approach guarantees that fragments copied from a raw XML read are valid search strings for subsequent edits.

Creating New Documents

The handler supports DOCX generation from markdown-style text via the write() method. Lines beginning with # convert to Word heading styles (Heading1, Heading2, etc.), while plain lines become standard paragraphs. Blank lines generate empty <w:p/> elements.

const content = `

# Executive Summary

Q4 exceeded targets by 15%.

## Details

Revenue grew across all sectors.
`;
await fileHandler.write('new_report.docx', content);

Summary

  • Outline mode (default) provides indexed summaries of paragraphs, tables, and images with XML tag hints, ideal for navigation and context gathering.
  • Raw XML mode delivers pretty-printed, paginated document.xml via offset and length parameters, enabling precise editing of complex structures.
  • The editing pipeline uses editRange() to substitute strings in pretty-printed XML, then compacts and repackages the ZIP via compactXml().
  • Implementation resides in src/utils/files/docx.ts, utilizing loadDocxZip(), extractOutline(), and prettyPrintXml() for transformation logic.
  • File creation accepts markdown-style input, automatically mapping heading levels to Word styles.

Frequently Asked Questions

How do I switch from outline view to raw XML in Desktop Commander MCP?

Pass offset and length parameters when calling the read method. Setting offset: 0 and specifying a line count returns the pretty-printed XML instead of the structural outline. The response includes a header indicating which lines you are viewing and how many remain.

Can I edit headers and footers using the raw XML mode?

Yes. The loadDocxZip() function extracts all XML parts including headers and footers. When using editRange(), the handler can target these parts if your search string includes content from those sections, though the outline view provides limited preview information for header/footer text via extractHeaderFooterOutline().

Why does the editing process require pretty-printed XML?

Pretty-printing ensures each XML tag occupies exactly one line, making string-based search and replacement reliable. After editing, compactXml() removes the added whitespace to restore valid Office Open XML before repackaging the DOCX ZIP container. This guarantees that copied fragments from raw reads match the source exactly.

What happens if my search string appears multiple times in the document?

The expected_replacements parameter in editRange() acts as a safety check. If the count of occurrences does not match your specified expectation, the operation aborts without modifying the file. This prevents accidental mass replacements when you intend to edit a single specific instance.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →