Skip to main content
Glama
README.md
# DocuHand πŸ–οΈ

**Give your AI agent hands to operate real Word & WPS documents on Windows.**

DocuHand is a Model Context Protocol (MCP) server that lets AI agents
(Claude Desktop, Hermes, Cursor, any MCP client) actually *drive* Microsoft
Word and WPS Office via COM β€” not just read docx XML, but automate the real
applications: legacy `.doc` binaries, documents currently open on screen,
and print-fidelity PDF export through the word processor's own layout engine.

Works on **both** Microsoft Word and WPS Office β€” when Word is absent or
crashes mid-batch, the WPS engine takes over transparently. (WPS support is
believed to be unique among Office MCP servers.)

```bash
uvx docuhand serve
```

Security posture: **zero network code** (audited β€” see
[THREAT-MODEL.md](THREAT-MODEL.md)): no telemetry, no update checks, a
local JSONL audit log, and an explicit non-goals section.

## Why DocuHand exists

Most Office MCP servers are built on `python-docx` β€” they manipulate XML in
idealized files. Real-world documents are not idealized:

- 20-year-old `.doc` binaries that XML libraries cannot touch
- Files currently **open and locked** in Word/WPS right now
- WPS Office β€” the default suite for hundreds of millions of users
- CJK fonts that silently reset when text is replaced (the `NameFarEast` trap)
- Password-protected and corrupt files that pop **modal dialogs** and hang
  headless servers

DocuHand drives the real applications, hardened by a year of production use
in government document processing. Every pitfall in
[docs/pitfalls.md](docs/pitfalls.md) cost real debugging time β€” you get them
pre-solved.

## Tools

| Tool | What it does |
|------|--------------|
| `inspect_document` | Pre-flight health check: real format vs extension, lock state, page/word counts, corruption & password detection, bookmarks, field codes |
| `convert_documents` | Batch `.doc ↔ .docx` for a whole directory in one call, Wordβ†’WPS failover, per-file fault isolation, sources never deleted |
| `fill_template` | Bookmarks / `{{placeholders}}` / content controls, CJK fonts protected through save |
| `export_pdf` | Print-fidelity PDF via the real layout engine, optional page range |
| `extract_content` | Text / tables (as JSON grids) / heading outline from legacy `.doc` |
| `edit_open_document` | Edit a document the user **has open right now** β€” changes appear in their window |
| `merge_documents` | Mail-merge style batch generation: one template Γ— N data rows |

Every tool returns structured JSON. Every error carries
`{error_code, human_message, llm_hint}` β€” errors are data the agent reads to
self-correct, not tracebacks it drowns in.

## Quick start

Install from PyPI:

```bash
# with pip
pip install docuhand

# or with uv (also gives you the `uvx docuhand serve` one-liner)
uv tool install docuhand
```

Requirements: **Windows** with either Microsoft Word or WPS Office installed
(either one is enough β€” the engine probes `Word.Application` first, then
`KWPS.Application`, overridable with `DOCUHAND_ENGINE=wps|word`).

Add to your MCP client (Claude Desktop example):

```json
{
  "mcpServers": {
    "docuhand": {
      "command": "docuhand",
      "args": ["serve"]
    }
  }
}
```

