@akiojin/unity-mcp-server 2.14.17 → 2.16.1

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 (127) hide show
  1. package/LICENSE +0 -0
  2. package/README.md +142 -99
  3. package/package.json +4 -3
  4. package/src/core/codeIndex.js +0 -0
  5. package/src/core/codeIndexDb.js +0 -0
  6. package/src/core/config.js +50 -15
  7. package/src/core/indexWatcher.js +2 -3
  8. package/src/core/projectInfo.js +27 -20
  9. package/src/core/unityConnection.js +3 -2
  10. package/src/handlers/analysis/AnalyzeSceneContentsToolHandler.js +1 -1
  11. package/src/handlers/analysis/FindByComponentToolHandler.js +2 -2
  12. package/src/handlers/analysis/GetAnimatorStateToolHandler.js +1 -1
  13. package/src/handlers/analysis/GetComponentValuesToolHandler.js +2 -2
  14. package/src/handlers/analysis/GetGameObjectDetailsToolHandler.js +1 -1
  15. package/src/handlers/analysis/GetInputActionsStateToolHandler.js +1 -1
  16. package/src/handlers/analysis/GetObjectReferencesToolHandler.js +2 -2
  17. package/src/handlers/asset/{AssetDatabaseToolHandler.js → AssetDatabaseManageToolHandler.js} +2 -2
  18. package/src/handlers/asset/{AssetDependencyToolHandler.js → AssetDependencyAnalyzeToolHandler.js} +2 -2
  19. package/src/handlers/asset/{AssetImportSettingsToolHandler.js → AssetImportSettingsManageToolHandler.js} +2 -2
  20. package/src/handlers/asset/{CreateMaterialToolHandler.js → AssetMaterialCreateToolHandler.js} +2 -2
  21. package/src/handlers/asset/{ModifyMaterialToolHandler.js → AssetMaterialModifyToolHandler.js} +2 -2
  22. package/src/handlers/asset/{CreatePrefabToolHandler.js → AssetPrefabCreateToolHandler.js} +2 -2
  23. package/src/handlers/asset/{ExitPrefabModeToolHandler.js → AssetPrefabExitModeToolHandler.js} +2 -2
  24. package/src/handlers/asset/{InstantiatePrefabToolHandler.js → AssetPrefabInstantiateToolHandler.js} +2 -2
  25. package/src/handlers/asset/{ModifyPrefabToolHandler.js → AssetPrefabModifyToolHandler.js} +2 -2
  26. package/src/handlers/asset/{OpenPrefabToolHandler.js → AssetPrefabOpenToolHandler.js} +2 -2
  27. package/src/handlers/asset/{SavePrefabToolHandler.js → AssetPrefabSaveToolHandler.js} +2 -2
  28. package/src/handlers/base/BaseToolHandler.js +0 -0
  29. package/src/handlers/compilation/{GetCompilationStateToolHandler.js → CompilationGetStateToolHandler.js} +2 -2
  30. package/src/handlers/component/{AddComponentToolHandler.js → ComponentAddToolHandler.js} +2 -2
  31. package/src/handlers/component/{GetComponentTypesToolHandler.js → ComponentGetTypesToolHandler.js} +2 -2
  32. package/src/handlers/component/{ListComponentsToolHandler.js → ComponentListToolHandler.js} +2 -2
  33. package/src/handlers/component/{ModifyComponentToolHandler.js → ComponentModifyToolHandler.js} +2 -2
  34. package/src/handlers/component/{RemoveComponentToolHandler.js → ComponentRemoveToolHandler.js} +2 -2
  35. package/src/handlers/console/{ClearConsoleToolHandler.js → ConsoleClearToolHandler.js} +2 -2
  36. package/src/handlers/console/{ReadConsoleToolHandler.js → ConsoleReadToolHandler.js} +2 -2
  37. package/src/handlers/editor/{LayerManagementToolHandler.js → EditorLayersManageToolHandler.js} +2 -2
  38. package/src/handlers/editor/{SelectionToolHandler.js → EditorSelectionManageToolHandler.js} +2 -2
  39. package/src/handlers/editor/{TagManagementToolHandler.js → EditorTagsManageToolHandler.js} +2 -2
  40. package/src/handlers/editor/{ToolManagementToolHandler.js → EditorToolsManageToolHandler.js} +2 -2
  41. package/src/handlers/editor/{WindowManagementToolHandler.js → EditorWindowsManageToolHandler.js} +2 -2
  42. package/src/handlers/gameobject/{CreateGameObjectToolHandler.js → GameObjectCreateToolHandler.js} +3 -3
  43. package/src/handlers/gameobject/{DeleteGameObjectToolHandler.js → GameObjectDeleteToolHandler.js} +3 -3
  44. package/src/handlers/gameobject/{FindGameObjectToolHandler.js → GameObjectFindToolHandler.js} +3 -3
  45. package/src/handlers/gameobject/{GetHierarchyToolHandler.js → GameObjectGetHierarchyToolHandler.js} +3 -3
  46. package/src/handlers/gameobject/{ModifyGameObjectToolHandler.js → GameObjectModifyToolHandler.js} +3 -3
  47. package/src/handlers/index.js +230 -213
  48. package/src/handlers/input/{AddInputActionToolHandler.js → InputActionAddToolHandler.js} +2 -2
  49. package/src/handlers/input/{CreateActionMapToolHandler.js → InputActionMapCreateToolHandler.js} +2 -2
  50. package/src/handlers/input/{RemoveActionMapToolHandler.js → InputActionMapRemoveToolHandler.js} +2 -2
  51. package/src/handlers/input/{RemoveInputActionToolHandler.js → InputActionRemoveToolHandler.js} +2 -2
  52. package/src/handlers/input/{AddInputBindingToolHandler.js → InputBindingAddToolHandler.js} +2 -2
  53. package/src/handlers/input/{CreateCompositeBindingToolHandler.js → InputBindingCompositeCreateToolHandler.js} +2 -2
  54. package/src/handlers/input/{RemoveAllBindingsToolHandler.js → InputBindingRemoveAllToolHandler.js} +2 -2
  55. package/src/handlers/input/{RemoveInputBindingToolHandler.js → InputBindingRemoveToolHandler.js} +2 -2
  56. package/src/handlers/input/{ManageControlSchemesToolHandler.js → InputControlSchemesManageToolHandler.js} +2 -2
  57. package/src/handlers/input/{GamepadSimulationHandler.js → InputGamepadSimulateToolHandler.js} +3 -3
  58. package/src/handlers/input/{KeyboardSimulationHandler.js → InputKeyboardSimulateToolHandler.js} +3 -3
  59. package/src/handlers/input/{MouseSimulationHandler.js → InputMouseSimulateToolHandler.js} +3 -3
  60. package/src/handlers/input/{InputSystemHandler.js → InputSystemControlToolHandler.js} +4 -4
  61. package/src/handlers/input/{TouchSimulationHandler.js → InputTouchSimulateToolHandler.js} +3 -3
  62. package/src/handlers/menu/{ExecuteMenuItemToolHandler.js → MenuItemExecuteToolHandler.js} +2 -2
  63. package/src/handlers/package/PackageManagerToolHandler.js +1 -1
  64. package/src/handlers/package/RegistryConfigToolHandler.js +1 -1
  65. package/src/handlers/playmode/{GetEditorStateToolHandler.js → PlaymodeGetStateToolHandler.js} +2 -2
  66. package/src/handlers/playmode/{PauseToolHandler.js → PlaymodePauseToolHandler.js} +3 -3
  67. package/src/handlers/playmode/{PlayToolHandler.js → PlaymodePlayToolHandler.js} +4 -4
  68. package/src/handlers/playmode/{StopToolHandler.js → PlaymodeStopToolHandler.js} +4 -4
  69. package/src/handlers/playmode/{WaitForEditorStateToolHandler.js → PlaymodeWaitForStateToolHandler.js} +3 -3
  70. package/src/handlers/scene/GetSceneInfoToolHandler.js +1 -1
  71. package/src/handlers/scene/{CreateSceneToolHandler.js → SceneCreateToolHandler.js} +2 -2
  72. package/src/handlers/scene/{ListScenesToolHandler.js → SceneListToolHandler.js} +2 -2
  73. package/src/handlers/scene/{LoadSceneToolHandler.js → SceneLoadToolHandler.js} +2 -2
  74. package/src/handlers/scene/{SaveSceneToolHandler.js → SceneSaveToolHandler.js} +2 -2
  75. package/src/handlers/screenshot/{AnalyzeScreenshotToolHandler.js → ScreenshotAnalyzeToolHandler.js} +2 -2
  76. package/src/handlers/screenshot/{CaptureScreenshotToolHandler.js → ScreenshotCaptureToolHandler.js} +2 -2
  77. package/src/handlers/script/{BuildCodeIndexToolHandler.js → CodeIndexBuildToolHandler.js} +4 -4
  78. package/src/handlers/script/CodeIndexUpdateToolHandler.js +234 -0
  79. package/src/handlers/script/{ScriptCreateClassFileToolHandler.js → ScriptCreateClassToolHandler.js} +2 -2
  80. package/src/handlers/script/ScriptEditSnippetToolHandler.js +272 -0
  81. package/src/handlers/script/ScriptEditStructuredToolHandler.js +0 -0
  82. package/src/handlers/script/ScriptIndexStatusToolHandler.js +2 -2
  83. package/src/handlers/script/ScriptPackagesListToolHandler.js +0 -0
  84. package/src/handlers/script/ScriptReadToolHandler.js +0 -0
  85. package/src/handlers/script/ScriptRefactorRenameToolHandler.js +0 -0
  86. package/src/handlers/script/ScriptRefsFindToolHandler.js +0 -0
  87. package/src/handlers/script/ScriptRemoveSymbolToolHandler.js +0 -0
  88. package/src/handlers/script/ScriptSearchToolHandler.js +0 -0
  89. package/src/handlers/script/ScriptSymbolFindToolHandler.js +0 -0
  90. package/src/handlers/script/ScriptSymbolsGetToolHandler.js +0 -0
  91. package/src/handlers/settings/{GetProjectSettingsToolHandler.js → SettingsGetToolHandler.js} +2 -2
  92. package/src/handlers/settings/{UpdateProjectSettingsToolHandler.js → SettingsUpdateToolHandler.js} +2 -2
  93. package/src/handlers/system/{GetCommandStatsToolHandler.js → SystemGetCommandStatsToolHandler.js} +2 -3
  94. package/src/handlers/system/{PingToolHandler.js → SystemPingToolHandler.js} +3 -3
  95. package/src/handlers/system/{RefreshAssetsToolHandler.js → SystemRefreshAssetsToolHandler.js} +3 -3
  96. package/src/handlers/test/TestGetStatusToolHandler.js +79 -0
  97. package/src/handlers/test/TestRunToolHandler.js +153 -0
  98. package/src/handlers/ui/{ClickUIElementToolHandler.js → UIClickElementToolHandler.js} +2 -2
  99. package/src/handlers/ui/{FindUIElementsToolHandler.js → UIFindElementsToolHandler.js} +2 -2
  100. package/src/handlers/ui/{GetUIElementStateToolHandler.js → UIGetElementStateToolHandler.js} +2 -2
  101. package/src/handlers/ui/{SetUIElementValueToolHandler.js → UISetElementValueToolHandler.js} +2 -2
  102. package/src/handlers/ui/{SimulateUIInputToolHandler.js → UISimulateInputToolHandler.js} +2 -2
  103. package/src/handlers/video/{CaptureVideoForToolHandler.js → VideoCaptureForToolHandler.js} +8 -8
  104. package/src/handlers/video/{CaptureVideoStartToolHandler.js → VideoCaptureStartToolHandler.js} +2 -2
  105. package/src/handlers/video/{CaptureVideoStatusToolHandler.js → VideoCaptureStatusToolHandler.js} +2 -2
  106. package/src/handlers/video/{CaptureVideoStopToolHandler.js → VideoCaptureStopToolHandler.js} +3 -3
  107. package/src/lsp/CSharpLspUtils.js +68 -10
  108. package/src/lsp/LspProcessManager.js +0 -0
  109. package/src/lsp/LspRpcClient.js +14 -0
  110. package/src/tools/analysis/analyzeSceneContents.js +3 -3
  111. package/src/tools/analysis/findByComponent.js +3 -3
  112. package/src/tools/analysis/getAnimatorState.js +6 -6
  113. package/src/tools/analysis/getComponentValues.js +3 -3
  114. package/src/tools/analysis/getGameObjectDetails.js +3 -3
  115. package/src/tools/analysis/getInputActionsState.js +4 -4
  116. package/src/tools/analysis/getObjectReferences.js +3 -3
  117. package/src/tools/input/inputActionsEditor.js +18 -18
  118. package/src/tools/scene/createScene.js +3 -3
  119. package/src/tools/scene/getSceneInfo.js +3 -3
  120. package/src/tools/scene/listScenes.js +3 -3
  121. package/src/tools/scene/loadScene.js +3 -3
  122. package/src/tools/scene/saveScene.js +3 -3
  123. package/src/tools/system/ping.js +5 -5
  124. package/src/tools/video/recordFor.js +2 -2
  125. package/src/tools/video/recordPlayMode.js +0 -0
  126. package/src/utils/csharpParse.js +0 -0
  127. package/src/utils/validators.js +0 -0
