Skip to main content

uni-diff-patch

MCP server + CLI tool for generating and maintaining valid unified diff patches deterministically.

LLM provides structured file changes, the tool computes correct diffs via mpatch (Rust), and output patch files can be inspected, appended, upserted, overwritten, created, or pruned entry-by-entry without tool-specific metadata in the .diff file.


MCP 服务器 + CLI 工具,确定性生成并维护合法的 unified diff patch。

LLM 只需提供结构化文件变更,工具通过 mpatch (Rust) 计算正确 diff,并支持对已有 patch 文件做条目级 inspect / append / upsert / overwrite / create_only / remove。生成的 .diff 文件不包含工具私有元数据。

Install / 安装

uv add uni-diff-patch      # as dependency / 作为依赖
uvx uni-diff-patch --help  # run directly / 直接运行

CLI / 命令行

# Generate patch from JSON spec / 从 JSON spec 生成 patch
uni-diff-patch generate --spec changes.json -o output.diff

# Upsert entries in an existing patch / 更新已有 patch 中的条目
uni-diff-patch generate --spec changes.json -o output.diff --write-mode upsert

# Generate with project_root_dir for absolute paths / 指定项目根目录(用于绝对路径)
uni-diff-patch generate --spec changes.json --project-root-dir /path/to/project

# Inspect entries covered by a patch / 枚举 patch 覆盖的文件条目
uni-diff-patch inspect output.diff

# Remove entries by path or entry index / 按路径或条目序号删除 patch 条目
uni-diff-patch remove output.diff --path src/main.py
uni-diff-patch remove output.diff --entry-index 0

# Validate a patch / 验证 patch
uni-diff-patch validate output.diff
uni-diff-patch validate output.diff --git-check --project-root-dir /path/to/repo

# Start MCP server / 启动 MCP 服务器
uni-diff-patch serve

MCP Configuration / MCP 配置

{
  "mcpServers": {
    "uni-diff-patch": {
      "command": "uvx",
      "args": ["uni-diff-patch", "serve"]
    }
  }
}

Entry Lifecycle / 条目生命周期

Patch files are treated as an ordered store of per-file entries. The patch path stored in the --- / +++ headers is the identity key. Rename entries occupy both the source and destination paths.

Patch 文件会被当作有序的文件条目集合。--- / +++ header 中的 patch path 是身份键;rename 条目同时占用源路径和目标路径。

Mode / 模式 Behavior / 行为
append Default. Add new entries and fail if any occupied path already exists. / 默认模式。追加新条目;若路径已存在则失败。
upsert Replace matching entries, append absent entries, and remove a matching entry when the recomputed diff is empty. / 替换已有条目、追加不存在的条目;若重新计算后无 diff,则删除已有条目。
overwrite Replace the entire output patch file. / 覆写整个输出 patch 文件。
create_only Create a new output patch file and fail if it already exists. / 仅新建;文件已存在则失败。

When using absolute file paths, pass project_root_dir and keep it constant for the lifetime of a given output patch file. If a later mutation computes no exact path match but finds an existing entry with the same basename, the result includes a non-blocking POSSIBLE_PROJECT_ROOT_DIR_DRIFT warning. The .diff file itself intentionally remains metadata-free, so callers are responsible for using one stable project root per output patch.

使用绝对路径时请传入 project_root_dir,并在同一个输出 patch 文件的整个生命周期中保持不变。若后续操作没有精确路径匹配,但发现已有条目 basename 相同,结果会包含非阻塞 POSSIBLE_PROJECT_ROOT_DIR_DRIFT 警告。.diff 文件本身不会写入工具私有元数据,因此调用方需要保证同一输出 patch 使用同一项目根目录。

MCP Result Fields / MCP 返回字段

generate_patch_tool keeps the existing fields patch, files_changed, self_valid, and skipped_files, and adds:

  • removed_entries: entries removed by upsert; empty-diff removals include reason: "empty_diff_upsert".
  • warnings: non-blocking warnings such as possible project-root drift.

inspect_patch_entries_tool returns entries, entry_count, duplicate_paths, and tool_policy_ok.

remove_patch_entries_tool returns removed_entries, remaining_count, and warnings. Removing the last entry leaves an empty patch file in place.

validate_patch_tool reports structural validity and also returns duplicate_paths plus tool_policy_ok, so callers can distinguish parseable patches from patches that violate this tool's single-entry-per-path policy.

JSON Spec Format / JSON Spec 格式

All modes are specified in a changes array. Each change requires file_path and mode.

