@akiojin/unity-mcp-server 2.46.0 → 3.0.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/README.md +28 -393
- package/package.json +1 -1
- package/src/core/config.js +41 -97
- package/src/core/projectInfo.js +45 -17
package/README.md
CHANGED
|
@@ -1,78 +1,12 @@
|
|
|
1
|
-
# Unity MCP Server
|
|
1
|
+
# Unity MCP Server (npm package)
|
|
2
2
|
|
|
3
|
-
MCP (Model Context Protocol) server for Unity Editor integration.
|
|
3
|
+
MCP (Model Context Protocol) server for Unity Editor integration.
|
|
4
|
+
Enables AI assistants like Claude and Cursor to interact directly with Unity Editor for automated workflows.
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
This README documents the **npm package**.
|
|
7
|
+
For the Unity package, OpenUPM setup, and the full repository documentation, see:
|
|
6
8
|
|
|
7
|
-
-
|
|
8
|
-
- **Tool discovery** - Efficient `search_tools` meta-tool for discovering relevant tools (96.2% token reduction)
|
|
9
|
-
- **GameObject management** - Create, find, modify, delete GameObjects with full hierarchy control
|
|
10
|
-
- **Component system** - Add, remove, modify components with property control
|
|
11
|
-
- **Scene management** - Create, load, save, list scenes with build settings integration
|
|
12
|
-
- **Scene analysis** - Deep inspection, component analysis, and performance metrics
|
|
13
|
-
- **Asset management** - Create and modify prefabs, materials, scripts with full control
|
|
14
|
-
- **UI automation** - Find, click, and interact with UI elements programmatically
|
|
15
|
-
- **Input simulation** - Simulate keyboard, mouse, gamepad, and touch input
|
|
16
|
-
- **Play mode controls** - Start, pause, stop Unity play mode for testing
|
|
17
|
-
- **Performance profiling** - Record profiling sessions, collect metrics, save .data files for analysis
|
|
18
|
-
- **Project settings** - Read and update Unity project settings safely
|
|
19
|
-
- **Editor operations** - Console logs, screenshots, video capture, compilation monitoring
|
|
20
|
-
- **Editor control** - Manage tags, layers, selection, windows, and tools
|
|
21
|
-
|
|
22
|
-
## Tool Discovery
|
|
23
|
-
|
|
24
|
-
Unity MCP Server provides a **`search_tools`** meta-tool for efficient tool discovery, helping you find relevant tools quickly.
|
|
25
|
-
|
|
26
|
-
### Usage Examples
|
|
27
|
-
|
|
28
|
-
```javascript
|
|
29
|
-
// Find tools for GameObject manipulation
|
|
30
|
-
{
|
|
31
|
-
"tool": "search_tools",
|
|
32
|
-
"params": {
|
|
33
|
-
"query": "gameobject",
|
|
34
|
-
"limit": 10
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
// Returns: gameobject_create, gameobject_find, gameobject_modify, ...
|
|
38
|
-
|
|
39
|
-
// Filter by category
|
|
40
|
-
{
|
|
41
|
-
"tool": "search_tools",
|
|
42
|
-
"params": {
|
|
43
|
-
"category": "scene",
|
|
44
|
-
"limit": 10
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
// Returns: scene_create, scene_load, scene_save, scene_list, scene_info_get
|
|
48
|
-
|
|
49
|
-
// Filter by tags
|
|
50
|
-
{
|
|
51
|
-
"tool": "search_tools",
|
|
52
|
-
"params": {
|
|
53
|
-
"tags": ["create", "asset"],
|
|
54
|
-
"limit": 5
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
// Returns tools that create assets
|
|
58
|
-
|
|
59
|
-
// Include full input schemas (when needed)
|
|
60
|
-
{
|
|
61
|
-
"tool": "search_tools",
|
|
62
|
-
"params": {
|
|
63
|
-
"query": "screenshot",
|
|
64
|
-
"includeSchemas": true
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
// Returns full tool definitions with inputSchema
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
### Benefits
|
|
71
|
-
|
|
72
|
-
- **Smart filtering** - Search by keywords, categories, tags, or scope (read/write/execute)
|
|
73
|
-
- **Relevance scoring** - Results sorted by relevance to your query
|
|
74
|
-
- **On-demand schemas** - Full inputSchema only when explicitly requested
|
|
75
|
-
- **Easy discovery** - Find the right tool without browsing all 103 tools manually
|
|
9
|
+
- [github.com/akiojin/unity-mcp-server](https://github.com/akiojin/unity-mcp-server)
|
|
76
10
|
|
|
77
11
|
## Quick Start
|
|
78
12
|
|
|
@@ -82,25 +16,19 @@ Unity MCP Server provides a **`search_tools`** meta-tool for efficient tool disc
|
|
|
82
16
|
npx @akiojin/unity-mcp-server@latest
|
|
83
17
|
```
|
|
84
18
|
|
|
85
|
-
###
|
|
19
|
+
### HTTP Mode (for HTTP-only networks)
|
|
86
20
|
|
|
87
21
|
```bash
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
### Local Installation
|
|
93
|
-
|
|
94
|
-
```bash
|
|
95
|
-
npm install @akiojin/unity-mcp-server
|
|
96
|
-
npx unity-mcp-server
|
|
22
|
+
npx @akiojin/unity-mcp-server@latest --http 6401 --no-telemetry
|
|
23
|
+
curl http://localhost:6401/healthz
|
|
97
24
|
```
|
|
98
25
|
|
|
99
26
|
## Unity Setup
|
|
100
27
|
|
|
101
|
-
1. Install the Unity package from:
|
|
28
|
+
1. Install the Unity package from:
|
|
29
|
+
- [`https://github.com/akiojin/unity-mcp-server.git?path=UnityMCPServer/Packages/unity-mcp-server`](https://github.com/akiojin/unity-mcp-server.git?path=UnityMCPServer/Packages/unity-mcp-server)
|
|
102
30
|
2. Open Unity Package Manager → Add package from git URL
|
|
103
|
-
3. The package
|
|
31
|
+
3. The package starts a TCP server (default port `6400`)
|
|
104
32
|
|
|
105
33
|
## MCP Client Configuration
|
|
106
34
|
|
|
@@ -119,328 +47,35 @@ Add to your `claude_desktop_config.json`:
|
|
|
119
47
|
}
|
|
120
48
|
```
|
|
121
49
|
|
|
122
|
-
|
|
50
|
+
## Tools & Discovery
|
|
51
|
+
|
|
52
|
+
Unity MCP Server ships **100+ tools**.
|
|
53
|
+
Use the `search_tools` meta-tool to find the right tool quickly.
|
|
123
54
|
|
|
124
55
|
```json
|
|
125
56
|
{
|
|
126
|
-
"
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
57
|
+
"tool": "search_tools",
|
|
58
|
+
"params": {
|
|
59
|
+
"query": "create gameobject",
|
|
60
|
+
"limit": 10
|
|
130
61
|
}
|
|
131
62
|
}
|
|
132
63
|
```
|
|
133
64
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
1. **初回または大幅変更後に `code_index_build`**
|
|
139
|
-
プロジェクト内の `Assets/` と `Packages/` の C# をすべてスキャンし、`.unity/cache/code-index/code-index.db` にシンボルを格納します。
|
|
140
|
-
2. **ファイル更新後は `code_index_update`**
|
|
141
|
-
編集したファイルだけを配列で渡すと、LSP から最新シンボルを再取得してインデックスを差分更新します。長時間待ちになる `code_index_build` の再実行を避けられます。
|
|
142
|
-
3. **シンボル取得時は `script_symbol_find`**
|
|
143
|
-
インデックスが利用可能なら高速に、未構築でも LSP にフォールバックして定義位置を取得できます。`script_edit_*` ツールに渡す `symbolName` 構築に活用してください。
|
|
144
|
-
|
|
145
|
-
> **Tip:** `code_index_update` は JSON パラメータ `paths` に絶対パス/相対パスの配列を渡すだけで完了します。内部で存在チェックと LSP リトライを行うため、Unity を起動したままでも短時間で終わります。
|
|
146
|
-
|
|
147
|
-
### インデックス状態を検証したいとき
|
|
148
|
-
|
|
149
|
-
ビルド中・未構築・完了後といったステータスを再現したい場合は、`code_index_build` に `delayStartMs` や `throttleMs` を付けて進捗を意図的に保ちます。さらに、`npm run simulate:code-index` を実行すると以下が自動化されます。
|
|
150
|
-
|
|
151
|
-
- 既存インデックスのリセット(`--reset`)
|
|
152
|
-
- `code_index_build` のバックグラウンド実行(任意の `--delayStart=MS` / `--throttle=MS`)
|
|
153
|
-
- 指定間隔での `code_index_status` ポーリング(`--poll=MS`)
|
|
154
|
-
|
|
155
|
-
出力される JSON を追うことで、`ready: false` → `true` へ遷移する様子や `buildJob.status` の変化を簡単に確認できます。
|
|
156
|
-
|
|
157
|
-
## Available Tools (Standardized Names)
|
|
158
|
-
|
|
159
|
-
### System & Core Tools
|
|
160
|
-
|
|
161
|
-
- `system_ping` — Test connection to Unity Editor and verify server status
|
|
162
|
-
- `system_refresh_assets` — Refresh Unity assets and trigger recompilation
|
|
163
|
-
- `system_get_command_stats` — Retrieve recent MCP command usage metrics
|
|
164
|
-
|
|
165
|
-
### GameObject Management
|
|
166
|
-
|
|
167
|
-
- `gameobject_create` — Create GameObjects with primitives, transforms, tags, and layers
|
|
168
|
-
- `gameobject_find` — Find GameObjects by name, tag, or layer with pattern matching
|
|
169
|
-
- `gameobject_modify` — Modify GameObject properties (transform, name, active state, parent)
|
|
170
|
-
- `gameobject_delete` — Delete single or multiple GameObjects with child handling
|
|
171
|
-
- `gameobject_get_hierarchy` — Inspect scene hierarchy with component details
|
|
172
|
-
|
|
173
|
-
### Component System
|
|
174
|
-
|
|
175
|
-
- `component_add` — Add Unity components to GameObjects with initial properties
|
|
176
|
-
- `component_remove` — Remove components from GameObjects with safety checks
|
|
177
|
-
- `component_modify` — Modify component properties with nested support
|
|
178
|
-
- `component_field_set` — Update serialized fields (including private `[SerializeField]` values, arrays, and nested structs)
|
|
179
|
-
- `component_list` — List all components on a GameObject with type information
|
|
180
|
-
- `component_get_types` — Discover available component types with filtering
|
|
181
|
-
|
|
182
|
-
### Scene Management
|
|
183
|
-
|
|
184
|
-
- `scene_create` — Create new scenes with build-settings integration
|
|
185
|
-
- `scene_load` — Load scenes in Single or Additive mode
|
|
186
|
-
- `scene_save` — Save the active scene (supports Save As)
|
|
187
|
-
- `scene_list` — List scenes in the project with filtering options
|
|
188
|
-
- `scene_info_get` — Retrieve detailed scene information including GameObject counts
|
|
189
|
-
|
|
190
|
-
### Analysis & Diagnostics
|
|
191
|
-
|
|
192
|
-
- `analysis_scene_contents_analyze` — Gather scene statistics and performance metrics
|
|
193
|
-
- `analysis_component_find` — Locate GameObjects by component type with scope filtering
|
|
194
|
-
- `analysis_component_values_get` — Inspect component properties and values
|
|
195
|
-
- `analysis_gameobject_details_get` — Deep inspection of GameObjects and components
|
|
196
|
-
- `analysis_object_references_get` — Trace references between objects and assets
|
|
197
|
-
- `analysis_animator_state_get` — Inspect Animator layers, parameters, and transitions
|
|
198
|
-
- `analysis_animator_runtime_info_get` — Retrieve runtime Animator diagnostics (Play Mode)
|
|
199
|
-
|
|
200
|
-
### Asset Management
|
|
201
|
-
|
|
202
|
-
- `asset_prefab_create` — Create prefabs from GameObjects or blank templates
|
|
203
|
-
- `asset_prefab_instantiate` — Instantiate prefabs with custom transforms
|
|
204
|
-
- `asset_prefab_modify` — Apply property overrides to existing prefabs
|
|
205
|
-
- `asset_prefab_open` — Open prefabs in Prefab Mode for editing
|
|
206
|
-
- `asset_prefab_exit_mode` — Exit Prefab Mode with save/discard handling
|
|
207
|
-
- `asset_prefab_save` — Save prefab changes or apply instance overrides
|
|
208
|
-
- `asset_material_create` — Create materials with shader and property setup
|
|
209
|
-
- `asset_material_modify` — Update material properties and shaders
|
|
210
|
-
- `asset_import_settings_manage` — Inspect or modify asset-import settings and presets
|
|
211
|
-
- `asset_database_manage` — Perform asset database operations (find, move, copy, delete)
|
|
212
|
-
- `asset_dependency_analyze` — Audit asset dependencies and identify unused assets
|
|
213
|
-
|
|
214
|
-
### Script & Code Tools
|
|
215
|
-
|
|
216
|
-
- `script_read` — Read script file contents with syntax-aware formatting
|
|
217
|
-
- `script_search` — Search C# sources by substring/regex/glob filters
|
|
218
|
-
- `script_symbols_get` — Enumerate symbols within a specific C# file
|
|
219
|
-
- `script_symbol_find` — Locate symbol definitions across the project
|
|
220
|
-
- `script_refs_find` — List references/usages for a symbol via C# LSP
|
|
221
|
-
- `script_edit_snippet` — Apply small text edits within C# files
|
|
222
|
-
- `script_edit_structured` — Perform structured symbol edits via C# LSP extensions
|
|
223
|
-
- `script_create_class` — Generate a new C# class file from parameters
|
|
224
|
-
- `script_remove_symbol` — Delete symbols (types/members) with reference checks
|
|
225
|
-
- `script_refactor_rename` — Rename symbols project-wide via LSP
|
|
226
|
-
- `script_packages_list` — List installed packages relevant to scripting
|
|
227
|
-
- `code_index_status` — Report status of the persistent code index
|
|
228
|
-
|
|
229
|
-
### Code Index Utilities
|
|
230
|
-
|
|
231
|
-
- `code_index_build` — フルスキャンでシンボルインデックスを再構築(開発時は `delayStartMs` や `throttleMs` で進捗観測用に速度調整可能)
|
|
232
|
-
- `code_index_update` — 変更した C# ファイルのみ差分再インデックス
|
|
233
|
-
|
|
234
|
-
### Play Mode Controls
|
|
235
|
-
|
|
236
|
-
- `playmode_play` — Enter Play Mode
|
|
237
|
-
- `playmode_pause` — Pause or resume Play Mode
|
|
238
|
-
- `playmode_stop` — Exit Play Mode back to Edit Mode
|
|
239
|
-
- `playmode_get_state` — Inspect current play/edit/compilation state
|
|
240
|
-
- `playmode_wait_for_state` — Await a target play/edit state
|
|
241
|
-
|
|
242
|
-
### UI Automation
|
|
243
|
-
|
|
244
|
-
- `ui_find_elements` — Locate UI elements by type, tag, or name
|
|
245
|
-
- `ui_click_element` — Simulate clicking UI buttons, toggles, etc.
|
|
246
|
-
- `ui_get_element_state` — Inspect UI element properties and interactability
|
|
247
|
-
- `ui_set_element_value` — Modify UI input values (sliders, fields, dropdowns)
|
|
248
|
-
- `ui_simulate_input` — Execute complex multi-step UI interaction sequences
|
|
249
|
-
|
|
250
|
-
### Input System Utilities
|
|
251
|
-
|
|
252
|
-
- `input_system_control` — Dispatch keyboard/mouse/gamepad/touch operations
|
|
253
|
-
- `input_keyboard` — Keyboard input with batching, combos, typing, and auto-hold durations
|
|
254
|
-
- `input_mouse` — Mouse movement, clicks, drags, per-button press/hold, and batching
|
|
255
|
-
- `input_gamepad` — Gamepad buttons, sticks, triggers, d-pad with batching and timed holds
|
|
256
|
-
- `input_touch` — Touch gestures (tap/swipe/pinch/multi) with batched steps
|
|
257
|
-
|
|
258
|
-
#### 同時押下とホールドの扱い
|
|
259
|
-
|
|
260
|
-
- **単一アクションで同時押下**: キーボードは `action:"combo"`、タッチは `action:"multi"`、ゲームパッドのスティックは `action:"stick"` で X/Y を同時指定できます。
|
|
261
|
-
- **`actions[]` バッチ**: 異なるボタンやデバイスを同一フレームで実行したい場合は `actions:[{...},{...}]` に並べて送信します(順番を保持)。
|
|
262
|
-
- **ホールド時間**: `holdSeconds` を press/button/trigger などに付与すると、その秒数後に自動で release/state reset がスケジュールされます。省略すると押下状態が維持されるので、手動で release アクションを送ってください。
|
|
263
|
-
- `input_action_add` — Add actions to an Input Action map
|
|
264
|
-
- `input_action_remove` — Remove actions from an Input Action map
|
|
265
|
-
- `input_action_map_create` — Create new Input Action maps
|
|
266
|
-
- `input_action_map_remove` — Delete existing Input Action maps
|
|
267
|
-
- `input_binding_add` — Add bindings to an action (including composites)
|
|
268
|
-
- `input_binding_remove` — Remove a specific binding from an action
|
|
269
|
-
- `input_binding_remove_all` — Clear all bindings from an action
|
|
270
|
-
- `input_binding_composite_create` — Create composite bindings (e.g., 2D vectors)
|
|
271
|
-
- `input_control_schemes_manage` — Manage control schemes and device lists
|
|
272
|
-
- `input_actions_state_get` — Inspect current Input Actions asset configuration
|
|
273
|
-
- `input_actions_asset_analyze` — Produce structured summaries of Input Actions assets
|
|
274
|
-
|
|
275
|
-
### Editor & Console Utilities
|
|
276
|
-
|
|
277
|
-
- `menu_item_execute` — Trigger Unity Editor menu items programmatically
|
|
278
|
-
- `console_clear` — Clear Unity console logs with filtering options
|
|
279
|
-
- `console_read` — Stream Unity console output with advanced filters
|
|
280
|
-
- `editor_tags_manage` — Manage project tags (add/remove/list)
|
|
281
|
-
- `editor_layers_manage` — Manage project layers with index conversion
|
|
282
|
-
- `editor_selection_manage` — Inspect or mutate the current editor selection
|
|
283
|
-
- `editor_windows_manage` — Enumerate or focus Unity editor windows
|
|
284
|
-
- `editor_tools_manage` — Manage editor tools and plugins
|
|
285
|
-
- `compilation_get_state` — Inspect current compilation state and errors
|
|
286
|
-
|
|
287
|
-
### Project Settings & Packages
|
|
288
|
-
|
|
289
|
-
- `settings_get` — Read Unity project settings with granular control
|
|
290
|
-
- `settings_update` — Safely update project settings (requires confirmation)
|
|
291
|
-
- `package_manage` — List or manage Unity packages via Package Manager
|
|
292
|
-
- `package_registry_config` — Configure package registries/scopes
|
|
293
|
-
|
|
294
|
-
### Asset Visualization & Capture
|
|
295
|
-
|
|
296
|
-
- `screenshot_capture` — Capture Game/Scene view screenshots
|
|
297
|
-
- `screenshot_analyze` — Run image analysis on captured screenshots
|
|
298
|
-
- `video_capture_start` — Begin recording the Game view to video
|
|
299
|
-
- `video_capture_stop` — Stop the current video recording
|
|
300
|
-
- `video_capture_status` — Inspect capture status / metadata
|
|
301
|
-
- `video_capture_for` — Record for a fixed duration before auto-stop
|
|
302
|
-
|
|
303
|
-
### Testing & Diagnostics
|
|
304
|
-
|
|
305
|
-
- `test_run` — Run Unity tests (EditMode/PlayMode)
|
|
306
|
-
- `test_get_status` — Query Unity Test Runner progress/results. Parameters:
|
|
307
|
-
- `includeTestResults` (bool, default `false`): attaches the latest exported `.unity/test-results/*.json` summary to the response when a test run has completed.
|
|
308
|
-
- `includeFileContent` (bool, default `false`): when combined with `includeTestResults`, also returns the JSON file contents as a string so agents can parse the detailed per-test data without reading the file directly.
|
|
309
|
-
|
|
310
|
-
## Architecture
|
|
311
|
-
|
|
312
|
-
### Dependencies
|
|
313
|
-
|
|
314
|
-
Unity MCP Server uses the following components for code indexing and analysis:
|
|
315
|
-
|
|
316
|
-
| Component | Purpose | Distribution | Notes |
|
|
317
|
-
|-----------|---------|--------------|-------|
|
|
318
|
-
| **fast-sql** | Code index database | npm dependency | Hybrid backend: better-sqlite3 (native) or sql.js (WASM) |
|
|
319
|
-
| **csharp-lsp** | C# symbol analysis | Downloaded on first use | ~80 MB per platform |
|
|
320
|
-
|
|
321
|
-
#### Why fast-sql?
|
|
322
|
-
|
|
323
|
-
We use **fast-sql** (hybrid SQLite library) for the code index database:
|
|
324
|
-
|
|
325
|
-
- **Optimal performance**: Uses better-sqlite3 (native) when available for ~34x faster queries
|
|
326
|
-
- **npx compatible**: Falls back to sql.js (WASM) when native bindings unavailable
|
|
327
|
-
- **Zero native compilation required**: Works immediately via `npx` without build tools
|
|
328
|
-
- **Cross-platform**: Automatic backend selection based on environment
|
|
329
|
-
|
|
330
|
-
**Performance comparison** (better-sqlite3 vs sql.js):
|
|
331
|
-
- 50K inserts: 29ms vs 53ms (1.8x faster)
|
|
332
|
-
- 1000 queries: 0.34μs vs 11.78μs (34x faster)
|
|
333
|
-
- LIKE search: 2.5ms vs 7ms (2.8x faster)
|
|
334
|
-
|
|
335
|
-
#### csharp-lsp Distribution
|
|
336
|
-
|
|
337
|
-
**csharp-lsp (downloaded on first use)**:
|
|
338
|
-
|
|
339
|
-
- Large size (~80 MB per platform, ~480 MB for all 6 platforms)
|
|
340
|
-
- Too large to bundle in npm package
|
|
341
|
-
- Downloaded from GitHub Release on first use of script editing tools
|
|
342
|
-
|
|
343
|
-
#### Supported Platforms
|
|
344
|
-
|
|
345
|
-
| Platform | fast-sql | csharp-lsp |
|
|
346
|
-
|----------|----------|------------|
|
|
347
|
-
| Linux x64 | ✅ Native (better-sqlite3) | ✅ Downloaded (~79 MB) |
|
|
348
|
-
| Linux arm64 | ✅ Native (better-sqlite3) | ✅ Downloaded (~86 MB) |
|
|
349
|
-
| macOS x64 | ✅ Native (better-sqlite3) | ✅ Downloaded (~80 MB) |
|
|
350
|
-
| macOS arm64 (Apple Silicon) | ✅ Native (better-sqlite3) | ✅ Downloaded (~86 MB) |
|
|
351
|
-
| Windows x64 | ✅ Native (better-sqlite3) | ✅ Downloaded (~80 MB) |
|
|
352
|
-
| Windows arm64 | ✅ WASM fallback (sql.js) | ✅ Downloaded (~85 MB) |
|
|
353
|
-
|
|
354
|
-
#### Storage Locations
|
|
355
|
-
|
|
356
|
-
- **csharp-lsp**: `~/.unity/tools/csharp-lsp/<rid>/`
|
|
357
|
-
- **Code index database**: `<workspace>/.unity/cache/code-index/code-index.db`
|
|
65
|
+
More details:
|
|
66
|
+
- Tools & Code Index workflow: [`docs/tools.md`](https://github.com/akiojin/unity-mcp-server/blob/main/docs/tools.md)
|
|
67
|
+
- Configuration reference: [`docs/configuration.md`](https://github.com/akiojin/unity-mcp-server/blob/main/docs/configuration.md)
|
|
358
68
|
|
|
359
69
|
## Requirements
|
|
360
70
|
|
|
361
|
-
-
|
|
362
|
-
-
|
|
363
|
-
-
|
|
364
|
-
|
|
365
|
-
## Performance Benchmark
|
|
366
|
-
|
|
367
|
-
Response time comparison between Code Index tools and standard Claude Code tools (with DB index built, `watch: true` enabled):
|
|
368
|
-
|
|
369
|
-
| Operation | unity-mcp-server | Time | Standard Tool | Time | Result |
|
|
370
|
-
|-----------|------------------|------|---------------|------|--------|
|
|
371
|
-
| File Read | `script_read` | **instant** | `Read` | **instant** | EQUAL |
|
|
372
|
-
| Symbol List | `script_symbols_get` | **instant** | N/A | N/A | PASS |
|
|
373
|
-
| Symbol Find | `script_symbol_find` | **instant** | `Grep` | **instant** | EQUAL |
|
|
374
|
-
| Text Search | `script_search` | **instant** | `Grep` | **instant** | EQUAL |
|
|
375
|
-
| Reference Find | `script_refs_find` | **instant** | `Grep` | **instant** | EQUAL |
|
|
376
|
-
| Index Status | `code_index_status` | **instant** | N/A | N/A | PASS |
|
|
377
|
-
|
|
378
|
-
**Note**: Performance above requires DB index to be built. Run `code_index_build` before first use.
|
|
379
|
-
|
|
380
|
-
### Worker Thread Implementation
|
|
381
|
-
|
|
382
|
-
Since v2.41.x, background index builds run in Worker Threads, so MCP tools are never blocked even with `watch: true`:
|
|
383
|
-
|
|
384
|
-
| Scenario | Before Worker Thread | After Worker Thread |
|
|
385
|
-
|----------|---------------------|---------------------|
|
|
386
|
-
| `system_ping` | **60+ seconds block** | **instant** |
|
|
387
|
-
| `code_index_status` | **60+ seconds block** | **instant** |
|
|
388
|
-
| Any MCP tool | timeout during build | **instant** |
|
|
389
|
-
|
|
390
|
-
See [`docs/benchmark.md`](../docs/benchmark.md) for detailed benchmark results.
|
|
71
|
+
- Unity 2020.3 LTS or newer
|
|
72
|
+
- Node.js 18.x / 20.x / 22.x LTS (23+ not supported)
|
|
73
|
+
- MCP client (Claude Desktop, Cursor, etc.)
|
|
391
74
|
|
|
392
75
|
## Troubleshooting
|
|
393
76
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
1. Ensure Unity Editor is running with the Unity package installed
|
|
397
|
-
2. Check Unity console for connection messages
|
|
398
|
-
3. Verify port 6400 is not blocked by firewall
|
|
399
|
-
|
|
400
|
-
### Installation Issues
|
|
401
|
-
|
|
402
|
-
```bash
|
|
403
|
-
# Clear npm cache
|
|
404
|
-
npm cache clean --force
|
|
405
|
-
|
|
406
|
-
# Reinstall
|
|
407
|
-
npm uninstall -g @akiojin/unity-mcp-server
|
|
408
|
-
npm install -g @akiojin/unity-mcp-server
|
|
409
|
-
```
|
|
410
|
-
|
|
411
|
-
### MCP Client Shows "Capabilities: none"
|
|
412
|
-
|
|
413
|
-
If your MCP client (Claude Code, Cursor, etc.) shows "Capabilities: none" despite successful connection:
|
|
414
|
-
|
|
415
|
-
**Symptom**: Server connects successfully, but no tools are visible to the client.
|
|
416
|
-
|
|
417
|
-
**Root Cause**: Empty capability objects (`resources: {}`, `prompts: {}`) in MCP SDK v0.6.1 cause capability validation to fail silently.
|
|
418
|
-
|
|
419
|
-
**Solution**: Update to latest version with the fix:
|
|
420
|
-
|
|
421
|
-
```bash
|
|
422
|
-
# Update to latest version (2.41.0+)
|
|
423
|
-
npm update -g @akiojin/unity-mcp-server
|
|
424
|
-
|
|
425
|
-
# Or reinstall
|
|
426
|
-
npm uninstall -g @akiojin/unity-mcp-server
|
|
427
|
-
npm install -g @akiojin/unity-mcp-server
|
|
428
|
-
```
|
|
429
|
-
|
|
430
|
-
**Verification**: After restart, your MCP client should display 107 available tools.
|
|
431
|
-
|
|
432
|
-
## Repository
|
|
433
|
-
|
|
434
|
-
Full source code and documentation: <https://github.com/akiojin/unity-mcp-server>
|
|
435
|
-
|
|
436
|
-
## Release Automation
|
|
437
|
-
|
|
438
|
-
- Releases are generated by release-please on `main`.
|
|
439
|
-
- If changes are in develop, run `scripts/prepare-release-pr.sh` (creates/auto-merges develop→main PR).
|
|
440
|
-
- release-please runs on main and publishes via tags; no automatic back-merge to develop.
|
|
441
|
-
- Required checks: Markdown, ESLint & Formatting / Commit Message Lint / Test & Coverage / Package.
|
|
442
|
-
- Tags trigger `publish.yml` to push npm, OpenUPM (via tag), and csharp-lsp artifacts; Unity package version is kept in sync via release-please extra-files.
|
|
443
|
-
- If OpenUPM lags, create a new release so the tag and Unity package version match.
|
|
77
|
+
- Troubleshooting index: [`docs/troubleshooting/README.md`](https://github.com/akiojin/unity-mcp-server/blob/main/docs/troubleshooting/README.md)
|
|
78
|
+
- “Capabilities: none” fix: [`docs/troubleshooting/capabilities-none.md`](https://github.com/akiojin/unity-mcp-server/blob/main/docs/troubleshooting/capabilities-none.md)
|
|
444
79
|
|
|
445
80
|
## License
|
|
446
81
|
|
package/package.json
CHANGED
package/src/core/config.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
|
+
import os from 'os';
|
|
2
3
|
import path from 'path';
|
|
3
4
|
import * as findUpPkg from 'find-up';
|
|
4
5
|
import { MCPLogger } from './mcpLogger.js';
|
|
@@ -67,20 +68,13 @@ function resolvePackageVersion() {
|
|
|
67
68
|
/**
|
|
68
69
|
* Base configuration for Unity MCP Server Server
|
|
69
70
|
*/
|
|
70
|
-
const envUnityHost = process.env.UNITY_BIND_HOST || process.env.UNITY_HOST || null;
|
|
71
|
-
|
|
72
|
-
const envMcpHost =
|
|
73
|
-
process.env.UNITY_MCP_HOST || process.env.UNITY_CLIENT_HOST || process.env.UNITY_HOST || null;
|
|
74
|
-
|
|
75
|
-
const envBindHost = process.env.UNITY_BIND_HOST || null;
|
|
76
|
-
|
|
77
71
|
const baseConfig = {
|
|
78
72
|
// Unity connection settings
|
|
79
73
|
unity: {
|
|
80
|
-
unityHost:
|
|
81
|
-
mcpHost:
|
|
82
|
-
bindHost:
|
|
83
|
-
port:
|
|
74
|
+
unityHost: null,
|
|
75
|
+
mcpHost: null,
|
|
76
|
+
bindHost: null,
|
|
77
|
+
port: 6400,
|
|
84
78
|
reconnectDelay: 1000,
|
|
85
79
|
maxReconnectDelay: 30000,
|
|
86
80
|
reconnectBackoffMultiplier: 2,
|
|
@@ -96,24 +90,21 @@ const baseConfig = {
|
|
|
96
90
|
|
|
97
91
|
// Logging settings
|
|
98
92
|
logging: {
|
|
99
|
-
level:
|
|
93
|
+
level: 'info',
|
|
100
94
|
prefix: '[unity-mcp-server]'
|
|
101
95
|
},
|
|
102
96
|
|
|
103
97
|
// HTTP transport (off by default)
|
|
104
98
|
http: {
|
|
105
|
-
enabled:
|
|
106
|
-
host:
|
|
107
|
-
port:
|
|
99
|
+
enabled: false,
|
|
100
|
+
host: '0.0.0.0',
|
|
101
|
+
port: 6401,
|
|
108
102
|
healthPath: '/healthz',
|
|
109
|
-
allowedHosts:
|
|
110
|
-
.split(',')
|
|
111
|
-
.map(h => h.trim())
|
|
112
|
-
.filter(h => h.length > 0)
|
|
103
|
+
allowedHosts: ['localhost', '127.0.0.1']
|
|
113
104
|
},
|
|
114
105
|
|
|
115
106
|
telemetry: {
|
|
116
|
-
enabled:
|
|
107
|
+
enabled: false,
|
|
117
108
|
destinations: [],
|
|
118
109
|
fields: []
|
|
119
110
|
},
|
|
@@ -123,13 +114,13 @@ const baseConfig = {
|
|
|
123
114
|
// Search-related defaults and engine selection
|
|
124
115
|
search: {
|
|
125
116
|
// detail alias: 'compact' maps to returnMode 'snippets'
|
|
126
|
-
defaultDetail:
|
|
127
|
-
engine:
|
|
117
|
+
defaultDetail: 'compact', // compact|metadata|snippets|full
|
|
118
|
+
engine: 'naive' // naive|treesitter (future)
|
|
128
119
|
},
|
|
129
120
|
|
|
130
121
|
// LSP client defaults
|
|
131
122
|
lsp: {
|
|
132
|
-
requestTimeoutMs:
|
|
123
|
+
requestTimeoutMs: 60000
|
|
133
124
|
},
|
|
134
125
|
|
|
135
126
|
// Indexing (code index) settings
|
|
@@ -137,95 +128,48 @@ const baseConfig = {
|
|
|
137
128
|
// Enable periodic incremental index updates (polling watcher)
|
|
138
129
|
watch: true,
|
|
139
130
|
// Polling interval (ms)
|
|
140
|
-
intervalMs:
|
|
131
|
+
intervalMs: 15000,
|
|
141
132
|
// Build options
|
|
142
|
-
concurrency:
|
|
143
|
-
retry:
|
|
144
|
-
reportEvery:
|
|
133
|
+
concurrency: 8,
|
|
134
|
+
retry: 2,
|
|
135
|
+
reportEvery: 500
|
|
145
136
|
}
|
|
146
137
|
};
|
|
147
138
|
|
|
148
139
|
/**
|
|
149
|
-
* External config resolution
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
* 2) ./.unity/config.json (project-local)
|
|
153
|
-
* 3) ~/.unity/config.json (user-global)
|
|
154
|
-
* If none found, create ./.unity/config.json with defaults.
|
|
140
|
+
* External config resolution:
|
|
141
|
+
* - Uses the nearest `.unity/config.json` found by walking up from the current working directory.
|
|
142
|
+
* - Intentionally ignores `~/.unity/config.json` (user-global).
|
|
155
143
|
*/
|
|
156
|
-
function ensureDefaultProjectConfig(baseDir) {
|
|
157
|
-
const dir = path.resolve(baseDir, '.unity');
|
|
158
|
-
const file = path.join(dir, 'config.json');
|
|
159
|
-
|
|
160
|
-
try {
|
|
161
|
-
if (!fs.existsSync(dir)) {
|
|
162
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
if (!fs.existsSync(file)) {
|
|
166
|
-
const inferredRoot = fs.existsSync(path.join(baseDir, 'Assets')) ? baseDir : '';
|
|
167
|
-
const defaultConfig = {
|
|
168
|
-
unity: {
|
|
169
|
-
unityHost: 'localhost',
|
|
170
|
-
mcpHost: 'localhost',
|
|
171
|
-
port: 6400
|
|
172
|
-
},
|
|
173
|
-
project: {
|
|
174
|
-
root: inferredRoot ? inferredRoot.replace(/\\/g, '/') : ''
|
|
175
|
-
}
|
|
176
|
-
};
|
|
177
|
-
fs.writeFileSync(file, `${JSON.stringify(defaultConfig, null, 2)}\n`, 'utf8');
|
|
178
|
-
}
|
|
179
|
-
return file;
|
|
180
|
-
} catch (error) {
|
|
181
|
-
return null;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
144
|
function loadExternalConfig() {
|
|
186
145
|
if (typeof findUpSyncCompat !== 'function') {
|
|
187
146
|
return {};
|
|
188
147
|
}
|
|
189
|
-
|
|
148
|
+
let userGlobalPath = null;
|
|
149
|
+
try {
|
|
150
|
+
const homeDir = os.homedir();
|
|
151
|
+
userGlobalPath = homeDir ? path.resolve(homeDir, '.unity', 'config.json') : null;
|
|
152
|
+
} catch {}
|
|
190
153
|
|
|
191
154
|
const projectPath = findUpSyncCompat(
|
|
192
155
|
directory => {
|
|
193
156
|
const candidate = path.resolve(directory, '.unity', 'config.json');
|
|
157
|
+
if (userGlobalPath && path.resolve(candidate) === userGlobalPath) return undefined;
|
|
194
158
|
return fs.existsSync(candidate) ? candidate : undefined;
|
|
195
159
|
},
|
|
196
160
|
{ cwd: process.cwd() }
|
|
197
161
|
);
|
|
198
|
-
const homeDir = process.env.HOME || process.env.USERPROFILE || '';
|
|
199
|
-
const userPath = homeDir ? path.resolve(homeDir, '.unity', 'config.json') : null;
|
|
200
162
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
211
|
-
} catch (e) {
|
|
212
|
-
return { __configLoadError: `${p}: ${e.message}` };
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
const fallbackPath = ensureDefaultProjectConfig(process.cwd());
|
|
216
|
-
if (fallbackPath && fs.existsSync(fallbackPath)) {
|
|
217
|
-
try {
|
|
218
|
-
const raw = fs.readFileSync(fallbackPath, 'utf8');
|
|
219
|
-
const json = JSON.parse(raw);
|
|
220
|
-
const out = json && typeof json === 'object' ? json : {};
|
|
221
|
-
out.__configPath = fallbackPath;
|
|
222
|
-
out.__configGenerated = true;
|
|
223
|
-
return out;
|
|
224
|
-
} catch (e) {
|
|
225
|
-
return { __configLoadError: `${fallbackPath}: ${e.message}` };
|
|
226
|
-
}
|
|
163
|
+
if (!projectPath) return {};
|
|
164
|
+
try {
|
|
165
|
+
const raw = fs.readFileSync(projectPath, 'utf8');
|
|
166
|
+
const json = JSON.parse(raw);
|
|
167
|
+
const out = json && typeof json === 'object' ? json : {};
|
|
168
|
+
out.__configPath = projectPath;
|
|
169
|
+
return out;
|
|
170
|
+
} catch (e) {
|
|
171
|
+
return { __configLoadError: `${projectPath}: ${e.message}` };
|
|
227
172
|
}
|
|
228
|
-
return {};
|
|
229
173
|
}
|
|
230
174
|
|
|
231
175
|
const external = loadExternalConfig();
|
|
@@ -234,22 +178,22 @@ export const config = merge(baseConfig, external);
|
|
|
234
178
|
const normalizeUnityConfig = () => {
|
|
235
179
|
const unityConfig = config.unity || (config.unity = {});
|
|
236
180
|
|
|
237
|
-
// Legacy aliases coming from config files
|
|
181
|
+
// Legacy aliases coming from config files
|
|
238
182
|
const legacyHost = unityConfig.host;
|
|
239
183
|
const legacyClientHost = unityConfig.clientHost;
|
|
240
184
|
const legacyBindHost = unityConfig.bindHost;
|
|
241
185
|
|
|
242
186
|
if (!unityConfig.unityHost) {
|
|
243
|
-
unityConfig.unityHost = legacyBindHost || legacyHost ||
|
|
187
|
+
unityConfig.unityHost = legacyBindHost || legacyHost || 'localhost';
|
|
244
188
|
}
|
|
245
189
|
|
|
246
190
|
if (!unityConfig.mcpHost) {
|
|
247
|
-
unityConfig.mcpHost = legacyClientHost ||
|
|
191
|
+
unityConfig.mcpHost = legacyClientHost || legacyHost || unityConfig.unityHost;
|
|
248
192
|
}
|
|
249
193
|
|
|
250
194
|
// Keep bindHost for backwards compatibility with legacy code paths
|
|
251
195
|
if (!unityConfig.bindHost) {
|
|
252
|
-
unityConfig.bindHost = legacyBindHost ||
|
|
196
|
+
unityConfig.bindHost = legacyBindHost || unityConfig.unityHost;
|
|
253
197
|
}
|
|
254
198
|
|
|
255
199
|
// Maintain legacy properties so older handlers keep working
|
|
@@ -291,7 +235,7 @@ if (config.__configLoadError) {
|
|
|
291
235
|
// Startup debug log: output config info to stderr for troubleshooting
|
|
292
236
|
// This helps diagnose connection issues (especially in WSL2/Docker environments)
|
|
293
237
|
console.error(`[unity-mcp-server] Startup config:`);
|
|
294
|
-
console.error(`[unity-mcp-server] Config file: ${config.__configPath || '(
|
|
238
|
+
console.error(`[unity-mcp-server] Config file: ${config.__configPath || '(not found)'}`);
|
|
295
239
|
console.error(
|
|
296
240
|
`[unity-mcp-server] Unity host: ${config.unity.mcpHost || config.unity.unityHost || 'localhost'}`
|
|
297
241
|
);
|
package/src/core/projectInfo.js
CHANGED
|
@@ -1,8 +1,36 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
1
2
|
import path from 'path';
|
|
2
3
|
import { logger, config, WORKSPACE_ROOT } from './config.js';
|
|
3
4
|
|
|
4
5
|
const normalize = p => p.replace(/\\/g, '/');
|
|
5
6
|
|
|
7
|
+
const looksLikeUnityProjectRoot = dir => {
|
|
8
|
+
try {
|
|
9
|
+
return (
|
|
10
|
+
fs.existsSync(path.join(dir, 'Assets')) &&
|
|
11
|
+
fs.existsSync(path.join(dir, 'Packages')) &&
|
|
12
|
+
fs.statSync(path.join(dir, 'Assets')).isDirectory() &&
|
|
13
|
+
fs.statSync(path.join(dir, 'Packages')).isDirectory()
|
|
14
|
+
);
|
|
15
|
+
} catch {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const inferUnityProjectRootFromDir = startDir => {
|
|
21
|
+
try {
|
|
22
|
+
let dir = startDir;
|
|
23
|
+
const { root } = path.parse(dir);
|
|
24
|
+
while (true) {
|
|
25
|
+
if (looksLikeUnityProjectRoot(dir)) return dir;
|
|
26
|
+
if (dir === root) return null;
|
|
27
|
+
dir = path.dirname(dir);
|
|
28
|
+
}
|
|
29
|
+
} catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
6
34
|
const resolveDefaultCodeIndexRoot = projectRoot => {
|
|
7
35
|
const base = WORKSPACE_ROOT || projectRoot || process.cwd();
|
|
8
36
|
return normalize(path.join(base, '.unity', 'cache', 'code-index'));
|
|
@@ -18,21 +46,6 @@ export class ProjectInfoProvider {
|
|
|
18
46
|
|
|
19
47
|
async get() {
|
|
20
48
|
if (this.cached) return this.cached;
|
|
21
|
-
// Env-driven project root override (primarily for tests)
|
|
22
|
-
const envRootRaw = process.env.UNITY_PROJECT_ROOT;
|
|
23
|
-
if (typeof envRootRaw === 'string' && envRootRaw.trim().length > 0) {
|
|
24
|
-
const envRoot = envRootRaw.trim();
|
|
25
|
-
const projectRoot = normalize(path.resolve(envRoot));
|
|
26
|
-
const codeIndexRoot = normalize(resolveDefaultCodeIndexRoot(projectRoot));
|
|
27
|
-
this.cached = {
|
|
28
|
-
projectRoot,
|
|
29
|
-
assetsPath: normalize(path.join(projectRoot, 'Assets')),
|
|
30
|
-
packagesPath: normalize(path.join(projectRoot, 'Packages')),
|
|
31
|
-
codeIndexRoot
|
|
32
|
-
};
|
|
33
|
-
return this.cached;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
49
|
// Config-driven project root (no env fallback)
|
|
37
50
|
const cfgRootRaw = config?.project?.root;
|
|
38
51
|
if (typeof cfgRootRaw === 'string' && cfgRootRaw.trim().length > 0) {
|
|
@@ -73,13 +86,28 @@ export class ProjectInfoProvider {
|
|
|
73
86
|
logger.warning(`get_editor_info failed: ${e.message}`);
|
|
74
87
|
}
|
|
75
88
|
}
|
|
89
|
+
|
|
90
|
+
// Best-effort local inference (when Unity isn't connected)
|
|
91
|
+
const inferredRoot = inferUnityProjectRootFromDir(process.cwd());
|
|
92
|
+
if (inferredRoot) {
|
|
93
|
+
const projectRoot = normalize(inferredRoot);
|
|
94
|
+
const codeIndexRoot = normalize(resolveDefaultCodeIndexRoot(projectRoot));
|
|
95
|
+
this.cached = {
|
|
96
|
+
projectRoot,
|
|
97
|
+
assetsPath: normalize(path.join(projectRoot, 'Assets')),
|
|
98
|
+
packagesPath: normalize(path.join(projectRoot, 'Packages')),
|
|
99
|
+
codeIndexRoot
|
|
100
|
+
};
|
|
101
|
+
return this.cached;
|
|
102
|
+
}
|
|
103
|
+
|
|
76
104
|
if (typeof cfgRootRaw === 'string') {
|
|
77
105
|
throw new Error(
|
|
78
|
-
'project.root is configured but empty. Set a valid path in .unity/config.json
|
|
106
|
+
'project.root is configured but empty. Set a valid path in .unity/config.json.'
|
|
79
107
|
);
|
|
80
108
|
}
|
|
81
109
|
throw new Error(
|
|
82
|
-
'Unable to resolve Unity project root.
|
|
110
|
+
'Unable to resolve Unity project root. Start the server inside a Unity project (directory containing Assets/ and Packages/) or configure project.root in .unity/config.json.'
|
|
83
111
|
);
|
|
84
112
|
}
|
|
85
113
|
|