@akiojin/unity-mcp-server 2.40.2 → 2.40.4

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 (93) hide show
  1. package/README.md +21 -0
  2. package/bin/unity-mcp-server +1 -1
  3. package/package.json +4 -3
  4. package/src/core/codeIndex.js +143 -20
  5. package/src/core/indexWatcher.js +11 -0
  6. package/src/core/server.js +9 -34
  7. package/src/core/sqliteFallback.js +75 -0
  8. package/src/handlers/analysis/AnalyzeSceneContentsToolHandler.js +27 -24
  9. package/src/handlers/analysis/FindByComponentToolHandler.js +4 -1
  10. package/src/handlers/analysis/GetAnimatorStateToolHandler.js +5 -5
  11. package/src/handlers/analysis/GetComponentValuesToolHandler.js +4 -1
  12. package/src/handlers/analysis/GetGameObjectDetailsToolHandler.js +27 -24
  13. package/src/handlers/analysis/GetInputActionsStateToolHandler.js +5 -5
  14. package/src/handlers/analysis/GetObjectReferencesToolHandler.js +4 -1
  15. package/src/handlers/asset/AssetDatabaseManageToolHandler.js +24 -6
  16. package/src/handlers/asset/AssetDependencyAnalyzeToolHandler.js +21 -11
  17. package/src/handlers/asset/AssetImportSettingsManageToolHandler.js +7 -7
  18. package/src/handlers/asset/AssetMaterialCreateToolHandler.js +78 -81
  19. package/src/handlers/asset/AssetMaterialModifyToolHandler.js +57 -61
  20. package/src/handlers/asset/AssetPrefabCreateToolHandler.js +61 -64
  21. package/src/handlers/asset/AssetPrefabExitModeToolHandler.js +9 -13
  22. package/src/handlers/asset/AssetPrefabInstantiateToolHandler.js +110 -116
  23. package/src/handlers/asset/AssetPrefabModifyToolHandler.js +58 -58
  24. package/src/handlers/asset/AssetPrefabOpenToolHandler.js +7 -5
  25. package/src/handlers/asset/AssetPrefabSaveToolHandler.js +13 -6
  26. package/src/handlers/compilation/CompilationGetStateToolHandler.js +4 -3
  27. package/src/handlers/component/ComponentAddToolHandler.js +2 -2
  28. package/src/handlers/component/ComponentGetTypesToolHandler.js +17 -21
  29. package/src/handlers/component/ComponentListToolHandler.js +5 -3
  30. package/src/handlers/component/ComponentModifyToolHandler.js +3 -3
  31. package/src/handlers/component/ComponentRemoveToolHandler.js +2 -2
  32. package/src/handlers/console/ConsoleClearToolHandler.js +36 -46
  33. package/src/handlers/editor/EditorLayersManageToolHandler.js +7 -6
  34. package/src/handlers/editor/EditorTagsManageToolHandler.js +20 -11
  35. package/src/handlers/editor/EditorToolsManageToolHandler.js +2 -2
  36. package/src/handlers/editor/EditorWindowsManageToolHandler.js +6 -5
  37. package/src/handlers/gameobject/GameObjectCreateToolHandler.js +62 -66
  38. package/src/handlers/gameobject/GameObjectDeleteToolHandler.js +9 -9
  39. package/src/handlers/gameobject/GameObjectFindToolHandler.js +13 -11
  40. package/src/handlers/gameobject/GameObjectGetHierarchyToolHandler.js +22 -16
  41. package/src/handlers/input/InputActionAddToolHandler.js +2 -2
  42. package/src/handlers/input/InputActionMapCreateToolHandler.js +2 -2
  43. package/src/handlers/input/InputActionMapRemoveToolHandler.js +2 -2
  44. package/src/handlers/input/InputActionRemoveToolHandler.js +2 -2
  45. package/src/handlers/input/InputBindingAddToolHandler.js +2 -2
  46. package/src/handlers/input/InputBindingCompositeCreateToolHandler.js +2 -2
  47. package/src/handlers/input/InputBindingRemoveAllToolHandler.js +2 -2
  48. package/src/handlers/input/InputBindingRemoveToolHandler.js +2 -2
  49. package/src/handlers/input/InputControlSchemesManageToolHandler.js +2 -2
  50. package/src/handlers/package/PackageManagerToolHandler.js +41 -44
  51. package/src/handlers/package/RegistryConfigToolHandler.js +28 -7
  52. package/src/handlers/playmode/PlaymodeGetStateToolHandler.js +12 -16
  53. package/src/handlers/playmode/PlaymodePauseToolHandler.js +8 -12
  54. package/src/handlers/playmode/PlaymodeWaitForStateToolHandler.js +6 -3
  55. package/src/handlers/scene/GetSceneInfoToolHandler.js +11 -11
  56. package/src/handlers/scene/SceneCreateToolHandler.js +28 -31
  57. package/src/handlers/scene/SceneListToolHandler.js +21 -24
  58. package/src/handlers/scene/SceneLoadToolHandler.js +27 -29
  59. package/src/handlers/scene/SceneSaveToolHandler.js +19 -22
  60. package/src/handlers/screenshot/ScreenshotCaptureToolHandler.js +88 -66
  61. package/src/handlers/script/CodeIndexBuildToolHandler.js +7 -0
  62. package/src/handlers/script/CodeIndexStatusToolHandler.js +4 -3
  63. package/src/handlers/script/CodeIndexUpdateToolHandler.js +24 -14
  64. package/src/handlers/script/ScriptCreateClassToolHandler.js +44 -9
  65. package/src/handlers/script/ScriptPackagesListToolHandler.js +91 -91
  66. package/src/handlers/script/ScriptRefactorRenameToolHandler.js +80 -71
  67. package/src/handlers/script/ScriptRemoveSymbolToolHandler.js +21 -7
  68. package/src/handlers/script/ScriptSearchToolHandler.js +299 -266
  69. package/src/handlers/script/ScriptSymbolsGetToolHandler.js +88 -79
  70. package/src/handlers/settings/SettingsGetToolHandler.js +28 -13
  71. package/src/handlers/settings/SettingsUpdateToolHandler.js +20 -6
  72. package/src/handlers/ui/UIClickElementToolHandler.js +87 -96
  73. package/src/handlers/ui/UIFindElementsToolHandler.js +45 -55
  74. package/src/handlers/ui/UIGetElementStateToolHandler.js +35 -43
  75. package/src/handlers/ui/UISetElementValueToolHandler.js +42 -49
  76. package/src/handlers/ui/UISimulateInputToolHandler.js +134 -136
  77. package/src/handlers/video/VideoCaptureForToolHandler.js +24 -7
  78. package/src/lsp/LspRpcClient.js +24 -12
  79. package/src/tools/analysis/analyzeSceneContents.js +85 -85
  80. package/src/tools/analysis/findByComponent.js +73 -73
  81. package/src/tools/analysis/getAnimatorState.js +287 -287
  82. package/src/tools/analysis/getComponentValues.js +161 -161
  83. package/src/tools/analysis/getGameObjectDetails.js +138 -138
  84. package/src/tools/analysis/getInputActionsState.js +291 -291
  85. package/src/tools/analysis/getObjectReferences.js +72 -72
  86. package/src/tools/input/inputActionsEditor.js +522 -474
  87. package/src/tools/scene/createScene.js +98 -97
  88. package/src/tools/scene/getSceneInfo.js +82 -81
  89. package/src/tools/scene/listScenes.js +70 -69
  90. package/src/tools/scene/loadScene.js +108 -106
  91. package/src/tools/scene/saveScene.js +78 -77
  92. package/src/tools/system/ping.js +9 -12
  93. package/src/utils/validators.js +2 -2