所有模式通过 changes 数组指定,每个变更需要 file_path 和 mode。

file_content — Full new content / 完整新内容

Tool reads original from disk and diffs. / 工具从磁盘读取原文件并生成 diff。

{
  "changes": [{
    "file_path": "src/main.py",
    "mode": "file_content",
    "new_content": "def main():\n    print('hello')\n"
  }]
}

search_replace — Targeted replacements / 精准替换(最省 token)

{
  "changes": [{
    "file_path": "src/main.py",
    "mode": "search_replace",
    "replacements": [
      {"search": "print('hello')", "replace": "print('world')"},
      {"search": "old_func()", "replace": "new_func()", "line_hint": 42}
    ]
  }]
}

line_hint: approximate line number for disambiguation when search matches multiple locations.

line_hint:当 search 匹配多处时,用大致行号消歧(选最近的匹配)。

create_file — New file / 新建文件

{
  "changes": [{
    "file_path": "src/new_module.py",
    "mode": "create_file",
    "new_content": "# New module\ndef init():\n    pass\n"
  }]
}

delete_file — Remove file / 删除文件

{
  "changes": [{
    "file_path": "src/deprecated.py",
    "mode": "delete_file"
  }]
}

text_pair — Both old and new text / 直接传入新旧文本(不读磁盘)

{
  "changes": [{
    "file_path": "virtual/path.py",
    "mode": "text_pair",
    "old_text": "x = 1\n",
    "new_text": "x = 2\n"
  }]
}

rename_file — Rename/move / 重命名/移动文件

Optionally with content change. / 可同时修改内容。

{
  "changes": [{
    "file_path": "src/old_name.py",
    "mode": "rename_file",
    "new_path": "src/new_name.py",
    "new_content": "# Optional modified content\n"
  }]
}

chmod — Permission change / 权限变更

Optionally with content change. / 可同时修改内容。

{
  "changes": [{
    "file_path": "scripts/deploy.sh",
    "mode": "chmod",
    "old_mode": "100644",
    "new_mode": "100755"
  }]
}

Multi-file patch / 多文件合并 patch

{
  "changes": [
    {"file_path": "src/a.py", "mode": "file_content", "new_content": "..."},
    {"file_path": "src/b.py", "mode": "search_replace", "replacements": [{"search": "old", "replace": "new"}]},
    {"file_path": "src/c.py", "mode": "create_file", "new_content": "..."}
  ],
  "output_path": "changes.diff",
  "write_mode": "upsert",
  "project_root_dir": "/path/to/project"
}

Optional Fields / 可选字段

Field / 字段 Default / 默认值 Description / 说明
encoding "utf-8" File encoding for disk-read modes / 磁盘读取模式的文件编码
context_lines 3 Context lines in diff / diff 中的上下文行数
output_path — Top-level, writes or mutates a patch file / 顶层字段,写入或修改 patch 文件
write_mode "append" Top-level, one of append/upsert/overwrite/create_only / 顶层字段,控制写入模式
project_root_dir current directory Top-level, project root for absolute path relativization / 顶层字段,绝对路径相对化的项目根目录

Development / 开发

uv sync
uv run pytest tests/ -v

Metadata

Release files for uni-diff-patch 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for uni-diff-patch 0.1.0
File Size Uploaded
uni_diff_patch-0.1.0.tar.gz 27.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for uni-diff-patch 0.1.0
File Interpreter ABI Platform
uni_diff_patch-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 64.3 kB

Release files / uni_diff_patch-0.1.0.tar.gz

Download URL uni_diff_patch-0.1.0.tar.gz
Size 27.6 kB
Tags Source
SHA-256 checksum
How to use checksums
6822e308265552ea25670c8568c501e41138aa1b81fa5a327867b44db8d899df
BLAKE2b-256 checksum
How to use checksums
5359c3adf1066244ca154029b1a3ef05ba478ce801d16f7c131b9240d00a0aed
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.1 {"installer":{"name":"uv","version":"0.12.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / uni_diff_patch-0.1.0-py3-none-any.whl

Download URL uni_diff_patch-0.1.0-py3-none-any.whl
Size 36.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
051cc7d2fec07556b33ded25d8b7dc418e64f0066685e68f660a5b98454ed5d6
BLAKE2b-256 checksum
How to use checksums
68e72af60264cb98662525875d4dd829c26eda0117506b096c672344b732232d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.1 {"installer":{"name":"uv","version":"0.12.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

This release

0.1.0 This release

2 release files

0.0.5

2 release files

0.0.4

2 release files

0.0.3

2 release files

0.0.2

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page