@@ -0,0 +1,79 @@
1
+ import { BaseToolHandler } from '../base/BaseToolHandler.js';
2
+
3
+ /**
4
+ * Handler for getting Unity test execution status
5
+ * Works with non-blocking test execution from RunUnityTestsToolHandler
6
+ */
7
+ export class TestGetStatusToolHandler extends BaseToolHandler {
8
+ constructor(unityConnection) {
9
+ super(
10
+ 'test_get_status',
11
+ 'Get current Unity test execution status and results',
12
+ {
13
+ type: 'object',
14
+ properties: {}
15
+ }
16
+ );
17
+
18
+ this.unityConnection = unityConnection;
19
+ }
20
+
21
+ /**
22
+ * Gets current test execution status
23
+ * @param {Object} params - The validated input parameters
24
+ * @returns {Promise<Object>} Test execution status and results
25
+ */
26
+ async execute(params) {
27
+ // Ensure connection to Unity
28
+ if (!this.unityConnection.isConnected()) {
29
+ await this.unityConnection.connect();
30
+ }
31
+
32
+ // Send command to Unity
33
+ const response = await this.unityConnection.sendCommand('get_test_status', params);
34
+
35
+ // Handle Unity response
36
+ if (response.error) {
37
+ throw new Error(response.error);
38
+ }
39
+
40
+ // Return status directly if still running or idle
41
+ if (response.status === 'running' || response.status === 'idle') {
42
+ return response;
43
+ }
44
+
45
+ // Format completed results
46
+ if (response.status === 'completed') {
47
+ const result = {
48
+ status: 'completed',
49
+ success: response.success,
50
+ totalTests: response.totalTests,
51
+ passedTests: response.passedTests,
52
+ failedTests: response.failedTests,
53
+ skippedTests: response.skippedTests,
54
+ inconclusiveTests: response.inconclusiveTests,
55
+ summary: `${response.passedTests}/${response.totalTests} tests passed`,
56
+ failures: response.failures || [],
57
+ tests: response.tests || []
58
+ };
59
+
60
+ return result;
61
+ }
62
+
63
+ // Error status
64
+ return response;
65
+ }
66
+
67
+ /**
68
+ * Gets example usage for this tool
69
+ * @returns {Object} Example usage scenarios
70
+ */
71
+ getExamples() {
72
+ return {
73
+ checkStatus: {
74
+ description: 'Check current test execution status',
75
+ params: {}
76
+ }
77
+ };
78
+ }
79
+ }
@@ -0,0 +1,153 @@
1
+ import { BaseToolHandler } from '../base/BaseToolHandler.js';
2
+
3
+ /**
4
+ * Handler for running Unity NUnit tests via Test Runner API
5
+ * Implements SPEC-e7c9b50c: Unity Test Execution Feature
6
+ */
7
+ export class TestRunToolHandler extends BaseToolHandler {
8
+ constructor(unityConnection) {
9
+ super(
10
+ 'test_run',
11
+ 'Run Unity NUnit tests in the current project',
12
+ {
13
+ type: 'object',
14
+ properties: {
15
+ testMode: {
16
+ type: 'string',
17
+ enum: ['EditMode', 'PlayMode', 'All'],
18
+ default: 'EditMode',
19
+ description: 'Test mode to run (EditMode: editor tests, PlayMode: runtime tests, All: both)'
20
+ },
21
+ filter: {
22
+ type: 'string',
23
+ description: 'Filter tests by class name (e.g., "PlayerControllerTests")'
24
+ },
25
+ category: {
26
+ type: 'string',
27
+ description: 'Filter tests by category attribute (e.g., "Integration")'
28
+ },
29
+ namespace: {
30
+ type: 'string',
31
+ description: 'Filter tests by namespace (e.g., "MyGame.Tests.Player")'
32
+ },
33
+ includeDetails: {
34
+ type: 'boolean',
35
+ default: false,
36
+ description: 'Include detailed test results for each individual test'
37
+ },
38
+ exportPath: {
39
+ type: 'string',
40
+ description: 'Export test results to NUnit XML file at specified path'
41
+ }
42
+ }
43
+ }
44
+ );
45
+
46
+ this.unityConnection = unityConnection;
47
+ }
48
+
49
+ /**
50
+ * Executes Unity tests based on specified parameters
51
+ * @param {Object} params - The validated input parameters
52
+ * @returns {Promise<Object>} Test execution results
53
+ */
54
+ async execute(params) {
55
+ // Ensure connection to Unity
56
+ if (!this.unityConnection.isConnected()) {
57
+ await this.unityConnection.connect();
58
+ }
59
+
60
+ // Send command to Unity
61
+ const response = await this.unityConnection.sendCommand('run_tests', params);
62
+
63
+ // Handle Unity response
64
+ if (response.error) {
65
+ throw new Error(response.error);
66
+ }
67
+
68
+ // Handle new non-blocking response format (status: "running")
69
+ if (response.status === 'running') {
70
+ return {
71
+ status: 'running',
72
+ message: response.message || 'Test execution started. Use get_test_status to check progress.'
73
+ };
74
+ }
75
+
76
+ // Legacy format support (for backwards compatibility)
77
+ // Format and return result
78
+ const result = {
79
+ success: response.success,
80
+ totalTests: response.totalTests,
81
+ passedTests: response.passedTests,
82
+ failedTests: response.failedTests,
83
+ skippedTests: response.skippedTests,
84
+ inconclusiveTests: response.inconclusiveTests,
85
+ duration: response.duration,
86
+ summary: `${response.passedTests}/${response.totalTests} tests passed in ${response.duration}s`,
87
+ failures: response.failures || []
88
+ };
89
+
90
+ // Include detailed test results if requested
91
+ if (params.includeDetails && response.tests) {
92
+ result.tests = response.tests;
93
+ }
94
+
95
+ return result;
96
+ }
97
+
98
+ /**
99
+ * Gets example usage for this tool
100
+ * @returns {Object} Example usage scenarios
101
+ */
102
+ getExamples() {
103
+ return {
104
+ runAllEditModeTests: {
105
+ description: 'Run all Edit Mode tests',
106
+ params: {
107
+ testMode: 'EditMode'
108
+ }
109
+ },
110
+ runSpecificClass: {
111
+ description: 'Run tests in a specific class',
112
+ params: {
113
+ testMode: 'EditMode',
114
+ filter: 'PlayerControllerTests'
115
+ }
116
+ },
117
+ runCategoryTests: {
118
+ description: 'Run tests in a specific category',
119
+ params: {
120
+ testMode: 'EditMode',
121
+ category: 'Integration'
122
+ }
123
+ },
124
+ runWithDetails: {
125
+ description: 'Run tests with detailed results for each test',
126
+ params: {
127
+ testMode: 'EditMode',
128
+ includeDetails: true
129
+ }
130
+ },
131
+ runAndExport: {
132
+ description: 'Run tests and export results to XML',
133
+ params: {
134
+ testMode: 'EditMode',
135
+ exportPath: 'TestResults/results.xml'
136
+ }
137
+ },
138
+ runPlayModeTests: {
139
+ description: 'Run Play Mode tests (requires Play Mode)',
140
+ params: {
141
+ testMode: 'PlayMode'
142
+ }
143
+ },
144
+ runAllTests: {
145
+ description: 'Run both Edit Mode and Play Mode tests',
146
+ params: {
147
+ testMode: 'All',
148
+ includeDetails: true
149
+ }
150
+ }
151
+ };
152
+ }
153
+ }
@@ -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',
@@ -1,5 +1,6 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
+ import os from 'os';
3
4
  import { logger } from '../core/config.js';