package/README.md CHANGED
@@ -332,6 +332,27 @@ npm uninstall -g @akiojin/unity-mcp-server
332
332
  npm install -g @akiojin/unity-mcp-server
333
333
  ```
334
334
 
335
+ ### MCP Client Shows "Capabilities: none"
336
+
337
+ If your MCP client (Claude Code, Cursor, etc.) shows "Capabilities: none" despite successful connection:
338
+
339
+ **Symptom**: Server connects successfully, but no tools are visible to the client.
340
+
341
+ **Root Cause**: Empty capability objects (`resources: {}`, `prompts: {}`) in MCP SDK v0.6.1 cause capability validation to fail silently.
342
+
343
+ **Solution**: Update to latest version with the fix:
344
+
345
+ ```bash
346
+ # Update to latest version (2.41.0+)
347
+ npm update -g @akiojin/unity-mcp-server
348
+
349
+ # Or reinstall
350
+ npm uninstall -g @akiojin/unity-mcp-server
351
+ npm install -g @akiojin/unity-mcp-server
352
+ ```
353
+
354
+ **Verification**: After restart, your MCP client should display 107 available tools.
355
+
335
356
  ## Repository
336
357
 
337
358
  Full source code and documentation: <https://github.com/akiojin/unity-mcp-server>
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { startServer } from '../src/core/server.js';
3
3
 
4
- startServer().catch((error) => {
4
+ startServer().catch(error => {
5
5
  console.error('Fatal error:', error);
6
6
  console.error('Stack trace:', error?.stack);
7
7
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akiojin/unity-mcp-server",
3
- "version": "2.40.2",
3
+ "version": "2.40.4",
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",
@@ -27,7 +27,7 @@
27
27
  "test:verbose": "VERBOSE_TEST=true node --test tests/**/*.test.js",
28
28
  "prepare": "cd .. && husky || true",
29
29
  "prepublishOnly": "npm run test:ci",
30
- "postinstall": "chmod +x bin/unity-mcp-server || true",
30
+ "postinstall": "node scripts/ensure-better-sqlite3.mjs && chmod +x bin/unity-mcp-server || true",
31
31
  "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",
32
32
  "test:unity": "node tests/run-unity-integration.mjs",
33
33
  "test:nounity": "npm run test:integration",
@@ -50,7 +50,8 @@
50
50
  "dependencies": {
51
51
  "@modelcontextprotocol/sdk": "^0.6.1",
52
52
  "better-sqlite3": "^9.4.3",
53
- "find-up": "^6.3.0"
53
+ "find-up": "^6.3.0",
54
+ "sql.js": "^1.13.0"
54
55
  },
55
56
  "engines": {
56
57
  "node": ">=18 <23"
@@ -1,6 +1,15 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
3
  import { ProjectInfoProvider } from './projectInfo.js';
4
+ import { logger } from './config.js';
5
+ import { createSqliteFallback } from './sqliteFallback.js';
6
+
7
+ // Shared driver availability state across CodeIndex instances
8
+ const driverStatus = {
9
+ available: null,
10
+ error: null,
11
+ logged: false
12
+ };
4
13
 
5
14
  export class CodeIndex {
6
15
  constructor(unityConnection) {
@@ -9,21 +18,42 @@ export class CodeIndex {
9
18
  this.db = null;
10
19
  this.dbPath = null;
11
20
  this.disabled = false; // set true if better-sqlite3 is unavailable
21
+ this.disableReason = null;
12
22
  this._Database = null;
23
+ this._openFallback = null;
24
+ this._persistFallback = null;
13
25
  }
14
26
 
15
27
  async _ensureDriver() {
16
- if (this.disabled) return false;
28
+ if (driverStatus.available === false || this.disabled) {
29
+ this.disabled = true;
30
+ this.disableReason = this.disableReason || driverStatus.error;
31
+ return false;
32
+ }
17
33
  if (this._Database) return true;
18
34
  try {
19
35
  // Dynamic import to avoid hard failure when native binding is missing
20
36
  const mod = await import('better-sqlite3');
21
37
  this._Database = mod.default || mod;
38
+ driverStatus.available = true;
39
+ driverStatus.error = null;
22
40
  return true;
23
41
  } catch (e) {
24
- // Mark as disabled and operate in fallback (index unavailable)
25
- this.disabled = true;
26
- return false;
42
+ // Try wasm fallback (sql.js) before giving up
43
+ try {
44
+ this._openFallback = createSqliteFallback;
45
+ driverStatus.available = true;
46
+ driverStatus.error = null;
47
+ logger?.info?.('[index] falling back to sql.js (WASM) for code index');
48
+ return true;
49
+ } catch (fallbackError) {
50
+ this.disabled = true;
51
+ this.disableReason = `better-sqlite3 unavailable: ${e?.message || e}. Fallback failed: ${fallbackError?.message || fallbackError}`;
52
+ driverStatus.available = false;
53
+ driverStatus.error = this.disableReason;
54
+ this._logDisable(this.disableReason);
55
+ return false;
56
+ }
27
57
  }
28
58
  }
29
59
 
@@ -36,11 +66,35 @@ export class CodeIndex {
36
66
  fs.mkdirSync(dir, { recursive: true });
37
67
  const dbPath = path.join(dir, 'code-index.db');
38
68
  this.dbPath = dbPath;
39
- this.db = new this._Database(dbPath);
69
+ try {
70
+ if (this._Database) {
71
+ this.db = new this._Database(dbPath);
72
+ } else if (this._openFallback) {
73
+ this.db = await this._openFallback(dbPath);
74
+ this._persistFallback = this.db.persist;
75
+ } else {
76
+ throw new Error('No database driver available');
77
+ }
78
+ } catch (e) {
79
+ this.disabled = true;
80
+ this.disableReason = e?.message || 'Failed to open code index database';
81
+ driverStatus.available = false;
82
+ driverStatus.error = this.disableReason;
83
+ this._logDisable(this.disableReason);
84
+ return null;
85
+ }
40
86
  this._initSchema();
41
87
  return this.db;
42
88
  }
43
89
 
90
+ _logDisable(reason) {
91
+ if (driverStatus.logged) return;
92
+ driverStatus.logged = true;
93
+ try {
94
+ logger?.warn?.(`[index] code index disabled: ${reason}`);
95
+ } catch {}
96
+ }
97
+
44
98
  _initSchema() {
45
99
  if (!this.db) return;
46
100
  const db = this.db;
@@ -68,6 +122,7 @@ export class CodeIndex {
68
122
  CREATE INDEX IF NOT EXISTS idx_symbols_kind ON symbols(kind);
69
123
  CREATE INDEX IF NOT EXISTS idx_symbols_path ON symbols(path);
70
124
  `);
