Unity MCP get_sha 工具实战:用 SHA256 指纹为 C 脚本变更与并发编辑保驾护航
2026/9/14 20:16:04 网站建设 项目流程

Unity MCP get_sha 工具实战:用 SHA256 指纹为 C# 脚本变更与并发编辑保驾护航

【免费下载链接】unity-mcpUnity MCP acts as a bridge between AI assistants and your Unity Editor. Give your LLM tools to manage assets, control scenes, edit scripts, and automate tasks within Unity.项目地址: https://gitcode.com/GitHub_Trending/un/unity-mcp

本文以 Unity MCP 开源仓库的 get_sha 工具文档 为骨架,完整讲解get_sha的调用方式、URI 解析规则、返回值语义,并结合 Python 服务端与 Unity Editor 端源码,深入剖析它在"编辑前并发保护(precondition)、编辑后变更验证、断连恢复校验"等真实场景中的底层工作原理,帮助 AI 客户端开发者安全、可靠地自动化 Unity C# 脚本的读写与编辑流程。

一、get_sha 是什么:只算指纹,不吐内容

get_sha是 Unity MCP 中core工具组的一员,模块位于 Server/src/services/tools/manage_script.py,归属于manage_script工具族。它的职责非常明确:

计算并返回一个 Unity C# 脚本的 SHA256 哈希及其基础元数据,但绝不返回文件内容

这一点在官方工具注册描述中被反复强调:Get SHA256 and basic metadata for a Unity C# script without returning file contents.(参见 get_sha 工具文档 与 manage_script.py 中的注册代码)。

为什么"只算指纹、不吐内容"如此重要?因为在 MCP 的请求-响应链路中,工具返回值最终会以 JSON 形式传输给 AI 客户端。对于大体积脚本文件,直接回传全文既浪费 token、拖慢响应,也容易触发传输层载荷上限。而 SHA256 作为确定性指纹,可以唯一标识文件当前状态——只要内容不变,哈希就不变——足以支撑"判断文件是否被改动"这类核心诉求,代价却极小。

从工具注解看,get_sha被标记为只读(readOnlyHint=True)、幂等(idempotentHint=True)、非破坏性(destructiveHint=False),即多次调用结果一致、不会对 Unity 项目产生任何副作用,可以放心地在工作流中高频使用。

二、参数与返回值:最小化接口设计

2.1 参数表

get_sha仅接受一个必填参数,接口极其精简:

名称类型必填说明
uristr目标脚本的 URI,支持三种形式:Assets/下的相对路径、mcpforunity://path/Assets/...协议形式、file://...文件协议形式

其中uri参数的官方描述为:"URI of the script to edit under Assets/ directory, mcpforunity://path/Assets/... or file://... or Assets/..."(源码注册处)。

2.2 返回结构

get_sha返回标准的 Unity 响应字典,核心数据位于data字段,经过 Python 服务端裁剪后仅保留两个最小字段(manage_script.py L688-L692):