Then ask your agent: *"Convert every .doc in D:\\Reports to PDF"* β€” see
[Examples](#examples).

## Examples

**Examples of what to ask your agent**

Batch conversion β€” one call converts the whole folder:

> "Convert every .doc file in D:\Reports to .docx"

Template fill with CJK font safety β€” layout and East-Asian fonts survive:

> "Fill D:\Templates\notice.docx with name=Zhang San, amount=426, save it to my desktop"

Edit a document that's open on screen β€” the agent works in the user's
live Word/WPS window:

> "In the contract I have open right now, replace every 2023 with 2024 β€” don't save yet"

Mail-merge batch:

> "Generate one offer letter per row in data.json from the offer.docx template"

A synthetic demo document lives in
[examples/demo-files/](examples/demo-files/) (generated by
`scripts/make_demo_docs.py`, no real/personal data), and
[examples/](examples/) has MCP client config snippets.

## Security model (read before granting file access)

- **100% local execution** β€” your documents never leave your machine
- **No telemetry**, ever
- **Explicit path allowlist** β€” set `DOCUHAND_ALLOWLIST` to a
  `os.pathsep`-separated list of directories; when unset, the fact that you
  launched the server yourself is the consent
- **Read-only mode** β€” set `DOCUHAND_READONLY=1` to block every mutating
  tool; only `inspect_document` / `extract_content` / `export_pdf` remain
- **JSONL audit log** β€” every operation is appended to
  `%LOCALAPPDATA%\docuhand\audit.jsonl` (disable: `DOCUHAND_AUDIT=off`,
  relocate: `DOCUHAND_AUDIT_PATH`)
- **Pre-COM container gates** β€” broken/encrypted files are rejected in pure
  Python *before* the engine opens them, so a corrupt file can never pop a
  modal dialog on your desktop or hang the server (the #1 killer of headless
  Office automation)

Environment variables (`DOCUHAND_ENGINE`, `DOCUHAND_ALLOWLIST`,
`DOCUHAND_READONLY`, `DOCUHAND_AUDIT*`, `DOCUHAND_COM_TIMEOUT`) must be
declared in your MCP client's server config β€” hosts pass stdio servers a
filtered environment.

## Why not just use Copilot in Word / WPS AI?

Those are copilots β€” they serve the *person sitting in front of the
document*, through a closed, human-driven interface with no public API.
DocuHand is ground crew: it serves *agents working unattended* β€” batch jobs
at 3 AM, pipelines, any MCP client. Different buyer, different mode of
operation.

**"Why operate legacy .doc files at all?"**
Nobody *chooses* legacy formats. Archives of millions of `.doc` files in
government, healthcare, legal and banking systems are locked by compliance
(retain-original requirements), ecosystem inertia (counterparties still
sending `.doc`), and sheer scale. Converting them *is* one of our tools.
Copilot can't touch them; python-docx can't either. Manual clerical work or
DocuHand β€” those are the options.

## DocuHand vs. XML-level Office tools

|  | python-docx based | DocuHand |
|---|---|---|
| Legacy `.doc` binaries | ❌ | βœ… (real app required) |
| Open/locked files | ❌ | βœ… `edit_open_document` |
| WPS Office | ❌ | βœ… dedicated engine |
| CJK font safety | ⚠️ n/a or silent corruption | βœ… hardened (`NameFarEast` re-assertion) |
| PDF fidelity | ⚠️ re-layout | βœ… print-engine export |
| Corrupt/password files | ⚠️ crash or hang | βœ… pre-COM gates, structured errors |

## Architecture notes

The short version of the six decisions that make this survive real machines
(full rationale in [docs/pitfalls.md](docs/pitfalls.md)):

- **One dedicated STA thread** owns all COM β€” the asyncio event loop must
  never touch COM directly (`RPC_E_WRONG_THREAD`), and one serial queue
  means no WINWORD.EXE instance explosion. A stuck call times out and the
  thread is abandoned wholesale.
- **Word β†’ WPS failover** β€” an engine that dies mid-call is discarded and
  the next one takes over; probe order is configurable.
- **Errors are structured data** for the calling agent, not stack traces.
- **Safety is registration-time middleware**, not "remember to check".

## Roadmap

- v0.2: `track_changes`, `compare_documents`, protect/unprotect, Excel family
- Pro (separate, later): batch queue/concurrency, service mode, priority support

The free core stays free (MIT).

## Development

```bash
git clone <this repo> && cd docuhand
uv sync

# CI-safe unit tests (no Office needed)
uv run pytest tests/unit

# COM integration tests (local machine with Word/WPS only, never CI)
uv run pytest tests -m com
```

## License

MIT β€” free core, forever. Pro is a separate commercial license, later.