@akiojin/unity-mcp-server 2.14.14

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.
Files changed (125) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +206 -0
  3. package/bin/unity-mcp-server +2 -0
  4. package/package.json +73 -0
  5. package/src/core/codeIndex.js +163 -0
  6. package/src/core/codeIndexDb.js +96 -0
  7. package/src/core/config.js +165 -0
  8. package/src/core/indexWatcher.js +52 -0
  9. package/src/core/projectInfo.js +111 -0
  10. package/src/core/server.js +294 -0
  11. package/src/core/unityConnection.js +426 -0
  12. package/src/handlers/analysis/AnalyzeSceneContentsToolHandler.js +35 -0
  13. package/src/handlers/analysis/FindByComponentToolHandler.js +20 -0
  14. package/src/handlers/analysis/GetAnimatorStateToolHandler.js +37 -0
  15. package/src/handlers/analysis/GetComponentValuesToolHandler.js +20 -0
  16. package/src/handlers/analysis/GetGameObjectDetailsToolHandler.js +35 -0
  17. package/src/handlers/analysis/GetInputActionsStateToolHandler.js +37 -0
  18. package/src/handlers/analysis/GetObjectReferencesToolHandler.js +20 -0
  19. package/src/handlers/asset/AssetDatabaseToolHandler.js +221 -0
  20. package/src/handlers/asset/AssetDependencyToolHandler.js +201 -0
  21. package/src/handlers/asset/AssetImportSettingsToolHandler.js +170 -0
  22. package/src/handlers/asset/CreateMaterialToolHandler.js +96 -0
  23. package/src/handlers/asset/CreatePrefabToolHandler.js +78 -0
  24. package/src/handlers/asset/ExitPrefabModeToolHandler.js +83 -0
  25. package/src/handlers/asset/InstantiatePrefabToolHandler.js +133 -0
  26. package/src/handlers/asset/ModifyMaterialToolHandler.js +76 -0
  27. package/src/handlers/asset/ModifyPrefabToolHandler.js +72 -0
  28. package/src/handlers/asset/OpenPrefabToolHandler.js +121 -0
  29. package/src/handlers/asset/SavePrefabToolHandler.js +106 -0
  30. package/src/handlers/base/BaseToolHandler.js +133 -0
  31. package/src/handlers/compilation/GetCompilationStateToolHandler.js +90 -0
  32. package/src/handlers/component/AddComponentToolHandler.js +126 -0
  33. package/src/handlers/component/GetComponentTypesToolHandler.js +100 -0
  34. package/src/handlers/component/ListComponentsToolHandler.js +85 -0
  35. package/src/handlers/component/ModifyComponentToolHandler.js +143 -0
  36. package/src/handlers/component/RemoveComponentToolHandler.js +108 -0
  37. package/src/handlers/console/ClearConsoleToolHandler.js +160 -0
  38. package/src/handlers/console/ReadConsoleToolHandler.js +276 -0
  39. package/src/handlers/editor/LayerManagementToolHandler.js +160 -0
  40. package/src/handlers/editor/SelectionToolHandler.js +141 -0
  41. package/src/handlers/editor/TagManagementToolHandler.js +129 -0
  42. package/src/handlers/editor/ToolManagementToolHandler.js +135 -0
  43. package/src/handlers/editor/WindowManagementToolHandler.js +125 -0
  44. package/src/handlers/gameobject/CreateGameObjectToolHandler.js +131 -0
  45. package/src/handlers/gameobject/DeleteGameObjectToolHandler.js +101 -0
  46. package/src/handlers/gameobject/FindGameObjectToolHandler.js +119 -0
  47. package/src/handlers/gameobject/GetHierarchyToolHandler.js +132 -0
  48. package/src/handlers/gameobject/ModifyGameObjectToolHandler.js +128 -0
  49. package/src/handlers/index.js +389 -0
  50. package/src/handlers/input/AddInputActionToolHandler.js +20 -0
  51. package/src/handlers/input/AddInputBindingToolHandler.js +20 -0
  52. package/src/handlers/input/CreateActionMapToolHandler.js +20 -0
  53. package/src/handlers/input/CreateCompositeBindingToolHandler.js +20 -0
  54. package/src/handlers/input/GamepadSimulationHandler.js +116 -0
  55. package/src/handlers/input/InputSystemHandler.js +80 -0
  56. package/src/handlers/input/KeyboardSimulationHandler.js +79 -0
  57. package/src/handlers/input/ManageControlSchemesToolHandler.js +20 -0
  58. package/src/handlers/input/MouseSimulationHandler.js +107 -0
  59. package/src/handlers/input/RemoveActionMapToolHandler.js +20 -0
  60. package/src/handlers/input/RemoveAllBindingsToolHandler.js +20 -0
  61. package/src/handlers/input/RemoveInputActionToolHandler.js +20 -0
  62. package/src/handlers/input/RemoveInputBindingToolHandler.js +20 -0
  63. package/src/handlers/input/TouchSimulationHandler.js +142 -0
  64. package/src/handlers/menu/ExecuteMenuItemToolHandler.js +304 -0
  65. package/src/handlers/package/PackageManagerToolHandler.js +248 -0
  66. package/src/handlers/package/RegistryConfigToolHandler.js +198 -0
  67. package/src/handlers/playmode/GetEditorStateToolHandler.js +81 -0
  68. package/src/handlers/playmode/PauseToolHandler.js +44 -0
  69. package/src/handlers/playmode/PlayToolHandler.js +91 -0
  70. package/src/handlers/playmode/StopToolHandler.js +77 -0
  71. package/src/handlers/playmode/WaitForEditorStateToolHandler.js +45 -0
  72. package/src/handlers/scene/CreateSceneToolHandler.js +91 -0
  73. package/src/handlers/scene/GetSceneInfoToolHandler.js +20 -0
  74. package/src/handlers/scene/ListScenesToolHandler.js +58 -0
  75. package/src/handlers/scene/LoadSceneToolHandler.js +92 -0
  76. package/src/handlers/scene/SaveSceneToolHandler.js +76 -0
  77. package/src/handlers/screenshot/AnalyzeScreenshotToolHandler.js +238 -0
  78. package/src/handlers/screenshot/CaptureScreenshotToolHandler.js +692 -0
  79. package/src/handlers/script/BuildCodeIndexToolHandler.js +163 -0
  80. package/src/handlers/script/ScriptCreateClassFileToolHandler.js +60 -0
  81. package/src/handlers/script/ScriptEditStructuredToolHandler.js +173 -0
  82. package/src/handlers/script/ScriptIndexStatusToolHandler.js +61 -0
  83. package/src/handlers/script/ScriptPackagesListToolHandler.js +103 -0
  84. package/src/handlers/script/ScriptReadToolHandler.js +106 -0
  85. package/src/handlers/script/ScriptRefactorRenameToolHandler.js +83 -0
  86. package/src/handlers/script/ScriptRefsFindToolHandler.js +144 -0
  87. package/src/handlers/script/ScriptRemoveSymbolToolHandler.js +79 -0
  88. package/src/handlers/script/ScriptSearchToolHandler.js +320 -0
  89. package/src/handlers/script/ScriptSymbolFindToolHandler.js +117 -0
  90. package/src/handlers/script/ScriptSymbolsGetToolHandler.js +96 -0
  91. package/src/handlers/settings/GetProjectSettingsToolHandler.js +161 -0
  92. package/src/handlers/settings/UpdateProjectSettingsToolHandler.js +272 -0
  93. package/src/handlers/system/GetCommandStatsToolHandler.js +25 -0
  94. package/src/handlers/system/PingToolHandler.js +53 -0
  95. package/src/handlers/system/RefreshAssetsToolHandler.js +45 -0
  96. package/src/handlers/ui/ClickUIElementToolHandler.js +110 -0
  97. package/src/handlers/ui/FindUIElementsToolHandler.js +63 -0
  98. package/src/handlers/ui/GetUIElementStateToolHandler.js +50 -0
  99. package/src/handlers/ui/SetUIElementValueToolHandler.js +49 -0
  100. package/src/handlers/ui/SimulateUIInputToolHandler.js +156 -0
  101. package/src/handlers/video/CaptureVideoForToolHandler.js +96 -0
  102. package/src/handlers/video/CaptureVideoStartToolHandler.js +38 -0
  103. package/src/handlers/video/CaptureVideoStatusToolHandler.js +30 -0
  104. package/src/handlers/video/CaptureVideoStopToolHandler.js +32 -0
  105. package/src/lsp/CSharpLspUtils.js +134 -0
  106. package/src/lsp/LspProcessManager.js +60 -0
  107. package/src/lsp/LspRpcClient.js +133 -0
  108. package/src/tools/analysis/analyzeSceneContents.js +100 -0
  109. package/src/tools/analysis/findByComponent.js +87 -0
  110. package/src/tools/analysis/getAnimatorState.js +326 -0
  111. package/src/tools/analysis/getComponentValues.js +182 -0
  112. package/src/tools/analysis/getGameObjectDetails.js +159 -0
  113. package/src/tools/analysis/getInputActionsState.js +329 -0
  114. package/src/tools/analysis/getObjectReferences.js +86 -0
  115. package/src/tools/input/inputActionsEditor.js +556 -0
  116. package/src/tools/scene/createScene.js +112 -0
  117. package/src/tools/scene/getSceneInfo.js +95 -0
  118. package/src/tools/scene/listScenes.js +82 -0
  119. package/src/tools/scene/loadScene.js +122 -0
  120. package/src/tools/scene/saveScene.js +91 -0
  121. package/src/tools/system/ping.js +72 -0
  122. package/src/tools/video/recordFor.js +31 -0
  123. package/src/tools/video/recordPlayMode.js +61 -0
  124. package/src/utils/csharpParse.js +88 -0
  125. package/src/utils/validators.js +90 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Ozan Kasıkçı
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,206 @@
1
+ # Unity Editor MCP Server
2
+
3
+ MCP (Model Context Protocol) server for Unity Editor integration. Enables AI assistants like Claude and Cursor to interact directly with Unity Editor for automated game development.
4
+
5
+ ## Features
6
+
7
+ - **70 comprehensive tools** across 13 categories for Unity Editor automation
8
+ - **GameObject management** - Create, find, modify, delete GameObjects with full hierarchy control
9
+ - **Component system** - Add, remove, modify components with property control
10
+ - **Scene management** - Create, load, save, list scenes with build settings integration
11
+ - **Scene analysis** - Deep inspection, component analysis, and performance metrics
12
+ - **Asset management** - Create and modify prefabs, materials, scripts with full control
13
+ - **UI automation** - Find, click, and interact with UI elements programmatically
14
+ - **Input simulation** - Simulate keyboard, mouse, gamepad, and touch input
15
+ - **Play mode controls** - Start, pause, stop Unity play mode for testing
16
+ - **Project settings** - Read and update Unity project settings safely
17
+ - **Editor operations** - Console logs, screenshots, compilation monitoring
18
+ - **Editor control** - Manage tags, layers, selection, windows, and tools
19
+
20
+ ## Quick Start
21
+
22
+ ### Using npx (Recommended)
23
+
24
+ ```bash
25
+ npx @akiojin/unity-mcp-server@latest
26
+ ```
27
+
28
+ ### Global Installation
29
+
30
+ ```bash
31
+ npm install -g @akiojin/unity-mcp-server
32
+ unity-mcp-server
33
+ ```
34
+
35
+ ### Local Installation
36
+
37
+ ```bash
38
+ npm install @akiojin/unity-mcp-server
39
+ npx unity-mcp-server
40
+ ```
41
+
42
+ ## Unity Setup
43
+
44
+ 1. Install the Unity package from: `https://github.com/akiojin/unity-mcp-server.git?path=UnityMCPServer/Packages/unity-mcp-server`
45
+ 2. Open Unity Package Manager → Add package from git URL
46
+ 3. The package will automatically start a TCP server on port 6400
47
+
48
+ ## MCP Client Configuration
49
+
50
+ ### Claude Desktop
51
+
52
+ Add to your `claude_desktop_config.json`:
53
+
54
+ ```json
55
+ {
56
+ "mcpServers": {
57
+ "unity-mcp-server": {
58
+ "command": "npx",
59
+ "args": ["@akiojin/unity-mcp-server@latest"]
60
+ }
61
+ }
62
+ }
63
+ ```
64
+
65
+ ### Alternative (if globally installed)
66
+
67
+ ```json
68
+ {
69
+ "mcpServers": {
70
+ "unity-mcp-server": {
71
+ "command": "unity-mcp-server"
72
+ }
73
+ }
74
+ }
75
+ ```
76
+
77
+ ## Available Tools (70 Tools)
78
+
79
+ ### System & Core Tools (2 tools)
80
+ - `ping` - Test connection to Unity Editor and verify server status
81
+ - `refresh_assets` - Refresh Unity assets and trigger recompilation
82
+
83
+ ### GameObject Management (5 tools)
84
+ - `create_gameobject` - Create GameObjects with primitives, transforms, tags, and layers
85
+ - `find_gameobject` - Find GameObjects by name, tag, layer with pattern matching
86
+ - `modify_gameobject` - Modify GameObject properties (transform, name, active state, parent)
87
+ - `delete_gameobject` - Delete single or multiple GameObjects with child handling
88
+ - `get_hierarchy` - Get scene hierarchy with components and depth control
89
+
90
+ ### Component System (5 tools)
91
+ - `add_component` - Add Unity components to GameObjects with initial properties
92
+ - `remove_component` - Remove components from GameObjects with safety checks
93
+ - `modify_component` - Modify component properties with nested property support
94
+ - `list_components` - List all components on a GameObject with type information
95
+ - `get_component_types` - Discover available component types with filtering
96
+
97
+ ### Scene Management (5 tools)
98
+ - `create_scene` - Create new scenes with build settings integration
99
+ - `load_scene` - Load scenes in Single or Additive mode
100
+ - `save_scene` - Save current scene with Save As functionality
101
+ - `list_scenes` - List all scenes in project with filtering options
102
+ - `get_scene_info` - Get detailed scene information including GameObject counts
103
+
104
+ ### Scene Analysis (7 tools)
105
+ - `get_gameobject_details` - Deep inspection of GameObjects with component details
106
+ - `analyze_scene_contents` - Comprehensive scene statistics and performance metrics
107
+ - `get_component_values` - Get all properties and values of specific components
108
+ - `find_by_component` - Find GameObjects by component type with scope filtering
109
+ - `get_object_references` - Analyze references between objects and assets
110
+ - `get_animator_state` - Get current Animator state, parameters, and transitions
111
+ - `get_animator_runtime_info` - Get runtime Animator info (Play mode only)
112
+
113
+ ### Asset Management (11 tools)
114
+ - `create_prefab` - Create prefabs from GameObjects or templates
115
+ - `modify_prefab` - Modify existing prefabs with property changes
116
+ - `instantiate_prefab` - Instantiate prefabs in scenes with transform options
117
+ - `open_prefab` - Open prefabs in Unity's prefab mode for editing
118
+ - `exit_prefab_mode` - Exit prefab mode with save/discard options
119
+ - `save_prefab` - Save prefab changes or apply instance overrides
120
+ - `create_material` - Create new materials with shader and properties
121
+ - `modify_material` - Modify material properties and shaders
122
+ - `manage_asset_import_settings` - Manage asset import settings and presets
123
+ - `manage_asset_database` - Asset database operations (find, move, copy, delete)
124
+ - `analyze_asset_dependencies` - Analyze asset dependencies and find unused assets
125
+
126
+ ### Script Management (6 tools)
127
+ - `create_script` - Create new C# scripts with templates (MonoBehaviour, ScriptableObject, etc.)
128
+ - `read_script` - Read script file contents with syntax highlighting
129
+ - `update_script` - Modify existing scripts with validation
130
+ - `delete_script` - Delete script files with dependency checking
131
+ - `list_scripts` - List all scripts in project with filtering and metadata
132
+ - `validate_script` - Validate script syntax and Unity compatibility
133
+
134
+ ### Play Mode Controls (4 tools)
135
+ - `play_game` - Start Unity play mode for testing
136
+ - `pause_game` - Pause or resume Unity play mode
137
+ - `stop_game` - Stop Unity play mode and return to edit mode
138
+ - `get_editor_state` - Get current editor state and compilation status
139
+
140
+ ### UI Automation (5 tools)
141
+ - `find_ui_elements` - Locate UI elements by type, tag, or name
142
+ - `click_ui_element` - Simulate clicking on UI elements (buttons, toggles)
143
+ - `get_ui_element_state` - Get UI element properties and interaction state
144
+ - `set_ui_element_value` - Set values for input fields, sliders, dropdowns
145
+ - `simulate_ui_input` - Execute complex UI interaction sequences
146
+
147
+ ### Input System Simulation (5 tools)
148
+ - `simulate_keyboard` - Simulate keyboard input with key combos and text typing
149
+ - `simulate_mouse` - Simulate mouse movement, clicks, drags, and scrolling
150
+ - `simulate_gamepad` - Simulate gamepad buttons, sticks, triggers, and d-pad
151
+ - `simulate_touch` - Simulate touch gestures (tap, swipe, pinch, multi-touch)
152
+ - `get_current_input_state` - Get current state of all input devices
153
+
154
+ ### Editor Operations (5 tools)
155
+ - `execute_menu_item` - Execute Unity menu items programmatically
156
+ - `clear_console` - Clear Unity console logs with filtering options
157
+ - `read_console` - Read console logs with advanced filtering and search
158
+ - `capture_screenshot` - Take screenshots of Game View or Scene View
159
+ - `analyze_screenshot` - Analyze screenshot content with image analysis
160
+
161
+ ### Editor Control & Automation (6 tools)
162
+ - `manage_tags` - Manage Unity project tags (add, remove, list)
163
+ - `manage_layers` - Manage Unity project layers with index conversion
164
+ - `manage_selection` - Manage Editor selection (get, set, clear)
165
+ - `manage_windows` - Manage Editor windows (list, focus, get state)
166
+ - `manage_tools` - Manage Editor tools and plugins
167
+ - `get_compilation_state` - Get current compilation state and errors
168
+
169
+ ### Project Settings Management (2 tools)
170
+ - `get_project_settings` - Read Unity project settings with granular control
171
+ - Player, Graphics, Quality, Physics, Audio, Time settings
172
+ - Build settings, Tags and layers configuration
173
+ - `update_project_settings` - Safely update project settings
174
+ - Requires explicit confirmation for safety
175
+ - Supports partial updates to specific categories
176
+
177
+ ## Requirements
178
+
179
+ - **Unity**: 2020.3 LTS or newer (Unity 6 supported)
180
+ - **Node.js**: 18.0.0 or newer
181
+ - **MCP Client**: Claude Desktop, Cursor, or compatible client
182
+
183
+ ## Troubleshooting
184
+
185
+ ### Connection Issues
186
+ 1. Ensure Unity Editor is running with the Unity package installed
187
+ 2. Check Unity console for connection messages
188
+ 3. Verify port 6400 is not blocked by firewall
189
+
190
+ ### Installation Issues
191
+ ```bash
192
+ # Clear npm cache
193
+ npm cache clean --force
194
+
195
+ # Reinstall
196
+ npm uninstall -g @akiojin/unity-mcp-server
197
+ npm install -g @akiojin/unity-mcp-server
198
+ ```
199
+
200
+ ## Repository
201
+
202
+ Full source code and documentation: https://github.com/akiojin/unity-mcp-server
203
+
204
+ ## License
205
+
206
+ MIT License - see LICENSE file for details.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import '../src/core/server.js';
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@akiojin/unity-mcp-server",
3
+ "version": "2.14.14",
4
+ "description": "MCP server and Unity Editor bridge — enables AI assistants to control Unity for AI-assisted workflows",
5
+ "type": "module",
6
+ "main": "src/core/server.js",
7
+ "bin": {
8
+ "unity-mcp-server": "./bin/unity-mcp-server"
9
+ },
10
+ "scripts": {
11
+ "start": "node src/core/server.js",
12
+ "dev": "node --watch src/core/server.js",
13
+ "build:index": "node src/tools/buildCodeIndex.js",
14
+ "test": "node --test tests/unit/**/*.test.js tests/integration/*.test.js",
15
+ "test:unit": "NODE_ENV=test node --test tests/unit/**/*.test.js",
16
+ "test:integration": "NODE_ENV=test node --test tests/integration/*.test.js",
17
+ "test:e2e": "NODE_ENV=test node --test tests/e2e/*.test.js",
18
+ "test:coverage": "c8 --reporter=lcov --reporter=text --reporter=html node --test tests/unit/**/*.test.js tests/integration/*.test.js",
19
+ "test:coverage:full": "c8 --reporter=lcov --reporter=text --reporter=html node --test tests/**/*.test.js",
20
+ "test:watch": "node --watch --test tests/unit/**/*.test.js",
21
+ "test:watch:all": "node --watch --test tests/**/*.test.js",
22
+ "test:performance": "node --test tests/performance/*.test.js",
23
+ "test:ci": "c8 --reporter=lcov --check-coverage=false node --test tests/unit/core/config.test.js tests/unit/handlers/PingToolHandler.test.js tests/unit/handlers/CreateGameObjectToolHandler.test.js",
24
+ "test:ci:all": "c8 --reporter=lcov node --test tests/unit/**/*.test.js",
25
+ "test:verbose": "VERBOSE_TEST=true node --test tests/**/*.test.js",
26
+ "prepublishOnly": "npm run test:ci",
27
+ "postinstall": "chmod +x bin/unity-mcp-server || true"
28
+ },
29
+ "keywords": [
30
+ "mcp",
31
+ "unity",
32
+ "unity-editor",
33
+ "model-context-protocol",
34
+ "ai",
35
+ "automation",
36
+ "claude",
37
+ "cursor",
38
+ "gamedev",
39
+ "unity3d"
40
+ ],
41
+ "author": "Akio Jinsenji <akio-jinsenji@cloud-creative-studios.com>",
42
+ "license": "MIT",
43
+ "dependencies": {
44
+ "@modelcontextprotocol/sdk": "^0.6.1",
45
+ "better-sqlite3": "^9.4.3"
46
+ },
47
+ "engines": {
48
+ "node": ">=18.0.0"
49
+ },
50
+ "repository": {
51
+ "type": "git",
52
+ "url": "https://github.com/akiojin/unity-mcp-server.git",
53
+ "directory": "mcp-server"
54
+ },
55
+ "homepage": "https://github.com/akiojin/unity-mcp-server#readme",
56
+ "bugs": {
57
+ "url": "https://github.com/akiojin/unity-mcp-server/issues"
58
+ },
59
+ "files": [
60
+ "src/",
61
+ "bin/",
62
+ "README.md",
63
+ "LICENSE"
64
+ ],
65
+ "preferGlobal": false,
66
+ "publishConfig": {
67
+ "access": "public"
68
+ },
69
+ "devDependencies": {
70
+ "c8": "^10.1.3",
71
+ "nodemon": "^3.1.7"
72
+ }
73
+ }
@@ -0,0 +1,163 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { ProjectInfoProvider } from './projectInfo.js';
4
+
5
+ export class CodeIndex {
6
+ constructor(unityConnection) {
7
+ this.unityConnection = unityConnection;
8
+ this.projectInfo = new ProjectInfoProvider(unityConnection);
9
+ this.db = null;
10
+ this.dbPath = null;
11
+ this.disabled = false; // set true if better-sqlite3 is unavailable
12
+ this._Database = null;
13
+ }
14
+
15
+ async _ensureDriver() {
16
+ if (this.disabled) return false;
17
+ if (this._Database) return true;
18
+ try {
19
+ // Dynamic import to avoid hard failure when native binding is missing
20
+ const mod = await import('better-sqlite3');
21
+ this._Database = mod.default || mod;
22
+ return true;
23
+ } catch (e) {
24
+ // Mark as disabled and operate in fallback (index unavailable)
25
+ this.disabled = true;
26
+ return false;
27
+ }
28
+ }
29
+
30
+ async open() {
31
+ if (this.db) return this.db;
32
+ const ok = await this._ensureDriver();
33
+ if (!ok) return null; // index disabled
34
+ const info = await this.projectInfo.get();
35
+ const dir = info.codeIndexRoot;
36
+ fs.mkdirSync(dir, { recursive: true });
37
+ const dbPath = path.join(dir, 'code-index.db');
38
+ this.dbPath = dbPath;
39
+ this.db = new this._Database(dbPath);
40
+ this._initSchema();
41
+ return this.db;
42
+ }
43
+
44
+ _initSchema() {
45
+ if (!this.db) return;
46
+ const db = this.db;
47
+ db.exec(`
48
+ PRAGMA journal_mode=WAL;
49
+ CREATE TABLE IF NOT EXISTS meta (
50
+ key TEXT PRIMARY KEY,
51
+ value TEXT
52
+ );
53
+ CREATE TABLE IF NOT EXISTS files (
54
+ path TEXT PRIMARY KEY,
55
+ sig TEXT,
56
+ updatedAt TEXT
57
+ );
58
+ CREATE TABLE IF NOT EXISTS symbols (
59
+ path TEXT NOT NULL,
60
+ name TEXT NOT NULL,
61
+ kind TEXT NOT NULL,
62
+ container TEXT,
63
+ namespace TEXT,
64
+ line INTEGER,
65
+ column INTEGER
66
+ );
67
+ CREATE INDEX IF NOT EXISTS idx_symbols_name ON symbols(name);
68
+ CREATE INDEX IF NOT EXISTS idx_symbols_kind ON symbols(kind);
69
+ CREATE INDEX IF NOT EXISTS idx_symbols_path ON symbols(path);
70
+ `);
71
+ }
72
+
73
+ async isReady() {
74
+ const db = await this.open();
75
+ if (!db) return false;
76
+ const row = db.prepare('SELECT COUNT(*) AS c FROM symbols').get();
77
+ return (row?.c || 0) > 0;
78
+ }
79
+
80
+ async clearAndLoad(symbols) {
81
+ const db = await this.open();
82
+ if (!db) throw new Error('CodeIndex is unavailable (better-sqlite3 not installed)');
83
+ const insert = db.prepare('INSERT INTO symbols(path,name,kind,container,namespace,line,column) VALUES (?,?,?,?,?,?,?)');
84
+ const tx = db.transaction((rows) => {
85
+ db.exec('DELETE FROM symbols');
86
+ db.exec('DELETE FROM files');
87
+ for (const r of rows) {
88
+ insert.run(r.path, r.name, r.kind, r.container || null, r.ns || r.namespace || null, r.line || null, r.column || null);
89
+ }
90
+ db.prepare('REPLACE INTO meta(key,value) VALUES (?,?)').run('lastIndexedAt', new Date().toISOString());
91
+ });
92
+ tx(symbols || []);
93
+ return { total: symbols?.length || 0 };
94
+ }
95
+
96
+ // Incremental APIs
97
+ async getFiles() {
98
+ const db = await this.open();
99
+ if (!db) return new Map();
100
+ const rows = db.prepare('SELECT path, sig FROM files').all();
101
+ const map = new Map();
102
+ for (const r of rows) map.set(String(r.path), String(r.sig || ''));
103
+ return map;
104
+ }
105
+
106
+ async upsertFile(pathStr, sig) {
107
+ const db = await this.open();
108
+ if (!db) return;
109
+ db.prepare('REPLACE INTO files(path,sig,updatedAt) VALUES (?,?,?)').run(pathStr, sig || '', new Date().toISOString());
110
+ }
111
+
112
+ async removeFile(pathStr) {
113
+ const db = await this.open();
114
+ if (!db) return;
115
+ const tx = db.transaction((p) => {
116
+ db.prepare('DELETE FROM symbols WHERE path = ?').run(p);
117
+ db.prepare('DELETE FROM files WHERE path = ?').run(p);
118
+ });
119
+ tx(pathStr);
120
+ }
121
+
122
+ async replaceSymbolsForPath(pathStr, rows) {
123
+ const db = await this.open();
124
+ if (!db) return;
125
+ const tx = db.transaction((p, list) => {
126
+ db.prepare('DELETE FROM symbols WHERE path = ?').run(p);
127
+ const insert = db.prepare('INSERT INTO symbols(path,name,kind,container,namespace,line,column) VALUES (?,?,?,?,?,?,?)');
128
+ for (const r of list) insert.run(p, r.name, r.kind, r.container || null, r.ns || r.namespace || null, r.line || null, r.column || null);
129
+ db.prepare('REPLACE INTO meta(key,value) VALUES (?,?)').run('lastIndexedAt', new Date().toISOString());
130
+ });
131
+ tx(pathStr, rows || []);
132
+ }
133
+
134
+ async querySymbols({ name, kind, scope = 'all', exact = false }) {
135
+ const db = await this.open();
136
+ if (!db) return [];
137
+ let sql = 'SELECT path,name,kind,container,namespace,line,column FROM symbols WHERE 1=1';
138
+ const params = {};
139
+ if (name) {
140
+ if (exact) { sql += ' AND name = @name'; params.name = name; }
141
+ else { sql += ' AND name LIKE @name'; params.name = `%${name}%`; }
142
+ }
143
+ if (kind) { sql += ' AND kind = @kind'; params.kind = kind; }
144
+ const rows = db.prepare(sql).all(params);
145
+ // Apply path-based scope filter in JS (simpler than CASE in SQL)
146
+ const filtered = rows.filter(r => {
147
+ const p = String(r.path || '').replace(/\\\\/g, '/');
148
+ if (scope === 'assets') return p.startsWith('Assets/');
149
+ if (scope === 'packages') return p.startsWith('Packages/') || p.includes('Library/PackageCache/');
150
+ if (scope === 'embedded') return p.startsWith('Packages/');
151
+ return true;
152
+ });
153
+ return filtered.map(r => ({ path: r.path, name: r.name, kind: r.kind, container: r.container, ns: r.namespace, line: r.line, column: r.column }));
154
+ }
155
+
156
+ async getStats() {
157
+ const db = await this.open();
158
+ if (!db) return { total: 0, lastIndexedAt: null };
159
+ const total = db.prepare('SELECT COUNT(*) AS c FROM symbols').get().c || 0;
160
+ const last = db.prepare("SELECT value AS v FROM meta WHERE key = 'lastIndexedAt'").get()?.v || null;
161
+ return { total, lastIndexedAt: last };
162
+ }
163
+ }
@@ -0,0 +1,96 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import Database from 'better-sqlite3';
4
+ import { logger } from './config.js';
5
+
6
+ let dbCache = new Map();
7
+
8
+ function getDbPath(projectRoot) {
9
+ const dir = path.join(projectRoot, 'Library', 'UnityMCP', 'CodeIndex');
10
+ fs.mkdirSync(dir, { recursive: true });
11
+ return path.join(dir, 'index.db');
12
+ }
13
+
14
+ export function openDb(projectRoot) {
15
+ const key = path.resolve(projectRoot);
16
+ if (dbCache.has(key)) return dbCache.get(key);
17
+ const dbPath = getDbPath(projectRoot);
18
+ const db = new Database(dbPath);
19
+ db.pragma('journal_mode = WAL');
20
+ db.exec(`
21
+ CREATE TABLE IF NOT EXISTS files (
22
+ path TEXT PRIMARY KEY,
23
+ mtime INTEGER NOT NULL
24
+ );
25
+ CREATE TABLE IF NOT EXISTS symbols (
26
+ path TEXT NOT NULL,
27
+ name TEXT NOT NULL,
28
+ kind TEXT,
29
+ container TEXT,
30
+ ns TEXT,
31
+ line INTEGER,
32
+ column INTEGER,
33
+ FOREIGN KEY(path) REFERENCES files(path)
34
+ );
35
+ CREATE INDEX IF NOT EXISTS idx_symbols_name ON symbols(name);
36
+ CREATE TABLE IF NOT EXISTS refs (
37
+ path TEXT NOT NULL,
38
+ name TEXT NOT NULL,
39
+ line INTEGER,
40
+ snippet TEXT,
41
+ FOREIGN KEY(path) REFERENCES files(path)
42
+ );
43
+ CREATE INDEX IF NOT EXISTS idx_refs_name ON refs(name);
44
+ `);
45
+ dbCache.set(key, db);
46
+ return db;
47
+ }
48
+
49
+ export function upsertFile(db, filePath, mtimeMs) {
50
+ const stmt = db.prepare('INSERT INTO files(path, mtime) VALUES(?, ?) ON CONFLICT(path) DO UPDATE SET mtime=excluded.mtime');
51
+ stmt.run(filePath, Math.floor(mtimeMs));
52
+ }
53
+
54
+ export function replaceSymbols(db, filePath, symbols) {
55
+ const del = db.prepare('DELETE FROM symbols WHERE path = ?');
56
+ del.run(filePath);
57
+ const ins = db.prepare('INSERT INTO symbols(path, name, kind, container, ns, line, column) VALUES(?,?,?,?,?,?,?)');
58
+ const tr = db.transaction((rows) => {
59
+ for (const s of rows) ins.run(filePath, s.name || '', s.kind || '', s.container || null, s.ns || null, s.line || 0, s.column || 0);
60
+ });
61
+ tr(symbols || []);
62
+ }
63
+
64
+ export function replaceReferences(db, filePath, refs) {
65
+ const del = db.prepare('DELETE FROM refs WHERE path = ?');
66
+ del.run(filePath);
67
+ const ins = db.prepare('INSERT INTO refs(path, name, line, snippet) VALUES(?,?,?,?)');
68
+ const tr = db.transaction((rows) => {
69
+ for (const r of rows) ins.run(filePath, r.name || '', r.line || 0, r.snippet || null);
70
+ });
71
+ tr(refs || []);
72
+ }
73
+
74
+ export function querySymbolsByName(db, name, kind = null) {
75
+ if (kind) {
76
+ return db.prepare('SELECT path,name,kind,container,ns,line,column FROM symbols WHERE name = ? AND kind = ? LIMIT 500').all(name, kind);
77
+ }
78
+ return db.prepare('SELECT path,name,kind,container,ns,line,column FROM symbols WHERE name = ? LIMIT 500').all(name);
79
+ }
80
+
81
+ export function queryRefsByName(db, name) {
82
+ return db.prepare('SELECT path,name,line,snippet FROM refs WHERE name = ? LIMIT 1000').all(name);
83
+ }
84
+
85
+ export function isFresh(projectRoot, filePath, db) {
86
+ try {
87
+ const row = db.prepare('SELECT mtime FROM files WHERE path = ?').get(filePath);
88
+ if (!row) return false;
89
+ const abs = path.join(projectRoot, filePath);
90
+ const st = fs.statSync(abs);
91
+ return Math.floor(st.mtimeMs) === row.mtime;
92
+ } catch {
93
+ return false;
94
+ }
95
+ }
96
+