This release is a pre-release and may not be stable for production use.
docx2typed
Give the agent installation workflow to an agent for automatic PyPI, MCP, and temporary Tailscale phone-collaboration setup.
Structure-preserving DOCX text editing for agents and reviewers.
docx2typedlets an agent edit the words in a.docxwithout flattening the document into a lossy text or HTML approximation. The typed workdir locks style ownership, anchors, tracked-revision identity, comments, table structure, content controls, and untouched package parts; only explicitly requested text moves.
Synthetic review demo: inline tracked revisions, a fixed paragraph-jump index, and an explicit build + verify handoff. No real user document data is used.
Why this is different
| What users see | What the engine guarantees |
|---|---|
| A continuous document surface with a fixed review index. | Review is rendered from the canonical typed AST, not the lossy edit.md projection. |
| Text edits, tracked revisions, comment instructions, table operations, and content controls. | Unchanged structure stays locked; ambiguous style ownership fails closed instead of being guessed. |
| A new DOCX plus an evidence trail. | build validates before writing, verify independently re-derives the result, and LibreOffice checks interoperability. |
The release bar
The release qualification suite is deliberately adversarial: 32 deterministic black-box tasks, 6 metamorphic relations, and a hard gate of 0 unknown capabilities and 0 silent corruption. The project is not “done” because a browser page looks plausible; the output DOCX must pass the structural and package-level gates.
Positioning: this is a structure-preserving editing engine, not a browser clone of Microsoft Word. The browser console is a semantic review surface;
build+verify+ LibreOffice interoperability are the delivery proof.
Core contract
| Contract | Guarantee |
|---|---|
| Only requested text moves. | Untouched paragraphs, styles, anchors, and package parts are replayed without side effects. |
| Source files stay safe. | extract never mutates the input; structural operations and wholesale revision decisions write a new DOCX/workdir. |
| Style ownership stays explicit. | Edits are planned against regions.md; mixed-region rewrites are rejected rather than guessed. |
| Comments stay by default. | Agents may act on comment instructions, but comment IDs, text, dates, authors, and anchors remain unless the user explicitly requests deletion. |
Install
Requirements
- Python 3.11+
python-docxandmcpare installed from the package metadata- LibreOffice Writer is recommended for the final interoperability check
- Tailscale is optional and only needed for phone access
One-command source install
From a source checkout, use the installer for your platform:
# Windows PowerShell
.\install.ps1
# Development install: reflect source edits immediately.
.\install.ps1 -Editable
# macOS / Linux
./install.sh
# Development install: reflect source edits immediately.
./install.sh --editable
Both installers create .venv, install the current checkout without changing
the system Python, and smoke-check the docx2typed CLI.
PyPI install
python -m pip install --upgrade docx2typed
# Or install an isolated CLI with uv.
uv tool install --upgrade docx2typed
# Confirm the installed CLI.
docx2typed extract --help
Source checkout manual install
python -m pip install .
# Development checkout:
python -m pip install -e .
The package exposes these entry points after installation:
docx2typed # CLI, including the mcp and review subcommands
docx2typed-mcp # stdio MCP server
docx2typed-review # localhost review server
The unified commands are useful for one-line tool configuration:
docx2typed mcp
docx2typed review workdir --host 127.0.0.1 --port 8876
When running directly from a source checkout, replace the installed module name with scripts:
python -m scripts extract input.docx -o workdir
python -m scripts.review_console workdir -o review.html
python -m scripts.review_server workdir --port 8876
Five-minute workflow
1. Extract
docx2typed extract input.docx -o workdir
input.docx remains unchanged. The workdir records the source/template fingerprints and creates the canonical typed projection plus editable sidecars.
2. Read once, then inspect locally
docx2typed view workdir --mode clean
docx2typed view workdir --mode style
docx2typed view workdir --mode raw
# Check freshness before editing.
docx2typed edit status workdir
clean: continuous prose for understanding the document.style: prose plus style-region diagnostics.raw: typed markers, paragraph IDs, tables, ranges, anchors, revisions, and structural tokens.
Read the full clean projection once. Use regions.md and paragraph-level inspection only for the passages you will change.
3. Edit
Open workdir/edit.md in an editor and change text inside the relevant style region. Do not rewrite the typed.md header or structural markers. Then synchronize the draft:
# Normal text edit: no new tracked revisions.
docx2typed edit sync workdir --no-track
# Or make the edit visible as real w:ins/w:del revisions.
docx2typed edit sync workdir --track --author "Reviewer"
For a raw typed.md change, refresh the projection first:
docx2typed edit refresh workdir
If the draft is intentionally discarded, use --discard; do not overwrite a dirty draft accidentally.
4. Build
docx2typed validate workdir
docx2typed build workdir -o output.docx
build requires a valid clean state and fails closed on invalid structure, stale edits, unresolved conflicts, or unsafe range changes.
5. Verify
docx2typed verify workdir output.docx
verify independently re-derives the output against the workdir. The structured evidence includes checks, revision counts/authors, and surviving comment IDs.
6. Interoperate with Word-compatible tooling
Open or convert the built DOCX with LibreOffice Writer. A deliverable is complete only when verify passes and LibreOffice opens/converts it without repair prompts.
Format inspection and review console
Standalone HTML
Generate a self-contained review page from the canonical typed AST:
# Installed package:
python -m docx2typed.review_console long-wd -o review.html
# Source checkout:
python -m scripts.review_console long-wd -o review.html
Open review.html in a browser. The console renders typed.md together with styles.json; it does not use the lossy edit.md projection or an external DOCX-to-HTML renderer.
Local interactive server
docx2typed-review long-wd --host 127.0.0.1 --port 8876
Open http://127.0.0.1:8876/. The server provides the review page and the local handoff API. The review index is sticky: selecting a revision or comment jumps the document surface to its corresponding paragraph while keeping the decision context visible. Technical style diagnostics stay opt-in so ordinary readers see the document, not internal metadata.
For a source checkout:
python -m scripts.review_server long-wd --host 127.0.0.1 --port 8876
Temporary phone collaboration over Tailscale
Run the review server on the machine that owns the workdir:
docx2typed review long-wd --tailscale --port 8876
The command queries tailscale ip -4, binds only to that exact Tailscale
address, and prints the phone URL, for example
http://100.x.y.z:8876/. Open that URL on a phone signed in to the same
tailnet. The browser and desktop/agent clients share the same server state;
the review page polls for new snapshots and queued decisions.
This is an interim private-network mode, not a public deployment:
- keep Tailscale ACLs restricted to the intended collaborators;
- do not replace
--tailscalewith--host 0.0.0.0; - the transport is plain HTTP inside the tailnet, so do not expose the port outside Tailscale.
Tailscale must be installed and its CLI must be available on PATH.
The browser surface is deliberately semantic and reader-first:
- paragraph flow remains continuous instead of becoming isolated evidence cards;
- tracked insertions/deletions retain their inline location and metadata;
- comment records show real text and anchor paragraphs;
- style ownership comes from the Word style registry and
styles.json; - unsupported Word layout details are not silently rewritten; final fidelity is checked in the built DOCX.
Screenshots
| Desktop — paper-like reading stage; the review index stays visible while the document remains continuous. | Mobile — the rail collapses into an accessible compact control without horizontal overflow. |
Common workflows
Clean text edit
docx2typed extract input.docx -o workdir
docx2typed view workdir --mode clean
# edit workdir/edit.md within one style region
docx2typed edit sync workdir --no-track
docx2typed build workdir -o edited.docx
docx2typed verify workdir edited.docx
Tracked edit
docx2typed extract input.docx -o tracked-wd
# edit tracked-wd/edit.md
docx2typed edit sync tracked-wd --track --author "AI Reviewer"
docx2typed build tracked-wd -o tracked.docx
docx2typed verify tracked-wd tracked.docx
The output contains real w:ins/w:del nodes. Existing revisions are left untouched; new revisions receive the session author/date.
Single revision decision
Read revisions.json first. A revision key has the form part|kind|w:id|fingerprint:
docx2typed decide accept "word/document.xml|insert|8|..." \
--workdir tracked-wd --fingerprint "..."
docx2typed decide reject "word/document.xml|delete|7|..." \
--workdir tracked-wd --fingerprint "..."
The fingerprint is a defensive check against stale review selections.
Wholesale settlement
docx2typed decide accept-all \
--workdir tracked-wd \
--output accepted.docx \
--workdir-out accepted-wd
docx2typed verify accepted-wd accepted.docx
reject-all has the same shape. The source workdir is never mutated; the new workdir is a clean baseline.
Comment review
Comments are first-class review objects in MCP:
workdir_open(workdir, author="AI Reviewer", track=true)
list_comments()
get_comment(comment_id)
# make region-scoped tracked edits
commit_sync()
build_docx(output="comment-reviewed.docx")
verify_output(output="comment-reviewed.docx")
The normal agent path leaves every comment ID, author, date, text, and anchor in place. Do not call delete_comment just because the requested edit is complete. If the user explicitly asks to delete comment 1, the CLI equivalent is:
docx2typed decide comment-delete 1 --workdir workdir
Table structure
Table references are body-level ordinals (T0, T1, ...), learned from view --mode raw:
docx2typed decide table-insert-row T0 \
--workdir workdir --args "2" \
--output table-row.docx --workdir-out table-row-wd
docx2typed decide table-merge-cells T0 \
--workdir workdir --args "0 1 2" \
--output table-merged.docx --workdir-out table-merged-wd
Rows/columns are 0-based. Merge is fail-closed if spanned cells contain text; use an explicit discard option only when content loss is intended. Table tools create a new DOCX/workdir and never rewrite cell text implicitly.
Unicode vertical normalization
docx2typed audit scan workdir -o scan.json
# A human reviews scan.json and creates a hash-bound approved policy.
docx2typed audit apply workdir \
--scan scan.json \
--policy policy.json \
-o normalized.docx \
--workdir-out normalized-wd
docx2typed verify normalized-wd normalized.docx
Classification is a suggestion. Ambiguous candidates are preserved until an approved policy says otherwise.
Content-control text
Content-control paragraphs are exposed as IDs such as S0.P0. Edit their text through edit.md or MCP exactly like body text. The control properties (w:sdtPr) stay locked; structural insertion/removal of the control itself is outside this contract.
MCP integration
After a source install, configure any stdio MCP host with:
{
"mcpServers": {
"docx2typed": {
"command": "python",
"args": ["-m", "docx2typed.mcp_server"]
}
}
}
After a tagged release is available on PyPI, the isolated one-line form is:
claude mcp add docx2typed -- uvx docx2typed mcp
For an MCP host JSON configuration, use:
{
"mcpServers": {
"docx2typed": {
"command": "uvx",
"args": ["docx2typed", "mcp"]
}
}
}
If the host does not inherit the environment where the package is installed, add a cwd or use the absolute Python executable for that environment. The server and CLI use the same typed-workdir engine and the same build/verify gates.
Recommended MCP sequence:
workdir_open
list_paragraphs / get_paragraph / list_comments
replace_text / batch_edit / insert_paragraph / delete_paragraph
diff_preview
commit_sync
build_docx
verify_output
MCP edits are region-scoped. Unchanged characters keep their exact style; replacements inherit the replaced region's style only when ownership is unambiguous; mixed-region rewrites are rejected instead of guessed.
Workdir artifacts
| File | Purpose |
|---|---|
typed.md |
Canonical typed AST projection; includes paragraph and structural markers. |
edit.md |
Human-readable editable draft; prose changes are synchronized back to the AST. |
format.json |
Paragraph/token/package-part structure and source metadata. |
styles.json |
Word rPr-derived style registry used by the semantic console. |
regions.md |
Style-region boundaries and indices for safe edit planning. |
revisions.json / revisions.md |
Tracked-revision inventory and revision keys. |
edit.state.json / edit.state.json.run.json |
Freshness state and hash-bound edit evidence. |
_template.docx |
Preserved package template used when rebuilding. |
.review/ |
Local review drafts, snapshots, history, and collaboration state when the review server is used. |
Verification and development
Run the focused suite with scratch space on a non-system drive when needed:
python -m pytest -q --basetemp=D:/L/AppData/pytest-tmp
Smoke commands:
python -m scripts.acceptance_corpus --workdir D:/L/AppData/docx2typed-corpus-run
python -m scripts.tool_smoke --workdir D:/L/AppData/smoke-run
Release qualification uses deterministic fixtures, 32 black-box tasks, 6 metamorphic relations, and agent prompts:
python -m scripts.release_fixtures --outdir corpus/release
python -m scripts.release_acceptance \
--report reports/release-local \
--workdir D:/L/AppData/release-run
python -m scripts.agent_bench --list
python -m scripts.agent_bench --grade <task> <out.docx> <workdir>
A green release summary requires:
task acceptance N/N
metamorphic N/N
unknown capability 0
silent corruption 0
Longer article example
The following creates a deliberately longer, structured article instead of a one-line fixture. It includes headings, long prose, mixed emphasis, superscript/subscript, and a table so the review surface has real vertical length and format markers to render.
Save it as make_long_article.py, then run it from the same directory:
from docx import Document
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.shared import Pt
def add_body(document: Document, text: str) -> None:
paragraph = document.add_paragraph(text)
paragraph.paragraph_format.space_after = Pt(8)
paragraph.paragraph_format.line_spacing = 1.25
doc = Document()
section = doc.sections[0]
section.top_margin = Pt(54)
section.bottom_margin = Pt(54)
section.left_margin = Pt(64)
section.right_margin = Pt(64)
heading = doc.add_heading("A verifiable document review workflow for research writing", level=0)
heading.alignment = WD_ALIGN_PARAGRAPH.CENTER
subtitle = doc.add_paragraph()
run = subtitle.add_run("A long-form fixture for testing structure, formatting, revisions, and comment boundaries")
run.italic = True
abstract = (
"Long technical documents often contain body text, heading levels, font changes, superscripts, comments, "
"revisions, tables, and pagination markers at the same time. If an editor turns a DOCX into plain text, "
"readers may see the sentences but cannot tell whether a rewrite crossed a style boundary or whether untouched "
"drawings, comment anchors, and package XML survived. This example splits document handling into six continuous "
"stages—extract, read, edit, review, build, and verify—and records each stage's inputs, outputs, and hash evidence."
)
doc.add_heading("Abstract", level=1)
add_body(doc, abstract)
doc.add_heading("I. Problem background", level=1)
add_body(
doc,
"When researchers process experiment reports, patent specifications, grant applications, and theses, the common "
"risk is not a missing adjective. It is a quiet change to the source document's formatting and structure after a "
"text edit. A centered heading can become left-aligned, a superscript variable can become ordinary text, an empty "
"table cell can inherit a neighbor's text, and a comment range can break when paragraphs are rebuilt. The difficulty "
"is that text semantics and WordprocessingML structure coexist: people need continuous prose, while the tool must "
"preserve every boundary that can be verified."
)
doc.add_heading("II. A verifiable intermediate representation", level=1)
add_body(
doc,
"The typed workdir is not a temporary text cache. It is an intermediate representation with paragraph IDs, style "
"regions, a baseline hash, and source-part metadata. The clean projection supports a quick read-through; the style "
"projection exposes font, size, color, superscript, and other style ownership; the raw projection puts paragraph "
"markers, table references, revision nodes, comment anchors, and opaque structural nodes back into view. Before an "
"edit is accepted, the engine checks whether it owns one style region; a mixed-region rewrite receives a split "
"suggestion instead of a guessed style."
)
doc.add_heading("III. The boundary between revisions and comments", level=1)
paragraph = doc.add_paragraph()
paragraph.add_run("A revision is a change that can be decided; a comment is an instruction from an external reviewer. ").bold = True
paragraph.add_run(
"In track mode, a replacement creates corresponding deletion and insertion revisions, with author and date written "
"to the Word revision nodes. In comment review, the tool reads the comment text and anchors, performs the requested "
"body edit, and keeps the comment itself by default. The teacher or user can still see the original instruction in Word "
"and decide when to clean it up. Only an explicit comment-delete operation removes the comment entry, range anchors, "
"and reference."
)
doc.add_heading("IV. Format markers and real output", level=1)
add_body(
doc,
"Format markers must not only look right in a browser. Bold, italic, strikethrough, fonts, sizes, colors, shading, "
"superscript, underline, paragraph alignment, page-break markers, tables, and content controls all travel through "
"the same structural path. The browser review console helps people locate paragraphs and revisions; the final DOCX is "
"reassembled by build, checked independently by verify, and opened through LibreOffice for interoperability. When a "
"browser cannot reproduce Word layout completely, the structure is not silently flattened; the delivery decision stays "
"at the DOCX evidence level."
)
doc.add_heading("V. A small experiment table", level=1)
table = doc.add_table(rows=1, cols=3)
table.style = "Table Grid"
for cell, value in zip(table.rows[0].cells, ["Stage", "Input", "Inspectable evidence"]):
cell.text = value
for row in [
("Extract", "source.docx", "source/template fingerprint"),
("Edit", "edit.md", "region ownership + run evidence"),
("Review", "revisions.json", "revision key + comment IDs"),
("Deliver", "output.docx", "verify + LibreOffice"),
]:
cells = table.add_row().cells
for cell, value in zip(cells, row):
cell.text = value
doc.add_heading("VI. Conclusion", level=1)
add_body(
doc,
"Verifiable document editing is not about making a Word file resemble text. It gives every text change a clear scope, "
"state, evidence trail, and rollback boundary. Once read, edit, review, build, and verify become one continuous chain, "
"format fidelity no longer depends on a lucky import/export or on manually checking the entire file after every paragraph. "
"This fixture is intentionally long so fixed-index jumps, continuous scrolling, style diagnostics, and final delivery checks "
"can be observed at a realistic document length."
)
# Add explicit format markers to the final paragraph.
marker = doc.add_paragraph("Marker probe: ")
marker.add_run("bold").bold = True
marker.add_run(" / ")
marker.add_run("italic").italic = True
marker.add_run(" / ")
sup = marker.add_run("x2")
sup.font.superscript = True
marker.add_run(" / ")
sub = marker.add_run("H2O")
sub.font.subscript = True
doc.save("long_article.docx")
print("wrote long_article.docx")
Run the full flow:
python make_long_article.py
docx2typed extract long_article.docx -o long-wd
docx2typed view long-wd --mode clean
docx2typed view long-wd --mode style
# Edit long-wd/edit.md, then:
docx2typed edit sync long-wd --track --author "Reviewer"
docx2typed build long-wd -o long_article-reviewed.docx
docx2typed verify long-wd long_article-reviewed.docx
Documentation map
SKILL.md— agent-facing contract and branch table.capabilities.md— every CLI atom and MCP tool with exact syntax and exit contracts.composites.md— seven workflows and end-to-end playbooks.verification.md— freshness, fail-closed, byte-fidelity, and interoperability gates.docs/rpr-reference.md— WordrPrXML to style translation notes.
Quick reference
- Install with
python -m pip install -e .. - Extract without mutating the source:
docx2typed extract input.docx -o workdir. - Read
view --mode cleanonce; inspectstyleorrawonly where needed. - Edit
edit.mdinside the regions listed byregions.md. - Synchronize with
edit sync --no-trackoredit sync --track --author NAME. - Build, independently verify, and run a LibreOffice interoperability check.
- Use
docx2typed-review workdir --port 8876for the fixed review index and paragraph jumps. - Use MCP for region-scoped agent edits, revision decisions, comment inventory, collaboration, table operations, build, and verification.
The safe default is conservative: comments stay, ambiguous style ownership fails closed, source workdirs are not mutated by structural operations, and no document is called finished until the output evidence is green.
Metadata
Release files for docx2typed 0.1.0rc1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| docx2typed-0.1.0rc1.tar.gz | 271.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| docx2typed-0.1.0rc1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 497.7 kB
Release files / docx2typed-0.1.0rc1.tar.gz
| Download URL | docx2typed-0.1.0rc1.tar.gz |
|---|---|
| Size | 271.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
748557feb6a3a78b20d4c52dda21147e6c23ac6888e24fec64ddf41c718f8eeb
|
|
BLAKE2b-256 checksum How to use checksums |
5f932e18ea6780f148137daaf41bb574f24537229b6aad8e9c234481ccb073cc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 9, 2026.
Transparency logRelease files / docx2typed-0.1.0rc1-py3-none-any.whl
| Download URL | docx2typed-0.1.0rc1-py3-none-any.whl |
|---|---|
| Size | 226.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3688ee9893aabafe510040d88ca1aa53360a9b2fd194b5aaa7d0e01d92c130d4
|
|
BLAKE2b-256 checksum How to use checksums |
e849e720568b6eee678751bf0e6acd3840ec32be5e9b460c82a5058eb41baf29
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 9, 2026.
Transparency log