125
+ if (this._persistFallback) this._persistFallback();
71
126
  }
72
127
 
73
128
  async isReady() {
@@ -80,16 +135,30 @@ export class CodeIndex {
80
135
  async clearAndLoad(symbols) {
81
136
  const db = await this.open();
82
137
  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) => {
138
+ const insert = db.prepare(
139
+ 'INSERT INTO symbols(path,name,kind,container,namespace,line,column) VALUES (?,?,?,?,?,?,?)'
140
+ );
141
+ const tx = db.transaction(rows => {
85
142
  db.exec('DELETE FROM symbols');
86
143
  db.exec('DELETE FROM files');
87
144
  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);
145
+ insert.run(
146
+ r.path,
147
+ r.name,
148
+ r.kind,
149
+ r.container || null,
150
+ r.ns || r.namespace || null,
151
+ r.line || null,
152
+ r.column || null
153
+ );
89
154
  }
90
- db.prepare('REPLACE INTO meta(key,value) VALUES (?,?)').run('lastIndexedAt', new Date().toISOString());
155
+ db.prepare('REPLACE INTO meta(key,value) VALUES (?,?)').run(
156
+ 'lastIndexedAt',
157
+ new Date().toISOString()
158
+ );
91
159
  });
92
160
  tx(symbols || []);
161
+ await this._flushFallback();
93
162
  return { total: symbols?.length || 0 };
94
163
  }
