@akiojin/unity-mcp-server 2.16.1 → 2.26.0
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/LICENSE +0 -0
- package/README.md +14 -4
- package/bin/unity-mcp-server +7 -1
- package/package.json +27 -5
- package/src/core/codeIndex.js +0 -0
- package/src/core/codeIndexDb.js +0 -0
- package/src/core/config.js +242 -200
- package/src/core/indexWatcher.js +83 -13
- package/src/core/jobManager.js +178 -0
- package/src/core/projectInfo.js +65 -118
- package/src/core/server.js +15 -2
- package/src/core/unityConnection.js +0 -0
- package/src/handlers/addressables/AddressablesAnalyzeToolHandler.js +180 -0
- package/src/handlers/addressables/AddressablesBuildToolHandler.js +146 -0
- package/src/handlers/addressables/AddressablesManageToolHandler.js +272 -0
- package/src/handlers/analysis/AnalyzeSceneContentsToolHandler.js +0 -0
- package/src/handlers/analysis/FindByComponentToolHandler.js +0 -0
- package/src/handlers/analysis/GetAnimatorStateToolHandler.js +0 -0
- package/src/handlers/analysis/GetComponentValuesToolHandler.js +0 -0
- package/src/handlers/analysis/GetGameObjectDetailsToolHandler.js +0 -0
- package/src/handlers/analysis/GetInputActionsStateToolHandler.js +0 -0
- package/src/handlers/analysis/GetObjectReferencesToolHandler.js +0 -0
- package/src/handlers/asset/AssetDatabaseManageToolHandler.js +0 -0
- package/src/handlers/asset/AssetDependencyAnalyzeToolHandler.js +0 -0
- package/src/handlers/asset/AssetImportSettingsManageToolHandler.js +0 -0
- package/src/handlers/asset/AssetMaterialCreateToolHandler.js +0 -0
- package/src/handlers/asset/AssetMaterialModifyToolHandler.js +0 -0
- package/src/handlers/asset/AssetPrefabCreateToolHandler.js +0 -0
- package/src/handlers/asset/AssetPrefabExitModeToolHandler.js +0 -0
- package/src/handlers/asset/AssetPrefabInstantiateToolHandler.js +0 -0
- package/src/handlers/asset/AssetPrefabModifyToolHandler.js +0 -0
- package/src/handlers/asset/AssetPrefabOpenToolHandler.js +0 -0
- package/src/handlers/asset/AssetPrefabSaveToolHandler.js +0 -0
- package/src/handlers/base/BaseToolHandler.js +0 -0
- package/src/handlers/compilation/CompilationGetStateToolHandler.js +0 -0
- package/src/handlers/component/ComponentAddToolHandler.js +0 -0
- package/src/handlers/component/ComponentFieldSetToolHandler.js +419 -0
- package/src/handlers/component/ComponentGetTypesToolHandler.js +0 -0
- package/src/handlers/component/ComponentListToolHandler.js +0 -0
- package/src/handlers/component/ComponentModifyToolHandler.js +0 -0
- package/src/handlers/component/ComponentRemoveToolHandler.js +0 -0
- package/src/handlers/console/ConsoleClearToolHandler.js +0 -0
- package/src/handlers/console/ConsoleReadToolHandler.js +295 -276
- package/src/handlers/editor/EditorLayersManageToolHandler.js +0 -0
- package/src/handlers/editor/EditorSelectionManageToolHandler.js +0 -0
- package/src/handlers/editor/EditorTagsManageToolHandler.js +0 -0
- package/src/handlers/editor/EditorToolsManageToolHandler.js +0 -0
- package/src/handlers/editor/EditorWindowsManageToolHandler.js +0 -0
- package/src/handlers/gameobject/GameObjectCreateToolHandler.js +0 -0
- package/src/handlers/gameobject/GameObjectDeleteToolHandler.js +0 -0
- package/src/handlers/gameobject/GameObjectFindToolHandler.js +0 -0
- package/src/handlers/gameobject/GameObjectGetHierarchyToolHandler.js +0 -0
- package/src/handlers/gameobject/GameObjectModifyToolHandler.js +0 -0
- package/src/handlers/index.js +437 -406
- package/src/handlers/input/InputActionAddToolHandler.js +0 -0
- package/src/handlers/input/InputActionMapCreateToolHandler.js +0 -0
- package/src/handlers/input/InputActionMapRemoveToolHandler.js +0 -0
- package/src/handlers/input/InputActionRemoveToolHandler.js +0 -0
- package/src/handlers/input/InputBindingAddToolHandler.js +0 -0
- package/src/handlers/input/InputBindingCompositeCreateToolHandler.js +0 -0
- package/src/handlers/input/InputBindingRemoveAllToolHandler.js +0 -0
- package/src/handlers/input/InputBindingRemoveToolHandler.js +0 -0
- package/src/handlers/input/InputControlSchemesManageToolHandler.js +0 -0
- package/src/handlers/input/InputGamepadSimulateToolHandler.js +0 -0
- package/src/handlers/input/InputKeyboardSimulateToolHandler.js +0 -0
- package/src/handlers/input/InputMouseSimulateToolHandler.js +0 -0
- package/src/handlers/input/InputSystemControlToolHandler.js +0 -0
- package/src/handlers/input/InputTouchSimulateToolHandler.js +0 -0
- package/src/handlers/menu/MenuItemExecuteToolHandler.js +0 -0
- package/src/handlers/package/PackageManagerToolHandler.js +0 -0
- package/src/handlers/package/RegistryConfigToolHandler.js +0 -0
- package/src/handlers/playmode/PlaymodeGetStateToolHandler.js +1 -1
- package/src/handlers/playmode/PlaymodePauseToolHandler.js +1 -1
- package/src/handlers/playmode/PlaymodePlayToolHandler.js +0 -0
- package/src/handlers/playmode/PlaymodeStopToolHandler.js +0 -0
- package/src/handlers/playmode/PlaymodeWaitForStateToolHandler.js +0 -0
- package/src/handlers/scene/GetSceneInfoToolHandler.js +0 -0
- package/src/handlers/scene/SceneCreateToolHandler.js +0 -0
- package/src/handlers/scene/SceneListToolHandler.js +0 -0
- package/src/handlers/scene/SceneLoadToolHandler.js +0 -0
- package/src/handlers/scene/SceneSaveToolHandler.js +0 -0
- package/src/handlers/screenshot/ScreenshotAnalyzeToolHandler.js +0 -0
- package/src/handlers/screenshot/ScreenshotCaptureToolHandler.js +0 -0
- package/src/handlers/script/CodeIndexBuildToolHandler.js +125 -15
- package/src/handlers/script/CodeIndexStatusToolHandler.js +129 -0
- package/src/handlers/script/ScriptCreateClassToolHandler.js +0 -0
- package/src/handlers/script/ScriptEditSnippetToolHandler.js +1 -1
- package/src/handlers/script/ScriptEditStructuredToolHandler.js +1 -1
- package/src/handlers/script/ScriptPackagesListToolHandler.js +0 -0
- package/src/handlers/script/ScriptReadToolHandler.js +0 -0
- package/src/handlers/script/ScriptRefactorRenameToolHandler.js +0 -0
- package/src/handlers/script/ScriptRefsFindToolHandler.js +0 -0
- package/src/handlers/script/ScriptRemoveSymbolToolHandler.js +0 -0
- package/src/handlers/script/ScriptSearchToolHandler.js +0 -0
- package/src/handlers/script/ScriptSymbolFindToolHandler.js +0 -0
- package/src/handlers/script/ScriptSymbolsGetToolHandler.js +0 -0
- package/src/handlers/settings/SettingsGetToolHandler.js +0 -0
- package/src/handlers/settings/SettingsUpdateToolHandler.js +0 -0
- package/src/handlers/system/SystemGetCommandStatsToolHandler.js +0 -0
- package/src/handlers/system/SystemPingToolHandler.js +0 -0
- package/src/handlers/system/SystemRefreshAssetsToolHandler.js +0 -0
- package/src/handlers/test/TestRunToolHandler.js +0 -0
- package/src/handlers/ui/UIClickElementToolHandler.js +0 -0
- package/src/handlers/ui/UIFindElementsToolHandler.js +0 -0
- package/src/handlers/ui/UIGetElementStateToolHandler.js +0 -0
- package/src/handlers/ui/UISetElementValueToolHandler.js +0 -0
- package/src/handlers/ui/UISimulateInputToolHandler.js +0 -0
- package/src/handlers/video/VideoCaptureForToolHandler.js +0 -0
- package/src/handlers/video/VideoCaptureStartToolHandler.js +0 -0
- package/src/handlers/video/VideoCaptureStatusToolHandler.js +0 -0
- package/src/handlers/video/VideoCaptureStopToolHandler.js +0 -0
- package/src/lsp/CSharpLspUtils.js +27 -4
- package/src/lsp/LspProcessManager.js +0 -0
- package/src/lsp/LspRpcClient.js +0 -0
- package/src/tools/analysis/analyzeSceneContents.js +0 -0
- package/src/tools/analysis/findByComponent.js +0 -0
- package/src/tools/analysis/getAnimatorState.js +0 -0
- package/src/tools/analysis/getComponentValues.js +0 -0
- package/src/tools/analysis/getGameObjectDetails.js +0 -0
- package/src/tools/analysis/getInputActionsState.js +0 -0
- package/src/tools/analysis/getObjectReferences.js +0 -0
- package/src/tools/input/inputActionsEditor.js +0 -0
- package/src/tools/scene/createScene.js +0 -0
- package/src/tools/scene/getSceneInfo.js +0 -0
- package/src/tools/scene/listScenes.js +0 -0
- package/src/tools/scene/loadScene.js +0 -0
- package/src/tools/scene/saveScene.js +0 -0
- package/src/tools/system/ping.js +0 -0
- package/src/tools/video/recordFor.js +0 -0
- package/src/tools/video/recordPlayMode.js +0 -0
- package/src/utils/csharpParse.js +0 -0
- package/src/utils/validators.js +0 -0
- package/src/handlers/script/ScriptIndexStatusToolHandler.js +0 -61
package/LICENSE
CHANGED
|
File without changes
|
package/README.md
CHANGED
|
@@ -87,6 +87,16 @@ Add to your `claude_desktop_config.json`:
|
|
|
87
87
|
|
|
88
88
|
> **Tip:** `code_index_update` は JSON パラメータ `paths` に絶対パス/相対パスの配列を渡すだけで完了します。内部で存在チェックと LSP リトライを行うため、Unity を起動したままでも短時間で終わります。
|
|
89
89
|
|
|
90
|
+
### インデックス状態を検証したいとき
|
|
91
|
+
|
|
92
|
+
ビルド中・未構築・完了後といったステータスを再現したい場合は、`code_index_build` に `delayStartMs` や `throttleMs` を付けて進捗を意図的に保ちます。さらに、`npm run simulate:code-index` を実行すると以下が自動化されます。
|
|
93
|
+
|
|
94
|
+
- 既存インデックスのリセット(`--reset`)
|
|
95
|
+
- `code_index_build` のバックグラウンド実行(任意の `--delayStart=MS` / `--throttle=MS`)
|
|
96
|
+
- 指定間隔での `code_index_status` ポーリング(`--poll=MS`)
|
|
97
|
+
|
|
98
|
+
出力される JSON を追うことで、`ready: false` → `true` へ遷移する様子や `buildJob.status` の変化を簡単に確認できます。
|
|
99
|
+
|
|
90
100
|
## Available Tools (Standardized Names)
|
|
91
101
|
|
|
92
102
|
### System & Core Tools
|
|
@@ -105,6 +115,7 @@ Add to your `claude_desktop_config.json`:
|
|
|
105
115
|
- `component_add` — Add Unity components to GameObjects with initial properties
|
|
106
116
|
- `component_remove` — Remove components from GameObjects with safety checks
|
|
107
117
|
- `component_modify` — Modify component properties with nested support
|
|
118
|
+
- `component_field_set` — Update serialized fields (including private `[SerializeField]` values, arrays, and nested structs)
|
|
108
119
|
- `component_list` — List all components on a GameObject with type information
|
|
109
120
|
- `component_get_types` — Discover available component types with filtering
|
|
110
121
|
|
|
@@ -149,10 +160,10 @@ Add to your `claude_desktop_config.json`:
|
|
|
149
160
|
- `script_remove_symbol` — Delete symbols (types/members) with reference checks
|
|
150
161
|
- `script_refactor_rename` — Rename symbols project-wide via LSP
|
|
151
162
|
- `script_packages_list` — List installed packages relevant to scripting
|
|
152
|
-
- `
|
|
163
|
+
- `code_index_status` — Report status of the persistent code index
|
|
153
164
|
|
|
154
165
|
### Code Index Utilities
|
|
155
|
-
- `code_index_build` —
|
|
166
|
+
- `code_index_build` — フルスキャンでシンボルインデックスを再構築(開発時は `delayStartMs` や `throttleMs` で進捗観測用に速度調整可能)
|
|
156
167
|
- `code_index_update` — 変更した C# ファイルのみ差分再インデックス
|
|
157
168
|
|
|
158
169
|
### Play Mode Controls
|
|
@@ -216,7 +227,6 @@ Add to your `claude_desktop_config.json`:
|
|
|
216
227
|
- `test_run` — Run Unity tests (EditMode/PlayMode)
|
|
217
228
|
- `test_get_status` — Query test runner progress/results
|
|
218
229
|
|
|
219
|
-
|
|
220
230
|
## Requirements
|
|
221
231
|
|
|
222
232
|
- **Unity**: 2020.3 LTS or newer (Unity 6 supported)
|
|
@@ -242,7 +252,7 @@ npm install -g @akiojin/unity-mcp-server
|
|
|
242
252
|
|
|
243
253
|
## Repository
|
|
244
254
|
|
|
245
|
-
Full source code and documentation: https://github.com/akiojin/unity-mcp-server
|
|
255
|
+
Full source code and documentation: <https://github.com/akiojin/unity-mcp-server>
|
|
246
256
|
|
|
247
257
|
## License
|
|
248
258
|
|
package/bin/unity-mcp-server
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akiojin/unity-mcp-server",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.26.0",
|
|
4
4
|
"description": "MCP server and Unity Editor bridge — enables AI assistants to control Unity for AI-assisted workflows",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/core/server.js",
|
|
@@ -13,18 +13,24 @@
|
|
|
13
13
|
"build:index": "node src/tools/buildCodeIndex.js",
|
|
14
14
|
"test": "node --test tests/unit/**/*.test.js tests/integration/*.test.js",
|
|
15
15
|
"test:unit": "NODE_ENV=test node --test tests/unit/**/*.test.js",
|
|
16
|
-
"test:integration": "NODE_ENV=test node
|
|
16
|
+
"test:integration": "NODE_ENV=test node scripts/run-non-unity-tests.mjs",
|
|
17
17
|
"test:e2e": "NODE_ENV=test node --test tests/e2e/*.test.js",
|
|
18
18
|
"test:coverage": "c8 --reporter=lcov --reporter=text --reporter=html node --test tests/unit/**/*.test.js tests/integration/*.test.js",
|
|
19
19
|
"test:coverage:full": "c8 --reporter=lcov --reporter=text --reporter=html node --test tests/**/*.test.js",
|
|
20
20
|
"test:watch": "node --watch --test tests/unit/**/*.test.js",
|
|
21
21
|
"test:watch:all": "node --watch --test tests/**/*.test.js",
|
|
22
22
|
"test:performance": "node --test tests/performance/*.test.js",
|
|
23
|
-
"test:ci": "
|
|
23
|
+
"test:ci": "CI=true NODE_ENV=test node --test tests/unit/core/codeIndex.test.js tests/unit/core/codeIndexDb.test.js tests/unit/core/config.test.js tests/unit/core/indexWatcher.test.js tests/unit/core/projectInfo.test.js tests/unit/core/server.test.js tests/unit/handlers/script/CodeIndexStatusToolHandler.test.js",
|
|
24
24
|
"test:ci:all": "c8 --reporter=lcov node --test tests/unit/**/*.test.js",
|
|
25
|
+
"simulate:code-index": "node scripts/simulate-code-index-status.mjs",
|
|
25
26
|
"test:verbose": "VERBOSE_TEST=true node --test tests/**/*.test.js",
|
|
27
|
+
"prepare": "cd .. && husky || true",
|
|
26
28
|
"prepublishOnly": "npm run test:ci",
|
|
27
|
-
"postinstall": "chmod +x bin/unity-mcp-server || true"
|
|
29
|
+
"postinstall": "chmod +x bin/unity-mcp-server || true",
|
|
30
|
+
"test:ci:unity": "timeout 60 node --test tests/unit/core/codeIndex.test.js tests/unit/core/codeIndexDb.test.js tests/unit/core/config.test.js tests/unit/core/indexWatcher.test.js tests/unit/core/projectInfo.test.js tests/unit/core/server.test.js || exit 0",
|
|
31
|
+
"test:unity": "node tests/run-unity-integration.mjs",
|
|
32
|
+
"test:nounity": "npm run test:integration",
|
|
33
|
+
"test:ci:integration": "CI=true NODE_ENV=test node --test tests/integration/code-index-background.test.js"
|
|
28
34
|
},
|
|
29
35
|
"keywords": [
|
|
30
36
|
"mcp",
|
|
@@ -68,7 +74,23 @@
|
|
|
68
74
|
"access": "public"
|
|
69
75
|
},
|
|
70
76
|
"devDependencies": {
|
|
77
|
+
"@commitlint/cli": "^18.6.1",
|
|
78
|
+
"@commitlint/config-conventional": "^18.6.3",
|
|
79
|
+
"@semantic-release/changelog": "^6.0.3",
|
|
80
|
+
"@semantic-release/commit-analyzer": "^11.1.0",
|
|
81
|
+
"@semantic-release/exec": "^6.0.3",
|
|
82
|
+
"@semantic-release/git": "^10.0.1",
|
|
83
|
+
"@semantic-release/release-notes-generator": "^12.1.0",
|
|
71
84
|
"c8": "^10.1.3",
|
|
72
|
-
"
|
|
85
|
+
"eslint": "^8.57.1",
|
|
86
|
+
"eslint-config-standard": "^17.1.0",
|
|
87
|
+
"eslint-plugin-import": "^2.31.0",
|
|
88
|
+
"eslint-plugin-n": "^16.6.2",
|
|
89
|
+
"eslint-plugin-promise": "^6.6.0",
|
|
90
|
+
"husky": "^9.1.7",
|
|
91
|
+
"markdownlint-cli": "^0.43.0",
|
|
92
|
+
"nodemon": "^3.1.7",
|
|
93
|
+
"prettier": "^3.4.2",
|
|
94
|
+
"semantic-release": "^22.0.12"
|
|
73
95
|
}
|
|
74
96
|
}
|
package/src/core/codeIndex.js
CHANGED
|
File without changes
|
package/src/core/codeIndexDb.js
CHANGED
|
File without changes
|
package/src/core/config.js
CHANGED
|
@@ -1,200 +1,242 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { findUpSync } from 'find-up';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Shallow merge utility (simple objects only)
|
|
7
|
-
*/
|
|
8
|
-
function merge(a, b) {
|
|
9
|
-
const out = { ...a };
|
|
10
|
-
for (const [k, v] of Object.entries(b || {})) {
|
|
11
|
-
if (v && typeof v === 'object' && !Array.isArray(v) && a[k] && typeof a[k] === 'object') {
|
|
12
|
-
out[k] = { ...a[k], ...v };
|
|
13
|
-
} else {
|
|
14
|
-
out[k] = v;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
return out;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Base configuration for Unity Editor MCP Server
|
|
22
|
-
*/
|
|
23
|
-
const envUnityHost =
|
|
24
|
-
process.env.UNITY_UNITY_HOST ||
|
|
25
|
-
process.env.UNITY_BIND_HOST ||
|
|
26
|
-
process.env.UNITY_HOST ||
|
|
27
|
-
null;
|
|
28
|
-
|
|
29
|
-
const envMcpHost =
|
|
30
|
-
process.env.UNITY_MCP_HOST ||
|
|
31
|
-
process.env.UNITY_CLIENT_HOST ||
|
|
32
|
-
process.env.UNITY_HOST ||
|
|
33
|
-
null;
|
|
34
|
-
|
|
35
|
-
const envBindHost = process.env.UNITY_BIND_HOST || null;
|
|
36
|
-
|
|
37
|
-
const baseConfig = {
|
|
38
|
-
// Unity connection settings
|
|
39
|
-
unity: {
|
|
40
|
-
unityHost: envUnityHost,
|
|
41
|
-
mcpHost: envMcpHost,
|
|
42
|
-
bindHost: envBindHost,
|
|
43
|
-
port: parseInt(process.env.UNITY_PORT || '', 10) || 6400,
|
|
44
|
-
reconnectDelay: 1000,
|
|
45
|
-
maxReconnectDelay: 30000,
|
|
46
|
-
reconnectBackoffMultiplier: 2,
|
|
47
|
-
commandTimeout: 30000,
|
|
48
|
-
},
|
|
49
|
-
|
|
50
|
-
// Server settings
|
|
51
|
-
server: {
|
|
52
|
-
name: 'unity-mcp-server',
|
|
53
|
-
version: '0.1.0',
|
|
54
|
-
description: 'MCP server for Unity Editor integration',
|
|
55
|
-
},
|
|
56
|
-
|
|
57
|
-
// Logging settings
|
|
58
|
-
logging: {
|
|
59
|
-
level: process.env.LOG_LEVEL || 'info',
|
|
60
|
-
prefix: '[Unity Editor MCP]',
|
|
61
|
-
},
|
|
62
|
-
|
|
63
|
-
// Write queue removed: all edits go through structured Roslyn tools.
|
|
64
|
-
|
|
65
|
-
// Search-related defaults and engine selection
|
|
66
|
-
search: {
|
|
67
|
-
// detail alias: 'compact' maps to returnMode 'snippets'
|
|
68
|
-
defaultDetail: (process.env.SEARCH_DEFAULT_DETAIL || 'compact').toLowerCase(), // compact|metadata|snippets|full
|
|
69
|
-
engine: (process.env.SEARCH_ENGINE || 'naive').toLowerCase(), // naive|treesitter (future)
|
|
70
|
-
},
|
|
71
|
-
|
|
72
|
-
// LSP client defaults
|
|
73
|
-
lsp: {
|
|
74
|
-
requestTimeoutMs: Number(process.env.LSP_REQUEST_TIMEOUT_MS || 60000),
|
|
75
|
-
},
|
|
76
|
-
|
|
77
|
-
// Indexing (code index) settings
|
|
78
|
-
indexing: {
|
|
79
|
-
// Enable periodic incremental index updates (polling watcher)
|
|
80
|
-
watch: (process.env.INDEX_WATCH || 'false').toLowerCase() === 'true',
|
|
81
|
-
// Polling interval (ms)
|
|
82
|
-
intervalMs: Number(process.env.INDEX_WATCH_INTERVAL_MS || 15000),
|
|
83
|
-
// Build options
|
|
84
|
-
concurrency: Number(process.env.INDEX_CONCURRENCY || 8),
|
|
85
|
-
retry: Number(process.env.INDEX_RETRY || 2),
|
|
86
|
-
reportEvery: Number(process.env.INDEX_REPORT_EVERY || 500),
|
|
87
|
-
},
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* External config resolution (no legacy compatibility):
|
|
92
|
-
* Priority:
|
|
93
|
-
* 1) UNITY_MCP_CONFIG (explicit file path)
|
|
94
|
-
* 2) ./.unity/config.json (project-local)
|
|
95
|
-
* 3) ~/.unity/config.json (user-global)
|
|
96
|
-
* If none found,
|
|
97
|
-
*/
|
|
98
|
-
function
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { findUpSync } from 'find-up';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Shallow merge utility (simple objects only)
|
|
7
|
+
*/
|
|
8
|
+
function merge(a, b) {
|
|
9
|
+
const out = { ...a };
|
|
10
|
+
for (const [k, v] of Object.entries(b || {})) {
|
|
11
|
+
if (v && typeof v === 'object' && !Array.isArray(v) && a[k] && typeof a[k] === 'object') {
|
|
12
|
+
out[k] = { ...a[k], ...v };
|
|
13
|
+
} else {
|
|
14
|
+
out[k] = v;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return out;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Base configuration for Unity Editor MCP Server
|
|
22
|
+
*/
|
|
23
|
+
const envUnityHost =
|
|
24
|
+
process.env.UNITY_UNITY_HOST ||
|
|
25
|
+
process.env.UNITY_BIND_HOST ||
|
|
26
|
+
process.env.UNITY_HOST ||
|
|
27
|
+
null;
|
|
28
|
+
|
|
29
|
+
const envMcpHost =
|
|
30
|
+
process.env.UNITY_MCP_HOST ||
|
|
31
|
+
process.env.UNITY_CLIENT_HOST ||
|
|
32
|
+
process.env.UNITY_HOST ||
|
|
33
|
+
null;
|
|
34
|
+
|
|
35
|
+
const envBindHost = process.env.UNITY_BIND_HOST || null;
|
|
36
|
+
|
|
37
|
+
const baseConfig = {
|
|
38
|
+
// Unity connection settings
|
|
39
|
+
unity: {
|
|
40
|
+
unityHost: envUnityHost,
|
|
41
|
+
mcpHost: envMcpHost,
|
|
42
|
+
bindHost: envBindHost,
|
|
43
|
+
port: parseInt(process.env.UNITY_PORT || '', 10) || 6400,
|
|
44
|
+
reconnectDelay: 1000,
|
|
45
|
+
maxReconnectDelay: 30000,
|
|
46
|
+
reconnectBackoffMultiplier: 2,
|
|
47
|
+
commandTimeout: 30000,
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
// Server settings
|
|
51
|
+
server: {
|
|
52
|
+
name: 'unity-mcp-server',
|
|
53
|
+
version: '0.1.0',
|
|
54
|
+
description: 'MCP server for Unity Editor integration',
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
// Logging settings
|
|
58
|
+
logging: {
|
|
59
|
+
level: process.env.LOG_LEVEL || 'info',
|
|
60
|
+
prefix: '[Unity Editor MCP]',
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
// Write queue removed: all edits go through structured Roslyn tools.
|
|
64
|
+
|
|
65
|
+
// Search-related defaults and engine selection
|
|
66
|
+
search: {
|
|
67
|
+
// detail alias: 'compact' maps to returnMode 'snippets'
|
|
68
|
+
defaultDetail: (process.env.SEARCH_DEFAULT_DETAIL || 'compact').toLowerCase(), // compact|metadata|snippets|full
|
|
69
|
+
engine: (process.env.SEARCH_ENGINE || 'naive').toLowerCase(), // naive|treesitter (future)
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
// LSP client defaults
|
|
73
|
+
lsp: {
|
|
74
|
+
requestTimeoutMs: Number(process.env.LSP_REQUEST_TIMEOUT_MS || 60000),
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
// Indexing (code index) settings
|
|
78
|
+
indexing: {
|
|
79
|
+
// Enable periodic incremental index updates (polling watcher)
|
|
80
|
+
watch: (process.env.INDEX_WATCH || 'false').toLowerCase() === 'true',
|
|
81
|
+
// Polling interval (ms)
|
|
82
|
+
intervalMs: Number(process.env.INDEX_WATCH_INTERVAL_MS || 15000),
|
|
83
|
+
// Build options
|
|
84
|
+
concurrency: Number(process.env.INDEX_CONCURRENCY || 8),
|
|
85
|
+
retry: Number(process.env.INDEX_RETRY || 2),
|
|
86
|
+
reportEvery: Number(process.env.INDEX_REPORT_EVERY || 500),
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* External config resolution (no legacy compatibility):
|
|
92
|
+
* Priority:
|
|
93
|
+
* 1) UNITY_MCP_CONFIG (explicit file path)
|
|
94
|
+
* 2) ./.unity/config.json (project-local)
|
|
95
|
+
* 3) ~/.unity/config.json (user-global)
|
|
96
|
+
* If none found, create ./.unity/config.json with defaults.
|
|
97
|
+
*/
|
|
98
|
+
function ensureDefaultProjectConfig(baseDir) {
|
|
99
|
+
const dir = path.resolve(baseDir, '.unity');
|
|
100
|
+
const file = path.join(dir, 'config.json');
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
if (!fs.existsSync(dir)) {
|
|
104
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (!fs.existsSync(file)) {
|
|
108
|
+
const inferredRoot = fs.existsSync(path.join(baseDir, 'Assets')) ? baseDir : '';
|
|
109
|
+
const defaultConfig = {
|
|
110
|
+
unity: {
|
|
111
|
+
unityHost: 'localhost',
|
|
112
|
+
mcpHost: 'localhost',
|
|
113
|
+
port: 6400,
|
|
114
|
+
},
|
|
115
|
+
project: {
|
|
116
|
+
root: inferredRoot ? inferredRoot.replace(/\\/g, '/') : '',
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
fs.writeFileSync(file, `${JSON.stringify(defaultConfig, null, 2)}\n`, 'utf8');
|
|
120
|
+
}
|
|
121
|
+
return file;
|
|
122
|
+
} catch (error) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function loadExternalConfig() {
|
|
128
|
+
const explicitPath = process.env.UNITY_MCP_CONFIG;
|
|
129
|
+
|
|
130
|
+
const projectPath = findUpSync((directory) => {
|
|
131
|
+
const candidate = path.resolve(directory, '.unity', 'config.json');
|
|
132
|
+
return fs.existsSync(candidate) ? candidate : undefined;
|
|
133
|
+
}, { cwd: process.cwd() });
|
|
134
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || '';
|
|
135
|
+
const userPath = homeDir ? path.resolve(homeDir, '.unity', 'config.json') : null;
|
|
136
|
+
|
|
137
|
+
const candidates = [explicitPath, projectPath, userPath].filter(Boolean);
|
|
138
|
+
for (const p of candidates) {
|
|
139
|
+
try {
|
|
140
|
+
if (p && fs.existsSync(p)) {
|
|
141
|
+
const raw = fs.readFileSync(p, 'utf8');
|
|
142
|
+
const json = JSON.parse(raw);
|
|
143
|
+
const out = json && typeof json === 'object' ? json : {};
|
|
144
|
+
out.__configPath = p;
|
|
145
|
+
return out;
|
|
146
|
+
}
|
|
147
|
+
} catch (e) {
|
|
148
|
+
return { __configLoadError: `${p}: ${e.message}` };
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
const fallbackPath = ensureDefaultProjectConfig(process.cwd());
|
|
152
|
+
if (fallbackPath && fs.existsSync(fallbackPath)) {
|
|
153
|
+
try {
|
|
154
|
+
const raw = fs.readFileSync(fallbackPath, 'utf8');
|
|
155
|
+
const json = JSON.parse(raw);
|
|
156
|
+
const out = json && typeof json === 'object' ? json : {};
|
|
157
|
+
out.__configPath = fallbackPath;
|
|
158
|
+
out.__configGenerated = true;
|
|
159
|
+
return out;
|
|
160
|
+
} catch (e) {
|
|
161
|
+
return { __configLoadError: `${fallbackPath}: ${e.message}` };
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return {};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const external = loadExternalConfig();
|
|
168
|
+
export const config = merge(baseConfig, external);
|
|
169
|
+
|
|
170
|
+
const normalizeUnityConfig = () => {
|
|
171
|
+
const unityConfig = config.unity || (config.unity = {});
|
|
172
|
+
|
|
173
|
+
// Legacy aliases coming from config files or env vars
|
|
174
|
+
const legacyHost = unityConfig.host;
|
|
175
|
+
const legacyClientHost = unityConfig.clientHost;
|
|
176
|
+
const legacyBindHost = unityConfig.bindHost;
|
|
177
|
+
|
|
178
|
+
if (!unityConfig.unityHost) {
|
|
179
|
+
unityConfig.unityHost = legacyBindHost || legacyHost || envUnityHost || 'localhost';
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (!unityConfig.mcpHost) {
|
|
183
|
+
unityConfig.mcpHost = legacyClientHost || envMcpHost || legacyHost || unityConfig.unityHost;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Keep bindHost for backwards compatibility with legacy code paths
|
|
187
|
+
if (!unityConfig.bindHost) {
|
|
188
|
+
unityConfig.bindHost = legacyBindHost || envBindHost || unityConfig.unityHost;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Maintain legacy properties so older handlers keep working
|
|
192
|
+
unityConfig.host = unityConfig.unityHost;
|
|
193
|
+
unityConfig.clientHost = unityConfig.mcpHost;
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
normalizeUnityConfig();
|
|
197
|
+
|
|
198
|
+
// Workspace root detection: directory that contains .unity/config.json used
|
|
199
|
+
const initialCwd = process.cwd();
|
|
200
|
+
let workspaceRoot = initialCwd;
|
|
201
|
+
try {
|
|
202
|
+
if (config.__configPath) {
|
|
203
|
+
const cfgDir = path.dirname(config.__configPath); // <workspace>/.unity
|
|
204
|
+
workspaceRoot = path.dirname(cfgDir); // <workspace>
|
|
205
|
+
}
|
|
206
|
+
} catch {}
|
|
207
|
+
export const WORKSPACE_ROOT = workspaceRoot;
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Logger utility
|
|
211
|
+
* IMPORTANT: In MCP servers, all stdout output must be JSON-RPC protocol messages.
|
|
212
|
+
* Logging must go to stderr to avoid breaking the protocol.
|
|
213
|
+
*/
|
|
214
|
+
export const logger = {
|
|
215
|
+
info: (message, ...args) => {
|
|
216
|
+
if (['info', 'debug'].includes(config.logging.level)) {
|
|
217
|
+
console.error(`${config.logging.prefix} ${message}`, ...args);
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
|
|
221
|
+
warn: (message, ...args) => {
|
|
222
|
+
if (['info', 'debug', 'warn'].includes(config.logging.level)) {
|
|
223
|
+
console.error(`${config.logging.prefix} WARN: ${message}`, ...args);
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
|
|
227
|
+
error: (message, ...args) => {
|
|
228
|
+
console.error(`${config.logging.prefix} ERROR: ${message}`, ...args);
|
|
229
|
+
},
|
|
230
|
+
|
|
231
|
+
debug: (message, ...args) => {
|
|
232
|
+
if (config.logging.level === 'debug') {
|
|
233
|
+
console.error(`${config.logging.prefix} DEBUG: ${message}`, ...args);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
// Late log if external config failed to load
|
|
239
|
+
if (config.__configLoadError) {
|
|
240
|
+
console.error(`${baseConfig.logging.prefix} WARN: Failed to load external config: ${config.__configLoadError}`);
|
|
241
|
+
delete config.__configLoadError;
|
|
242
|
+
}
|