docuflow

docuflow

Provides a unified interface for AI agents and scripts to read, create, and edit office documents (.docx, .xlsx, .pdf) via MCP tools, using Markdown as the intermediary format. Supports in-place patching with stable IDs, Jinja2 templating, and formatting preservation.

Category
访问服务器

README

docuflow

A unified interface for AI agents and scripts to work with office documents: .docx, .xlsx, .pdf via MCP tools and a Python library. Markdown is the lingua franca for reading, templating, and patching.

Python License: MIT MCP Coverage


Why

Documents in .docx/.xlsx share the same logic (headings, paragraphs, tables), but each library has its own API — and any non-trivial edit usually breaks the formatting (fonts, line spacing, alignment, table borders, headers/footers, sectPr).

docuflow solves this with three ideas:

  1. Markdown as the lingua franca. read → markdown + outline + tables; create/render → markdown → docx. The agent works with familiar text instead of raw XML.
  2. Patches by stable IDs. apply_patches with target_id="h:0.1.0" / "t:0" — replace / insert_after / delete / append without losing formatting.
  3. .docx templates as a Normal-style source. Point at a reference document — the new file inherits the font, size, line spacing, page margins, and headers/footers. The "Times New Roman 14 / 1.5 / justified" template look is no longer lost.

Features

  • 📖 Read documents as markdown + structural outline + tables (.docx, .xlsx, .pdf)
  • ✍️ Create from markdown (with Jinja2 templating and YAML frontmatter)
  • 🔧 In-place patches by stable IDs — replace, insert_after, delete, append
  • 🛡 Run-aware in-place replacement that preserves bold, italic, color — both inside and outside the match
  • 🧬 Inline styles (Pandoc-like): {#id .class key=value} on headings, {color="red" bold}…{/} on paragraphs
  • 📐 Template inheritance — fonts, sizes, margins, sectPr from a reference .docx
  • 🔍 Bulk search/replace (including regex) with run-aware formatting preservation
  • 🔒 Sandbox with a directory whitelist and path-traversal protection
  • 📕 PDF reading via opendataloader-pdf (Java 11+)
  • 🤖 MCP server with 11 tools for Claude Desktop / Cursor / Claude Code
  • 🎓 Claude skill at .claude/skills/docuflow-using/SKILL.md — teaching an AI agent how to pick the right tool

Installation

pip install docuflow

# optional: PDF reading via opendataloader-pdf
pip install 'docuflow[pdf]'

Requirements:

  • Python 3.11+
  • Java 11+ (only for PDF reading; everything else works without it)

Quick start

Python API

from pathlib import Path
from docuflow import Sandbox
from docuflow.formats import build_default_registry

sb = Sandbox(roots=[Path.home() / "Documents" / "Work"])
reg = build_default_registry()

# Read
content = reg.get_reader("docx").read(sb.resolve("report.docx"))
print(content.markdown)
for node in content.outline:
    print(node.id, "—", node.title)

# Create from markdown
reg.get_writer("docx").write(
    sb.resolve("new.docx"),
    type(content)(format="docx", markdown="# Title\n\nBody.",
                 outline=[], tables=[], metadata={}),
    template=sb.resolve("reference.docx"),   # ← inherits fonts, margins, sectPr
)

# In-place patches (preserve fonts, alignment, table borders)
from docuflow.core import EditPatch
reg.get_editor("docx").apply(sb.resolve("report.docx"), [
    EditPatch(op="replace",     target_id="h:0",   content="# Renamed"),
    EditPatch(op="replace",     target_id="t:0",   content="| A | B |\n|---|---|\n| 1 | 2 |"),
    EditPatch(op="insert_after", target_id="h:0.0", content="## New section"),
])

MCP server

docuflow-mcp --root ~/Documents/Work --root ~/Documents/Templates

Or via environment variable:

export DOCUFLOW_ROOTS="~/Documents/Work:~/Documents/Templates"
docuflow-mcp --env-roots

Claude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json):

