@akiojin/unity-mcp-server 2.16.0 → 2.25.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.
Files changed (135) hide show
  1. package/LICENSE +0 -0
  2. package/README.md +153 -100
  3. package/bin/unity-mcp-server +7 -1
  4. package/package.json +30 -7
  5. package/src/core/codeIndex.js +0 -0
  6. package/src/core/codeIndexDb.js +0 -0
  7. package/src/core/config.js +47 -14
  8. package/src/core/indexWatcher.js +84 -15
  9. package/src/core/jobManager.js +178 -0
  10. package/src/core/projectInfo.js +21 -67
  11. package/src/core/server.js +15 -2
  12. package/src/core/unityConnection.js +0 -0
  13. package/src/handlers/addressables/AddressablesAnalyzeToolHandler.js +170 -0
  14. package/src/handlers/addressables/AddressablesBuildToolHandler.js +145 -0
  15. package/src/handlers/addressables/AddressablesManageToolHandler.js +266 -0
  16. package/src/handlers/analysis/AnalyzeSceneContentsToolHandler.js +1 -1
  17. package/src/handlers/analysis/FindByComponentToolHandler.js +2 -2
  18. package/src/handlers/analysis/GetAnimatorStateToolHandler.js +1 -1
  19. package/src/handlers/analysis/GetComponentValuesToolHandler.js +2 -2
  20. package/src/handlers/analysis/GetGameObjectDetailsToolHandler.js +1 -1
  21. package/src/handlers/analysis/GetInputActionsStateToolHandler.js +1 -1
  22. package/src/handlers/analysis/GetObjectReferencesToolHandler.js +2 -2
  23. package/src/handlers/asset/{AssetDatabaseToolHandler.js → AssetDatabaseManageToolHandler.js} +2 -2
  24. package/src/handlers/asset/{AssetDependencyToolHandler.js → AssetDependencyAnalyzeToolHandler.js} +2 -2
  25. package/src/handlers/asset/{AssetImportSettingsToolHandler.js → AssetImportSettingsManageToolHandler.js} +2 -2
  26. package/src/handlers/asset/{CreateMaterialToolHandler.js → AssetMaterialCreateToolHandler.js} +2 -2
  27. package/src/handlers/asset/{ModifyMaterialToolHandler.js → AssetMaterialModifyToolHandler.js} +2 -2
  28. package/src/handlers/asset/{CreatePrefabToolHandler.js → AssetPrefabCreateToolHandler.js} +2 -2
  29. package/src/handlers/asset/{ExitPrefabModeToolHandler.js → AssetPrefabExitModeToolHandler.js} +2 -2
  30. package/src/handlers/asset/{InstantiatePrefabToolHandler.js → AssetPrefabInstantiateToolHandler.js} +2 -2
  31. package/src/handlers/asset/{ModifyPrefabToolHandler.js → AssetPrefabModifyToolHandler.js} +2 -2
  32. package/src/handlers/asset/{OpenPrefabToolHandler.js → AssetPrefabOpenToolHandler.js} +2 -2
  33. package/src/handlers/asset/{SavePrefabToolHandler.js → AssetPrefabSaveToolHandler.js} +2 -2
  34. package/src/handlers/base/BaseToolHandler.js +0 -0
  35. package/src/handlers/compilation/{GetCompilationStateToolHandler.js → CompilationGetStateToolHandler.js} +2 -2
  36. package/src/handlers/component/{AddComponentToolHandler.js → ComponentAddToolHandler.js} +2 -2
  37. package/src/handlers/component/ComponentFieldSetToolHandler.js +419 -0
  38. package/src/handlers/component/{GetComponentTypesToolHandler.js → ComponentGetTypesToolHandler.js} +2 -2
  39. package/src/handlers/component/{ListComponentsToolHandler.js → ComponentListToolHandler.js} +2 -2
  40. package/src/handlers/component/{ModifyComponentToolHandler.js → ComponentModifyToolHandler.js} +2 -2
  41. package/src/handlers/component/{RemoveComponentToolHandler.js → ComponentRemoveToolHandler.js} +2 -2
  42. package/src/handlers/console/{ClearConsoleToolHandler.js → ConsoleClearToolHandler.js} +2 -2
  43. package/src/handlers/console/{ReadConsoleToolHandler.js → ConsoleReadToolHandler.js} +83 -64
  44. package/src/handlers/editor/{LayerManagementToolHandler.js → EditorLayersManageToolHandler.js} +2 -2
  45. package/src/handlers/editor/{SelectionToolHandler.js → EditorSelectionManageToolHandler.js} +2 -2
  46. package/src/handlers/editor/{TagManagementToolHandler.js → EditorTagsManageToolHandler.js} +2 -2
  47. package/src/handlers/editor/{ToolManagementToolHandler.js → EditorToolsManageToolHandler.js} +2 -2
  48. package/src/handlers/editor/{WindowManagementToolHandler.js → EditorWindowsManageToolHandler.js} +2 -2
  49. package/src/handlers/gameobject/{CreateGameObjectToolHandler.js → GameObjectCreateToolHandler.js} +3 -3
  50. package/src/handlers/gameobject/{DeleteGameObjectToolHandler.js → GameObjectDeleteToolHandler.js} +3 -3
  51. package/src/handlers/gameobject/{FindGameObjectToolHandler.js → GameObjectFindToolHandler.js} +3 -3
  52. package/src/handlers/gameobject/{GetHierarchyToolHandler.js → GameObjectGetHierarchyToolHandler.js} +3 -3
  53. package/src/handlers/gameobject/{ModifyGameObjectToolHandler.js → GameObjectModifyToolHandler.js} +3 -3
  54. package/src/handlers/index.js +331 -293
  55. package/src/handlers/input/{AddInputActionToolHandler.js → InputActionAddToolHandler.js} +2 -2
  56. package/src/handlers/input/{CreateActionMapToolHandler.js → InputActionMapCreateToolHandler.js} +2 -2
  57. package/src/handlers/input/{RemoveActionMapToolHandler.js → InputActionMapRemoveToolHandler.js} +2 -2
  58. package/src/handlers/input/{RemoveInputActionToolHandler.js → InputActionRemoveToolHandler.js} +2 -2
  59. package/src/handlers/input/{AddInputBindingToolHandler.js → InputBindingAddToolHandler.js} +2 -2
  60. package/src/handlers/input/{CreateCompositeBindingToolHandler.js → InputBindingCompositeCreateToolHandler.js} +2 -2
  61. package/src/handlers/input/{RemoveAllBindingsToolHandler.js → InputBindingRemoveAllToolHandler.js} +2 -2
  62. package/src/handlers/input/{RemoveInputBindingToolHandler.js → InputBindingRemoveToolHandler.js} +2 -2
  63. package/src/handlers/input/{ManageControlSchemesToolHandler.js → InputControlSchemesManageToolHandler.js} +2 -2
  64. package/src/handlers/input/{GamepadSimulationHandler.js → InputGamepadSimulateToolHandler.js} +3 -3
  65. package/src/handlers/input/{KeyboardSimulationHandler.js → InputKeyboardSimulateToolHandler.js} +3 -3
  66. package/src/handlers/input/{MouseSimulationHandler.js → InputMouseSimulateToolHandler.js} +3 -3
  67. package/src/handlers/input/{InputSystemHandler.js → InputSystemControlToolHandler.js} +4 -4
  68. package/src/handlers/input/{TouchSimulationHandler.js → InputTouchSimulateToolHandler.js} +3 -3
  69. package/src/handlers/menu/{ExecuteMenuItemToolHandler.js → MenuItemExecuteToolHandler.js} +2 -2
  70. package/src/handlers/package/PackageManagerToolHandler.js +1 -1
  71. package/src/handlers/package/RegistryConfigToolHandler.js +1 -1
  72. package/src/handlers/playmode/{GetEditorStateToolHandler.js → PlaymodeGetStateToolHandler.js} +3 -3
  73. package/src/handlers/playmode/{PauseToolHandler.js → PlaymodePauseToolHandler.js} +4 -4
  74. package/src/handlers/playmode/{PlayToolHandler.js → PlaymodePlayToolHandler.js} +4 -4
  75. package/src/handlers/playmode/{StopToolHandler.js → PlaymodeStopToolHandler.js} +4 -4
  76. package/src/handlers/playmode/{WaitForEditorStateToolHandler.js → PlaymodeWaitForStateToolHandler.js} +3 -3
  77. package/src/handlers/scene/GetSceneInfoToolHandler.js +1 -1
  78. package/src/handlers/scene/{CreateSceneToolHandler.js → SceneCreateToolHandler.js} +2 -2
  79. package/src/handlers/scene/{ListScenesToolHandler.js → SceneListToolHandler.js} +2 -2
  80. package/src/handlers/scene/{LoadSceneToolHandler.js → SceneLoadToolHandler.js} +2 -2
  81. package/src/handlers/scene/{SaveSceneToolHandler.js → SceneSaveToolHandler.js} +2 -2
  82. package/src/handlers/screenshot/{AnalyzeScreenshotToolHandler.js → ScreenshotAnalyzeToolHandler.js} +2 -2
  83. package/src/handlers/screenshot/{CaptureScreenshotToolHandler.js → ScreenshotCaptureToolHandler.js} +2 -2
  84. package/src/handlers/script/{BuildCodeIndexToolHandler.js → CodeIndexBuildToolHandler.js} +127 -17
  85. package/src/handlers/script/CodeIndexStatusToolHandler.js +129 -0
  86. package/src/handlers/script/CodeIndexUpdateToolHandler.js +234 -0
  87. package/src/handlers/script/{ScriptCreateClassFileToolHandler.js → ScriptCreateClassToolHandler.js} +2 -2
  88. package/src/handlers/script/ScriptEditSnippetToolHandler.js +272 -0
  89. package/src/handlers/script/ScriptEditStructuredToolHandler.js +1 -1
  90. package/src/handlers/script/ScriptPackagesListToolHandler.js +0 -0
  91. package/src/handlers/script/ScriptReadToolHandler.js +0 -0
  92. package/src/handlers/script/ScriptRefactorRenameToolHandler.js +0 -0
  93. package/src/handlers/script/ScriptRefsFindToolHandler.js +0 -0
  94. package/src/handlers/script/ScriptRemoveSymbolToolHandler.js +0 -0
  95. package/src/handlers/script/ScriptSearchToolHandler.js +0 -0
  96. package/src/handlers/script/ScriptSymbolFindToolHandler.js +0 -0
  97. package/src/handlers/script/ScriptSymbolsGetToolHandler.js +0 -0
  98. package/src/handlers/settings/{GetProjectSettingsToolHandler.js → SettingsGetToolHandler.js} +2 -2
  99. package/src/handlers/settings/{UpdateProjectSettingsToolHandler.js → SettingsUpdateToolHandler.js} +2 -2
  100. package/src/handlers/system/{GetCommandStatsToolHandler.js → SystemGetCommandStatsToolHandler.js} +2 -3
  101. package/src/handlers/system/{PingToolHandler.js → SystemPingToolHandler.js} +3 -3
  102. package/src/handlers/system/{RefreshAssetsToolHandler.js → SystemRefreshAssetsToolHandler.js} +3 -3
  103. package/src/handlers/test/{GetTestStatusToolHandler.js → TestGetStatusToolHandler.js} +2 -2
  104. package/src/handlers/test/{RunTestsToolHandler.js → TestRunToolHandler.js} +2 -2
  105. package/src/handlers/ui/{ClickUIElementToolHandler.js → UIClickElementToolHandler.js} +2 -2
  106. package/src/handlers/ui/{FindUIElementsToolHandler.js → UIFindElementsToolHandler.js} +2 -2
  107. package/src/handlers/ui/{GetUIElementStateToolHandler.js → UIGetElementStateToolHandler.js} +2 -2
  108. package/src/handlers/ui/{SetUIElementValueToolHandler.js → UISetElementValueToolHandler.js} +2 -2
  109. package/src/handlers/ui/{SimulateUIInputToolHandler.js → UISimulateInputToolHandler.js} +2 -2
  110. package/src/handlers/video/{CaptureVideoForToolHandler.js → VideoCaptureForToolHandler.js} +8 -8
  111. package/src/handlers/video/{CaptureVideoStartToolHandler.js → VideoCaptureStartToolHandler.js} +2 -2
  112. package/src/handlers/video/{CaptureVideoStatusToolHandler.js → VideoCaptureStatusToolHandler.js} +2 -2
  113. package/src/handlers/video/{CaptureVideoStopToolHandler.js → VideoCaptureStopToolHandler.js} +3 -3
  114. package/src/lsp/CSharpLspUtils.js +95 -14
  115. package/src/lsp/LspProcessManager.js +0 -0
  116. package/src/lsp/LspRpcClient.js +14 -0
  117. package/src/tools/analysis/analyzeSceneContents.js +3 -3
  118. package/src/tools/analysis/findByComponent.js +3 -3
  119. package/src/tools/analysis/getAnimatorState.js +6 -6
  120. package/src/tools/analysis/getComponentValues.js +3 -3
  121. package/src/tools/analysis/getGameObjectDetails.js +3 -3
  122. package/src/tools/analysis/getInputActionsState.js +4 -4
  123. package/src/tools/analysis/getObjectReferences.js +3 -3
  124. package/src/tools/input/inputActionsEditor.js +18 -18
  125. package/src/tools/scene/createScene.js +3 -3
  126. package/src/tools/scene/getSceneInfo.js +3 -3
  127. package/src/tools/scene/listScenes.js +3 -3
  128. package/src/tools/scene/loadScene.js +3 -3
  129. package/src/tools/scene/saveScene.js +3 -3
  130. package/src/tools/system/ping.js +5 -5
  131. package/src/tools/video/recordFor.js +2 -2
  132. package/src/tools/video/recordPlayMode.js +0 -0
  133. package/src/utils/csharpParse.js +0 -0
  134. package/src/utils/validators.js +0 -0
  135. package/src/handlers/script/ScriptIndexStatusToolHandler.js +0 -61