{ "success": true, "data": { "sha256": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", "lengthBytes": 1234 } }
字段类型含义
sha256str脚本 UTF-8 文本内容的 SHA256 十六进制小写摘要
lengthBytesint脚本按 UTF-8 编码后的字节长度

需要注意的是,Unity Editor 端返回的原始数据其实更丰富(见下文"底层实现"),但 Python 服务端刻意做了裁剪(minimal提取),只把sha256lengthBytes透传给客户端。这意味着 Agent 拿到的永远是最精简、最必要的指纹信息。

三、URI 解析规则:三种写法都能用

uri参数之所以支持三种形式,是因为 Python 端通过_split_uri函数(manage_script.py L19-L68)做了统一的归一化解析。理解这个函数,就能明白各种 URI 写法的行为差异:

  1. mcpforunity://path/Assets/...:去掉mcpforunity://path/前缀,其余部分按Assets相对路径处理。这是 Unity MCP 内部的标准协议写法,Unity 端返回的uri字段也使用此格式(如mcpforunity://path/Assets/Scripts/A.cs)。
  2. file://...:使用urllib.parse.urlparse拆解,对路径做百分号解码(unquote);对非localhost主机名按 UNC 路径(//server/share/...)处理;随后若路径中存在Assets段,则截取该段起作为相对路径。
  3. 普通路径(Assets/...:直接使用,同样会做解码与分隔符归一化。

_split_uri最终把 URI 拆成(name, directory)二元组:name是去掉扩展名的文件名,directory是相对于Assets的目录部分。解析细节还包括:

  • 反斜杠统一替换为正斜杠(\/);
  • 使用os.path.normpath折叠.././等冗余段;
  • Assets段的匹配不区分大小写
  • Windows 盘符路径(如/C:/...)会剥掉前导斜杠。

这解释了为什么文档中强调"Requires uri (script path under Assets/ ...)"——所有脚本操作都强制限定在Assets/目录内,防止越界访问项目外的任意文件。

四、底层实现:Python 服务端与 Unity Editor 的协作

get_sha的实现横跨两层:Python 服务端负责参数解析与响应裁剪,Unity Editor 端负责真实读取文件并计算哈希。

4.1 Unity Editor 端:真正的哈希计算者

Unity 端实现在 MCPForUnity/Editor/Tools/ManageScript.cs 的"get_sha"action 分支(L282-L309):

  1. 先检查File.Exists(fullPath),文件不存在则直接返回错误Script not found at '{relativePath}'.
  2. File.ReadAllText(fullPath)读入文本,调用ComputeSha256(text)计算哈希;
  3. 用无 BOM 的UTF8Encoding统计lengthBytes
  4. 返回包含uripathsha256lengthByteslastModifiedUtc(ISO 8601 格式的 UTC 最后修改时间)的完整数据。

ComputeSha256的实现(ManageScript.cs L856-L864)值得留意:

private static string ComputeSha256(string contents) { using (var sha = SHA256.Create()) { var bytes = System.Text.Encoding.UTF8.GetBytes(contents); var hash = sha.ComputeHash(bytes); return BitConverter.ToString(hash).Replace("-", string.Empty).ToLowerInvariant(); } }

它先按UTF-8 字节序列对文件内容编码,再计算 SHA256,输出 64 位十六进制小写字符串。注意:哈希的对象是解码后的文本字符串,而非磁盘上的原始字节——因此 BOM、行尾符(CRLF/LF)等差异会直接影响哈希结果。这保证了 Python 端apply_text_edits使用的precondition_sha256与 Unity 端校验时使用的是同一套口径,不会因编码处理差异产生误判。

4.2 Python 服务端:调用链与响应裁剪

Python 端的get_sha函数(manage_script.py L672-L695)流程为:

  1. 通过get_unity_instance_from_context(ctx)从 MCP 上下文中解析当前目标 Unity 实例(支持多实例路由);
  2. 调用_split_uri(uri)得到(name, directory)
  3. 构造参数{"action": "get_sha", "name": name, "path": directory}
  4. 通过send_with_unity_instance+async_send_command_with_retry发送给 Unity 端manage_script处理器(带重试机制);
  5. 成功后仅保留sha256lengthBytes两个最小字段返回。

这一调用链与集成测试 Server/tests/integration/test_get_sha.py 完全吻合:测试用 monkeypatch 替换底层发送函数,断言命令名为manage_scriptactionget_shaname提取为ApathAssets/Scripts结尾,并验证返回的data被裁剪为{"sha256": ..., "lengthBytes": ...}(test_get_sha.py L27-L33)。该测试是理解参数形状与路由行为的权威参考。

五、典型应用场景:并发保护、变更验证与断连恢复

get_sha单独看只是一个只读指纹工具,但它在 Unity MCP 的脚本编辑体系中扮演着枢纽角色,主要服务于三个关键场景。

5.1 场景一:编辑前的并发保护(precondition)

apply_text_edits工具接受一个可选的precondition_sha256参数(manage_script.py L96-L97),用途是防止并发编辑:AI 客户端在读取脚本并准备编辑时,可先用get_sha取得当前指纹,再把指纹作为 precondition 随编辑请求一起提交。Unity 端在真正落盘前会做双重校验(ManageScript.cs L551-L556):

string currentSha = ComputeSha256(original); if (string.IsNullOrEmpty(preconditionSha256)) return new ErrorResponse("precondition_required", ...); if (!preconditionSha256.Equals(currentSha, StringComparison.OrdinalIgnoreCase)) return new ErrorResponse("stale_file", new { status = "stale_file", expected_sha256 = preconditionSha256, current_sha256 = currentSha });
  • 未提供 precondition → 返回precondition_required(大文件编辑强制要求指纹,避免盲目覆盖);
  • 提供的指纹与当前文件哈希不一致 → 返回stale_file,并同时带回expected_sha256current_sha256,客户端可据此判断文件已被其他进程(或用户手改、Unity 域重载等)改动,从而决定是重新get_sha再合并编辑,还是放弃本次修改。

这正是"检查-修改-写入"(Check-Modify-Write)乐观锁范式在 Unity 脚本编辑上的落地:get_sha是锁的获取动作,precondition_sha256是锁的校验动作。

5.2 场景二:编辑后的变更验证

get_sha还被用于验证"编辑是否真的生效"。在 Server/src/services/tools/refresh_unity.py 中,verify_edit_by_sha函数(L138-L165)专门负责这件事:

async def verify_edit_by_sha(unity_instance, name, path, pre_sha): if not pre_sha: return False try: verify = await unity_transport.send_with_unity_instance( _legacy_conn.async_send_command_with_retry, unity_instance, "manage_script", {"action": "get_sha", "name": name, "path": path}, ) ... new_sha = (verify.get("data") or {}).get("sha256") return bool(new_sha and new_sha != pre_sha)

其核心思想:编辑前记录旧哈希,编辑后再调get_sha取新哈希,二者不同即说明文件确实被改写apply_text_edits在断连后通过_verify_edit回调复用此逻辑(manage_script.py L329-L332),返回"Edit applied (verified after domain reload)."

5.3 场景三:Unity 域重载 / 连接中断后的恢复校验

脚本编辑通常会触发 Unity 编译与域重载(domain reload),期间 MCP 连接可能短暂中断,导致编辑请求发出后无法立即确认结果。send_mutation封装了完整的恢复模式(refresh_unity.py L92-L135):

  1. retry_on_reload=False发送变更(避免重载时重复执行);
  2. 若收到重载拒绝 → 等待编辑器就绪后重试一次;
  3. 若发送后连接丢失 → 等待编辑器就绪,通过verify_after_disconnect回调(内部即verify_edit_by_shaget_sha)确认变更是否已落盘;
  4. 最终等待编辑器就绪后再返回。

get_sha在这里成为"连接断开后判定编辑成败"的唯一依据,是保证脚本编辑工作流在域重载场景下可靠性的关键一环。

此外,结构化编辑工具script_apply_edits在部分操作中同样依赖get_sha动作(见 script_apply_edits.py L958),而manage_script_capabilities也会在extras中声明"get_sha": True(manage_script.py L649),表明该能力对客户端是公开且可查询的。

六、实战调用示例

由于get_sha是 MCP 工具,实际调用由 AI 客户端通过 MCP 协议完成。以下展示三种uri写法的等价调用语义(返回结构均相同):

// 写法一:Assets 相对路径 { "uri": "Assets/Scripts/PlayerController.cs" } // 写法二:mcpforunity 协议形式 { "uri": "mcpforunity://path/Assets/Scripts/PlayerController.cs" } // 写法三:file 协议形式 { "uri": "file:///path/to/project/Assets/Scripts/PlayerController.cs" }

预期返回:

{ "success": true, "message": "SHA computed for 'Assets/Scripts/PlayerController.cs'.", "data": { "sha256": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", "lengthBytes": 2048 } }

一个推荐的"安全编辑"组合工作流如下:

  1. 调用get_sha获取目标脚本当前指纹sha_before
  2. (可选)先resources/readfind_in_file确认目标行的精确内容;
  3. 调用apply_text_edits,在precondition_sha256字段填入sha_before
  4. 若返回stale_file,说明文件在编辑前已被改动,重新执行步骤 1,基于最新指纹合并修改后重试;
  5. 编辑完成后再次get_sha,若sha256 != sha_before则确认变更已生效。

七、小结与最佳实践

get_sha以极小的接口面积(一个参数、两个返回值)承载了 Unity MCP 脚本编辑体系中最关键的一致性保障职责。使用时的最佳实践总结如下:

  • 编辑前必取指纹:对大文件或多人/多 Agent 协作场景,任何写入类操作前都应先get_sha,并将指纹作为precondition_sha256提交,以获取stale_file保护;
  • 指纹即变更信号:利用"哈希变化 = 文件被改写"这一特性,在域重载或断连后验证编辑是否真正落盘;
  • 区分编码口径:哈希基于 UTF-8 解码后的文本计算,BOM 与行尾符变化会导致哈希变化,不要误判为内容被实质修改;
  • 只读、幂等、可高频调用get_sha对 Unity 项目无任何副作用,可放心在循环、批量场景中反复使用;
  • URI 越界防护:所有脚本路径都被限定在Assets/下,若收到path_outside_assets类错误,请检查uri是否指向了项目目录之外。

如需继续深入,可进一步阅读:get_sha 工具文档、Python 服务端实现、Unity Editor 端实现、集成测试 以及 变更恢复与校验逻辑。

【免费下载链接】unity-mcpUnity MCP acts as a bridge between AI assistants and your Unity Editor. Give your LLM tools to manage assets, control scenes, edit scripts, and automate tasks within Unity.项目地址: https://gitcode.com/GitHub_Trending/un/unity-mcp

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询