95
164
 
@@ -106,17 +175,23 @@ export class CodeIndex {
106
175
  async upsertFile(pathStr, sig) {
107
176
  const db = await this.open();
108
177
  if (!db) return;
109
- db.prepare('REPLACE INTO files(path,sig,updatedAt) VALUES (?,?,?)').run(pathStr, sig || '', new Date().toISOString());
178
+ db.prepare('REPLACE INTO files(path,sig,updatedAt) VALUES (?,?,?)').run(
179
+ pathStr,
180
+ sig || '',
181
+ new Date().toISOString()
182
+ );
183
+ await this._flushFallback();
110
184
  }
111
185
 
112
186
  async removeFile(pathStr) {
113
187
  const db = await this.open();
114
188
  if (!db) return;
115
- const tx = db.transaction((p) => {
189
+ const tx = db.transaction(p => {
116
190
  db.prepare('DELETE FROM symbols WHERE path = ?').run(p);
117
191
  db.prepare('DELETE FROM files WHERE path = ?').run(p);
118
192
  });
119
193
  tx(pathStr);
194
+ await this._flushFallback();
120
195
  }
121
196
 
122
197
  async replaceSymbolsForPath(pathStr, rows) {
@@ -124,11 +199,26 @@ export class CodeIndex {
124
199
  if (!db) return;
125
200
  const tx = db.transaction((p, list) => {
126
201
  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());
202
+ const insert = db.prepare(
203
+ 'INSERT INTO symbols(path,name,kind,container,namespace,line,column) VALUES (?,?,?,?,?,?,?)'
204
+ );
205
+ for (const r of list)
206
+ insert.run(
207
+ p,
208
+ r.name,
209
+ r.kind,
210
+ r.container || null,
211
+ r.ns || r.namespace || null,
212
+ r.line || null,
213
+ r.column || null
214
+ );
215
+ db.prepare('REPLACE INTO meta(key,value) VALUES (?,?)').run(
216
+ 'lastIndexedAt',
217
+ new Date().toISOString()
218
+ );
130
219
  });
131
220
  tx(pathStr, rows || []);
221
+ await this._flushFallback();
132
222
  }
133
223
 
134
224
  async querySymbols({ name, kind, scope = 'all', exact = false }) {
@@ -137,27 +227,60 @@ export class CodeIndex {
137
227
  let sql = 'SELECT path,name,kind,container,namespace,line,column FROM symbols WHERE 1=1';
138
228
  const params = {};
139
229
  if (name) {
140
- if (exact) { sql += ' AND name = @name'; params.name = name; }
141
- else { sql += ' AND name LIKE @name'; params.name = `%${name}%`; }
230
+ if (exact) {
231
+ sql += ' AND name = @name';
232
+ params.name = name;
233
+ } else {
234
+ sql += ' AND name LIKE @name';
235
+ params.name = `%${name}%`;
236
+ }
237
+ }
238
+ if (kind) {
239
+ sql += ' AND kind = @kind';
240
+ params.kind = kind;
142
241
  }
143
- if (kind) { sql += ' AND kind = @kind'; params.kind = kind; }
144
242
  const rows = db.prepare(sql).all(params);
145
243
  // Apply path-based scope filter in JS (simpler than CASE in SQL)
146
244
  const filtered = rows.filter(r => {
147
245
  const p = String(r.path || '').replace(/\\\\/g, '/');
148
246
  if (scope === 'assets') return p.startsWith('Assets/');
149
- if (scope === 'packages') return p.startsWith('Packages/') || p.includes('Library/PackageCache/');
247
+ if (scope === 'packages')
248
+ return p.startsWith('Packages/') || p.includes('Library/PackageCache/');
150
249
  if (scope === 'embedded') return p.startsWith('Packages/');
151
250
  return true;
152
251
  });
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 }));
252
+ return filtered.map(r => ({
253
+ path: r.path,
254
+ name: r.name,
255
+ kind: r.kind,
256
+ container: r.container,
257
+ ns: r.namespace,
258
+ line: r.line,
259
+ column: r.column
260
+ }));
154
261
  }
155
262
 
156
263
  async getStats() {
157
264
  const db = await this.open();
158
265
  if (!db) return { total: 0, lastIndexedAt: null };
159
266
  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;
267
+ const last =
268
+ db.prepare("SELECT value AS v FROM meta WHERE key = 'lastIndexedAt'").get()?.v || null;
161
269
  return { total, lastIndexedAt: last };
162
270
  }
271
+
272
+ async _flushFallback() {
273
+ if (typeof this._persistFallback === 'function') {
274
+ try {
275
+ await this._persistFallback();
276
+ } catch {}
277
+ }
278
+ }
279
+ }
280
+
281
+ // Test-only helper to reset cached driver status between runs
282
+ export function __resetCodeIndexDriverStatusForTest() {
283
+ driverStatus.available = null;
284
+ driverStatus.error = null;
285
+ driverStatus.logged = false;
163
286
  }
