法律文档修订引擎

一款将AI生成的变更(结构化JSON)应用为专业修订标记和注释的.docx文件处理工具,输出与律师在Microsoft Word中制作的修订文档无异。

  • by sontanon
  • Python
  • 本地部署
  • 1 星标
  • 更新于
在 GitHub 查看 sontanon/docx-mcp

使用说明

docx mcp

法律文件对发动机进行了红线标记。采用人工智能生成的更改(结构化JSON) 并将其作为专业跟踪的更改进行应用,并在其中添加注释 .docx 文件夹。产出与律师在 微软Word——正确 w:ins/w:del 标记、注释注释 对齐文本和保留的格式。

安装

需要Python 3.14+。

uv sync

快速开始

Python API

from docx_mcp import (
    ParagraphChange, ParagraphChangeType,
    TableChange, TableChangeType,
    RedlineConfig, apply_redlines,
)

changes = [
    # Modify a body paragraph
    ParagraphChange(
        kind="paragraph",
        fragment_id="3",              # ← str (was int in v0.1.0)
        change_type=ParagraphChangeType.MODIFY,
        new_text="The Company **shall** provide written notice.",
        justification="Strengthened obligation language.",
    ),
    # Delete a paragraph
    ParagraphChange(
        kind="paragraph",
        fragment_id="5",
        change_type=ParagraphChangeType.DELETE,
        justification="Removed redundant clause.",
    ),
    # Append a new paragraph
    ParagraphChange(
        kind="paragraph",
        fragment_id="7",
        change_type=ParagraphChangeType.APPEND_AFTER,
        new_text="The foregoing shall survive termination.",
        justification="Added survival provision.",
    ),
    # Modify a header paragraph
    ParagraphChange(
        kind="paragraph",
        fragment_id="header_1.1",
        change_type=ParagraphChangeType.MODIFY,
        new_text="CONFIDENTIAL",
        justification="Updated header text.",
    ),
    # Modify a table cell
    TableChange(
        kind="table",
        table_id=2,
        row=1,
        col=1,
        change_type=TableChangeType.MODIFY_CELL,
        new_text="Updated **cell** content",
        justification="Corrected table entry.",
    ),
    # Clear a table cell
    TableChange(
        kind="table",
        table_id=2,
        row=3,
        col=2,
        change_type=TableChangeType.CLEAR_CELL,
        justification="Removed obsolete data.",
    ),
]

doc = apply_redlines("contract.docx", changes)
doc.save("contract_redlined.docx")

命令行界面

# Extract fragment text from a document
docx-mcp convert input.docx
docx-mcp convert input.docx --format json

# Apply changes
docx-mcp apply input.docx changes.json -o output.docx

# Validate a redlined document
docx-mcp validate output.docx

# Audit a document for structural issues
docx-mcp audit input.docx
docx-mcp audit input.docx --format json

注: CLI convert 命令仅提取正文内容(没有标题, 例如桌子、脚或桌子)。要提取完整文档,请使用MCP extract_fragments 工具或Python full_to_fragments() 功能。

MCP服务器

该库包括MCP服务器, 等等)可以红线 .docx 文件直接。

# Start the server (stdio transport)
docx-mcp-server

在Claude桌面中配置 (claude_desktop_config.json):

{
  "mcpServers": {
    "docx-mcp": {
      "command": "uv",
      "args": ["run", "--directory", "/path/to/docx-mcp", "docx-mcp-server"]
    }
  }
}

在游标中配置 (.cursor/mcp.json):

{
  "mcpServers": {
    "docx-mcp": {
      "command": "uv",
      "args": ["run", "--directory", "/path/to/docx-mcp", "docx-mcp-server"]
    }
  }
}

工具

工具 说明
extract_fragments 阅读a .docx 并将段落、表格、页眉和页脚作为标记文本返回
apply_changes 应用内联列表中的跟踪更改并保存
apply_changes_from_file 从磁盘上的JSON文件应用跟踪的更改
validate_document_tool 运行结构验证检查
diff_fragments 比较两个 .docx 逐段归档(完整文档)
audit_document_tool 审计a .docx 用于标题、图像、表格、分节符等

资源

URI 描述
docx-fragments://{document_path} 浏览段落片段(URL编码路径)

工作流程示例

LLM客户通常会:

  1. 呼叫 extract_fragments 读取文档并获取片段ID。
  2. 解释内容并构建更改列表。
  3. 呼叫 apply_changes 使用更改列表生成带红线的文档。
  4. 可选呼叫 diff_fragments 比较原始输出与红线输出。

概念

片段

文档被分解为 碎片:段落、表格、标题和 页脚,全部按文档顺序索引。每个片段都有一个 字符串ID.

片段ID:

