How to Add Comments to DOCX Files Using XML Manipulation: A Complete Guide
The anthropics/skills repository provides a pure-Python utility that inserts Word comments by directly editing the underlying OpenXML parts of a DOCX file without requiring Microsoft Office libraries.
Adding comments to DOCX files using XML manipulation allows you to programmatically annotate Word documents at the byte level. This approach unpacks the DOCX archive—a ZIP file containing XML parts—and modifies the OpenXML structure directly, offering precision that high-level libraries cannot match.
Understanding the DOCX Comment Architecture
The OpenXML Package Structure
A DOCX file is essentially a ZIP archive containing multiple XML parts. When you add comments to DOCX files using XML manipulation, you work with an unpacked directory structure that includes:
word/document.xml– The main document contentword/comments.xml– The actual comment text and metadataword/commentsExtended.xml– Threading information for repliesword/commentsIds.xml– Mapping between paragraph IDs and durable IDsword/commentsExtensible.xml– Creation timestamps for extensible commentsword/_rels/document.xml.rels– Relationships defining connections between parts[Content_Types].xml– MIME type declarations for all document parts
Required XML Components for Comments
Every comment requires two distinct identifiers generated by the _generate_hex_id() function in skills/docx/scripts/comment.py:
- Comment ID (
cid) – An 8-digit hexadecimal value used for markers indocument.xml - Durable ID – A separate 8-digit hexadecimal value for cross-referencing in
commentsIds.xml
The system also maintains relationship entries in word/_rels/document.xml.rels and content-type overrides in [Content_Types].xml to ensure the document remains valid.
Step-by-Step XML Manipulation Process
Step 1: Unpack the DOCX Archive
Before you can add comments to DOCX files using XML manipulation, you must extract the ZIP archive:
unzip document.docx -d unpacked_docx/
This creates a directory structure where individual XML parts can be edited directly.
Step 2: Generate Unique Identifiers
The _generate_hex_id() function in skills/docx/scripts/comment.py creates the necessary hexadecimal identifiers:
from skills.docx.scripts.comment import _generate_hex_id
para_id = _generate_hex_id() # For paragraph identification
durable_id = _generate_hex_id() # For durable cross-references
Both values are 8-digit hexadecimal strings used throughout the OpenXML parts.
Step 3: Ensure Relationship and Content-Type Entries
The utility automatically checks and updates package infrastructure:
_ensure_comment_relationships()– Adds<Relationship>elements toword/_rels/document.xml.relsif comment parts are not already referenced_ensure_comment_content_types()– Inserts<Override>elements into[Content_Types].xmlto declare MIME types forcomments.xml,commentsExtended.xml, and related parts
These functions prevent "part not found" errors when Word opens the document.
Step 4: Insert Comment XML Fragments
The _append_xml() helper function in skills/docx/scripts/comment.py handles the actual XML insertion. It:
- Parses the target XML file
- Wraps new fragments in a temporary
<root>element with proper namespace declarations - Appends child nodes to the appropriate parent element
- Re-serializes the DOM and re-escapes smart-quote entities via
_encode_smart_quotes()
The comment body uses the COMMENT_XML template to populate comments.xml with author, timestamp, and text data.
Step 5: Add Comment Markers to document.xml
Unlike automated insertion, markers must be manually placed. The script outputs a snippet using COMMENT_MARKER_TEMPLATE or REPLY_MARKER_TEMPLATE:
<w:commentRangeStart w:id="0"/>
<!-- commented content -->
<w:commentRangeEnd w:id="0"/>
<w:r>
<w:rPr>
<w:rStyle w:val="CommentReference"/>
</w:rPr>
<w:commentReference w:id="0"/>
</w:r>
Critical constraint: Markers must be direct children of <w:p> elements—never nested inside <w:r> tags. The validate_comment_markers() function in skills/xlsx/scripts/office/validators/docx.py enforces this rule (lines 298-384).
Step 6: Validate the Result
After repacking the DOCX, run the validator to ensure integrity:
from skills.xlsx.scripts.office.validators.docx import validate_comment_markers
# Validates that each commentRangeStart has matching commentRangeEnd
# and that IDs correspond to real comments in comments.xml
validate_comment_markers("unpacked_docx/")
Practical Implementation Using the Skills Utility
The add_comment() function in skills/docx/scripts/comment.py encapsulates the entire workflow. It returns the generated para_id (used for threading replies) and a status message.
Adding a Top-Level Comment
python skills/docx/scripts/comment.py unpacked/ 0 "Review this paragraph for accuracy" \
--author "Alice Smith" \
--initials "AS"
This generates:
- A new entry in
comments.xmlwith ID 0 - Relationship entries if missing
- A marker snippet to paste into
document.xml
Adding Threaded Replies
To create a reply to an existing comment, use the --parent flag with the parent comment's para_id:
python skills/docx/scripts/comment.py unpacked/ 1 "I agree with this assessment" \
--author "Bob Jones" \
--initials "BJ" \
--parent <para_id_from_parent>
The script outputs a REPLY_MARKER_TEMPLATE that nests the reply markers within the parent's context in commentsExtended.xml.
Key Files and Functions Reference
| Path | Purpose |
|---|---|
skills/docx/scripts/comment.py |
Core utility containing add_comment(), _generate_hex_id(), _ensure_comment_relationships(), _ensure_comment_content_types(), and _append_xml() |
skills/docx/scripts/templates/comments.xml |
Minimal starter template used when initializing comments.xml for the first time |
skills/xlsx/scripts/office/validators/docx.py |
Validation logic including validate_comment_markers() (lines 298-384) that enforces proper marker placement and ID matching |
word/_rels/document.xml.rels |
Relationship part defining connections between document.xml and comment parts |
[Content_Types].xml |
Package content type declarations required for all comment-related XML parts |
Summary
- Unpack first: DOCX files are ZIP archives that must be extracted before XML manipulation.
- ID management: Every comment requires two 8-digit hexadecimal IDs generated by
_generate_hex_id()for cross-referencing between parts. - Infrastructure checks: Use
_ensure_comment_relationships()and_ensure_comment_content_types()to maintain valid package structure. - Manual marker placement: Comment markers must be pasted manually into
document.xmlas direct children of<w:p>elements, never nested inside<w:r>tags. - Validation: Run
validate_comment_markers()fromskills/xlsx/scripts/office/validators/docx.pyto ensure all comment ranges have matching start/end pairs and valid ID references.
Frequently Asked Questions
Can I add comments without unpacking the DOCX file?
No, the skills/docx/scripts/comment.py utility requires an unpacked directory structure because it performs direct file system operations on individual XML parts like word/document.xml and word/comments.xml. You must first extract the DOCX using unzip or similar tools, run the manipulation scripts, and then repackage the directory contents into a ZIP file with the .docx extension.
What are the risks of manual XML manipulation?
The primary risk is producing invalid OpenXML that Microsoft Word cannot open. Common failure modes include: missing relationship entries in word/_rels/document.xml.rels (solved by _ensure_comment_relationships()), missing content-type declarations in [Content_Types].xml (solved by _ensure_comment_content_types()), and improperly nested comment markers inside <w:r> elements rather than as direct children of <w:p> (detected by validate_comment_markers() in skills/xlsx/scripts/office/validators/docx.py).
How do I add threaded replies to existing comments?
To create a reply, invoke add_comment() with the --parent flag set to the para_id value returned when the parent comment was created. The script generates a REPLY_MARKER_TEMPLATE snippet that you paste into document.xml near the parent comment markers. Internally, this creates a <w15:commentEx> entry in commentsExtended.xml linking the reply to its parent, enabling Word's threaded conversation view.
Is Microsoft Office required to validate the modified DOCX?
No, the skills repository includes a standalone validator in skills/xlsx/scripts/office/validators/docx.py that checks document integrity without requiring Microsoft Office installation. The validate_comment_markers() function specifically verifies that every <w:commentRangeStart> has a corresponding <w:commentRangeEnd> with matching IDs, and that these IDs reference actual comments defined in comments.xml. This allows you to verify document integrity in CI/CD pipelines or server environments where Office is not available.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →