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:

  • added_entries / replaced_entries / removed_entries: what this call did to the entry store. Replacements carry replaced_path; empty-diff removals carry reason: "empty_diff_upsert".
  • entries_before / entries_after: entry count around the write. entries_before is null for overwrite, which deliberately does not parse the existing file so that a corrupt patch can still be replaced.
  • warnings: non-blocking warnings such as possible project-root drift.

Note patch is this call's generated diff, not the current contents of the whole patch file. Use inspect_patch_entries_tool for the latter.

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 per-entry change_kind (a pure rename or chmod has hunk_count: 0 but is not an empty modification), plus duplicate_paths and 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.1

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.1
File Size Uploaded
uni_diff_patch-0.1.1.tar.gz 29.1 kB Details

Built distribution (wheel)

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

Total release size: 67.5 kB

Release files / uni_diff_patch-0.1.1.tar.gz

Download URL uni_diff_patch-0.1.1.tar.gz
Size 29.1 kB
Tags Source
SHA-256 checksum
How to use checksums
e2698bf0d783dc27f0573b5e28babbd8a2d31ad7a2bd4565274301828cc73684
BLAKE2b-256 checksum
How to use checksums
2f30d866a3ff90eed90fe4dbb35f94d437bd8bc8354bf8f3d64a66632b9d60d2
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.1-py3-none-any.whl

Download URL uni_diff_patch-0.1.1-py3-none-any.whl
Size 38.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1cfe6e63c620d2c75f9ebd2ed08c3ece6019a4be516c436fcbd745a759e4e8f7
BLAKE2b-256 checksum
How to use checksums
6c0038bf70ec62bd058ad1fe621adca23a78f752a7182651841cbd977dc4d61d
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

This release

0.1.1 This release

2 release files

0.1.0

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