dsh-unity-mcp-bridge 0.8.0 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/README.zh.md +2 -1
- package/docs/DETAILS.md +6 -4
- package/lib/index.js +23 -16
- package/package.json +2 -2
- package/skills/unity-mcp-check/SKILL.md +92 -31
- package/skills/unity-mcp-delete/SKILL.md +150 -0
- package/skills/unity-mcp-install/SKILL.md +29 -10
- package/skills/unity-mcp-update/SKILL.md +1 -1
- package/tools/normalize-toolset.ps1 +107 -0
- package/tools/offline-package.mjs +36 -7
- package/vendor/README.md +2 -2
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ A DeepSeek Harness (DSH) plugin that bridges a Unity MCP server (`com.ivanmurzak
|
|
|
9
9
|
- Dormant from boot: no connection, no Unity tools — two hook tools, ~224 tokens.
|
|
10
10
|
- `/unity-mcp-tools` then `unity_mcp_activate` connects and registers `mcp__unity__*`.
|
|
11
11
|
- Enabled commands take effect on the **next** step; `unity_tools_restore` disables them and disconnects.
|
|
12
|
-
- Ships the command catalog for plugin 1.6.0 (19 commands, RQL-first; regenerable from the offline package via `/unity-mcp-update-tools`) and the `/unity-mcp-install` / `/unity-mcp-check` / `/unity-mcp-update` / `/unity-mcp-update-tools` skills.
|
|
12
|
+
- Ships the command catalog for plugin 1.6.0 (19 commands, RQL-first; regenerable from the offline package via `/unity-mcp-update-tools`) and the `/unity-mcp-install` / `/unity-mcp-delete` / `/unity-mcp-check` / `/unity-mcp-update` / `/unity-mcp-update-tools` skills.
|
|
13
13
|
- `resyncAfterTools`: after a management-tool call the bridge re-fetches `tools/list` itself, because DSH's long-lived session never receives `list_changed`.
|
|
14
14
|
|
|
15
15
|
## Install
|
|
@@ -35,6 +35,7 @@ Uninstall: `dsh plugin --profile <profile> remove dsh-unity-mcp-bridge`
|
|
|
35
35
|
| Entry | What it does |
|
|
36
36
|
|---|---|
|
|
37
37
|
| `/unity-mcp-install` | Installs the Unity-side plugin and the MCP Server into the workspace's Unity project |
|
|
38
|
+
| `/unity-mcp-delete` | Uninstalls the Unity-MCP plugin and its extensions, cleaning config and cache leftovers |
|
|
38
39
|
| `/unity-mcp-check` | Checks plugin install, port 20000, DSH bundle loading, minimal tool set and catalog version, then runs the connect test |
|
|
39
40
|
| `/unity-mcp-update` | Checks the official CLI, the local `vendor/` offline package and the Unity plugin for updates; upgrades only after you agree |
|
|
40
41
|
| `/unity-mcp-update-tools` | Refreshes the offline package and regenerates the `/unity-mcp-tools` command and extension indexes |
|
package/README.zh.md
CHANGED
|
@@ -9,7 +9,7 @@ English → [README.md](README.md)
|
|
|
9
9
|
- 开机即休眠:不连接、不注册任何 Unity 工具,只有 2 个钩子工具(约 224 tokens)。
|
|
10
10
|
- `/unity-mcp-tools` → `unity_mcp_activate` 才连接并注册 `mcp__unity__*`。
|
|
11
11
|
- 开启的命令**下一步**才生效;`unity_tools_restore` 关闭它们并断开,回到休眠。
|
|
12
|
-
- 随包发布插件 1.6.0 的命令目录(19 个命令,RQL-first;`/unity-mcp-update-tools` 可按新离线包重生成)与 `/unity-mcp-install`、`/unity-mcp-check`、`/unity-mcp-update`、`/unity-mcp-update-tools`
|
|
12
|
+
- 随包发布插件 1.6.0 的命令目录(19 个命令,RQL-first;`/unity-mcp-update-tools` 可按新离线包重生成)与 `/unity-mcp-install`、`/unity-mcp-delete`、`/unity-mcp-check`、`/unity-mcp-update`、`/unity-mcp-update-tools` 五个技能。
|
|
13
13
|
- `resyncAfterTools`:管理工具调用返回后桥接自己重拉 `tools/list`,不依赖 DSH 长会话收不到的 `list_changed` 通知。
|
|
14
14
|
|
|
15
15
|
## 安装
|
|
@@ -35,6 +35,7 @@ dsh plugin --profile <profile> add git+https://github.com/xiake-1/dsh-unity-mcp-
|
|
|
35
35
|
| 入口 | 作用 |
|
|
36
36
|
|---|---|
|
|
37
37
|
| `/unity-mcp-install` | 给工作区里的 Unity 工程装上 Unity 侧插件与 MCP Server |
|
|
38
|
+
| `/unity-mcp-delete` | 从工程里卸载 Unity-MCP 插件与扩展,并清理配置/缓存残留 |
|
|
38
39
|
| `/unity-mcp-check` | 检查插件安装、端口 20000(不是就改)、DSH bundle 加载、工具集初始化、命令目录版本,再实测连通 |
|
|
39
40
|
| `/unity-mcp-update` | 检查 CLI、本地离线包(`vendor/`)与 Unity 插件的新版本,问过你才更新 |
|
|
40
41
|
| `/unity-mcp-update-tools` | 把离线包刷到最新并重生成 `/unity-mcp-tools` 的命令索引与扩展索引 |
|
package/docs/DETAILS.md
CHANGED
|
@@ -53,8 +53,9 @@ dsh plugin --profile <profile> add file:<path-to-this-checkout>
|
|
|
53
53
|
| `keepEnabledTools` | `[]` | 恢复时额外保留的原始工具名(除 `tool-set-enabled-state` 之外)。 |
|
|
54
54
|
| `registerCatalogSkill` | `true` | 是否注册随包的命令目录技能。 |
|
|
55
55
|
| `skillName` | `unity-mcp-tools` | 技能名 / 斜杠命令。 |
|
|
56
|
-
| `registerHelperSkills` | `true` |
|
|
56
|
+
| `registerHelperSkills` | `true` | 是否注册五个辅助技能。 |
|
|
57
57
|
| `installSkillName` | `unity-mcp-install` | 安装 Unity 侧插件的技能名。 |
|
|
58
|
+
| `deleteSkillName` | `unity-mcp-delete` | 卸载 Unity 侧插件(含扩展与残留)的技能名。 |
|
|
58
59
|
| `checkSkillName` | `unity-mcp-check` | 检查「插件已装 / 端口 20000 / DSH bundle 加载 / 工具集 / 命令目录 / 能连上 DSH」的技能名。 |
|
|
59
60
|
| `updateSkillName` | `unity-mcp-update` | 检查并(经用户同意后)更新官方 CLI、本地 `vendor/` 离线包与 Unity 插件的技能名。 |
|
|
60
61
|
| `updateToolsSkillName` | `unity-mcp-update-tools` | 刷新本地离线包并重生成命令目录与扩展索引的技能名。 |
|
|
@@ -81,11 +82,12 @@ boot ──► DORMANT ──(/unity-mcp-tools ─► unity_mcp_activate)──
|
|
|
81
82
|
| 技能 | 内容 |
|
|
82
83
|
|---|---|
|
|
83
84
|
| `/unity-mcp-install` | 检查 `unity-mcp-cli` 是否在本机(缺则 `npm install -g unity-mcp-cli`),并核对 **CLI / Unity 插件 / MCP Server 三者 lockstep**(老 CLI 的 AI agent 列表会缺项,例如没有 `kilo-code`)。在工作区内按 `ProjectSettings/ProjectVersion.txt` 定位 Unity 工程;`Packages/manifest.json` 里已有 `com.ivanmurzak.unity.mcp` 就报告,否则 `unity-mcp-cli install-plugin <project> --with-server`(同时把 `GameDev-MCP-Server` 二进制放进 CLI 托管目录 `~/.ai-game-dev/server/<rid>/`),再重读 manifest 验证。GitHub 下载失败时走本机系统代理(Clash):端口从注册表读而不是猜,`NODE_USE_ENV_PROXY=1` 必需(Node 24 的全局 `fetch` 否则忽略 `HTTP_PROXY`),`NO_PROXY` 必须让 OpenUPM/npm 直连。最后手段:带断点续传的代理下载 + 对 release `SHA256SUMS` 校验 + `--server-source` 安装。仓库带 `vendor/` 离线包时优先离线:`tools/offline-package.mjs install` 写 Unity 官方支持的 `file:` 本地 tarball 依赖(自动备份 manifest),Server 来自缓存 zip,全新工程不联网即可装好。 |
|
|
84
|
-
| `/unity-mcp-
|
|
85
|
+
| `/unity-mcp-delete` | 与 `/unity-mcp-install` 相反:定位工程 → 列出将删的 `com.ivanmurzak.unity.mcp` 与 `com.ivanmurzak.unity.mcp.*` 扩展 → 备份 `Packages/manifest.json` 与 `UserSettings/AI-Game-Developer-Config.json` → `unity-mcp-cli close --force` 关编辑器 → `unity-mcp-cli remove-plugin` 删基础包 → 手动从 manifest 移除扩展依赖 → 删配置、`Library/mcp-server` 与 `Library/PackageCache/com.ivanmurzak.unity.mcp@*` → 校验 manifest 干净。不动 DSH 桥接插件、CLI 托管 Server、其他工程与 agent 配置。 |
|
|
86
|
+
| `/unity-mcp-check` | 链路体检:**① Unity 工程里是否装了插件**(`Packages/manifest.json` 依赖 + `Library/PackageCache/com.ivanmurzak.unity.mcp@*`);**② 端口 20000 + 模式 Custom**(Unity 侧读项目 `UserSettings/AI-Game-Developer-Config.json` 的 `host` 与 `connectionMode`,不对就 `bootstrap-local --url http://localhost:20000/` 改过来并备份;Cloud 模式会跳过本地服务器自动启动,所以最后**统一重启一次编辑器**(`close --force` → `open` → `wait-for-ready`),只改 JSON 不生效;DSH 侧只看 profile 有没有把 url 覆盖成别的端口);**③ DSH 依赖与 bundle**(`package.json` 依赖 + `dsh.profile.bundles` + bundle patch 里的 `unity-mcp` 行,确认没有第二份同 `serverName` 的 stock `dsh-mcp-client`);**④ 工具集**(`configure --list` 确认只剩 `tool-set-enabled-state`;新工程 `tools: []` 时 `--disable-all-tools` 是空操作,必须用 `--disable-tools <命令目录里的显式名单>` 物化条目、再 `--enable-tools tool-set-enabled-state`,`tools/normalize-toolset.ps1` 封装并校验);**⑤ 命令目录版本自检**(`skills/unity-mcp-tools/SKILL.md` 标题行版本 vs 项目 `PackageCache` 里的包版本,不一致就重生成 + 重启 `dsh web`);**⑥ 连通性**(`unity_mcp_activate` 实测,`ECONNREFUSED` = 没人听、404/405 = 端点不对、401/403 = token 不对)。不做版本检查与升级(→ `/unity-mcp-update`),也不配 AI agent。 |
|
|
85
87
|
| `/unity-mcp-update` | 检查并更新三样东西:**官方 CLI**(npm `unity-mcp-cli`)、**本地离线包**(插件自带 `vendor/`:插件 tarball + `GameDev-MCP-Server` zip + `SHA256SUMS` + `manifest.json`)、**Unity 内插件**(`com.ivanmurzak.unity.mcp`,Server 跟随插件钉住的版本)。`tools/offline-package.mjs check --project <proj> --cli-version (unity-mcp-cli --version)` 一次比「上游最新 / `vendor/` / 项目已装 / 本机 CLI」四层,只读不改。**有更新先问用户**(列出目标版本、会动哪些文件、需要重启什么),用户明确同意才更新:CLI `npm install -g unity-mcp-cli` → 离线包 `fetch --plugin-version <latest>`(只动 `vendor/`,可单独执行,不动工程、不用重启)→ 工程 `install --project`(自动备份 manifest)+ Server 按插件钉住的版本 `install-plugin --with-server --server-source <vendor zip>` → 重生成命令目录并重启 `dsh web`。用户不同意就只给报告与可复制命令。 |
|
|
86
88
|
| `/unity-mcp-update-tools` | 只刷新「知识层」:把 `vendor/` 离线包升到上游最新(`offline-package.mjs fetch`,SHA256 校验 + 断点续传,Server 按新插件钉住的版本),再**依据新离线包**重生成 `skills/unity-mcp-tools/SKILL.md`、`reference/*.md` 与 `reference/extensions.md`。生成器直接读 tarball 解出的包目录(`-PackageRoot`,版本取自包内 `package.json`),不需要 Unity 工程。`extensions.md` 列出插件包 `extensions.catalog.json` 里的 10 个可选扩展及各自会带的命令(扩展命令本身仍不进索引,装完用 `unity-tool-list` 取实时清单)。只改 `<bridge>`,不碰工程内插件;最后提示重启 `dsh web`。 |
|
|
87
89
|
|
|
88
|
-
|
|
90
|
+
五者都遵循官方安装流程 <https://ai-game.dev/engines/unity>。设 `registerHelperSkills: false` 可不发布它们。
|
|
89
91
|
|
|
90
92
|
---
|
|
91
93
|
|
|
@@ -111,7 +113,7 @@ unity-mcp-cli install-plugin <unity-project> --with-server --server-version 9.3.
|
|
|
111
113
|
--server-source "<plugin>\vendor\gamedev-mcp-server-win-x64-9.3.0.zip"
|
|
112
114
|
```
|
|
113
115
|
|
|
114
|
-
`install-plugin` **没有** `--plugin-source`;离线路径是 Unity 官方支持的 `file:` 本地 tarball
|
|
116
|
+
`install-plugin` **没有** `--plugin-source`;离线路径是 Unity 官方支持的 `file:` 本地 tarball 依赖。`offline-package.mjs install` 默认(`--vendor project`)把 tarball 复制到工程根 `UnityMcpOffline/`,写相对 `Packages` 的依赖(`file:../UnityMcpOffline/…`,profile 重建/换机器都不断);`--vendor bridge` 保留旧的插件目录绝对路径。CLI 不会覆盖 `file:` / `git+` / `http:` 依赖(`dist/utils/manifest.js` 的 `nonSemverPrefixes`),所以之后重跑 `install-plugin` 也安全。
|
|
115
117
|
|
|
116
118
|
---
|
|
117
119
|
|
package/lib/index.js
CHANGED
|
@@ -845,13 +845,14 @@ const Config = z.union([z.object({
|
|
|
845
845
|
// model never sees or auto-loads it, and Unity capability never turns on by itself.
|
|
846
846
|
skillModelInvocable: z.boolean().default(false),
|
|
847
847
|
skillUserInvocable: z.boolean().default(true),
|
|
848
|
-
// Shipped helper skills: put the Unity-side plugin in (`/unity-mcp-install`),
|
|
849
|
-
//
|
|
850
|
-
// and refresh the offline package + this command
|
|
851
|
-
// Model-visible by default: they are the on-ramp and the
|
|
852
|
-
// itself.
|
|
848
|
+
// Shipped helper skills: put the Unity-side plugin in (`/unity-mcp-install`), remove it again
|
|
849
|
+
// (`/unity-mcp-delete`), prove the bridge actually reaches it (`/unity-mcp-check`),
|
|
850
|
+
// check/offer updates (`/unity-mcp-update`), and refresh the offline package + this command
|
|
851
|
+
// catalog (`/unity-mcp-update-tools`). Model-visible by default: they are the on-ramp and the
|
|
852
|
+
// doctor, not the Unity capability itself.
|
|
853
853
|
registerHelperSkills: z.boolean().default(true),
|
|
854
854
|
installSkillName: z.string().default("unity-mcp-install"),
|
|
855
|
+
deleteSkillName: z.string().default("unity-mcp-delete"),
|
|
855
856
|
checkSkillName: z.string().default("unity-mcp-check"),
|
|
856
857
|
updateSkillName: z.string().default("unity-mcp-update"),
|
|
857
858
|
updateToolsSkillName: z.string().default("unity-mcp-update-tools"),
|
|
@@ -880,13 +881,14 @@ const Config = z.union([z.object({
|
|
|
880
881
|
// model never sees or auto-loads it, and Unity capability never turns on by itself.
|
|
881
882
|
skillModelInvocable: z.boolean().default(false),
|
|
882
883
|
skillUserInvocable: z.boolean().default(true),
|
|
883
|
-
// Shipped helper skills: put the Unity-side plugin in (`/unity-mcp-install`),
|
|
884
|
-
//
|
|
885
|
-
// and refresh the offline package + this command
|
|
886
|
-
// Model-visible by default: they are the on-ramp and the
|
|
887
|
-
// itself.
|
|
884
|
+
// Shipped helper skills: put the Unity-side plugin in (`/unity-mcp-install`), remove it again
|
|
885
|
+
// (`/unity-mcp-delete`), prove the bridge actually reaches it (`/unity-mcp-check`),
|
|
886
|
+
// check/offer updates (`/unity-mcp-update`), and refresh the offline package + this command
|
|
887
|
+
// catalog (`/unity-mcp-update-tools`). Model-visible by default: they are the on-ramp and the
|
|
888
|
+
// doctor, not the Unity capability itself.
|
|
888
889
|
registerHelperSkills: z.boolean().default(true),
|
|
889
890
|
installSkillName: z.string().default("unity-mcp-install"),
|
|
891
|
+
deleteSkillName: z.string().default("unity-mcp-delete"),
|
|
890
892
|
checkSkillName: z.string().default("unity-mcp-check"),
|
|
891
893
|
updateSkillName: z.string().default("unity-mcp-update"),
|
|
892
894
|
updateToolsSkillName: z.string().default("unity-mcp-update-tools"),
|
|
@@ -1079,12 +1081,12 @@ function registerCatalogSkill(ctx, config) {
|
|
|
1079
1081
|
}
|
|
1080
1082
|
}
|
|
1081
1083
|
/**
|
|
1082
|
-
* The
|
|
1083
|
-
* into the workspace's Unity project; `/unity-mcp-
|
|
1084
|
-
* (plugin installed, port 20000, bridge
|
|
1085
|
-
*
|
|
1086
|
-
* `/unity-mcp-update-tools` refreshes the offline
|
|
1087
|
-
* command catalog (index + extension list) from it.
|
|
1084
|
+
* The five shipped operational helper skills. `/unity-mcp-install` gets the Unity-side plugin
|
|
1085
|
+
* into the workspace's Unity project; `/unity-mcp-delete` removes it (and its extensions) again;
|
|
1086
|
+
* `/unity-mcp-check` only proves the three things it owns (plugin installed, port 20000, bridge
|
|
1087
|
+
* reaches DSH); `/unity-mcp-update` checks the official CLI and the Unity plugin for new versions
|
|
1088
|
+
* and updates them once the user agrees; `/unity-mcp-update-tools` refreshes the offline
|
|
1089
|
+
* `vendor/` package and regenerates this command catalog (index + extension list) from it.
|
|
1088
1090
|
* Same best-effort contract as the catalog skill: no skill registry, no skills, no failure.
|
|
1089
1091
|
*/
|
|
1090
1092
|
const HELPER_SKILLS = [
|
|
@@ -1093,6 +1095,11 @@ const HELPER_SKILLS = [
|
|
|
1093
1095
|
file: "../skills/unity-mcp-install/SKILL.md",
|
|
1094
1096
|
nameKey: "installSkillName"
|
|
1095
1097
|
},
|
|
1098
|
+
{
|
|
1099
|
+
label: "delete",
|
|
1100
|
+
file: "../skills/unity-mcp-delete/SKILL.md",
|
|
1101
|
+
nameKey: "deleteSkillName"
|
|
1102
|
+
},
|
|
1096
1103
|
{
|
|
1097
1104
|
label: "check",
|
|
1098
1105
|
file: "../skills/unity-mcp-check/SKILL.md",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-unity-mcp-bridge",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Opt-in Unity-MCP bridge for DeepSeek Harness: dormant by default, /unity-mcp-tools activates it, ships the 1.6.0 command catalog plus /unity-mcp-install, /unity-mcp-check, /unity-mcp-update and /unity-mcp-update-tools.",
|
|
3
|
+
"version": "0.9.1",
|
|
4
|
+
"description": "Opt-in Unity-MCP bridge for DeepSeek Harness: dormant by default, /unity-mcp-tools activates it, ships the 1.6.0 command catalog plus /unity-mcp-install, /unity-mcp-delete, /unity-mcp-check, /unity-mcp-update and /unity-mcp-update-tools.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"repository": {
|
|
@@ -36,23 +36,32 @@ $projects
|
|
|
36
36
|
$manifest = Join-Path $proj 'Packages\manifest.json'
|
|
37
37
|
Select-String -Path $manifest -Pattern 'com\.ivanmurzak\.unity\.mcp'
|
|
38
38
|
Get-ChildItem (Join-Path $proj 'Library\PackageCache') -Directory -Filter 'com.ivanmurzak.unity.mcp@*' -ErrorAction SilentlyContinue
|
|
39
|
+
|
|
40
|
+
# 若依赖是 file:,解析目标是否存在(Unity 相对 Packages 目录;绝对路径直接用)
|
|
41
|
+
$dep = (Get-Content $manifest -Raw | ConvertFrom-Json).dependencies.'com.ivanmurzak.unity.mcp'
|
|
42
|
+
if ($dep -like 'file:*') {
|
|
43
|
+
$rel = $dep.Substring(5)
|
|
44
|
+
$target = if ($rel -match '^[A-Za-z]:' -or $rel.StartsWith('/')) { $rel } else { Join-Path (Split-Path $manifest) $rel }
|
|
45
|
+
"dep target: $target exists=$(Test-Path $target) points-into-node_modules=$($target -like '*node_modules*')"
|
|
46
|
+
}
|
|
39
47
|
```
|
|
40
48
|
|
|
41
49
|
- manifest 依赖 **和** `Library\PackageCache` 里都有 → 已装(依赖可能是 `file:` 本地包,属正常)。
|
|
42
50
|
- 只有 manifest、没有 `PackageCache` → Unity 还没解析包:让 Unity 打开/重新加载项目一次再看。
|
|
43
51
|
- 两处都没有 → **未装**:报告后交给 `/unity-mcp-install`,不要在这里装。
|
|
52
|
+
- `file:` 目标 **不存在** → Unity 解析必失败:重跑 `/unity-mcp-install`(现在默认 `--vendor project`,会复制 tarball 到工程 `UnityMcpOffline/` 并写相对路径)。
|
|
53
|
+
- `file:` 目标指向 **`node_modules`**(例如旧版写进 profile 的绝对路径)→ 现在能用,但 profile 重建/重装即失效:建议重跑 `/unity-mcp-install` 迁移到工程内相对路径。
|
|
44
54
|
|
|
45
|
-
## 2. 端口是不是 20000
|
|
55
|
+
## 2. 端口是不是 20000、模式是不是 Custom(不对就改)
|
|
46
56
|
|
|
47
|
-
**20000
|
|
57
|
+
**20000 是唯一标准端口;并且必须是 `connectionMode: Custom`** —— Cloud 模式不会启动本地服务器,DSH 永远连不上。
|
|
48
58
|
|
|
49
59
|
### 2.1 读现状
|
|
50
60
|
|
|
51
61
|
```powershell
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
Select-String '"host"|"token"|"connectionMode"'
|
|
62
|
+
$cfgPath = Join-Path $proj 'UserSettings\AI-Game-Developer-Config.json'
|
|
63
|
+
$cfg = Get-Content $cfgPath -Raw | ConvertFrom-Json
|
|
64
|
+
$cfg.host; $cfg.connectionMode; $cfg.keepServerRunning
|
|
56
65
|
|
|
57
66
|
# Unity 侧实际监听的端口(编辑器在跑时才有意义,取最后一条)
|
|
58
67
|
Select-String -Path (Join-Path $proj 'Library\mcp-server\win-x64\logs\server-log.txt') `
|
|
@@ -66,23 +75,28 @@ Select-String -Path (Join-Path $prof 'cordis.patch.yml') -Pattern 'unity-mcp' -C
|
|
|
66
75
|
|
|
67
76
|
`unity-mcp-cli status $proj` 会同时列出 **Config Server**(项目配置里的地址,就是 Unity 插件对外服务的地址)和它自己的 **Local MCP Server** 地址 —— **以 Config Server 为准**,不要被另一个端口带跑。
|
|
68
77
|
|
|
69
|
-
### 2.2 Unity 侧不是 20000 →
|
|
78
|
+
### 2.2 Unity 侧不是 20000 / 仍是 Cloud → 改配置(重启统一在 4.5)
|
|
70
79
|
|
|
71
|
-
|
|
80
|
+
判定:`host` 不是 `http://localhost:20000` **或** `connectionMode` 不是 `Custom`,就执行下面整段。改写的是项目自己的配置,不是 DSH。需要 `unity-mcp-cli`;缺了就按 `/unity-mcp-install` 第 2 步装(`npm install -g unity-mcp-cli`)。
|
|
72
81
|
|
|
73
82
|
```powershell
|
|
74
|
-
|
|
75
|
-
|
|
83
|
+
$cfgPath = Join-Path $proj 'UserSettings\AI-Game-Developer-Config.json'
|
|
84
|
+
$cfg = Get-Content $cfgPath -Raw | ConvertFrom-Json
|
|
85
|
+
$token = $cfg.token
|
|
86
|
+
|
|
87
|
+
# 0) 执行前提醒用户保存未保存的场景/改动(close 会优雅退出,30s 不退则强杀)
|
|
88
|
+
# 先看会改什么,不落盘
|
|
89
|
+
unity-mcp-cli bootstrap-local $proj --url 'http://localhost:20000/' --token $token --dry-run
|
|
76
90
|
|
|
77
|
-
#
|
|
78
|
-
Copy-Item (
|
|
79
|
-
|
|
80
|
-
unity-mcp-cli bootstrap-local $proj --url 'http://localhost:20000/' --token '<项目现有 token>'
|
|
91
|
+
# 备份后落盘(Cloud → Custom,host → 20000)
|
|
92
|
+
Copy-Item $cfgPath "$cfgPath.bak-$(Get-Date -Format yyyyMMdd-HHmmss)"
|
|
93
|
+
unity-mcp-cli bootstrap-local $proj --url 'http://localhost:20000/' --token $token
|
|
81
94
|
```
|
|
82
95
|
|
|
83
|
-
-
|
|
96
|
+
- **这里先不重启**:第 4 步还要写工具集配置;所有文件改完后在 **4.5 统一重启一次编辑器**(直接 `close`/`open`,不再给别的选项)。
|
|
97
|
+
- **为什么必须重启编辑器**:`McpServerManager` 是 `[InitializeOnLoad]`,静态构造函数只在**域重载**时读一次 `connectionMode`;Cloud 模式下 `StartServerIfNeeded()` 打印 `Cloud mode active, skipping local server auto-start` 后直接返回,并且该方法第一行就 `EditorApplication.update -= StartServerIfNeeded`(**只跑一次**)。所以只改 JSON 不会启动服务器。
|
|
98
|
+
- `keepServerRunning` 必须是 `true`:false 时 `StartServerIfNeeded()` 同样会跳过(日志 `KeepServerRunning is false, skipping auto-start`)。
|
|
84
99
|
- token 用**项目里已有的那个**,不要凭空生成;`--dry-run` 的 diff 要念给用户看。
|
|
85
|
-
- **必须让 Unity 重新加载项目**(或重启编辑器),服务器才会在新端口上监听 —— 只改文件不算生效。
|
|
86
100
|
- 用户明确要求保留项目原端口时**停下来问**,不要强行统一。
|
|
87
101
|
|
|
88
102
|
### 2.3 DSH 侧不是 20000 → 改回 20000
|
|
@@ -110,24 +124,46 @@ Select-String -Path (Join-Path $prof 'cordis.patch.yml') -Pattern 'unity-mcp' -C
|
|
|
110
124
|
|
|
111
125
|
## 4. 初始化工具集(参考 `reference/tool.md`)与命令目录版本自检
|
|
112
126
|
|
|
113
|
-
Unity 侧**只留一个**可见工具 `tool-set-enabled-state`(1.6.0
|
|
127
|
+
Unity 侧**只留一个**可见工具 `tool-set-enabled-state`(1.6.0 正式名 `tool-set-visibility`,旧名等价):它既是唯一的"开关",也是桥接 `unity_tools_restore` 的保留集,等于最小常驻状态。
|
|
114
128
|
|
|
115
|
-
> **1.6.0 起是「可见性」而不是禁用**:默认只有 7 个核心工具可见,且管理开关本身默认隐藏。隐藏的工具仍可按名调用,但**不会出现在 `tools/list`**(不进入模型上下文,桥接激活后也注册不到),所以这一步必须把开关显式设为 visible
|
|
129
|
+
> **1.6.0 起是「可见性」而不是禁用**:默认只有 7 个核心工具可见,且管理开关本身默认隐藏。隐藏的工具仍可按名调用,但**不会出现在 `tools/list`**(不进入模型上下文,桥接激活后也注册不到),所以这一步必须把开关显式设为 visible。
|
|
130
|
+
|
|
131
|
+
**新工程不能先跑 `--disable-all-tools`**:CLI 1.6.0 的 `updateFeatures` 只修改配置里**已存在**的条目,`tools: []` 时 `--disable-all-tools` / `--enable-all-tools` 都是空操作;随后 `--enable-tools` 只 push 一条,结果仍是「默认 7 + 开关 = 8 个可见」。必须用**显式名单**把条目物化。
|
|
132
|
+
|
|
133
|
+
先看现状:如果 `--list` 里已经只有开关一条 `[visible]`,且第 2 步判定无需改端口/模式,就不必重写配置,直接去 4.5(20000 已监听则连 4.5 也跳过):
|
|
116
134
|
|
|
117
135
|
```powershell
|
|
118
|
-
|
|
119
|
-
|
|
136
|
+
unity-mcp-cli configure $proj --list | Select-String '\[(visible|enabled)\]'
|
|
137
|
+
```
|
|
120
138
|
|
|
121
|
-
|
|
122
|
-
|
|
139
|
+
```powershell
|
|
140
|
+
$prof = $env:DSH_PROFILE_DIR
|
|
141
|
+
$bridge = Join-Path $prof 'node_modules\dsh-unity-mcp-bridge'
|
|
142
|
+
$refDir = Join-Path $bridge 'skills\unity-mcp-tools\reference'
|
|
143
|
+
$names = Get-ChildItem $refDir -Filter *.md |
|
|
144
|
+
Where-Object Name -ne 'extensions.md' |
|
|
145
|
+
ForEach-Object { Select-String -Path $_.FullName -Pattern '^- `([a-z0-9-]+)`' -AllMatches } |
|
|
146
|
+
ForEach-Object { $_.Matches } | ForEach-Object { $_.Groups[1].Value } |
|
|
147
|
+
Sort-Object -Unique
|
|
148
|
+
|
|
149
|
+
# 1) 显式关掉全部基础工具(缺失条目会被 push 进配置)
|
|
150
|
+
unity-mcp-cli configure $proj --disable-tools ($names -join ',')
|
|
151
|
+
# 2) 只显示开关
|
|
123
152
|
unity-mcp-cli configure $proj --enable-tools tool-set-enabled-state
|
|
124
|
-
|
|
125
|
-
unity-mcp-cli configure $proj --list
|
|
153
|
+
# 3) 复验:Tools 条目数 = 命令数(1.6.0 为 19),[visible] 恰好 1 条
|
|
154
|
+
unity-mcp-cli configure $proj --list
|
|
126
155
|
```
|
|
127
156
|
|
|
128
|
-
-
|
|
129
|
-
|
|
130
|
-
|
|
157
|
+
- 等价脚本(插件目录 `tools\normalize-toolset.ps1`,存在时优先用):它完成上面三步,并断言「条目数 = 目录命令数、只有开关可见」,失败返回非 0:
|
|
158
|
+
|
|
159
|
+
```powershell
|
|
160
|
+
powershell -NoProfile -ExecutionPolicy Bypass -File "<bridge>\tools\normalize-toolset.ps1" -ProjectPath $proj
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
- 以上规避的是 **CLI 1.6.0 的已知行为**:`updateFeatures` 只改已有条目,空配置时 `--disable-all-tools` / `--enable-all-tools` 是空操作。上游修了之后这段仍安全(幂等)。
|
|
164
|
+
- 扩展包的工具不在基础目录里:装完扩展后,激活连接再跑一次 `unity_tools_restore`,把剩余可见的扩展工具一并隐藏(开关保留)。
|
|
165
|
+
- 万一被全部隐藏:单独跑 `unity-mcp-cli configure $proj --enable-tools tool-set-enabled-state` 即可救回。
|
|
166
|
+
- 复验结果应当**只有** `tool-set-enabled-state` 是 `[visible]`(旧版 CLI 显示 `[enabled]`),其余全是 `[hidden]` / `[disabled]`。注册总数随插件版本变:**1.6.0 = 19 个(默认 7 可见)**,1.2.0 = 18 个(默认 7),0.9x = 73。
|
|
131
167
|
- 这一步直接读写项目配置,**不需要 Unity 在运行**。
|
|
132
168
|
- prompts / resources 默认全隐藏,保持关闭即可;除非用户明确要求,不要打开。
|
|
133
169
|
- 需要别的命令时,交给 `/unity-mcp-tools` 的按需流程(显示 → 用 → `unity_tools_restore` 收回),不要在这里预先全开:每个可见命令的 schema 都会进入之后每一次模型请求。
|
|
@@ -145,6 +181,27 @@ unity-mcp-cli configure $proj --list | Select-String '\[(visible|enabled)\]' #
|
|
|
145
181
|
|
|
146
182
|
实测代价:目录停在 0.91.0 而项目已是 1.2.0 时,18 个分类文件里有 13 个写的是**已删除**的命令(`gameobject-*`、`assets-*`、`scene-*`、`profiler-*`、`console-*`、`editor-application-*`、`editor-selection-*`、`object-*`、`reflection-method-*`、`type-get-json-schema`),1.2.0 由 `rql-query` / `rql-mutate` / `rql-schema` 承担 —— 照旧目录调用必然 `tool not found`。
|
|
147
183
|
|
|
184
|
+
### 4.5 重启编辑器让配置生效(直接重启,只一次)
|
|
185
|
+
|
|
186
|
+
**只要第 2 步改过 host/connectionMode,或第 4 步写过工具集配置**,就在这里统一重启一次编辑器(不要在 2.2 里先重启一遍):
|
|
187
|
+
|
|
188
|
+
```powershell
|
|
189
|
+
# 执行前必须让用户保存未保存的场景/改动;30s 内没优雅退出会强杀
|
|
190
|
+
unity-mcp-cli close $proj --timeout 30 --force
|
|
191
|
+
unity-mcp-cli open $proj
|
|
192
|
+
unity-mcp-cli wait-for-ready $proj --timeout 300000
|
|
193
|
+
|
|
194
|
+
# 复验
|
|
195
|
+
unity-mcp-cli configure $proj --list | Select-String 'Host'
|
|
196
|
+
Select-String -Path (Join-Path $proj 'Library\mcp-server\win-x64\logs\server-log.txt') `
|
|
197
|
+
-Pattern 'Start listening on port' | Select-Object -Last 1
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
- 为什么必须重启:`McpServerManager` 是 `[InitializeOnLoad]`,只在域重载时读 `connectionMode` 与工具集配置;Cloud 下 `StartServerIfNeeded()` 打印 `Cloud mode active, skipping local server auto-start` 后返回,且只跑一次。
|
|
201
|
+
- Unity 本来没开时 `close` 无操作,`open` 会直接启动;首次解析包 + 起服务器可能超过 2 分钟,`wait-for-ready` 给 300s。
|
|
202
|
+
- 什么都没改(第 2 步无需改 host/模式、`--list` 本就只有开关一条可见)且 20000 已在监听 → 跳过本节,直接去第 5 步。
|
|
203
|
+
- 如果 `Host` 不是 20000 或日志没有 20000 监听 → 回第 2 步核对配置与模式,不要继续第 5 步。
|
|
204
|
+
|
|
148
205
|
## 5. 是否连接到 DSH(实测)
|
|
149
206
|
|
|
150
207
|
```powershell
|
|
@@ -165,7 +222,7 @@ unity-mcp-cli wait-for-ready $proj # 最长 120s
|
|
|
165
222
|
| 检查项 | 结果 |
|
|
166
223
|
|---|---|
|
|
167
224
|
| Unity 插件 `com.ivanmurzak.unity.mcp` | 已装(manifest 依赖 = …,PackageCache = …)/ 未装 → 交由 `/unity-mcp-install` |
|
|
168
|
-
| Unity 侧端口 | 本来就是 20000 / 原 24009 → 已改 20000
|
|
225
|
+
| Unity 侧端口 | 本来就是 20000 + Custom / 原 24009 或 Cloud → 已改 20000 + Custom,并已重启编辑器(附备份路径) / 未改(用户要求保留) |
|
|
169
226
|
| DSH 侧 url | `http://localhost:20000/mcp`(一致 / 已改 / 无覆盖行) |
|
|
170
227
|
| 插件加载 | bundle 已插入 `unity-mcp` 行(0.5.0)/ bundle 为空 → 已补覆盖行 / `dsh.profile.bundles` 缺该包 |
|
|
171
228
|
| 命令目录(`/unity-mcp-tools`) | 按 1.6.0 生成,与项目一致 / 按 0.91.0 生成 → 已重生成(需重启 `dsh web`) |
|
|
@@ -177,12 +234,16 @@ unity-mcp-cli wait-for-ready $proj # 最长 120s
|
|
|
177
234
|
|
|
178
235
|
| 现象 | 原因 | 处理 |
|
|
179
236
|
|---|---|---|
|
|
180
|
-
| 20000 没人听 | Unity
|
|
181
|
-
|
|
|
237
|
+
| 20000 没人听 | Unity 没开、服务器没启动、或仍是 Cloud 模式 | 先看 `connectionMode`;Cloud 就 `bootstrap-local` 后**重启编辑器**(`close --force` → `open`) |
|
|
238
|
+
| 日志有 `Cloud mode active, skipping local server auto-start` | 改完配置没触发域重载;`StartServerIfNeeded` 只跑一次 | `bootstrap-local` 后重启编辑器(`unity-mcp-cli close $proj --timeout 30 --force` → `open $proj` → `wait-for-ready`) |
|
|
239
|
+
| 改了 host / 模式但服务器没起 | 插件静态构造只在域重载时读 `connectionMode`,只改 JSON 不生效 | 同上,重启编辑器后再看 `Start listening on port 20000` |
|
|
240
|
+
| 全新工程的可见工具是 8 个(默认 7 + 开关) | `tools: []` 时 `--disable-all-tools` 是空操作(CLI 1.6.0 只改已有条目) | 按第 4 步用 `--disable-tools <显式名单>` 物化条目,再 `--enable-tools tool-set-enabled-state` |
|
|
182
241
|
| manifest 里有包但连不上 | Unity 还没解析包 | 用 Unity 打开项目一次 |
|
|
242
|
+
| `file:` 依赖找不到目标 | tarball 被删/工程从别的机器拷来但没带 `UnityMcpOffline/`,或旧版指向了已重建的 profile 路径 | 重跑 `/unity-mcp-install`(默认 `--vendor project`,复制 tarball 到工程并写相对路径) |
|
|
243
|
+
| `file:` 路径里含 `node_modules` | 旧版 `install` 写的是插件目录绝对路径,生命周期绑在 profile 上 | 能连就先不动;建议重跑 `/unity-mcp-install` 迁移到 `UnityMcpOffline/` 相对路径 |
|
|
183
244
|
| 404 / 405 | 端点路径不对(`/mcp` ↔ `/p/<token>`) | 以 `http://localhost:20000/mcp` 为准;需要 token 路径时再补 |
|
|
184
245
|
| 401 / 403 | token 缺失或过期 | 用项目配置里的 token;或 `bootstrap-local --url ... --token ...` 重新固定 |
|
|
185
|
-
| 工具全被隐藏(含 `tool-set-enabled-state`) |
|
|
246
|
+
| 工具全被隐藏(含 `tool-set-enabled-state`) | 被 `--disable-all-tools` 或历史上写死的配置隐藏 | 单独跑一条 `unity-mcp-cli configure $proj --enable-tools tool-set-enabled-state` 救回 |
|
|
186
247
|
| `mcp__unity__gameobject-find` 之类报 `tool not found` | 命令目录是按旧插件版本生成的(1.2.0 已移除 `gameobject-*` / `assets-*` / `scene-*` / `profiler-*` 等) | 按第 4 步做目录版本自检并重生成 + 重启 `dsh web`;读写 Unity 内容改走 `rql-query` / `rql-mutate` / `rql-schema` |
|
|
187
248
|
| 工具名冲突 / 行为异常 | stock `dsh-mcp-client` 与桥接插件同时跑同一 `serverName` | 只留一个 |
|
|
188
249
|
| 插件页显示「已安装,未生效」 | bundle 没有插入插件行(0.4.0 及以前 bundle patch 是 `[]`),或 profile 的 `dsh.profile.bundles` 里没有这个包 | 升级到 ≥0.5.0(行随 bundle 提供);确认 `dsh.profile.bundles` 含 `dsh-unity-mcp-bridge`;重启 |
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: unity-mcp-delete
|
|
3
|
+
description: 从当前工作区的 Unity 工程里**卸载** Unity-MCP 插件(com.ivanmurzak.unity.mcp)及其扩展包:先备份 Packages/manifest.json 与项目配置,关闭正在运行的编辑器,用 unity-mcp-cli remove-plugin 删掉基础包,再手动移除 com.ivanmurzak.unity.mcp.* 扩展依赖,清理 UserSettings 配置与 Library 下的 mcp-server / PackageCache 残留,最后校验 manifest 已干净。与 /unity-mcp-install 相反;只删 Unity 工程里的插件,不动 DSH 桥接插件、不动 CLI 全托管 Server、不动其他工程。
|
|
4
|
+
whenToUse: 用户要求「卸载 / 删除 Unity 项目里的 Unity-MCP 插件」「把 AI Game Developer 从工程里移除」「删掉后重装」时。只想删某个扩展、或只想停用连接,不要用本技能。
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 卸载 Unity 工程里的 Unity-MCP 插件
|
|
8
|
+
|
|
9
|
+
本技能只处理**当前工作区**里的一个 Unity 工程;删除范围:
|
|
10
|
+
|
|
11
|
+
| 删除 | 位置 |
|
|
12
|
+
|---|---|
|
|
13
|
+
| 基础包 `com.ivanmurzak.unity.mcp` | `Packages\manifest.json` 的 `dependencies`(`unity-mcp-cli remove-plugin` 负责) |
|
|
14
|
+
| 扩展包 `com.ivanmurzak.unity.mcp.*`(Animation / Cinemachine / InputSystem / Navigation / ParticleSystem / ProBuilder / Splines / Terrain / Tilemap / Timeline) | 同一个 `dependencies`(CLI 不删,本技能手动删;它们依赖基础包,必须一起走) |
|
|
15
|
+
| 插件配置 | `UserSettings\AI-Game-Developer-Config.json` |
|
|
16
|
+
| 运行时残留 | `Library\mcp-server\`、`Library\PackageCache\com.ivanmurzak.unity.mcp@*` |
|
|
17
|
+
|
|
18
|
+
**不动**:DSH 的 `dsh-unity-mcp-bridge` 插件、CLI 的全托管 Server `~\.ai-game-dev\server\<rid>\`(其他工程可能还在用)、别的 Unity 工程、`.kilocode\mcp.json` / `.agents\skills`(这些是 agent 配置,不是插件安装产物)。
|
|
19
|
+
|
|
20
|
+
## 1. 定位 Unity 项目
|
|
21
|
+
|
|
22
|
+
```powershell
|
|
23
|
+
$ws = (Get-Location).Path
|
|
24
|
+
$projects = Get-ChildItem $ws -Directory -Recurse -Depth 4 -ErrorAction SilentlyContinue |
|
|
25
|
+
Where-Object { Test-Path (Join-Path $_.FullName 'ProjectSettings\ProjectVersion.txt') } |
|
|
26
|
+
Where-Object { $_.FullName -notmatch '\\(Library|Temp|obj|node_modules|\.git)\\' } |
|
|
27
|
+
Select-Object -ExpandProperty FullName -Unique
|
|
28
|
+
$projects
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
恰好 1 个 → `$proj = $projects[0]`;0 个或多个 → 停下来问用户,不要替他挑。
|
|
32
|
+
|
|
33
|
+
## 2. 判断装了什么(只读)
|
|
34
|
+
|
|
35
|
+
```powershell
|
|
36
|
+
$manifestPath = Join-Path $proj 'Packages\manifest.json'
|
|
37
|
+
$manifest = Get-Content $manifestPath -Raw | ConvertFrom-Json
|
|
38
|
+
$manifest.dependencies.PSObject.Properties |
|
|
39
|
+
Where-Object { $_.Name -like 'com.ivanmurzak.unity.mcp*' } |
|
|
40
|
+
Select-Object Name, Value
|
|
41
|
+
|
|
42
|
+
Get-ChildItem (Join-Path $proj 'Library\PackageCache') -Directory -Filter 'com.ivanmurzak.unity.mcp*' -ErrorAction SilentlyContinue |
|
|
43
|
+
Select-Object -ExpandProperty Name
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
- 两处都没有 → 报告「未安装,无需卸载」,**结束**。
|
|
47
|
+
- 有基础包 + 扩展包 → 都要删;把将删除的包名列表念给用户。
|
|
48
|
+
- 只有 `PackageCache` 残留、manifest 已无依赖 → 跳过 CLI,只清理残留(第 5 步)。
|
|
49
|
+
|
|
50
|
+
## 3. 备份 + 关闭编辑器
|
|
51
|
+
|
|
52
|
+
```powershell
|
|
53
|
+
$stamp = Get-Date -Format yyyyMMdd-HHmmss
|
|
54
|
+
Copy-Item $manifestPath "$manifestPath.bak-$stamp"
|
|
55
|
+
$cfgPath = Join-Path $proj 'UserSettings\AI-Game-Developer-Config.json'
|
|
56
|
+
if (Test-Path $cfgPath) { Copy-Item $cfgPath "$cfgPath.bak-$stamp" }
|
|
57
|
+
|
|
58
|
+
# manifest 若带 UTF-8 BOM,CLI 的 JSON.parse 会报 "Unexpected token ''" —— 先去掉 BOM(上面已备份)
|
|
59
|
+
$bytes = [System.IO.File]::ReadAllBytes($manifestPath)
|
|
60
|
+
if ($bytes.Length -ge 3 -and $bytes[0] -eq 0xEF -and $bytes[1] -eq 0xBB -and $bytes[2] -eq 0xBF) {
|
|
61
|
+
$text = [System.Text.Encoding]::UTF8.GetString($bytes, 3, $bytes.Length - 3)
|
|
62
|
+
[System.IO.File]::WriteAllText($manifestPath, $text, (New-Object System.Text.UTF8Encoding($false)))
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
# 正在运行就先存盘,再优雅退出;30s 不退则强杀
|
|
66
|
+
unity-mcp-cli close $proj --timeout 30 --force
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
- `close` 会终止该工程的编辑器实例;**执行前必须提醒用户保存未保存的场景/改动**。
|
|
70
|
+
- 编辑器没在运行 → `close` 无操作,忽略报错继续。
|
|
71
|
+
- CLI 读 manifest 不做 BOM 容错;Unity 自己写出的文件通常无 BOM,但手改过的可能是 BOM UTF-8,所以上面先剥掉。
|
|
72
|
+
|
|
73
|
+
## 4. 删除基础包 + 扩展包
|
|
74
|
+
|
|
75
|
+
```powershell
|
|
76
|
+
# 4.1 基础包(官方命令,只删 com.ivanmurzak.unity.mcp)
|
|
77
|
+
unity-mcp-cli remove-plugin $proj
|
|
78
|
+
|
|
79
|
+
# 4.2 扩展包(CLI 不删,手动从 manifest 移除;文件已在第 3 步备份)
|
|
80
|
+
$manifest = Get-Content $manifestPath -Raw | ConvertFrom-Json
|
|
81
|
+
$extIds = @($manifest.dependencies.PSObject.Properties.Name |
|
|
82
|
+
Where-Object { $_ -like 'com.ivanmurzak.unity.mcp.*' })
|
|
83
|
+
foreach ($id in $extIds) { $manifest.dependencies.PSObject.Properties.Remove($id) }
|
|
84
|
+
if ($extIds.Count -gt 0) {
|
|
85
|
+
$json = $manifest | ConvertTo-Json -Depth 100
|
|
86
|
+
[System.IO.File]::WriteAllText($manifestPath, $json + "`n", (New-Object System.Text.UTF8Encoding($false)))
|
|
87
|
+
"removed extensions: $($extIds -join ', ')"
|
|
88
|
+
} else { 'no extension packages' }
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
- `remove-plugin` 是幂等的:已经删过会提示 not installed,不要当失败。
|
|
92
|
+
- 只删 `com.ivanmurzak.unity.mcp` 与 `com.ivanmurzak.unity.mcp.*`,**不要**动 `com.unity.*`、`extensions.unity.*` 或其它依赖。
|
|
93
|
+
- `scopedRegistries` 里的 OpenUPM 条目保留(其他包可能还在用),不清理。
|
|
94
|
+
|
|
95
|
+
## 5. 清理项目内残留
|
|
96
|
+
|
|
97
|
+
```powershell
|
|
98
|
+
# 插件配置(token/host/tool 列表都在这里;重新安装时会重建)
|
|
99
|
+
if (Test-Path $cfgPath) { Remove-Item $cfgPath -Force }
|
|
100
|
+
|
|
101
|
+
# 插件下载的 Server 二进制与日志(Library 是生成目录,下次打开会重建)
|
|
102
|
+
$serverDir = Join-Path $proj 'Library\mcp-server'
|
|
103
|
+
if (Test-Path $serverDir) { Remove-Item $serverDir -Recurse -Force }
|
|
104
|
+
|
|
105
|
+
# 新版 install 默认复制进工程的离线 tarball(UnityMcpOffline/,相对依赖用)
|
|
106
|
+
$offlineDir = Join-Path $proj 'UnityMcpOffline'
|
|
107
|
+
if (Test-Path $offlineDir) { Remove-Item $offlineDir -Recurse -Force }
|
|
108
|
+
|
|
109
|
+
# 已解析的包缓存(Unity 下次打开也会自动清;编辑器已关闭时手动删更干净)
|
|
110
|
+
Get-ChildItem (Join-Path $proj 'Library\PackageCache') -Directory -Filter 'com.ivanmurzak.unity.mcp*' -ErrorAction SilentlyContinue |
|
|
111
|
+
ForEach-Object { Remove-Item $_.FullName -Recurse -Force }
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
- `Library\PackageCache` 其他包不要动。
|
|
115
|
+
- `Packages\packages-lock.json` 里可能还留着条目,Unity 重新打开工程 sourcing manifest 后会自行修正;不手改。
|
|
116
|
+
|
|
117
|
+
## 6. 校验
|
|
118
|
+
|
|
119
|
+
```powershell
|
|
120
|
+
# manifest 里不应再有该插件/扩展
|
|
121
|
+
Select-String -Path $manifestPath -Pattern 'com\.ivanmurzak\.unity\.mcp'
|
|
122
|
+
# 残留目录
|
|
123
|
+
Get-ChildItem (Join-Path $proj 'Library\PackageCache') -Directory -Filter 'com.ivanmurzak.unity.mcp*' -ErrorAction SilentlyContinue
|
|
124
|
+
# 配置与 server 目录
|
|
125
|
+
Test-Path $cfgPath
|
|
126
|
+
Test-Path (Join-Path $proj 'Library\mcp-server')
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
`Select-String` 无输出 + 目录都不存在 = 卸载完成。之后让用户用 Unity 打开工程一次,让包依赖重新解析(`Library/PackageCache` 会按新 manifest 重建)。
|
|
130
|
+
|
|
131
|
+
## 7. 报告格式
|
|
132
|
+
|
|
133
|
+
| 项 | 结果 |
|
|
134
|
+
|---|---|
|
|
135
|
+
| 定位到的工程 | `$proj` |
|
|
136
|
+
| 已删除的包 | `com.ivanmurzak.unity.mcp` + 扩展列表(或「未安装,无需卸载」) |
|
|
137
|
+
| 清理的配置/残留 | `UserSettings\AI-Game-Developer-Config.json`、`Library\mcp-server`、`UnityMcpOffline\`、PackageCache 条目 |
|
|
138
|
+
| 备份 | `Packages\manifest.json.bak-<stamp>`、`AI-Game-Developer-Config.json.bak-<stamp>` |
|
|
139
|
+
| 未动的 | DSH 桥接插件、CLI 托管 Server、其他工程、`.kilocode`/agent 配置 |
|
|
140
|
+
| 下一步 | 用 Unity 打开工程让其重新解析;之后 `/unity-mcp-check` 应报告「未装」 |
|
|
141
|
+
|
|
142
|
+
## 常见故障
|
|
143
|
+
|
|
144
|
+
| 现象 | 原因 | 处理 |
|
|
145
|
+
|---|---|---|
|
|
146
|
+
| `remove-plugin` 报 not installed / 未删除 | manifest 里本来就没基础包(可能只剩扩展或文件缓存) | 正常继续第 4.2/5 步;把扩展与残留清掉 |
|
|
147
|
+
| 删依赖后 Unity 仍能连上 20000 | 编辑器还开着旧域、或 `Library\mcp-server` 进程还在 | 先 `close --force` 再删;必要时任务管理器结束 `gamedev-mcp-server` |
|
|
148
|
+
| 打开 Unity 后包又回来了 | 工程里还有 `Packages\packages-lock.json` 或别的 manifest 引用了该包;或误删了备份里的内容 | 检查 manifest 与 lock 文件;确认没有其他依赖指向 `com.ivanmurzak.unity.mcp` |
|
|
149
|
+
| 重装后配置又出现旧 host/token | 第 5 步没删 `UserSettings\AI-Game-Developer-Config.json` | 删除该文件后重装;或重装后手工改 Custom/20000 |
|
|
150
|
+
| `/unity-mcp-check` 仍显示已装 | 检查的是别的工程/`PackageCache` 还没重建 | 确认 `$proj` 路径;用 Unity 打开一次 |
|
|
@@ -165,28 +165,33 @@ ERROR: fetch failed
|
|
|
165
165
|
|
|
166
166
|
这是**兜底而非首选**:绕过了官方 zip 的哈希校验,必须向用户说明来源与版本。
|
|
167
167
|
|
|
168
|
-
### 5.2 离线本地包(插件自带 `vendor
|
|
168
|
+
### 5.2 离线本地包(插件自带 `vendor/`;现在默认写工程内相对路径)
|
|
169
169
|
|
|
170
170
|
插件目录下的 `vendor/` 是一份离线包缓存:`vendor/README.md`(版本/大小/SHA256/来源)+ `vendor/manifest.json`(机器可读)。当前内容:插件 `com.ivanmurzak.unity.mcp@1.6.0`(960,306 B,SHA256 `13fd2579…c5178b`)与 Server `9.3.0` win-x64(44,088,479 B,SHA256 `53a79b42…e01dc2f`,已对官方 `SHA256SUMS` 校验通过)。
|
|
171
171
|
|
|
172
172
|
```powershell
|
|
173
|
-
# 1)
|
|
173
|
+
# 1) 先看会复制什么、写什么依赖(不落盘)
|
|
174
|
+
node "<bridge>\tools\offline-package.mjs" install --project $proj --dry-run
|
|
175
|
+
|
|
176
|
+
# 2) 复制 tarball 到工程根并写相对依赖(自动备份 manifest)
|
|
174
177
|
node "<bridge>\tools\offline-package.mjs" install --project $proj
|
|
175
178
|
|
|
176
|
-
#
|
|
179
|
+
# 3) Server 从 vendor 的 zip 装进 CLI 托管目录(不联网)
|
|
177
180
|
unity-mcp-cli install-plugin $proj --with-server --server-version 9.3.0 `
|
|
178
181
|
--server-source "<bridge>\vendor\gamedev-mcp-server-win-x64-9.3.0.zip"
|
|
179
182
|
```
|
|
180
183
|
|
|
181
184
|
要点(都实测过):
|
|
182
185
|
|
|
183
|
-
-
|
|
184
|
-
-
|
|
186
|
+
- **默认 `--vendor project`**:把 tarball 复制到 `<工程>\UnityMcpOffline\`,manifest 写成 `"com.ivanmurzak.unity.mcp": "file:../UnityMcpOffline/com.ivanmurzak.unity.mcp-1.6.0.tgz"`。Unity 的 `file:` 相对路径**相对 `Packages` 文件夹**解析([Unity 手册](https://docs.unity3d.com/2022.3/Documentation/Manual/upm-localpath.html)),所以 `../UnityMcpOffline/…` 就是工程根下的目录。这样 **profile 重建/删除、工程拷到别的机器都不会断**;tarball 约 1 MB,可以一起提交到版本库。
|
|
187
|
+
- 旧工程(依赖指向 `node_modules\...\vendor\` 绝对路径)**重跑一次 `install` 就会迁移**到工程内相对路径;manifest 先备份。想保留旧行为用 `--vendor bridge`(绝对路径,profile 重建后需要重跑 install)。
|
|
188
|
+
- `install` 检测工程内 tarball 的 SHA256;已存在且一致就跳过复制,不一致(换了插件版本)会覆盖。
|
|
189
|
+
- `install-plugin` **没有** `--plugin-source`。离线靠的就是上面的本地 tarball 依赖;之后重跑 `unity-mcp-cli install-plugin` **不会**把它改回版本号(`dist/utils/manifest.js` 的 `nonSemverPrefixes` 跳过 `file:` / `git+` / `http:`)。
|
|
185
190
|
- 目标版本从哪来:`vendor/manifest.json` 的 `plugin.pinnedServerVersion` —— 脚本从 tarball 里的 `Editor/Scripts/McpServerManager.cs` 读 `ServerVersion` 常量(1.6.0 钉住 **9.3.0**)。别拿「Server 上游最新」当目标。
|
|
186
191
|
- 完全离线还剩一个小尾巴:插件的传递依赖 `extensions.unity.playerprefsex` 要 Unity 的 Package Manager 从 OpenUPM 解析(实测 OpenUPM 直连可达)。彻底断网的机器要么预先让 Unity 解析过一次,要么接受首次解析需要联网。
|
|
187
|
-
- `file:` 依赖**不会自动升级**:升级 = 重新 `fetch`
|
|
192
|
+
- `file:` 依赖**不会自动升级**:升级 = 重新 `fetch` 刷新插件目录的离线包 + 重新 `install` 进工程(见 `/unity-mcp-update` 的 4.2 / 4.3),换版本后照例重生成命令目录。
|
|
188
193
|
- 换平台:`node "<bridge>\tools\offline-package.mjs" fetch --rid osx-arm64` 先补对应 zip,再 `--server-source` 指过去。
|
|
189
|
-
- **profile 里的安装副本常常缺 zip**:40 MB 级二进制不进 git/npm,`dsh plugin add
|
|
194
|
+
- **profile 里的安装副本常常缺 zip**:40 MB 级二进制不进 git/npm,`dsh plugin add …` 装出来的 `node_modules\dsh-unity-mcp-bridge\vendor\` 通常只有 tarball + manifest。`install` 会明确告诉你缺不缺;缺就先 `fetch`(代理优先)补齐再跑 `--server-source`,不要拿旧版本的 zip 顶。
|
|
190
195
|
|
|
191
196
|
## 6. 验证与下一步
|
|
192
197
|
|
|
@@ -202,12 +207,26 @@ Push-Location $proj; & "$env:USERPROFILE\.ai-game-dev\server\win-x64\gamedev-mcp
|
|
|
202
207
|
|
|
203
208
|
> ⚠️ 别对 Server 二进制跑裸 `--help`:`gamedev-mcp-server.exe --help` **不是打印帮助,而是直接启动 server**(默认 8080,会一直占着)。只有 `configure --help` 这类子命令帮助是安全的;误启动就按端口找到 PID 杀掉。
|
|
204
209
|
|
|
205
|
-
|
|
210
|
+
然后告诉用户:
|
|
206
211
|
|
|
207
212
|
1. 用 Unity 打开该项目(`unity-mcp-cli open $proj`)—— 包在编辑器里解析,`Library/PackageCache/` 才会出现。
|
|
208
213
|
2. 在 Unity 里打开 **Window → AI Game Developer**,确认连接方式(自建本地端点选 Custom,或走 Cloud),并把顶部的 **AI agent** 选成要用的那个(如 **Kilo Code**)—— Skills 的自动生成只认这个下拉框,CLI 的 `setup-skills` 不支持 `kilo-code`。
|
|
209
|
-
3.
|
|
210
|
-
|
|
214
|
+
3. **若客户端是 DSH(本地 20000):新工程默认是 Cloud,而 Cloud 模式不会启动本地服务器。** 先让用户保存工程,再改配置并**直接重启编辑器**:
|
|
215
|
+
|
|
216
|
+
```powershell
|
|
217
|
+
$cfgPath = Join-Path $proj 'UserSettings\AI-Game-Developer-Config.json'
|
|
218
|
+
$token = (Get-Content $cfgPath -Raw | ConvertFrom-Json).token
|
|
219
|
+
Copy-Item $cfgPath "$cfgPath.bak-$(Get-Date -Format yyyyMMdd-HHmmss)"
|
|
220
|
+
unity-mcp-cli bootstrap-local $proj --url 'http://localhost:20000/' --token $token
|
|
221
|
+
unity-mcp-cli close $proj --timeout 30 --force
|
|
222
|
+
unity-mcp-cli open $proj
|
|
223
|
+
unity-mcp-cli wait-for-ready $proj --timeout 300000
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
- 插件 `McpServerManager` 是 `[InitializeOnLoad]`,**只在域重载时读一次 `connectionMode`**;Cloud 下 `StartServerIfNeeded()` 打印 `Cloud mode active, skipping local server auto-start` 后直接返回,而且该方法只跑一次 —— 只改 JSON 不会启动服务器,必须重启编辑器。
|
|
227
|
+
- 确认配置里 `keepServerRunning: true`;false 时自动启动同样会跳过。
|
|
228
|
+
4. 回到 DSH 跑 `/unity-mcp-check`:它会检查插件是否装上、端口是否 **20000** 且模式是 Custom、DSH 是否加载了插件行、工具集是否只剩 `tool-set-enabled-state`、命令目录是否与插件版本一致,最后实测连通。要查版本或升级用 `/unity-mcp-update`(检查后先问用户,同意才更新)。
|
|
229
|
+
5. **本次装了或升级了插件版本 → 必须重生成桥接自带的命令目录**,否则 `/unity-mcp-tools` 会教模型去开不存在的命令(顺带刷新 `reference/extensions.md` 的可选扩展清单;也可以直接跑 `/unity-mcp-update-tools`)。命令名随插件版本变(实测 0.9x = 73 个;1.2.0 = 18 个;**分水岭是 1.0.0「RQL replaces the data tools」**,`assets-*` / `gameobject-*` / `scene-*` / `profiler-*` 等已被 `rql-query` / `rql-mutate` / `rql-schema` 取代):
|
|
211
230
|
|
|
212
231
|
```powershell
|
|
213
232
|
# 桥接目录是按哪个版本生成的(SKILL.md 标题行)
|
|
@@ -139,7 +139,7 @@ node "<bridge>\tools\offline-package.mjs" fetch --plugin-version <latest>
|
|
|
139
139
|
# 先看会改什么,不落盘
|
|
140
140
|
node "<bridge>\tools\offline-package.mjs" install --project $proj --dry-run
|
|
141
141
|
|
|
142
|
-
# 落盘(自动备份 Packages\manifest.json
|
|
142
|
+
# 落盘(自动备份 Packages\manifest.json;默认把 tarball 复制到工程 UnityMcpOffline/ 并写相对依赖)
|
|
143
143
|
node "<bridge>\tools\offline-package.mjs" install --project $proj
|
|
144
144
|
|
|
145
145
|
# Server 从 vendor 的 zip 装进 CLI 托管目录(不联网)
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#requires -Version 5.1
|
|
2
|
+
<#
|
|
3
|
+
.SYNOPSIS
|
|
4
|
+
把 Unity 工程的 MCP 工具集收敛成「只有管理开关可见」。
|
|
5
|
+
|
|
6
|
+
.DESCRIPTION
|
|
7
|
+
unity-mcp-cli 1.6.0 的 updateFeatures 只修改配置里**已存在**的条目:
|
|
8
|
+
新工程的 tools 数组为空时,--disable-all-tools 是空操作,随后 --enable-tools
|
|
9
|
+
只会 push 一条,可见工具仍会是「默认 7 个 + 开关 = 8 个」。
|
|
10
|
+
|
|
11
|
+
本脚本改用显式名单(读 /unity-mcp-tools 的命令目录)把全部基础工具物化,
|
|
12
|
+
再只显示开关,最后用 `configure --list` 校验:
|
|
13
|
+
- Tools 条目数 >= 命令目录里的工具数
|
|
14
|
+
- [visible] 恰好 1 条,且就是管理开关
|
|
15
|
+
|
|
16
|
+
.PARAMETER ProjectPath
|
|
17
|
+
Unity 工程路径。
|
|
18
|
+
|
|
19
|
+
.PARAMETER BridgeRoot
|
|
20
|
+
插件目录(默认取本脚本上一级)。已安装副本在
|
|
21
|
+
%DSH_PROFILE_DIR%\node_modules\dsh-unity-mcp-bridge。
|
|
22
|
+
|
|
23
|
+
.PARAMETER Cli
|
|
24
|
+
unity-mcp-cli 的命令名或绝对路径。
|
|
25
|
+
|
|
26
|
+
.PARAMETER SwitchTool
|
|
27
|
+
要保留为可见的管理工具名(默认 tool-set-enabled-state)。
|
|
28
|
+
|
|
29
|
+
.EXAMPLE
|
|
30
|
+
powershell -NoProfile -ExecutionPolicy Bypass -File tools\normalize-toolset.ps1 -ProjectPath D:\Work\Unity\MyProject
|
|
31
|
+
#>
|
|
32
|
+
[CmdletBinding()]
|
|
33
|
+
param(
|
|
34
|
+
[Parameter(Mandatory = $true)][string]$ProjectPath,
|
|
35
|
+
[string]$BridgeRoot,
|
|
36
|
+
[string]$Cli = 'unity-mcp-cli',
|
|
37
|
+
[string]$SwitchTool = 'tool-set-enabled-state'
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
$ErrorActionPreference = 'Stop'
|
|
41
|
+
|
|
42
|
+
if (-not $BridgeRoot) {
|
|
43
|
+
$BridgeRoot = Split-Path -Parent $PSScriptRoot
|
|
44
|
+
if (-not $BridgeRoot) { throw "无法推断插件目录,请用 -BridgeRoot 指定" }
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
$proj = (Resolve-Path $ProjectPath).Path
|
|
48
|
+
$refDir = Join-Path $BridgeRoot 'skills\unity-mcp-tools\reference'
|
|
49
|
+
if (-not (Test-Path $refDir)) {
|
|
50
|
+
throw "找不到命令目录: $refDir(先跑 /unity-mcp-update-tools 生成,或用 -BridgeRoot 指向插件目录)"
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
$names = @(Get-ChildItem $refDir -Filter *.md |
|
|
54
|
+
Where-Object { $_.Name -ne 'extensions.md' } |
|
|
55
|
+
ForEach-Object { Select-String -Path $_.FullName -Pattern '^- `([a-z0-9-]+)`' -AllMatches } |
|
|
56
|
+
ForEach-Object { $_.Matches } |
|
|
57
|
+
ForEach-Object { $_.Groups[1].Value } |
|
|
58
|
+
Sort-Object -Unique)
|
|
59
|
+
|
|
60
|
+
if ($names.Count -eq 0) { throw "命令目录里没有解析到工具名: $refDir" }
|
|
61
|
+
if ($names -notcontains $SwitchTool) {
|
|
62
|
+
throw "命令目录里没有 $SwitchTool —— 目录与插件版本不一致,先重生成命令目录"
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (-not (Get-Command $Cli -ErrorAction SilentlyContinue)) {
|
|
66
|
+
throw "找不到 $Cli;先 npm install -g unity-mcp-cli"
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
Write-Host "工程: $proj"
|
|
70
|
+
Write-Host "命令目录: $($names.Count) 个工具($BridgeRoot)"
|
|
71
|
+
|
|
72
|
+
# 1) 显式名单:disable-tools 会为缺失条目 push 一条 visible=false
|
|
73
|
+
& $Cli configure $proj --disable-tools ($names -join ',')
|
|
74
|
+
if ($LASTEXITCODE -ne 0) { throw "configure --disable-tools 失败(exit $LASTEXITCODE)" }
|
|
75
|
+
|
|
76
|
+
# 2) 只显示管理开关
|
|
77
|
+
& $Cli configure $proj --enable-tools $SwitchTool
|
|
78
|
+
if ($LASTEXITCODE -ne 0) { throw "configure --enable-tools 失败(exit $LASTEXITCODE)" }
|
|
79
|
+
|
|
80
|
+
# 3) 校验:解析 --list 的 Tools 段(1.6.0 用 [visible]/[hidden],旧版用 [enabled]/[disabled])
|
|
81
|
+
$list = (& $Cli configure $proj --list 2>&1 | Out-String)
|
|
82
|
+
$inTools = $false
|
|
83
|
+
$total = 0
|
|
84
|
+
$visible = New-Object System.Collections.Generic.List[string]
|
|
85
|
+
foreach ($line in ($list -split "`r?`n")) {
|
|
86
|
+
if ($line -match '^\s*Tools\s*$') { $inTools = $true; continue }
|
|
87
|
+
if ($line -match '^\s*(Prompts|Resources)\s*$') { $inTools = $false; continue }
|
|
88
|
+
if (-not $inTools) { continue }
|
|
89
|
+
$m = [regex]::Match($line, '^\s*\[(?<state>visible|hidden|enabled|disabled)\]\s+(?<name>\S+)')
|
|
90
|
+
if (-not $m.Success) { continue }
|
|
91
|
+
$total++
|
|
92
|
+
if ($m.Groups['state'].Value -in @('visible', 'enabled')) {
|
|
93
|
+
$visible.Add($m.Groups['name'].Value)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
Write-Host "配置条目: $total 可见: $($visible.Count) [$($visible -join ', ')]"
|
|
98
|
+
|
|
99
|
+
if ($total -lt $names.Count) {
|
|
100
|
+
throw "配置条目 $total 少于命令目录的 $($names.Count) —— 工具集没有完全物化"
|
|
101
|
+
}
|
|
102
|
+
if ($visible.Count -ne 1 -or $visible[0] -ne $SwitchTool) {
|
|
103
|
+
throw "工具集未收敛:期望只有 $SwitchTool 可见,实际可见 [$($visible -join ', ')]"
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
Write-Host "OK: 只有 $SwitchTool 可见(条目 $total / 目录 $($names.Count))"
|
|
107
|
+
exit 0
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* 三个子命令:
|
|
6
6
|
* check 比对「上游最新 / 本地 vendor 离线包 / 项目已装」三层版本,给出该不该更新
|
|
7
7
|
* fetch 下载插件 tarball + GameDev-MCP-Server zip 到 vendor/,校验 SHA256,写 vendor/manifest.json
|
|
8
|
-
* install
|
|
8
|
+
* install 把 tarball 复制到 <工程>/UnityMcpOffline/ 并写相对 Packages 的 file: 依赖(--vendor bridge 则写插件目录绝对路径)
|
|
9
9
|
*
|
|
10
10
|
* 网络策略(实测):
|
|
11
11
|
* - OpenUPM / npm registry 直连可达 → 插件 tarball 与版本查询优先直连,失败再走代理。
|
|
@@ -38,7 +38,7 @@ function parseArgs(argv) {
|
|
|
38
38
|
for (let i = 0; i < argv.length; i++) {
|
|
39
39
|
const a = argv[i];
|
|
40
40
|
if (a === '--no-proxy') { out.noProxy = true; continue; }
|
|
41
|
-
const flag = ['proxy', 'project', 'plugin-version', 'server-version', 'rid', 'cli-version', 'out'].find(f => a === `--${f}`);
|
|
41
|
+
const flag = ['proxy', 'project', 'plugin-version', 'server-version', 'rid', 'cli-version', 'out', 'vendor'].find(f => a === `--${f}`);
|
|
42
42
|
if (flag) { out[flag] = argv[++i]; continue; }
|
|
43
43
|
if (a === '--json') { out.json = true; continue; }
|
|
44
44
|
if (a === '--force') { out.force = true; continue; }
|
|
@@ -53,7 +53,10 @@ function usage() {
|
|
|
53
53
|
console.log(`用法:
|
|
54
54
|
node tools/offline-package.mjs check [--project <Unity工程>] [--cli-version <v>] [--json]
|
|
55
55
|
node tools/offline-package.mjs fetch [--plugin-version <v>] [--server-version <v>] [--rid <rid>] [--proxy <url>] [--force]
|
|
56
|
-
node tools/offline-package.mjs install [--project <Unity工程>] [--dry-run]
|
|
56
|
+
node tools/offline-package.mjs install [--project <Unity工程>] [--vendor project|bridge] [--dry-run]
|
|
57
|
+
|
|
58
|
+
--vendor project(默认)把 tarball 复制到 <工程>/UnityMcpOffline/ 并写相对路径(可移植);
|
|
59
|
+
--vendor bridge 沿用旧行为,写 <插件目录>/vendor/ 的绝对 file: 路径。
|
|
57
60
|
|
|
58
61
|
vendor/ 目录: ${VENDOR_DIR}`);
|
|
59
62
|
process.exit(0);
|
|
@@ -394,20 +397,40 @@ function cmdInstall(args) {
|
|
|
394
397
|
const actual = sha256(tgz);
|
|
395
398
|
if (vendor.plugin.sha256 && actual !== vendor.plugin.sha256) fail(`离线包 sha256 与 manifest 不符: ${actual}`);
|
|
396
399
|
|
|
400
|
+
const mode = args.vendor ?? 'project';
|
|
401
|
+
if (mode !== 'project' && mode !== 'bridge') fail(`--vendor 只能是 project 或 bridge(收到 ${mode})`);
|
|
402
|
+
|
|
397
403
|
const proj = args.project || fail('需要 --project <Unity工程>');
|
|
398
404
|
const manifestPath = path.join(proj, 'Packages', 'manifest.json');
|
|
399
405
|
if (!fs.existsSync(path.join(proj, 'ProjectSettings', 'ProjectVersion.txt'))) fail(`不是 Unity 工程: ${proj}`);
|
|
400
406
|
if (!fs.existsSync(manifestPath)) fail(`缺 ${manifestPath}`);
|
|
401
407
|
|
|
402
|
-
//
|
|
403
|
-
|
|
408
|
+
// 默认把 tarball 复制进工程,依赖写相对 Packages 的路径(Unity 手册:本地 tarball 路径相对 Packages 文件夹)。
|
|
409
|
+
// 这样 profile 重建、工程换机器都不会断;--vendor bridge 保留旧的绝对路径行为。
|
|
410
|
+
const offlineDir = path.join(proj, 'UnityMcpOffline');
|
|
411
|
+
const offlineTgz = path.join(offlineDir, path.basename(tgz));
|
|
412
|
+
const spec = mode === 'project'
|
|
413
|
+
? `file:../UnityMcpOffline/${path.basename(tgz)}`
|
|
414
|
+
: `file:${tgz.replace(/\\/g, '/')}`;
|
|
415
|
+
|
|
404
416
|
const mj = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
405
417
|
mj.dependencies ??= {};
|
|
406
418
|
const before = mj.dependencies[PKG_ID] ?? null;
|
|
407
419
|
if (args.dryRun) {
|
|
408
|
-
console.log(`dry-run: ${PKG_ID}: ${before ?? '(无)'} → ${spec}`);
|
|
420
|
+
console.log(`dry-run (--vendor ${mode}): ${PKG_ID}: ${before ?? '(无)'} → ${spec}`);
|
|
421
|
+
if (mode === 'project') console.log(` 复制: ${tgz} → ${offlineTgz}`);
|
|
409
422
|
return;
|
|
410
423
|
}
|
|
424
|
+
|
|
425
|
+
if (mode === 'project') {
|
|
426
|
+
fs.mkdirSync(offlineDir, { recursive: true });
|
|
427
|
+
const same = fs.existsSync(offlineTgz) && sha256(offlineTgz) === vendor.plugin.sha256;
|
|
428
|
+
if (!same) fs.copyFileSync(tgz, offlineTgz);
|
|
429
|
+
const copied = sha256(offlineTgz);
|
|
430
|
+
if (vendor.plugin.sha256 && copied !== vendor.plugin.sha256) fail(`工程内离线包 sha256 与 manifest 不符: ${copied}`);
|
|
431
|
+
console.log(`已复制离线包到工程: ${offlineTgz}${same ? '(已存在且哈希一致,跳过复制)' : ''}`);
|
|
432
|
+
}
|
|
433
|
+
|
|
411
434
|
// 与 unity-mcp-cli 一致:补 OpenUPM 作用域注册表(插件的传递依赖 extensions.unity.* 要它)
|
|
412
435
|
mj.scopedRegistries ??= [];
|
|
413
436
|
let reg = mj.scopedRegistries.find(r => r.name === 'package.openupm.com');
|
|
@@ -423,11 +446,17 @@ function cmdInstall(args) {
|
|
|
423
446
|
console.log(` ${PKG_ID}: ${before ?? '(无)'} → ${spec}`);
|
|
424
447
|
console.log(` 备份: ${bak}`);
|
|
425
448
|
console.log(` 插件版本 ${vendor.plugin.version}(钉住 Server ${vendor.plugin.pinnedServerVersion})`);
|
|
449
|
+
if (mode === 'project') {
|
|
450
|
+
console.log(' 提示: 依赖是相对 Packages 的路径;profile 重建/删除不会断,工程拷到别的机器也能离线解析。');
|
|
451
|
+
console.log(' tarball 在工程根 UnityMcpOffline/ 下(约 1 MB),可一起提交到版本库。');
|
|
452
|
+
} else {
|
|
453
|
+
console.log(' 提示: 依赖是插件目录的绝对路径;重建 profile / 移动工程后会失效,需要重跑 install。');
|
|
454
|
+
}
|
|
426
455
|
const serverZip = vendor.server?.file ? path.join(VENDOR_DIR, vendor.server.file) : null;
|
|
427
456
|
if (serverZip && fs.existsSync(serverZip)) {
|
|
428
457
|
console.log(`下一步:unity-mcp-cli install-plugin ${proj} --with-server --server-version ${vendor.server.version} --server-source "${serverZip}"`);
|
|
429
458
|
} else {
|
|
430
|
-
console.log(`⚠ 本地缺 Server zip(${vendor.server?.file ?? '未记录'};
|
|
459
|
+
console.log(`⚠ 本地缺 Server zip(${vendor.server?.file ?? '未记录'};40 MB 级二进制不进 git/npm)。先补齐再装:`);
|
|
431
460
|
console.log(` node tools/offline-package.mjs fetch --rid ${vendor.server?.rid ?? defaultRid()} # 代理优先`);
|
|
432
461
|
}
|
|
433
462
|
}
|
package/vendor/README.md
CHANGED
|
@@ -33,11 +33,11 @@ node "<插件目录>\tools\offline-package.mjs" fetch
|
|
|
33
33
|
## 离线装进 Unity 工程
|
|
34
34
|
|
|
35
35
|
```powershell
|
|
36
|
-
node "<插件目录>\tools\offline-package.mjs" install --project <Unity工程> #
|
|
36
|
+
node "<插件目录>\tools\offline-package.mjs" install --project <Unity工程> # 复制 tarball 到工程 UnityMcpOffline/ + 写相对依赖 + 备份 manifest
|
|
37
37
|
unity-mcp-cli install-plugin <Unity工程> --with-server --server-version 9.3.0 --server-source "<插件目录>\vendor\gamedev-mcp-server-win-x64-9.3.0.zip"
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
-
`install`
|
|
40
|
+
`install` 默认(`--vendor project`)把 tarball 复制到工程根的 `UnityMcpOffline/`,并写成相对 `Packages` 的依赖 `"com.ivanmurzak.unity.mcp": "file:../UnityMcpOffline/com.ivanmurzak.unity.mcp-1.6.0.tgz"`(见 Unity 手册 *Local folder or tarball paths*)。这样 profile 重建、工程换机器都不会断;旧行为用 `--vendor bridge` 写插件目录的绝对路径。`unity-mcp-cli` **没有** `--plugin-source`,但它对 `file:` 这类非 semver 依赖**不会覆盖**(`dist/utils/manifest.js` 的 `nonSemverPrefixes`),所以两条命令可以并存。
|
|
41
41
|
|
|
42
42
|
## 进 git 的边界
|
|
43
43
|
|