@@ -34,6 +34,17 @@ export class IndexWatcher {
34
34
  if (this.running) return;
35
35
  this.running = true;
36
36
  try {
37
+ // Skip watcher entirely when the native SQLite binding is unavailable
38
+ const { CodeIndex } = await import('./codeIndex.js');
39
+ const probe = new CodeIndex(this.unityConnection);
40
+ const driverOk = await probe._ensureDriver();
41
+ if (!driverOk || probe.disabled) {
42
+ const reason = probe.disableReason || 'SQLite native binding not available';
43
+ logger.warn(`[index] watcher: code index disabled (${reason}); stopping watcher`);
44
+ this.stop();
45
+ return;
46
+ }
47
+
37
48
  // Check if code index DB file exists (before opening DB)
38
49
  const { ProjectInfoProvider } = await import('./projectInfo.js');
39
50
  const projectInfo = new ProjectInfoProvider(this.unityConnection);
@@ -1,11 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
- import {
4
- ListToolsRequestSchema,
5
- CallToolRequestSchema,
6
- ListResourcesRequestSchema,
7
- ListPromptsRequestSchema
8
- } from '@modelcontextprotocol/sdk/types.js';
3
+ import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
9
4
  // Note: filename is lowercase on disk; use exact casing for POSIX filesystems
10
5
  import { UnityConnection } from './unityConnection.js';
11
6
  import { createHandlers } from '../handlers/index.js';
@@ -29,9 +24,7 @@ const server = new Server(
29
24
  capabilities: {
30
25
  // Explicitly advertise tool support; some MCP clients expect a non-empty object
31
26
  // Setting listChanged enables future push updates if we emit notifications
32
- tools: { listChanged: true },
33
- resources: {},
34
- prompts: {}
27
+ tools: { listChanged: true }
35
28
  }
36
29
  }
37
30
  );
@@ -63,20 +56,6 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
63
56
  return { tools };
64
57
  });