4
5
  import { WORKSPACE_ROOT } from '../core/config.js';
5
6
 
@@ -21,29 +22,86 @@ export class CSharpLspUtils {
21
22
  }
22
23
  }
23
24
 
24
- getLocalPath(rid) {
25
+ getExecutableName() {
26
+ return process.platform === 'win32' ? 'server.exe' : 'server';
27
+ }
28
+
29
+ getPrimaryToolRoot() {
30
+ const envRoot = process.env.UNITY_MCP_TOOLS_ROOT;
31
+ if (envRoot && envRoot.trim().length > 0) {
32
+ return path.resolve(envRoot.trim());
33
+ }
34
+ return path.join(os.homedir(), '.unity', 'tools');
35
+ }
36
+
37
+ getLegacyToolRoot() {
25
38
  const root = WORKSPACE_ROOT || process.cwd();
26
- const exe = process.platform === 'win32' ? 'server.exe' : 'server';
27
- return path.resolve(root, '.unity', 'tools', 'csharp-lsp', rid, exe);
39
+ return path.resolve(root, '.unity', 'tools');
40
+ }
41
+
42
+ resolveToolPaths(rid) {
43
+ const exe = this.getExecutableName();
44
+ const primary = path.resolve(this.getPrimaryToolRoot(), 'csharp-lsp', rid, exe);
45
+ const legacy = path.resolve(this.getLegacyToolRoot(), 'csharp-lsp', rid, exe);
46
+ return { primary, legacy };
47
+ }
48
+
49
+ resolveVersionPaths(rid) {
50
+ const { primary, legacy } = this.resolveToolPaths(rid);
51
+ return {
52
+ primary: path.resolve(path.dirname(primary), 'VERSION'),
53
+ legacy: path.resolve(path.dirname(legacy), 'VERSION')
54
+ };
55
+ }
56
+
57
+ migrateLegacyIfNeeded(rid) {
58
+ const { primary, legacy } = this.resolveToolPaths(rid);
59
+ if (!fs.existsSync(legacy)) return;
60
+ if (fs.existsSync(primary)) return;
61
+ try {
62
+ fs.mkdirSync(path.dirname(primary), { recursive: true });
63
+ fs.copyFileSync(legacy, primary);
64
+ const { primary: primaryVersion, legacy: legacyVersion } = this.resolveVersionPaths(rid);
65
+ if (fs.existsSync(legacyVersion) && !fs.existsSync(primaryVersion)) {
66
+ try {
67
+ fs.copyFileSync(legacyVersion, primaryVersion);
68
+ } catch {}
69
+ }
70
+ logger.info(`[csharp-lsp] migrated legacy binary to ${path.dirname(primary)}`);
71
+ } catch (e) {
72
+ logger.warn(`[csharp-lsp] legacy migration failed: ${e.message}`);
73
+ }
74
+ }
75
+
76
+ getLocalPath(rid) {
77
+ const { primary, legacy } = this.resolveToolPaths(rid);
78
+ this.migrateLegacyIfNeeded(rid);
79
+ if (fs.existsSync(primary)) return primary;
80
+ if (fs.existsSync(legacy)) return legacy;
81
+ return primary;
28
82
  }
29
83
 
30
84
  getVersionMarkerPath(rid) {
31
- const bin = this.getLocalPath(rid);
32
- return path.resolve(path.dirname(bin), 'VERSION');
85
+ const { primary, legacy } = this.resolveVersionPaths(rid);
86
+ if (fs.existsSync(primary)) return primary;
87
+ if (fs.existsSync(legacy)) return legacy;
88
+ return primary;
33
89
  }
34
90
 
35
91
  readLocalVersion(rid) {
36
92
  try {
37
- const m = this.getVersionMarkerPath(rid);
38
- if (fs.existsSync(m)) return fs.readFileSync(m, 'utf8').trim();
93
+ const { primary, legacy } = this.resolveVersionPaths(rid);
94
+ if (fs.existsSync(primary)) return fs.readFileSync(primary, 'utf8').trim();
95
+ if (fs.existsSync(legacy)) return fs.readFileSync(legacy, 'utf8').trim();
39
96
  } catch {}
40
97
  return null;
41
98
  }
42
99
 
43
100
  writeLocalVersion(rid, version) {
44
101
  try {
45
- const m = this.getVersionMarkerPath(rid);
46
- fs.writeFileSync(m, String(version || '').trim() + '\n', 'utf8');
102
+ const marker = this.getVersionMarkerPath(rid);
103
+ fs.mkdirSync(path.dirname(marker), { recursive: true });
104
+ fs.writeFileSync(marker, String(version || '').trim() + '\n', 'utf8');
47
105
  } catch {}
48
106
  }
49
107
 
@@ -67,7 +125,7 @@ export class CSharpLspUtils {
67
125
  const entry = manifest?.assets?.[rid];
68
126
  if (!entry?.url || !entry?.sha256) throw new Error(`manifest missing entry for ${rid}`);
69
127
 
70
- const dest = this.getLocalPath(rid);
128
+ const { primary: dest } = this.resolveToolPaths(rid);
71
129
  fs.mkdirSync(path.dirname(dest), { recursive: true });
72
130
  const tmp = dest + '.download';
73
131
  await this.downloadTo(entry.url, tmp);
File without changes
@@ -99,6 +99,20 @@ export class LspRpcClient {
99
99
  return await this.#requestWithRetry(method, params, 1);
100
100
  }
101
101
 
102
+ async validateText(relative, newText) {
103
+ const resp = await this.request('mcp/validateTextEdits', { relative, newText });
104
+ if (!resp) return [];
105
+ const payload = resp.result ?? resp;
106
+ const diagnostics = Array.isArray(payload?.diagnostics) ? payload.diagnostics : [];
107
+ return diagnostics.map((d) => ({
108
+ severity: d?.severity,
109
+ message: d?.message,
110
+ id: d?.id,
111
+ line: d?.line,
112
+ column: d?.column,
113
+ }));
114
+ }
115
+
102
116
  async #requestWithRetry(method, params, attempt) {
103
117
  await this.ensure();
104
118
  const id = this.seq++;
@@ -1,8 +1,8 @@
1
1
  /**
2
- * Tool definition for analyze_scene_contents
2
+ * Tool definition for analysis_scene_contents_analyze
3
3
  */
4
4
  export const analyzeSceneContentsToolDefinition = {
5
- name: 'analyze_scene_contents',
5
+ name: 'analysis_scene_contents_analyze',
6
6
  description: 'Analyze current scene: object counts, types, prefabs, and memory stats.',
7
7
  inputSchema: {
8
8
  type: 'object',
@@ -47,7 +47,7 @@ export async function analyzeSceneContentsHandler(unityConnection, args) {
47
47
  }
48
48
 
49
49
  // Send command to Unity with provided parameters
50
- const result = await unityConnection.sendCommand('analyze_scene_contents', args);
50
+ const result = await unityConnection.sendCommand('analysis_scene_contents_analyze', args);
51
51
 
52
52
  // The unityConnection.sendCommand already extracts the result field
53
53
  // from the response, so we access properties directly on result
@@ -1,8 +1,8 @@
1
1
  /**
2
- * Tool definition for find_by_component
2
+ * Tool definition for analysis_component_find
3
3
  */
4
4
  export const findByComponentToolDefinition = {
5
- name: 'find_by_component',
5
+ name: 'analysis_component_find',
6
6
  description: 'Find GameObjects that have a specific component type (scene/prefabs/all).',
7
7
  inputSchema: {
8
8
  type: 'object',
@@ -48,7 +48,7 @@ export async function findByComponentHandler(unityConnection, args) {
48
48
  }
49
49
 
50
50
  // Send command to Unity
51
- const result = await unityConnection.sendCommand('find_by_component', args);
51
+ const result = await unityConnection.sendCommand('analysis_component_find', args);
52
52
 
53
53
  // Handle Unity response
54
54
  if (result.status === 'error') {
@@ -1,6 +1,6 @@
1
- // Tool definition for get_animator_state
1
+ // Tool definition for analysis_animator_state_get
2
2
  export const getAnimatorStateToolDefinition = {
3
- name: 'get_animator_state',
3
+ name: 'analysis_animator_state_get',
4
4
  description: 'Get Animator state: layers, transitions, and parameter values for a GameObject.',
5
5
  inputSchema: {
6
6
  type: 'object',
@@ -39,9 +39,9 @@ export const getAnimatorStateToolDefinition = {
39
39
  }
40
40
  };
41
41
 
42
- // Tool definition for get_animator_runtime_info
42
+ // Tool definition for analysis_animator_runtime_info_get
43
43
  export const getAnimatorRuntimeInfoToolDefinition = {
44
- name: 'get_animator_runtime_info',
44
+ name: 'analysis_animator_runtime_info_get',
45
45
  description: 'Get Animator runtime info (IK, root motion, performance) — Play mode only.',
46
46
  inputSchema: {
47
47
  type: 'object',
@@ -100,7 +100,7 @@ export async function getAnimatorStateHandler(unityConnection, args) {
100
100
  }
101
101
 
102
102
  // Send command to Unity
103
- const result = await unityConnection.sendCommand('get_animator_state', args);
103
+ const result = await unityConnection.sendCommand('analysis_animator_state_get', args);
104
104
 
105
105
  // Check for errors
106
106
  if (!result || typeof result === 'string') {
@@ -222,7 +222,7 @@ export async function getAnimatorRuntimeInfoHandler(unityConnection, args) {
222
222
  }
223
223
 
224
224
  // Send command to Unity
225
- const result = await unityConnection.sendCommand('get_animator_runtime_info', args);
225
+ const result = await unityConnection.sendCommand('analysis_animator_runtime_info_get', args);
226
226
 
227
227
  // Check for errors
228
228
  if (!result || typeof result === 'string') {
@@ -1,8 +1,8 @@
1
1
  /**
2
- * Tool definition for get_component_values
2
+ * Tool definition for analysis_component_values_get
3
3
  */
4
4
  export const getComponentValuesToolDefinition = {
5
- name: 'get_component_values',
5
+ name: 'analysis_component_values_get',
6
6
  description: 'Get properties/values from a component on a GameObject (scene or prefab mode).',
7
7
  inputSchema: {
8
8
  type: 'object',
@@ -77,7 +77,7 @@ export async function getComponentValuesHandler(unityConnection, args) {
77
77
  }
78
78
 
79
79
  // Send command to Unity
80
- const result = await unityConnection.sendCommand('get_component_values', args);
80
+ const result = await unityConnection.sendCommand('analysis_component_values_get', args);
81
81
 
82
82
  // The unityConnection.sendCommand already extracts the result field
83
83
  // from the response, so we access properties directly on result
@@ -1,8 +1,8 @@
1
1
  /**
2
- * Tool definition for get_gameobject_details
2
+ * Tool definition for analysis_gameobject_details_get
3
3
  */
4
4
  export const getGameObjectDetailsToolDefinition = {
5
- name: 'get_gameobject_details',
5
+ name: 'analysis_gameobject_details_get',
6
6
  description: 'Get details for a GameObject by name or path (children/components/materials).',
7
7
  inputSchema: {
8
8
  type: 'object',
@@ -106,7 +106,7 @@ export async function getGameObjectDetailsHandler(unityConnection, args) {
106
106
  if (args.maxDepth !== undefined) params.maxDepth = args.maxDepth;
107
107
 
108
108
  // Send command to Unity
109
- const result = await unityConnection.sendCommand('get_gameobject_details', args);
109
+ const result = await unityConnection.sendCommand('analysis_gameobject_details_get', args);
110
110
 
111
111
  // The unityConnection.sendCommand already extracts the result field
112
112
  // from the response, so we access properties directly on result