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 with the Python standard library, 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 只需提供结构化文件变更,工具通过 Python 标准库计算正确 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. added_entries and replaced_entries are indexed by their position after the write; removed_entries by its position before. For overwrite, entries_before and removed_entries are null if the replaced file could not be parsed — an OVERWROTE_UNREADABLE_PATCH warning explains why, and the overwrite still succeeds so a corrupt patch stays repairable.
  • 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. And self_valid only means the diff is structurally well-formed — use validate_patch_tool with git_check: true to check it actually applies.

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.3

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.3
File Size Uploaded
uni_diff_patch-0.1.3.tar.gz 30.2 kB Details

Built distribution (wheel)

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

Total release size: 69.8 kB

Release files / uni_diff_patch-0.1.3.tar.gz

Download URL uni_diff_patch-0.1.3.tar.gz
Size 30.2 kB
Tags Source
SHA-256 checksum
How to use checksums
7b525de7cc3616d7acb45eae63bcf9043e73b72c5d3a7edd332b3d05a5e9f15a
BLAKE2b-256 checksum
How to use checksums
489769f5565cd491593370ada6f7a17a28973209ed16ce55ece0086c446f6aaa
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.2 {"installer":{"name":"uv","version":"0.12.2","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.3-py3-none-any.whl

Download URL uni_diff_patch-0.1.3-py3-none-any.whl
Size 39.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6b10f21c8a41e9377875f69bab41a68858665502384968921da91f69d90eae79
BLAKE2b-256 checksum
How to use checksums
9c837fba9db0e8ed402f653fdc43b48e6414effd6fa3f0d440e2d84b94c62f1e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.2 {"installer":{"name":"uv","version":"0.12.2","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

This release

0.1.3 This release

2 release files

0.1.2

2 release files

0.1.1

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