{
  "mcpServers": {
    "docuflow": {
      "command": "docuflow-mcp",
      "args": ["--root", "/Users/me/Documents/Work"]
    }
  }
}

MCP tools (11)

Group Tool Purpose
Read read_document markdown + outline + tables
get_outline structure only (cheap for large files)
extract_tables tables only
Create create_document new docx/xlsx from markdown
render_template Jinja2 + YAML frontmatter → docx/xlsx
list_template_variables which {{variables}} a template needs
Edit edit_document rewrite from markdown (legacy)
apply_patches in-place patches by ID (recommended)
find_and_replace in-place search/replace (run-aware)
Meta get_document_info author, size, pages
get_sandbox_roots available directories

Edit pattern: outline → patches

[
  {"op": "replace",      "target_id": "h:0",    "content": "# Renamed"},
  {"op": "insert_after", "target_id": "h:1.0",  "content": "## New section\n\nBody."},
  {"op": "replace",      "target_id": "t:0",    "content": "| A | B |\n|---|---|\n| X | Y |"},
  {"op": "delete",       "target_id": "h:2.1"},
  {"op": "append",       "content": "# Tail\n\nFinal paragraph."}
]

Identifiers:

  • h:N.N.N — heading (N — 0-indexed sibling position; number of segments = level).
  • t:N — table (N — global index in the document).
  • h:0 / h:0.0 / h:0.0.0 — 1st, 2nd, 3rd level respectively.

Why not edit_document for patches? A full rewrite loses styles, fonts, headers/footers, and embedded images. Use apply_patches for targeted changes.


Templates

Jinja2 + YAML frontmatter

templates/contract.md:

---
template_version: 1
style:
  default_font: Times New Roman
  default_size: 12
  align: justify
---

# Contract No. {{number}}

City of {{city}}, {{date}}

{% for stage in stages %}
- **Stage {{loop.index}}**: {{stage.description}} — {{stage.amount}} ₽
{% endfor %}
render_template(
    template_path="templates/contract.md",
    output_path="contracts/2026-001.docx",
    data={"number": "2026-001", "city": "Moscow", "date": "2026-06-17",
          "stages": [{"description": "Prepayment", "amount": "100000"},
                     {"description": "Settlement", "amount": "900000"}]},
)

render_template is strict-undefined: a missing variable raises TemplateRenderError. For large templates call list_template_variables first and diff against data.

.docx template as a formatting source

If you need a specific look ("Times New Roman 14, line spacing 1.5, 2 cm margins, table borders"), pass a reference .docx to template=:

writer.write(out, content, template=Path("reference.docx"))

The writer clears the reference body and fills it with markdown, inheriting the Normal style, sectPr (page margins, page size, headers/footers), and table-style definitions. If the reference is minimal (missing Heading 1, List Bullet, Table Grid), those styles are auto-created.


Inline styles (Pandoc-like)

Heading attributes

# Introduction {#intro}

## Methods {#methods color="#1f4e79" align="center" bold}

# Sheet: Data {#hdr bold color="#1f4e79"}
  • #id — fixes the node ID in the outline.
  • .class — classes (.foo .bar).
  • key=value — properties (color, align, bold, size, font, italic, underline).
  • A bare token (no value) is treated as =true (bold → bold=true).

Color is #rrggbb or #rgb only. For .docx the attributes are applied to all runs of the heading. For .xlsx — to the first header row of the sheet.

Inline spans in paragraphs (.docx only)

Text with {color="red" bold}important{/} part.