65
58
 
66
- // Handle resources listing
67
- server.setRequestHandler(ListResourcesRequestSchema, async () => {
68
- logger.debug('[MCP] Received resources/list request');
69
- // Unity MCP server doesn't provide resources
70
- return { resources: [] };
71
- });
72
-
73
- // Handle prompts listing
74
- server.setRequestHandler(ListPromptsRequestSchema, async () => {
75
- logger.debug('[MCP] Received prompts/list request');
76
- // Unity MCP server doesn't provide prompts
77
- return { prompts: [] };
78
- });
79
-
80
59
  // Handle tool execution
81
60
  server.setRequestHandler(CallToolRequestSchema, async request => {
82
61
  const { name, arguments: args } = request.params;
@@ -240,6 +219,12 @@ export async function startServer() {
240
219
  const ready = await index.isReady();
241
220
 
242
221
  if (!ready) {
222
+ if (index.disabled) {
223
+ logger.warn(
224
+ `[startup] Code index disabled: ${index.disableReason || 'SQLite native binding missing'}. Skipping auto-build.`
225
+ );
226
+ return;
227
+ }
243
228
  logger.info('[startup] Code index DB not ready. Starting auto-build...');
244
229
  const { CodeIndexBuildToolHandler } = await import(
245
230
  '../handlers/script/CodeIndexBuildToolHandler.js'
@@ -298,9 +283,7 @@ export async function createServer(customConfig = config) {
298
283
  },
299
284
  {
300
285
  capabilities: {
301
- tools: { listChanged: true },
302
- resources: {},
303
- prompts: {}
286
+ tools: { listChanged: true }
304
287
  }
305
288
  }
306
289
  );
@@ -311,14 +294,6 @@ export async function createServer(customConfig = config) {
311
294
  return { tools };
312
295
  });
313
296
 
314
- testServer.setRequestHandler(ListResourcesRequestSchema, async () => {
315
- return { resources: [] };
316
- });
317
-
318
- testServer.setRequestHandler(ListPromptsRequestSchema, async () => {
319
- return { prompts: [] };
320
- });
321
-
322
297
  testServer.setRequestHandler(CallToolRequestSchema, async request => {
323
298
  const { name, arguments: args } = request.params;
324
299
 
@@ -0,0 +1,75 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import initSqlJs from 'sql.js';
4
+
5
+ // Create a lightweight better-sqlite3 compatible surface using sql.js (WASM)
6
+ export async function createSqliteFallback(dbPath) {
7
+ const wasmPath = path.resolve('node_modules/sql.js/dist/sql-wasm.wasm');
8
+ const SQL = await initSqlJs({ locateFile: () => wasmPath });
9
+
10
+ const loadDb = () => {
11
+ fs.mkdirSync(path.dirname(dbPath), { recursive: true });
12
+ if (fs.existsSync(dbPath)) {
13
+ const data = fs.readFileSync(dbPath);
14
+ return new SQL.Database(new Uint8Array(data));
15
+ }
16
+ return new SQL.Database();
17
+ };
18
+
19
+ const db = loadDb();
20
+
21
+ const persist = () => {
22
+ const data = db.export();
23
+ fs.writeFileSync(dbPath, Buffer.from(data));
24
+ };
25
+
26
+ // Wrap sql.js Statement to look like better-sqlite3's
27
+ const wrapStatement = stmt => ({
28
+ run(...params) {
29
+ stmt.bind(params);
30
+ // sql.js run via stepping through the statement
31
+ while (stmt.step()) {
32
+ /* consume rows for statements that return data */
33
+ }
34
+ stmt.reset();
35
+ persist();
36
+ return this;
37
+ },
38
+ get(...params) {
39
+ stmt.bind(params);
40
+ const has = stmt.step();
41
+ const row = has ? stmt.getAsObject() : undefined;
42
+ stmt.reset();
43
+ return row;
44
+ },
45
+ all(...params) {
46
+ stmt.bind(params);
47
+ const rows = [];
48
+ while (stmt.step()) rows.push(stmt.getAsObject());
49
+ stmt.reset();
50
+ return rows;
51
+ }
52
+ });
53
+
54
+ const prepare = sql => wrapStatement(db.prepare(sql));
55
+
56
+ // Mimic better-sqlite3 transaction(fn)
57
+ const transaction =
58
+ fn =>
59
+ (...args) => {
60
+ const result = fn(...args);
61
+ persist();
62
+ return result;
63
+ };
64
+
65
+ // Minimal surface used by CodeIndex
66
+ return {
67
+ exec: sql => {
68
+ db.exec(sql);
69
+ persist();
70
+ },
71
+ prepare,
72
+ transaction,
73
+ persist
74
+ };
75
+ }
@@ -1,35 +1,38 @@
1
1
  import { BaseToolHandler } from '../base/BaseToolHandler.js';
2
- import { analyzeSceneContentsToolDefinition, analyzeSceneContentsHandler } from '../../tools/analysis/analyzeSceneContents.js';
2
+ import {
3
+ analyzeSceneContentsToolDefinition,
4
+ analyzeSceneContentsHandler
5
+ } from '../../tools/analysis/analyzeSceneContents.js';
3
6
 
4
7
  /**
5
8
  * Handler for analyze_scene_contents tool
6
9
  */
7
10
  export class AnalyzeSceneContentsToolHandler extends BaseToolHandler {
8
- constructor(unityConnection) {
9
- super(
10
- analyzeSceneContentsToolDefinition.name,
11
- analyzeSceneContentsToolDefinition.description,
12
- analyzeSceneContentsToolDefinition.inputSchema
13
- );
14
- this.unityConnection = unityConnection;
15
- this.handler = analyzeSceneContentsHandler;
11
+ constructor(unityConnection) {
12
+ super(
13
+ analyzeSceneContentsToolDefinition.name,
14
+ analyzeSceneContentsToolDefinition.description,
15
+ analyzeSceneContentsToolDefinition.inputSchema
16
+ );
17
+ this.unityConnection = unityConnection;
18
+ this.handler = analyzeSceneContentsHandler;
19
+ }
20
+
21
+ async execute(args) {
22
+ // Check connection
23
+ if (!this.unityConnection.isConnected()) {
24
+ throw new Error('Unity connection not available');
16
25
  }
17
26
 
18
- async execute(args) {
19
- // Check connection
20
- if (!this.unityConnection.isConnected()) {
21
- throw new Error('Unity connection not available');
22
- }
27
+ // Use the handler function
28
+ const result = await this.handler(this.unityConnection, args);
23
29
 
24
- // Use the handler function
25
- const result = await this.handler(this.unityConnection, args);
26
-
27
- // If the handler returns an error response, throw it
28
- if (result.isError) {
29
- throw new Error(result.content[0].text);
30
- }
31
-
32
- // Return the content
33
- return result;
30
+ // If the handler returns an error response, throw it
31
+ if (result.isError) {
32
+ throw new Error(result.content[0].text);
34
33
  }
34
+
35
+ // Return the content
36
+ return result;
37
+ }
35
38
  }
@@ -1,5 +1,8 @@
1
1
  import { BaseToolHandler } from '../base/BaseToolHandler.js';
2
- import { findByComponentToolDefinition, findByComponentHandler } from '../../tools/analysis/findByComponent.js';
2
+ import {
3
+ findByComponentToolDefinition,
4
+ findByComponentHandler
5
+ } from '../../tools/analysis/findByComponent.js';
3
6
 
4
7
  /**
5
8
  * Handler for the analysis_component_find tool
@@ -1,9 +1,9 @@
1
1
  import { BaseToolHandler } from '../base/BaseToolHandler.js';
2
- import {
3
- getAnimatorStateToolDefinition,
4
- getAnimatorRuntimeInfoToolDefinition,
5
- getAnimatorStateHandler,
6
- getAnimatorRuntimeInfoHandler
2
+ import {
3
+ getAnimatorStateToolDefinition,
4
+ getAnimatorRuntimeInfoToolDefinition,
5
+ getAnimatorStateHandler,
6
+ getAnimatorRuntimeInfoHandler
7
7
  } from '../../tools/analysis/getAnimatorState.js';
8
8
 
9
9
  export class GetAnimatorStateToolHandler extends BaseToolHandler {
@@ -1,5 +1,8 @@
1
1
  import { BaseToolHandler } from '../base/BaseToolHandler.js';
2
- import { getComponentValuesToolDefinition, getComponentValuesHandler } from '../../tools/analysis/getComponentValues.js';
2
+ import {
3
+ getComponentValuesToolDefinition,
4
+ getComponentValuesHandler
5
+ } from '../../tools/analysis/getComponentValues.js';
3
6
 
4
7
  /**
5
8
  * Handler for the analysis_component_values_get tool
@@ -1,35 +1,38 @@
1
1
  import { BaseToolHandler } from '../base/BaseToolHandler.js';
2
- import { getGameObjectDetailsToolDefinition, getGameObjectDetailsHandler } from '../../tools/analysis/getGameObjectDetails.js';
2
+ import {
3
+ getGameObjectDetailsToolDefinition,
4
+ getGameObjectDetailsHandler
5
+ } from '../../tools/analysis/getGameObjectDetails.js';
3
6
 
4
7
  /**
5
8
  * Handler for get_gameobject_details tool
6
9
  */
7
10
  export class GetGameObjectDetailsToolHandler extends BaseToolHandler {
8
- constructor(unityConnection) {
9
- super(
10
- getGameObjectDetailsToolDefinition.name,
11
- getGameObjectDetailsToolDefinition.description,
12
- getGameObjectDetailsToolDefinition.inputSchema
13
- );
14
- this.unityConnection = unityConnection;
15
- this.handler = getGameObjectDetailsHandler;
11
+ constructor(unityConnection) {
12
+ super(
13
+ getGameObjectDetailsToolDefinition.name,
14
+ getGameObjectDetailsToolDefinition.description,
15
+ getGameObjectDetailsToolDefinition.inputSchema
16
+ );
17
+ this.unityConnection = unityConnection;
18
+ this.handler = getGameObjectDetailsHandler;
19
+ }
20
+
21
+ async execute(args) {
22
+ // Check connection
23
+ if (!this.unityConnection.isConnected()) {
24
+ throw new Error('Unity connection not available');
16
25
  }
17
26
 
18
- async execute(args) {
19
- // Check connection
20
- if (!this.unityConnection.isConnected()) {
21
- throw new Error('Unity connection not available');
22
- }
27
+ // Use the handler function
28
+ const result = await this.handler(this.unityConnection, args);
23
29
 
24
- // Use the handler function
25
- const result = await this.handler(this.unityConnection, args);
26
-
27
- // If the handler returns an error response, throw it
28
- if (result.isError) {
29
- throw new Error(result.content[0].text);
30
- }
31
-
32
- // Return the content
33
- return result;
30
+ // If the handler returns an error response, throw it
31
+ if (result.isError) {
32
+ throw new Error(result.content[0].text);
34
33
  }
34
+
35
+ // Return the content
36
+ return result;
37
+ }
35
38
  }