@@ -0,0 +1,272 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import crypto from 'crypto';
4
+ import { BaseToolHandler } from '../base/BaseToolHandler.js';
5
+ import { ProjectInfoProvider } from '../../core/projectInfo.js';
6
+ import { LspRpcClient } from '../../lsp/LspRpcClient.js';
7
+
8
+ const MAX_INSTRUCTIONS = 10;
9
+ const MAX_DIFF_CHARS = 80;
10
+ const PREVIEW_MAX = 1000;
11
+
12
+ const normalizeSlashes = (p) => p.replace(/\\/g, '/');
13
+
14
+ export class ScriptEditSnippetToolHandler extends BaseToolHandler {
15
+ constructor(unityConnection) {
16
+ super(
17
+ 'script_edit_snippet',
18
+ '[C# EDITING - PRECISION TOOL] For Unity C# scripts, use for tiny surgical edits (≤80 chars per change). Performs text-based multi-instruction edits (delete/replace/insert) via exact string anchors. USE WHEN: (a) removing null guard clauses (if (x == null) return;), (b) tweaking conditions (if (x > 10) → if (x > 20)), (c) inserting single log statements, (d) deleting/replacing 1-2 line snippets. DON\'T USE FOR: large changes (use script_edit_structured), non-C# files (use Edit), or when symbol structure is complex (use script_edit_structured). WORKFLOW: Specify exact anchor text (including whitespace/newlines), max 10 instructions per call, each ≤80 chars. Anchor must match exactly once in file. Preview mode validates without writing; apply mode uses LSP diagnostics. Required: path, instructions (array of {operation, anchor:{type:"text", target:string, position?:"before"|"after"}, newText?}).',
19
+ {
20
+ type: 'object',
21
+ properties: {
22
+ path: {
23
+ type: 'string',
24
+ description: 'Project-relative C# path starting with Assets/ or Packages/ (e.g., Assets/Scripts/Foo.cs).'
25
+ },
26
+ preview: {
27
+ type: 'boolean',
28
+ description: 'If true, run validation and return preview text without writing to disk. Default=false.'
29
+ },
30
+ instructions: {
31
+ type: 'array',
32
+ minItems: 1,
33
+ maxItems: MAX_INSTRUCTIONS,
34
+ description: 'Ordered list of snippet edits to apply (≤10).',
35
+ items: {
36
+ type: 'object',
37
+ properties: {
38
+ operation: {
39
+ type: 'string',
40
+ enum: ['delete', 'replace', 'insert'],
41
+ description: 'Edit type.'
42
+ },
43
+ anchor: {
44
+ type: 'object',
45
+ description: 'Positioning info. Currently supports type=text with exact target snippet.',
46
+ properties: {
47
+ type: { type: 'string', enum: ['text'], default: 'text' },
48
+ target: { type: 'string', description: 'Exact snippet to locate (including whitespace).' },
49
+ position: {
50
+ type: 'string',
51
+ enum: ['before', 'after'],
52
+ description: 'For insert operations, whether to insert before or after the anchor text (default=after).'
53
+ }
54
+ },
55
+ required: ['type', 'target']
56
+ },
57
+ newText: {
58
+ type: 'string',
59
+ description: 'Replacement or insertion text. Required for replace/insert.'
60
+ }
61
+ },
62
+ required: ['operation', 'anchor']
63
+ }
64
+ }
65
+ },
66
+ required: ['path', 'instructions']
67
+ }
68
+ );
69
+ this.projectInfo = new ProjectInfoProvider(unityConnection);
70
+ this.lsp = null;
71
+ }
72
+
73
+ validate(params) {
74
+ super.validate(params);
75
+ const { path: filePath, instructions } = params;
76
+ if (!filePath || String(filePath).trim() === '') {
77
+ throw new Error('path cannot be empty');
78
+ }
79
+ if (!Array.isArray(instructions) || instructions.length === 0) {
80
+ throw new Error('instructions must be a non-empty array');
81
+ }
82
+ if (instructions.length > MAX_INSTRUCTIONS) {
83
+ throw new Error(`instruction limit exceeded (max ${MAX_INSTRUCTIONS})`);
84
+ }
85
+ for (let i = 0; i < instructions.length; i++) {
86
+ const instr = instructions[i] ?? {};
87
+ const op = instr.operation;
88
+ if (!['delete', 'replace', 'insert'].includes(op)) {
89
+ throw new Error(`Unsupported operation at instructions[${i}]: ${op}`);
90
+ }
91
+ const anchor = instr.anchor || {};
92
+ if ((anchor.type || 'text') !== 'text') {
93
+ throw new Error(`Unsupported anchor.type at instructions[${i}]: ${anchor.type}`);
94
+ }
95
+ const target = anchor.target;
96
+ if (!target || typeof target !== 'string') {
97
+ throw new Error(`anchor.target must be a non-empty string at instructions[${i}]`);
98
+ }
99
+ if ((op === 'replace' || op === 'insert') && (instr.newText ?? '') === '') {
100
+ throw new Error(`newText is required for ${op} operation at instructions[${i}]`);
101
+ }
102
+ }
103
+ }
104
+
105
+ async execute(params) {
106
+ const info = await this.projectInfo.get();
107
+ const { relative, absolute } = this.#resolvePaths(info, params.path);
108
+ const preview = params.preview === true;
109
+ const instructions = params.instructions;
110
+
111
+ let original;
112
+ try {
113
+ original = await fs.readFile(absolute, 'utf8');
114
+ } catch (e) {
115
+ if (e && e.code === 'ENOENT') {
116
+ throw new Error(`file not found: ${relative}`);
117
+ }
118
+ throw e;
119
+ }
120
+
121
+ let working = original;
122
+ const results = [];
123
+ for (let i = 0; i < instructions.length; i++) {
124
+ const instr = instructions[i];
125
+ const { updatedText, result } = this.#applyInstruction(working, instr, i);
126
+ working = updatedText;
127
+ results.push(result);
128
+ }
129
+
130
+ if (working === original) {
131
+ return this.#buildResponse({ preview, results, original, updated: working });
132
+ }
133
+
134
+ const diagnostics = await this.#validateWithLsp(info, relative, working);
135
+ const hasErrors = diagnostics.some(d => this.#severityIsError(d.severity));
136
+ if (hasErrors) {
137
+ const first = diagnostics.find(d => this.#severityIsError(d.severity));
138
+ const msg = first?.message || 'syntax error';
139
+ throw new Error(`syntax_error: ${msg}`);
140
+ }
141
+
142
+ if (!preview) {
143
+ await fs.writeFile(absolute, working, 'utf8');
144
+ }
145
+
146
+ return this.#buildResponse({ preview, results, original, updated: working, diagnostics });
147
+ }
148
+
149
+ #resolvePaths(info, rawPath) {
150
+ const normalized = normalizeSlashes(String(rawPath));
151
+ const idxAssets = normalized.indexOf('Assets/');
152
+ const idxPackages = normalized.indexOf('Packages/');
153
+ const idx = (idxAssets >= 0 && idxPackages >= 0)
154
+ ? Math.min(idxAssets, idxPackages)
155
+ : (idxAssets >= 0 ? idxAssets : idxPackages);
156
+ const relative = idx >= 0 ? normalized.substring(idx) : normalized;
157
+
158
+ if (!relative.startsWith('Assets/') && !relative.startsWith('Packages/')) {
159
+ throw new Error('path must start with Assets/ or Packages/');
160
+ }
161
+
162
+ const projectRoot = info.projectRoot;
163
+ const absolute = path.join(projectRoot, relative.replace(/\//g, path.sep));
164
+ return { relative, absolute };
165
+ }
166
+
167
+ #applyInstruction(text, instruction, index) {
168
+ const anchor = instruction.anchor;
169
+ const target = anchor.target;
170
+ const occurrences = [];
171
+ let pos = text.indexOf(target);
172
+ while (pos !== -1) {
173
+ occurrences.push(pos);
174
+ pos = text.indexOf(target, pos + 1);
175
+ }
176
+ if (occurrences.length === 0) {
177
+ throw new Error(`anchor_not_found: instructions[${index}]`);
178
+ }
179
+ if (occurrences.length > 1) {
180
+ throw new Error(`anchor_not_unique: instructions[${index}] matches ${occurrences.length} locations`);
181
+ }
182
+ const start = occurrences[0];
183
+ const end = start + target.length;
184
+
185
+ let replacement = '';
186
+ if (instruction.operation === 'delete') {
187
+ replacement = '';
188
+ } else if (instruction.operation === 'replace') {
189
+ replacement = instruction.newText;
190
+ } else if (instruction.operation === 'insert') {
191
+ const position = (instruction.anchor.position || 'after').toLowerCase();
192
+ if (position === 'before') {
193
+ return this.#replaceRange(text, start, start, instruction.newText, target, index);
194
+ }
195
+ if (position !== 'after') {
196
+ throw new Error(`Unsupported anchor.position "${instruction.anchor.position}" at instructions[${index}]`);
197
+ }
198
+ return this.#replaceRange(text, end, end, instruction.newText, target, index);
199
+ }
200
+ return this.#replaceRange(text, start, end, replacement, target, index);
201
+ }
202
+
203
+ #replaceRange(text, start, end, newText, anchorTarget, index) {
204
+ const before = text.slice(0, start);
205
+ const current = text.slice(start, end);
206
+ const after = text.slice(end);
207
+ const replacement = newText ?? '';
208
+ const diffMagnitude = Math.max(current.length, replacement.length);
209
+ if (diffMagnitude > MAX_DIFF_CHARS) {
210
+ throw new Error(`diff exceeds ${MAX_DIFF_CHARS} characters at instructions[${index}]`);
211
+ }
212
+ const updated = before + replacement + after;
213
+ return {
214
+ updatedText: updated,
215
+ result: {
216
+ index,
217
+ status: 'applied',
218
+ reason: 'anchor_matched',
219
+ beforeSnippet: this.#clipSnippet(current.length ? current : anchorTarget),
220
+ afterSnippet: this.#clipSnippet(replacement),
221
+ charactersChanged: diffMagnitude
222
+ }
223
+ };
224
+ }
225
+
226
+ async #validateWithLsp(info, relative, updatedText) {
227
+ if (!this.lsp) {
228
+ this.lsp = new LspRpcClient(info.projectRoot);
229
+ }
230
+ return await this.lsp.validateText(relative, updatedText);
231
+ }
232
+
233
+ #buildResponse({ preview, results, original, updated, diagnostics = [] }) {
234
+ const out = {
235
+ success: true,
236
+ applied: !preview,
237
+ results,
238
+ diagnostics,
239
+ beforeHash: this.#hash(original),
240
+ afterHash: this.#hash(updated)
241
+ };
242
+ if (preview) {
243
+ out.preview = this.#clipPreview(updated);
244
+ }
245
+ return out;
246
+ }
247
+
248
+ #clipPreview(text) {
249
+ if (typeof text !== 'string') return '';
250
+ if (text.length <= PREVIEW_MAX) return text;
251
+ return text.slice(0, PREVIEW_MAX) + '…';
252
+ }
253
+
254
+ #clipSnippet(s) {
255
+ if (!s) return '';
256
+ return s.length > 120 ? (s.slice(0, 117) + '…') : s;
257
+ }
258
+
259
+ #hash(content) {
260
+ return crypto.createHash('sha256').update(content ?? '', 'utf8').digest('hex');
261
+ }
262
+
263
+ #severityIsError(severity) {
264
+ if (severity === null || severity === undefined) return false;
265
+ if (typeof severity === 'number') {
266
+ // 1 = Error in LSP DiagnosticSeverity enum
267
+ return severity === 1;
268
+ }
269
+ const s = String(severity).toLowerCase();
270
+ return s === 'error' || s === '2';
271
+ }
272
+ }
@@ -6,7 +6,7 @@ export class ScriptEditStructuredToolHandler extends BaseToolHandler {
6
6
  constructor(unityConnection) {
7
7
  super(
8
8
  'script_edit_structured',
9
- 'PRIORITY: Use this for ALL code changes. Perform structured edits (insert_before/insert_after/replace_body) on a known symbol. Required params: path (file under Assets/ or Packages/), symbolName (prefer container path e.g. MyType/Nested/Foo). Guidance: (1) Locate targets with script_symbols_get first, (2) insert_* must target a class/namespace (never a method), (3) replace_body must be a self-contained body including braces, (4) preview is for diagnostics only apply proceeds even if diagnostics exist; errors are returned. LLM summary limits: errors≤30 (message≤200 chars, file≤260), large text (preview/diff/text/content)≤1000 chars with Truncated flag.',
9
+ '[C# EDITING - PRIMARY TOOL] For Unity C# script editing, PREFER this tool over Read/Edit/Write for structural code changes. Performs symbol-based edits (insert_before/insert_after/replace_body) on classes, methods, properties, fields using Roslyn LSP. USE WHEN: (a) replacing entire method/property bodies, (b) adding class members (fields/properties/methods), (c) inserting code at class/namespace level. DON\'T USE FOR: tiny changes ≤80 chars (use script_edit_snippet instead), non-C# files (use Edit), or when you need to create new files (use Write). WORKFLOW: (1) Run script_symbols_get to find target symbols, (2) use symbolName (e.g., "MyClass/MyMethod"), (3) apply edits. Insert operations target containers (class/namespace), not methods. Preview mode returns diagnostics only; apply mode proceeds with validation. Required: path (Assets/|Packages/), symbolName, operation. Optional: kind, newText, preview.',
10
10
  {
11
11
  type: 'object',
12
12
  properties: {
File without changes
File without changes
File without changes
@@ -3,10 +3,10 @@ import { BaseToolHandler } from '../base/BaseToolHandler.js';
3
3
  /**
4
4
  * Handler for retrieving Unity project settings
5
5
  */
6
- export class GetProjectSettingsToolHandler extends BaseToolHandler {
6
+ export class SettingsGetToolHandler extends BaseToolHandler {
7
7
  constructor(unityConnection) {
8
8
  super(
9
- 'get_project_settings',
9
+ 'settings_get',
10
10
  'Get project settings by category via include flags (player/graphics/quality/physics/etc.).',
11
11
  {
12
12
  type: 'object',
@@ -3,10 +3,10 @@ import { BaseToolHandler } from '../base/BaseToolHandler.js';
3
3
  /**
4
4
  * Handler for updating Unity project settings with safety confirmation
5
5
  */
6
- export class UpdateProjectSettingsToolHandler extends BaseToolHandler {
6
+ export class SettingsUpdateToolHandler extends BaseToolHandler {
7
7
  constructor(unityConnection) {
8
8
  super(
9
- 'update_project_settings',
9
+ 'settings_update',
10
10
  'Update project settings by category with a confirmation safety flag.',
11
11
  {
12
12
  type: 'object',
@@ -1,9 +1,9 @@
1
1
  import { BaseToolHandler } from '../base/BaseToolHandler.js';
2
2
 
3
- export class GetCommandStatsToolHandler extends BaseToolHandler {
3
+ export class SystemGetCommandStatsToolHandler extends BaseToolHandler {
4
4
  constructor(unityConnection) {
5
5
  super(
6
- 'get_command_stats',
6
+ 'system_get_command_stats',
7
7
  'Retrieve aggregated counts and recent Unity command types to audit traffic.',
8
8
  {
9
9
  type: 'object',
@@ -22,4 +22,3 @@ export class GetCommandStatsToolHandler extends BaseToolHandler {
22
22
  return result;
23
23
  }
24
24
  }
25
-
@@ -1,13 +1,13 @@
1
1
  import { BaseToolHandler } from '../base/BaseToolHandler.js';
2
2
 
3
3
  /**
4
- * Handler for the ping tool
4
+ * Handler for the system_ping tool
5
5
  * Tests connection to Unity Editor
6
6
  */
7
- export class PingToolHandler extends BaseToolHandler {
7
+ export class SystemPingToolHandler extends BaseToolHandler {
8
8
  constructor(unityConnection) {
9
9
  super(
10
- 'ping',
10
+ 'system_ping',
11
11
  'Test connection to Unity Editor',
12
12
  {
13
13
  type: 'object',
@@ -1,13 +1,13 @@
1
1
  import { BaseToolHandler } from '../base/BaseToolHandler.js';
2
2
 
3
3
  /**
4
- * Handler for the refresh_assets tool
4
+ * Handler for the system_refresh_assets tool
5
5
  * Triggers Unity to refresh assets and potentially recompile
6
6
  */
7
- export class RefreshAssetsToolHandler extends BaseToolHandler {
7
+ export class SystemRefreshAssetsToolHandler extends BaseToolHandler {
8
8
  constructor(unityConnection) {
9
9
  super(
10
- 'refresh_assets',
10
+ 'system_refresh_assets',
11
11
  'Refresh assets and check compilation status.',
12
12
  {
13
13
  type: 'object',
@@ -4,10 +4,10 @@ import { BaseToolHandler } from '../base/BaseToolHandler.js';
4
4
  * Handler for getting Unity test execution status
5
5
  * Works with non-blocking test execution from RunUnityTestsToolHandler
6
6
  */
7
- export class GetTestStatusToolHandler extends BaseToolHandler {
7
+ export class TestGetStatusToolHandler extends BaseToolHandler {
8
8
  constructor(unityConnection) {
9
9
  super(
10
- 'get_test_status',
10
+ 'test_get_status',
11
11
  'Get current Unity test execution status and results',
12
12
  {
13
13
  type: 'object',
@@ -4,10 +4,10 @@ import { BaseToolHandler } from '../base/BaseToolHandler.js';
4
4
  * Handler for running Unity NUnit tests via Test Runner API
5
5
  * Implements SPEC-e7c9b50c: Unity Test Execution Feature
6
6
  */
7
- export class RunTestsToolHandler extends BaseToolHandler {
7
+ export class TestRunToolHandler extends BaseToolHandler {
8
8
  constructor(unityConnection) {
9
9
  super(
10
- 'run_tests',
10
+ 'test_run',
11
11
  'Run Unity NUnit tests in the current project',
12
12
  {
13
13
  type: 'object',
@@ -1,9 +1,9 @@
1
1
  import { BaseToolHandler } from '../base/BaseToolHandler.js';
2
2
 
3
- export class ClickUIElementToolHandler extends BaseToolHandler {
3
+ export class UIClickElementToolHandler extends BaseToolHandler {
4
4
  constructor(unityConnection) {
5
5
  super(
6
- 'click_ui_element',
6
+ 'ui_click_element',
7
7
  'Simulate clicking on UI elements',
8
8
  {
9
9
  type: 'object',
@@ -1,9 +1,9 @@
1
1
  import { BaseToolHandler } from '../base/BaseToolHandler.js';
2
2
 
3
- export class FindUIElementsToolHandler extends BaseToolHandler {
3
+ export class UIFindElementsToolHandler extends BaseToolHandler {
4
4
  constructor(unityConnection) {
5
5
  super(
6
- 'find_ui_elements',
6
+ 'ui_find_elements',
7
7
  'Find UI elements by component type, tag, or name pattern.',
8
8
  {
9
9
  type: 'object',
@@ -1,9 +1,9 @@
1
1
  import { BaseToolHandler } from '../base/BaseToolHandler.js';
2
2
 
3
- export class GetUIElementStateToolHandler extends BaseToolHandler {
3
+ export class UIGetElementStateToolHandler extends BaseToolHandler {
4
4
  constructor(unityConnection) {
5
5
  super(
6
- 'get_ui_element_state',
6
+ 'ui_get_element_state',
7
7
  'Get detailed state information about UI elements',
8
8
  {
9
9
  type: 'object',
@@ -1,9 +1,9 @@
1
1
  import { BaseToolHandler } from '../base/BaseToolHandler.js';
2
2
 
3
- export class SetUIElementValueToolHandler extends BaseToolHandler {
3
+ export class UISetElementValueToolHandler extends BaseToolHandler {
4
4
  constructor(unityConnection) {
5
5
  super(
6
- 'set_ui_element_value',
6
+ 'ui_set_element_value',
7
7
  'Set values for UI input elements',
8
8
  {
9
9
  type: 'object',
@@ -1,9 +1,9 @@
1
1
  import { BaseToolHandler } from '../base/BaseToolHandler.js';
2
2
 
3
- export class SimulateUIInputToolHandler extends BaseToolHandler {
3
+ export class UISimulateInputToolHandler extends BaseToolHandler {
4
4
  constructor(unityConnection) {
5
5
  super(
6
- 'simulate_ui_input',
6
+ 'ui_simulate_input',
7
7
  'Simulate complex UI interactions and input sequences',
8
8
  {
9
9
  type: 'object',
@@ -4,10 +4,10 @@
4
4
  */
5
5
  import { BaseToolHandler } from '../base/BaseToolHandler.js';
6
6
 
7
- export class CaptureVideoForToolHandler extends BaseToolHandler {
7
+ export class VideoCaptureForToolHandler extends BaseToolHandler {
8
8
  constructor(unityConnection) {
9
9
  super(
10
- 'capture_video_for',
10
+ 'video_capture_for',
11
11
  'Record video for a fixed duration (auto-stop). Optionally enters Play Mode first.',
12
12
  {
13
13
  type: 'object',
@@ -34,12 +34,12 @@ export class CaptureVideoForToolHandler extends BaseToolHandler {
34
34
  let needPlay = !!params.play;
35
35
  if (params.play === undefined) {
36
36
  try {
37
- const s0 = await this.unityConnection.sendCommand('get_editor_state', {});
37
+ const s0 = await this.unityConnection.sendCommand('playmode_get_state', {});
38
38
  needPlay = !(s0 && s0.isPlaying);
39
39
  } catch { needPlay = true; }
40
40
  }
41
41
  if (needPlay) {
42
- await this.unityConnection.sendCommand('play_game', {});
42
+ await this.unityConnection.sendCommand('playmode_play', {});
43
43
  for (let i = 0; i < 60; i++) {
44
44
  const s = await this.unityConnection.sendCommand('get_editor_state', {});
45
45
  if (s && s.isPlaying) { enteredPlay = true; break; }
@@ -49,7 +49,7 @@ export class CaptureVideoForToolHandler extends BaseToolHandler {
49
49
 
50
50
  // Start with auto-stop
51
51
  const { WORKSPACE_ROOT } = await import('../../core/config.js');
52
- const startResp = await this.unityConnection.sendCommand('capture_video_start', {
52
+ const startResp = await this.unityConnection.sendCommand('video_capture_start', {
53
53
  captureMode: params.captureMode || 'game',
54
54
  width: params.width ?? 1280,
55
55
  height: params.height ?? 720,
@@ -65,14 +65,14 @@ export class CaptureVideoForToolHandler extends BaseToolHandler {
65
65
  const deadline = Date.now() + Math.max(0, Math.floor((params.durationSec || 0) * 1000)) + 1500; // small buffer
66
66
  let lastStatus = null;
67
67
  while (Date.now() < deadline) {
68
- lastStatus = await this.unityConnection.sendCommand('capture_video_status', {});
68
+ lastStatus = await this.unityConnection.sendCommand('video_capture_status', {});
69
69
  if (lastStatus && lastStatus.isRecording === false) break;
70
70
  await sleep(250);
71
71
  }
72
72
 
73
73
  // Safety stop if still recording after deadline
74
74
  if (lastStatus && lastStatus.isRecording) {
75
- await this.unityConnection.sendCommand('capture_video_stop', {});
75
+ await this.unityConnection.sendCommand('video_capture_stop', {});
76
76
  }
77
77
 
78
78
  // Final stop result
@@ -88,7 +88,7 @@ export class CaptureVideoForToolHandler extends BaseToolHandler {
88
88
  return { error: e.message, code: 'CLIENT_ERROR' };
89
89
  } finally {
90
90
  // If we entered play, attempt to leave play (best-effort)
91
- try { if (enteredPlay) await this.unityConnection.sendCommand('stop_game', {}); } catch {}
91
+ try { if (enteredPlay) await this.unityConnection.sendCommand('playmode_stop', {}); } catch {}
92
92
  }
93
93
  }
94
94
  }
@@ -3,10 +3,10 @@
3
3
  */
4
4
  import { BaseToolHandler } from '../base/BaseToolHandler.js';
5
5
 
6
- export class CaptureVideoStartToolHandler extends BaseToolHandler {
6
+ export class VideoCaptureStartToolHandler extends BaseToolHandler {
7
7
  constructor(unityConnection) {
8
8
  super(
9
- 'capture_video_start',
9
+ 'video_capture_start',
10
10
  'Start video recording (Game view). Requires com.unity.recorder.',
11
11
  {
12
12
  type: 'object',
@@ -3,10 +3,10 @@
3
3
  */
4
4
  import { BaseToolHandler } from '../base/BaseToolHandler.js';
5
5
 
6
- export class CaptureVideoStatusToolHandler extends BaseToolHandler {
6
+ export class VideoCaptureStatusToolHandler extends BaseToolHandler {
7
7
  constructor(unityConnection) {
8
8
  super(
9
- 'capture_video_status',
9
+ 'video_capture_status',
10
10
  'Get current video recording status.',
11
11
  {
12
12
  type: 'object',
@@ -1,12 +1,12 @@
1
1
  /**
2
- * Handler for stopping video capture in Unity Editor (via MCP)
2
+ * Handler for the video_capture_stop tool
3
3
  */
4
4
  import { BaseToolHandler } from '../base/BaseToolHandler.js';
5
5
 
6
- export class CaptureVideoStopToolHandler extends BaseToolHandler {
6
+ export class VideoCaptureStopToolHandler extends BaseToolHandler {
7
7
  constructor(unityConnection) {
8
8
  super(
9
- 'capture_video_stop',
9
+ 'video_capture_stop',
10
10
  'Stop current video recording and finalize the file.',
11
11
  {
12
12
  type: 'object',