Syntax {attrs}text{/} — text between { and {/} receives the attributes. The closing {/} is required. Supported attributes: color, bold, italic, underline, size, font.

Document style (YAML frontmatter)

---
title: Quarterly Report
author: Analytics
style:
  default_font: Arial
  default_size: 11
  align: justify
---

For .docx, default_font / default_size / align apply to the Normal style. For .xlsx, they apply to cells from the 2nd row on (the header row stays as is).

Heading IDs

Auto-generated as 0-indexed hierarchical (h:0, h:0.0, h:0.1.0). {#custom-id} overrides.


Architecture

docuflow/
├── core/                ← models, sandbox, markdown, registry
├── formats/
│   ├── docx/            ← Reader, Writer, Editor (in-place)
│   ├── xlsx/            ← Reader, Writer, Editor
│   └── pdf/             ← Reader (opendataloader-pdf)
└── mcp_server.py        ← MCP tools

.claude/skills/
└── docuflow-using/      ← Claude skill for agent integration

Two editor modes:

  • markdown-roundtrip (apply_markdown): rewrite the document from markdown — for "create from scratch".
  • in-place (apply, find_and_replace_in_place): modify XML elements directly, preserving run fonts, table borders, line spacing, sectPr, headers/footers. Recommended for edits.

In-place find_and_replace runs in run-aware mode: for each match it splits the first and last run at the match boundaries, removes runs that fall entirely inside the match, and inserts a single new run with the replacement text, inheriting the first affected run's formatting. Bold/italic/color outside the matched fragment is preserved. Paragraph-level format (alignment, indent, line-spacing, style) is always preserved.


Security

All paths pass through Sandbox.resolve(). Attempts to escape the allowed roots raise PathSecurityError. Symlinks are resolved; relative paths are resolved against the first root.


Limitations

Preserved NOT preserved from .docx template
Normal style (font, size) Page headers/footers (unless via template=)
sectPr (margins, page size, orientation) Section breaks, page numbering
paragraph_format (line-spacing, alignment) Embedded images, OLE objects
Table Grid (borders, if the style exists) Track changes, comments
Headers/footers (when template= is used)

For full visual fidelity with a complex template, use template= + apply_patches — the markdown parser keeps most of the template's formatting because you're editing text rather than recreating the document.


Common errors

  • Path outside sandbox → PathSecurityError. Don't try to bypass via .. — it's blocked. Ask to add a root.
  • Unknown format → UnsupportedFormatError. Supported: docx, xlsx, pdf (read-only).
  • Missing template variable → TemplateRenderError. Strict-undefined by design.
  • edit_document instead of apply_patches and lost formatting → restore from a backup; next time use patches.
  • PDF without Java → PdfJavaNotFoundError. Install JDK 11+.

Claude skill

A Claude skill ships with the repo at .claude/skills/docuflow-using/SKILL.md. It teaches the agent:

  • which MCP tool to pick for a given task (read / create / patch / template),
  • the outline→patches workflow and how to read stable IDs (h:N.N.N, t:N),
  • inline-styles syntax (Pandoc-like),
  • the correct workflow for full template fidelity (template= + apply_patches),
  • common errors and their fixes.

For Russian-speaking agents the same skill is available at .claude/skills/docuflow-using/SKILL.md — it's authored in Russian and references README_RU.md.


Development

# Clone
git clone https://github.com/deja111vu/docuflow.git
cd docuflow

# Create venv
python -m venv .venv
. .venv/bin/activate          # Linux/macOS
.venv\Scripts\activate.bat    # Windows

# Install editable + dev deps
pip install -e '.[dev,pdf]'

# Tests + coverage (≥85%)
pytest --cov=docuflow

# Lint
ruff check src tests
mypy src/docuflow

Test stack

  • pytest + pytest-cov
  • hypothesis (property-based tests for sandbox and outline)
  • All tests are isolated: tmp_path + Sandbox(roots=[tmp_path])

CI

GitHub Actions runs ruff, mypy --strict, pytest --cov on Python 3.11 / 3.12 / 3.13.


Roadmap

  • v0.2.0 (current): in-place editor, run-aware replace, template-aware writer, auto-style creation, inline styles, PDF reader.
  • v0.3.0: pptx (read/write/edit), track changes, comments.
  • v0.4.0: bulk operations (merge, split), benchmarks, performance budgets.

See CHANGELOG.md for the full version history.


License

MIT. See LICENSE.

Acknowledgements

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选