模式 含义 示例
"1", "2",… 正文段落/表格 <f=1>Introduction.</f=1>
"header_P.I" 标题P部分,第一段 <f=header_1.3>Confidential</f=header_1.3>
"footer_P.I" 脚注P部分,第一段 <f=footer_2.1>Page 1 of 10</f=footer_2.1>

表和正文段落共享相同的ID空间(它们在文档中交错 订单)。片段 "3" 可能是表和片段 "4" 一个段落。

使用 extract_fragments (MCP)或 full_to_fragments() (Python)查看 任何文档的片段映射:

<f=1>Introduction paragraph.</f=1>
<f=2>**Definitions.** The following terms shall apply.</f=2>
<table=3 rows=2 cols=3>
<cell=3.1.1 span="2">Merged Header</cell=3.1.1>
<cell=3.1.3>Header C</cell=3.1.3>
<cell=3.2.1>Data 1</cell=3.2.1>
<cell=3.2.2>Data 2</cell=3.2.2>
<cell=3.2.3>Data 3</cell=3.2.3>
</table=3>
<f=4>Closing paragraph. See [Section 2](https://example.com).</f=4>
<f=header_1.1>Confidential</f=header_1.1>
<f=footer_1.1>Page 1 of 10</f=footer_1.1>

表格

简单表格

简单(矩形)表被提取为 <table=N> 阻碍。每个细胞都有 一 cell_id 在 "table_id.row.col" 格式(例如。, "3.1.2").

合并单元格表

具有水平或垂直合并单元格的表(gridSpan / vMerge)都是 现在支持。合并跨度显示为属性:

  • span="2" --单元格跨2列(水平合并)
  • vspan="3" --单元格跨越3行(垂直合并)

跨越单元格(合并覆盖的位置)的是 从输出中省略. 例如,如果 cell=3.1.1 有 span="2"那么 cell=3.1.2 没有出现。

当以具有更改的合并单元格为目标时,始终以 原始细胞 (与 span/vspan 属性)。瞄准一个跨越的位置 提高a ValueError.

跳过的桌子

无法处理的表(嵌套表、格式错误的合并、内部表 页眉/页脚)显示为:

<table=5 skipped reason="table 5, cell 2.3 contains nested table"/>

页眉和页脚

页眉和页脚段落用前缀片段ID提取: header_1.1, footer_2.1等等。第一个数字是基于1的零件索引 (通常 1 对于默认的页眉/页脚),第二个是基于1的 该部分内的段落索引。

页眉/页脚段落可以修改、删除和附加到以下内容 正文段落。 页眉/页脚内的表格不可编辑 并且是 报告为跳过的元素。

限制: 页眉/页脚更改的评论未附在 输出(Word和LibreOffice不支持这些部分的注释范围)。 他们触发了 UserWarning 并被丢弃。

超链接

超链接提取为 [link text](url) 在段落文本中内联。 链接内部的格式得以保留: [**bold link**](url).

当修改现有段落时, [text] 没有 (url) 蜜饯 原始超链接URL。 [text](new_url) 创建新链接。

当附加新文本时, [text](url) 创建超链接。 [text] 没有 (url) 生成纯文本--始终指定 (url) 如果你想要一个 超链接。

跟踪更改策略

具有预先存在的跟踪更改的文档(<w:ins>, <w:del>, <w:moveFrom>, <w:moveTo>)都是 坚决拒绝 两者都 extract_fragments 和 apply_redlines。在处理之前,接受或拒绝Word中的所有更改。

collapse_empty 模式

可选模式,禁止提取空段落并加上红线。 为LLM消费生产更清洁的产品。启用后,它 必须 是 在提取和红线之间一致使用——不匹配的值会导致 片段ID错位。

更改类型

段落更改

类型 描述 必填 new_text
modify 单词级别差异作为跟踪更改应用 是
delete 整个段落标记为已删除 否
append_after 在引用片段后插入新段落 是

表格单元格更改

类型 描述 必填 new_text
modify_cell 修改单元格内容(单个或多个段落) 是
clear_cell 删除单元格中的所有内容(保留结构) 否

细胞修饰 使用位置对齐:如果单元格有多个 段落,新文本被拆分为换行符(\n)每条线都被应用 按照相应段落的顺序。单元格内容标记为已跟踪 更改和注释就像段落修改一样。

空白行管理

添加新段落时,可以控制周围的空白行:

Change(
    fragment_id=10,
    change_type=ChangeType.APPEND_AFTER,
    new_text="New clause text here.",
    justification="Added new provision.",
    blank_lines_before=1,  # Insert 1 blank line before the new paragraph
    blank_lines_after=1,   # Insert 1 blank line after the new paragraph
)

删除段落时,您可以自动删除尾随的空白行:

Change(
    fragment_id=15,
    change_type=ChangeType.DELETE,
    justification="Removed obsolete clause.",
    delete_next_blanks=1,  # Also delete the next blank paragraph
)

所有空白行将标记为跟踪插入/删除,并将显示在 红线文件。

伪Markdown

文本内容使用简化的类似Markdown的格式进行内联格式化:

  • **bold**
  • _italic_
  • __underline__

Unicode字符(智能引号、em破折号、节符号、非换行符 空间)保持原样。

字体继承:添加新段落时,字体系列、大小和 颜色会自动从参考段落的第一个文本方位复制 跑。伪Markdown的粗体、斜体和下划线格式是分层的 在继承的基本格式之上。

更改JSON

CLI接受包含裸数组或 {"changes": [...]} 包装。

段落更改示例

[
  {
    "fragment_id": "1",
    "change_type": "modify",
    "new_text": "The Seller agrees to deliver within **sixty** days.",
    "justification": "Extended delivery window."
  },
  {
    "fragment_id": "3",
    "change_type": "delete",
    "justification": "Removed governing law clause.",
    "delete_next_blanks": 1
  },
  {
    "fragment_id": "5",
    "change_type": "append_after",
    "new_text": "This Agreement shall be governed by Delaware law.",
    "justification": "Added Delaware governing law.",
    "blank_lines_before": 1,
    "blank_lines_after": 0
  },
  {
    "fragment_id": "header_1.1",
    "change_type": "modify",
    "new_text": "CONFIDENTIAL",
    "justification": "Updated header marking."
  }
]

表格单元格更改示例

[
  {
    "cell_id": "2.1.1",
    "change_type": "modify_cell",
    "new_text": "Updated **cell** content",
    "justification": "Corrected cell value."
  },
  {
    "cell_id": "2.3.2",
    "change_type": "clear_cell",
    "justification": "Cleared obsolete data."
  }
]

单元格ID使用以下格式 "table_id.row.col" 其中行和列是从1开始的。

验证

这 validate_document() 功能(和 docx-mcp validate CLI)检查:

  • 注释ID隔离 --跟踪的更改和注释ID不会冲突 跨组
  • 评论完整性 --每一个 <w:comment> 中有匹配的范围标记 文档正文,反之亦然
  • 跟踪更改属性 --每一个 <w:ins> 和 <w:del> 已要求 w:id, w:author,以及 w:date
  • 包装一致性 --存在以下内容类型和关系条目 comments.xml
from docx_mcp import validate_document

result = validate_document(doc)
if not result.ok:
    for error in result.errors:
        print(error)

建筑

库直接通过以下方式操纵OOXML lxml (不是 python-docx)因为 python-docx 没有跟踪更改支持。关键设计决策:

  • 单词级别差异 通过 diff-match-patch 使用单词到字符的映射 为了获得高质量的差异
  • 保守突变 --只有修改过的段落才会被触及;一切 否则传递相同的字节
  • 全局唯一的注释ID 通过单调 IdManager 种子从 文档的现有最大ID
  • python-docx 仅用于测试夹具生成,不用于 图书馆本身

模块映射

src/docx_mcp/
  __init__.py        Public API
  cli.py             CLI entry point (apply, convert, validate)
  models.py          Pydantic data models (Change, ChangeType, RedlineConfig, ...)
  document.py        DocxDocument: ZIP parsing, XML tree access, serialization
  converter.py       Paragraph & table XML -> pseudo-Markdown conversion
  table_utils.py     Table inspection utilities (cell access, simplicity checks)
  tokenizer.py       Word-level tokenization
  differ.py          Word-level diff engine (diff-match-patch wrapper)
  run_ops.py         Diff-to-XML-run mapping, run splitting, element building
  id_manager.py      Monotonic annotation ID allocator
  comments.py        Comment creation and range marker insertion
  redliner.py        Main orchestrator: apply_redlines()
  table_redliner.py  Table cell change application
  audit.py           Document structural audit (headers, images, tables, etc.)
  validator.py       Structural validation checks
  server.py          MCP server (FastMCP 3.x, stdio transport)
  handlers/
    modify.py        Word-level tracked changes on existing paragraphs
    delete.py        Full paragraph deletion markup
    append.py        New paragraph insertion markup

发展

# Run tests
uv run pytest tests/ -v

# Lint
uvx ruff check src/ tests/

# Auto-fix lint issues
uvx ruff check src/ tests/ --fix

# Type check
uvx ty check src/ tests/

431个测试涵盖了所有模块、处理程序、表操作、页眉/页脚、超链接、跟踪更改拒绝、合并单元格表、分节符、CLI、验证和MCP服务器。

许可证

麻省理工学院