@akiojin/unity-mcp-server 2.14.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +206 -0
  3. package/bin/unity-mcp-server +2 -0
  4. package/package.json +73 -0
  5. package/src/core/codeIndex.js +163 -0
  6. package/src/core/codeIndexDb.js +96 -0
  7. package/src/core/config.js +165 -0
  8. package/src/core/indexWatcher.js +52 -0
  9. package/src/core/projectInfo.js +111 -0
  10. package/src/core/server.js +294 -0
  11. package/src/core/unityConnection.js +426 -0
  12. package/src/handlers/analysis/AnalyzeSceneContentsToolHandler.js +35 -0
  13. package/src/handlers/analysis/FindByComponentToolHandler.js +20 -0
  14. package/src/handlers/analysis/GetAnimatorStateToolHandler.js +37 -0
  15. package/src/handlers/analysis/GetComponentValuesToolHandler.js +20 -0
  16. package/src/handlers/analysis/GetGameObjectDetailsToolHandler.js +35 -0
  17. package/src/handlers/analysis/GetInputActionsStateToolHandler.js +37 -0
  18. package/src/handlers/analysis/GetObjectReferencesToolHandler.js +20 -0
  19. package/src/handlers/asset/AssetDatabaseToolHandler.js +221 -0
  20. package/src/handlers/asset/AssetDependencyToolHandler.js +201 -0
  21. package/src/handlers/asset/AssetImportSettingsToolHandler.js +170 -0
  22. package/src/handlers/asset/CreateMaterialToolHandler.js +96 -0
  23. package/src/handlers/asset/CreatePrefabToolHandler.js +78 -0
  24. package/src/handlers/asset/ExitPrefabModeToolHandler.js +83 -0
  25. package/src/handlers/asset/InstantiatePrefabToolHandler.js +133 -0
  26. package/src/handlers/asset/ModifyMaterialToolHandler.js +76 -0
  27. package/src/handlers/asset/ModifyPrefabToolHandler.js +72 -0
  28. package/src/handlers/asset/OpenPrefabToolHandler.js +121 -0
  29. package/src/handlers/asset/SavePrefabToolHandler.js +106 -0
  30. package/src/handlers/base/BaseToolHandler.js +133 -0
  31. package/src/handlers/compilation/GetCompilationStateToolHandler.js +90 -0
  32. package/src/handlers/component/AddComponentToolHandler.js +126 -0
  33. package/src/handlers/component/GetComponentTypesToolHandler.js +100 -0
  34. package/src/handlers/component/ListComponentsToolHandler.js +85 -0
  35. package/src/handlers/component/ModifyComponentToolHandler.js +143 -0
  36. package/src/handlers/component/RemoveComponentToolHandler.js +108 -0
  37. package/src/handlers/console/ClearConsoleToolHandler.js +160 -0
  38. package/src/handlers/console/ReadConsoleToolHandler.js +276 -0
  39. package/src/handlers/editor/LayerManagementToolHandler.js +160 -0
  40. package/src/handlers/editor/SelectionToolHandler.js +141 -0
  41. package/src/handlers/editor/TagManagementToolHandler.js +129 -0
  42. package/src/handlers/editor/ToolManagementToolHandler.js +135 -0
  43. package/src/handlers/editor/WindowManagementToolHandler.js +125 -0
  44. package/src/handlers/gameobject/CreateGameObjectToolHandler.js +131 -0
  45. package/src/handlers/gameobject/DeleteGameObjectToolHandler.js +101 -0
  46. package/src/handlers/gameobject/FindGameObjectToolHandler.js +119 -0
  47. package/src/handlers/gameobject/GetHierarchyToolHandler.js +132 -0
  48. package/src/handlers/gameobject/ModifyGameObjectToolHandler.js +128 -0
  49. package/src/handlers/index.js +389 -0
  50. package/src/handlers/input/AddInputActionToolHandler.js +20 -0
  51. package/src/handlers/input/AddInputBindingToolHandler.js +20 -0
  52. package/src/handlers/input/CreateActionMapToolHandler.js +20 -0
  53. package/src/handlers/input/CreateCompositeBindingToolHandler.js +20 -0
  54. package/src/handlers/input/GamepadSimulationHandler.js +116 -0
  55. package/src/handlers/input/InputSystemHandler.js +80 -0
  56. package/src/handlers/input/KeyboardSimulationHandler.js +79 -0
  57. package/src/handlers/input/ManageControlSchemesToolHandler.js +20 -0
  58. package/src/handlers/input/MouseSimulationHandler.js +107 -0
  59. package/src/handlers/input/RemoveActionMapToolHandler.js +20 -0
  60. package/src/handlers/input/RemoveAllBindingsToolHandler.js +20 -0
  61. package/src/handlers/input/RemoveInputActionToolHandler.js +20 -0
  62. package/src/handlers/input/RemoveInputBindingToolHandler.js +20 -0
  63. package/src/handlers/input/TouchSimulationHandler.js +142 -0
  64. package/src/handlers/menu/ExecuteMenuItemToolHandler.js +304 -0
  65. package/src/handlers/package/PackageManagerToolHandler.js +248 -0
  66. package/src/handlers/package/RegistryConfigToolHandler.js +198 -0
  67. package/src/handlers/playmode/GetEditorStateToolHandler.js +81 -0
  68. package/src/handlers/playmode/PauseToolHandler.js +44 -0
  69. package/src/handlers/playmode/PlayToolHandler.js +91 -0
  70. package/src/handlers/playmode/StopToolHandler.js +77 -0
  71. package/src/handlers/playmode/WaitForEditorStateToolHandler.js +45 -0
  72. package/src/handlers/scene/CreateSceneToolHandler.js +91 -0
  73. package/src/handlers/scene/GetSceneInfoToolHandler.js +20 -0
  74. package/src/handlers/scene/ListScenesToolHandler.js +58 -0
  75. package/src/handlers/scene/LoadSceneToolHandler.js +92 -0
  76. package/src/handlers/scene/SaveSceneToolHandler.js +76 -0
  77. package/src/handlers/screenshot/AnalyzeScreenshotToolHandler.js +238 -0
  78. package/src/handlers/screenshot/CaptureScreenshotToolHandler.js +692 -0
  79. package/src/handlers/script/BuildCodeIndexToolHandler.js +163 -0
  80. package/src/handlers/script/ScriptCreateClassFileToolHandler.js +60 -0
  81. package/src/handlers/script/ScriptEditStructuredToolHandler.js +173 -0
  82. package/src/handlers/script/ScriptIndexStatusToolHandler.js +61 -0
  83. package/src/handlers/script/ScriptPackagesListToolHandler.js +103 -0
  84. package/src/handlers/script/ScriptReadToolHandler.js +106 -0
  85. package/src/handlers/script/ScriptRefactorRenameToolHandler.js +83 -0
  86. package/src/handlers/script/ScriptRefsFindToolHandler.js +144 -0
  87. package/src/handlers/script/ScriptRemoveSymbolToolHandler.js +79 -0
  88. package/src/handlers/script/ScriptSearchToolHandler.js +320 -0
  89. package/src/handlers/script/ScriptSymbolFindToolHandler.js +117 -0
  90. package/src/handlers/script/ScriptSymbolsGetToolHandler.js +96 -0
  91. package/src/handlers/settings/GetProjectSettingsToolHandler.js +161 -0
  92. package/src/handlers/settings/UpdateProjectSettingsToolHandler.js +272 -0
  93. package/src/handlers/system/GetCommandStatsToolHandler.js +25 -0
  94. package/src/handlers/system/PingToolHandler.js +53 -0
  95. package/src/handlers/system/RefreshAssetsToolHandler.js +45 -0
  96. package/src/handlers/ui/ClickUIElementToolHandler.js +110 -0
  97. package/src/handlers/ui/FindUIElementsToolHandler.js +63 -0
  98. package/src/handlers/ui/GetUIElementStateToolHandler.js +50 -0
  99. package/src/handlers/ui/SetUIElementValueToolHandler.js +49 -0
  100. package/src/handlers/ui/SimulateUIInputToolHandler.js +156 -0
  101. package/src/handlers/video/CaptureVideoForToolHandler.js +96 -0
  102. package/src/handlers/video/CaptureVideoStartToolHandler.js +38 -0
  103. package/src/handlers/video/CaptureVideoStatusToolHandler.js +30 -0
  104. package/src/handlers/video/CaptureVideoStopToolHandler.js +32 -0
  105. package/src/lsp/CSharpLspUtils.js +134 -0
  106. package/src/lsp/LspProcessManager.js +60 -0
  107. package/src/lsp/LspRpcClient.js +133 -0
  108. package/src/tools/analysis/analyzeSceneContents.js +100 -0
  109. package/src/tools/analysis/findByComponent.js +87 -0
  110. package/src/tools/analysis/getAnimatorState.js +326 -0
  111. package/src/tools/analysis/getComponentValues.js +182 -0
  112. package/src/tools/analysis/getGameObjectDetails.js +159 -0
  113. package/src/tools/analysis/getInputActionsState.js +329 -0
  114. package/src/tools/analysis/getObjectReferences.js +86 -0
  115. package/src/tools/input/inputActionsEditor.js +556 -0
  116. package/src/tools/scene/createScene.js +112 -0
  117. package/src/tools/scene/getSceneInfo.js +95 -0
  118. package/src/tools/scene/listScenes.js +82 -0
  119. package/src/tools/scene/loadScene.js +122 -0
  120. package/src/tools/scene/saveScene.js +91 -0
  121. package/src/tools/system/ping.js +72 -0
  122. package/src/tools/video/recordFor.js +31 -0
  123. package/src/tools/video/recordPlayMode.js +61 -0
  124. package/src/utils/csharpParse.js +88 -0
  125. package/src/utils/validators.js +90 -0
@@ -0,0 +1,108 @@
1
+ import { BaseToolHandler } from '../base/BaseToolHandler.js';
2
+
3
+ /**
4
+ * Handler for removing components from GameObjects in Unity
5
+ */
6
+ export class RemoveComponentToolHandler extends BaseToolHandler {
7
+ constructor(unityConnection) {
8
+ super(
9
+ 'remove_component',
10
+ 'Remove a component from a GameObject (scene or prefab mode) by type/index.',
11
+ {
12
+ type: 'object',
13
+ properties: {
14
+ gameObjectPath: {
15
+ type: 'string',
16
+ description: 'Path to the GameObject (e.g., "/Player" or "/Canvas/Button")'
17
+ },
18
+ componentType: {
19
+ type: 'string',
20
+ description: 'Type of component to remove (e.g., "Rigidbody", "BoxCollider")'
21
+ },
22
+ componentIndex: {
23
+ type: 'number',
24
+ description: 'Index of component if multiple of same type exist (default: 0)'
25
+ }
26
+ },
27
+ required: ['gameObjectPath', 'componentType']
28
+ }
29
+ );
30
+
31
+ this.unityConnection = unityConnection;
32
+ }
33
+
34
+ /**
35
+ * Validates the input parameters
36
+ * @param {Object} params - The input parameters
37
+ * @throws {Error} If validation fails
38
+ */
39
+ validate(params) {
40
+ super.validate(params); // Check required fields
41
+
42
+ const { componentIndex } = params;
43
+
44
+ // Validate component index if provided
45
+ if (componentIndex !== undefined && componentIndex < 0) {
46
+ throw new Error('componentIndex must be non-negative');
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Executes the remove component operation
52
+ * @param {Object} params - The validated input parameters
53
+ * @returns {Promise<Object>} The result of removing the component
54
+ */
55
+ async execute(params) {
56
+ // Ensure connection to Unity
57
+ if (!this.unityConnection.isConnected()) {
58
+ await this.unityConnection.connect();
59
+ }
60
+
61
+ // Send command to Unity
62
+ const response = await this.unityConnection.sendCommand('remove_component', params);
63
+
64
+ // Handle Unity response
65
+ if (response.error) {
66
+ throw new Error(response.error);
67
+ }
68
+
69
+ // Return result
70
+ return {
71
+ removed: response.removed,
72
+ componentType: response.componentType,
73
+ message: response.message || 'Component removal completed',
74
+ ...(response.componentIndex !== undefined && { componentIndex: response.componentIndex })
75
+ };
76
+ }
77
+
78
+ /**
79
+ * Gets example usage for this tool
80
+ * @returns {Object} Example usage scenarios
81
+ */
82
+ getExamples() {
83
+ return {
84
+ removeRigidbody: {
85
+ description: 'Remove Rigidbody component',
86
+ params: {
87
+ gameObjectPath: '/Player',
88
+ componentType: 'Rigidbody'
89
+ }
90
+ },
91
+ removeSpecificCollider: {
92
+ description: 'Remove specific collider when multiple exist',
93
+ params: {
94
+ gameObjectPath: '/ComplexObject',
95
+ componentType: 'BoxCollider',
96
+ componentIndex: 1
97
+ }
98
+ },
99
+ removeLight: {
100
+ description: 'Remove Light component',
101
+ params: {
102
+ gameObjectPath: '/Lighting/OldLight',
103
+ componentType: 'Light'
104
+ }
105
+ }
106
+ };
107
+ }
108
+ }
@@ -0,0 +1,160 @@
1
+ import { BaseToolHandler } from '../base/BaseToolHandler.js';
2
+
3
+ /**
4
+ * Handler for clearing Unity Editor console logs
5
+ */
6
+ export class ClearConsoleToolHandler extends BaseToolHandler {
7
+ constructor(unityConnection) {
8
+ super(
9
+ 'clear_console',
10
+ 'Clear Console logs (optionally set auto-clear and preserve levels).',
11
+ {
12
+ type: 'object',
13
+ properties: {
14
+ clearOnPlay: {
15
+ type: 'boolean',
16
+ description: 'Clear console when entering play mode. Unity default: false. Set to true for cleaner testing sessions'
17
+ },
18
+ clearOnRecompile: {
19
+ type: 'boolean',
20
+ description: 'Clear console on script recompilation. Unity default: false. Set to true to focus on current compilation errors'
21
+ },
22
+ clearOnBuild: {
23
+ type: 'boolean',
24
+ description: 'Clear console when building. Unity default: false. Set to true for clean build logs'
25
+ },
26
+ preserveWarnings: {
27
+ type: 'boolean',
28
+ description: 'Preserve warning messages when clearing (default: false)'
29
+ },
30
+ preserveErrors: {
31
+ type: 'boolean',
32
+ description: 'Preserve error messages when clearing (default: false)'
33
+ }
34
+ },
35
+ required: []
36
+ }
37
+ );
38
+
39
+ this.unityConnection = unityConnection;
40
+ }
41
+
42
+ /**
43
+ * Validates the input parameters
44
+ * @param {Object} params - The input parameters
45
+ * @throws {Error} If validation fails
46
+ */
47
+ validate(params) {
48
+ const {
49
+ clearOnPlay,
50
+ clearOnRecompile,
51
+ clearOnBuild,
52
+ preserveWarnings,
53
+ preserveErrors
54
+ } = params;
55
+
56
+ // Validate boolean parameters
57
+ if (clearOnPlay !== undefined && typeof clearOnPlay !== 'boolean') {
58
+ throw new Error('clearOnPlay must be a boolean');
59
+ }
60
+
61
+ if (clearOnRecompile !== undefined && typeof clearOnRecompile !== 'boolean') {
62
+ throw new Error('clearOnRecompile must be a boolean');
63
+ }
64
+
65
+ if (clearOnBuild !== undefined && typeof clearOnBuild !== 'boolean') {
66
+ throw new Error('clearOnBuild must be a boolean');
67
+ }
68
+
69
+ if (preserveWarnings !== undefined && typeof preserveWarnings !== 'boolean') {
70
+ throw new Error('preserveWarnings must be a boolean');
71
+ }
72
+
73
+ if (preserveErrors !== undefined && typeof preserveErrors !== 'boolean') {
74
+ throw new Error('preserveErrors must be a boolean');
75
+ }
76
+
77
+ // Validate logical consistency
78
+ const isClearing = clearOnPlay !== false || clearOnRecompile !== false || clearOnBuild !== false;
79
+ const isPreserving = preserveWarnings === true || preserveErrors === true;
80
+
81
+ if (isPreserving && !isClearing) {
82
+ throw new Error('Cannot preserve specific log types when not clearing console');
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Executes the console clear operation
88
+ * @param {Object} params - The input parameters
89
+ * @returns {Promise<Object>} The result of the clear operation
90
+ */
91
+ async execute(params) {
92
+ const {
93
+ clearOnPlay,
94
+ clearOnRecompile,
95
+ clearOnBuild,
96
+ preserveWarnings,
97
+ preserveErrors
98
+ } = params;
99
+
100
+ // Ensure connection to Unity
101
+ if (!this.unityConnection.isConnected()) {
102
+ await this.unityConnection.connect();
103
+ }
104
+
105
+ // Prepare command parameters
106
+ const commandParams = {
107
+ clearOnPlay,
108
+ clearOnRecompile,
109
+ clearOnBuild,
110
+ preserveWarnings,
111
+ preserveErrors
112
+ };
113
+
114
+ // Send command to Unity
115
+ const response = await this.unityConnection.sendCommand('clear_console', commandParams);
116
+
117
+ // Handle Unity response
118
+ if (response.success === false) {
119
+ throw new Error(response.error || 'Failed to clear console');
120
+ }
121
+
122
+ // Build result object
123
+ const result = {
124
+ message: response.message || 'Console cleared successfully',
125
+ timestamp: response.timestamp || new Date().toISOString()
126
+ };
127
+
128
+ // Include optional statistics if available
129
+ if (response.clearedCount !== undefined) {
130
+ result.clearedCount = response.clearedCount;
131
+ }
132
+ if (response.remainingCount !== undefined) {
133
+ result.remainingCount = response.remainingCount;
134
+ }
135
+ if (response.preservedWarnings !== undefined) {
136
+ result.preservedWarnings = response.preservedWarnings;
137
+ }
138
+ if (response.preservedErrors !== undefined) {
139
+ result.preservedErrors = response.preservedErrors;
140
+ }
141
+ if (response.settingsUpdated !== undefined) {
142
+ result.settingsUpdated = response.settingsUpdated;
143
+ }
144
+
145
+ // Include settings if they were updated
146
+ if (response.settingsUpdated) {
147
+ if (response.clearOnPlay !== undefined) {
148
+ result.clearOnPlay = response.clearOnPlay;
149
+ }
150
+ if (response.clearOnRecompile !== undefined) {
151
+ result.clearOnRecompile = response.clearOnRecompile;
152
+ }
153
+ if (response.clearOnBuild !== undefined) {
154
+ result.clearOnBuild = response.clearOnBuild;
155
+ }
156
+ }
157
+
158
+ return result;
159
+ }
160
+ }
@@ -0,0 +1,276 @@
1
+ import { BaseToolHandler } from '../base/BaseToolHandler.js';
2
+
3
+ /**
4
+ * Handler for reading Unity Editor console logs with advanced filtering
5
+ */
6
+ export class ReadConsoleToolHandler extends BaseToolHandler {
7
+ constructor(unityConnection) {
8
+ super(
9
+ 'read_console',
10
+ 'Read Console logs with filters (type/text/time), formatting, sort, and grouping.',
11
+ {
12
+ type: 'object',
13
+ properties: {
14
+ count: {
15
+ type: 'number',
16
+ description: 'Number of logs to retrieve (1-1000, default: 100)',
17
+ minimum: 1,
18
+ maximum: 1000
19
+ },
20
+ logTypes: {
21
+ type: 'array',
22
+ description: 'Filter by log types (default: ["All"])',
23
+ items: {
24
+ type: 'string',
25
+ enum: ['Info', 'Warning', 'Error', 'All']
26
+ }
27
+ },
28
+ filterText: {
29
+ type: 'string',
30
+ description: 'Filter logs containing this text (case-insensitive)'
31
+ },
32
+ includeStackTrace: {
33
+ type: 'boolean',
34
+ description: 'Include stack traces in results. Unity default: false (set to true for debugging)'
35
+ },
36
+ format: {
37
+ type: 'string',
38
+ description: 'Output format for logs. Unity default: compact. RECOMMENDED: compact for general use, detailed for debugging',
39
+ enum: ['detailed', 'compact', 'json', 'plain']
40
+ },
41
+ sinceTimestamp: {
42
+ type: 'string',
43
+ description: 'Only return logs after this timestamp (ISO 8601)'
44
+ },
45
+ untilTimestamp: {
46
+ type: 'string',
47
+ description: 'Only return logs before this timestamp (ISO 8601)'
48
+ },
49
+ sortOrder: {
50
+ type: 'string',
51
+ description: 'Sort order for logs (default: newest)',
52
+ enum: ['newest', 'oldest']
53
+ },
54
+ groupBy: {
55
+ type: 'string',
56
+ description: 'Group logs by criteria (default: none)',
57
+ enum: ['none', 'type', 'file', 'time']
58
+ }
59
+ },
60
+ required: []
61
+ }
62
+ );
63
+
64
+ this.unityConnection = unityConnection;
65
+ }
66
+
67
+ /**
68
+ * Validates the input parameters
69
+ * @param {Object} params - The input parameters
70
+ * @throws {Error} If validation fails
71
+ */
72
+ validate(params) {
73
+ const {
74
+ count,
75
+ logTypes,
76
+ filterText,
77
+ includeStackTrace,
78
+ format,
79
+ sinceTimestamp,
80
+ untilTimestamp,
81
+ sortOrder,
82
+ groupBy
83
+ } = params;
84
+
85
+ // Validate count
86
+ if (count !== undefined) {
87
+ if (typeof count !== 'number' || count < 1 || count > 1000) {
88
+ throw new Error('count must be between 1 and 1000');
89
+ }
90
+ }
91
+
92
+ // Validate log types
93
+ if (logTypes !== undefined) {
94
+ if (!Array.isArray(logTypes)) {
95
+ throw new Error('logTypes must be an array');
96
+ }
97
+
98
+ const validTypes = ['Info', 'Warning', 'Error', 'All'];
99
+ for (const type of logTypes) {
100
+ if (!validTypes.includes(type)) {
101
+ // Invalid types are treated as 'All' later, so just log a warning
102
+ console.warn(`Invalid log type: ${type}. Will be treated as 'All'.`);
103
+ }
104
+ }
105
+ }
106
+
107
+ // Validate timestamps
108
+ if (sinceTimestamp !== undefined) {
109
+ if (!this.isValidISO8601(sinceTimestamp)) {
110
+ throw new Error('sinceTimestamp must be a valid ISO 8601 timestamp');
111
+ }
112
+ }
113
+
114
+ if (untilTimestamp !== undefined) {
115
+ if (!this.isValidISO8601(untilTimestamp)) {
116
+ throw new Error('untilTimestamp must be a valid ISO 8601 timestamp');
117
+ }
118
+ }
119
+
120
+ // Validate timestamp order
121
+ if (sinceTimestamp && untilTimestamp) {
122
+ const since = new Date(sinceTimestamp);
123
+ const until = new Date(untilTimestamp);
124
+ if (until <= since) {
125
+ throw new Error('untilTimestamp must be after sinceTimestamp');
126
+ }
127
+ }
128
+
129
+ // Validate format
130
+ if (format !== undefined) {
131
+ const validFormats = ['detailed', 'compact', 'json', 'plain'];
132
+ if (!validFormats.includes(format)) {
133
+ throw new Error(`format must be one of: ${validFormats.join(', ')}`);
134
+ }
135
+ }
136
+
137
+ // Validate sort order
138
+ if (sortOrder !== undefined) {
139
+ const validOrders = ['newest', 'oldest'];
140
+ if (!validOrders.includes(sortOrder)) {
141
+ throw new Error(`sortOrder must be one of: ${validOrders.join(', ')}`);
142
+ }
143
+ }
144
+
145
+ // Validate groupBy
146
+ if (groupBy !== undefined) {
147
+ const validGroups = ['none', 'type', 'file', 'time'];
148
+ if (!validGroups.includes(groupBy)) {
149
+ throw new Error(`groupBy must be one of: ${validGroups.join(', ')}`);
150
+ }
151
+ }
152
+ }
153
+
154
+ /**
155
+ * Checks if a string is a valid ISO 8601 timestamp
156
+ * @param {string} timestamp - The timestamp to validate
157
+ * @returns {boolean} True if valid
158
+ */
159
+ isValidISO8601(timestamp) {
160
+ const regex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z?$/;
161
+ if (!regex.test(timestamp)) {
162
+ return false;
163
+ }
164
+
165
+ const date = new Date(timestamp);
166
+ return !isNaN(date.getTime());
167
+ }
168
+
169
+ /**
170
+ * Executes the enhanced log reading operation
171
+ * @param {Object} params - The input parameters
172
+ * @returns {Promise<Object>} The result of the log reading
173
+ */
174
+ async execute(params) {
175
+ const {
176
+ count,
177
+ logTypes,
178
+ filterText,
179
+ includeStackTrace,
180
+ format,
181
+ sinceTimestamp,
182
+ untilTimestamp,
183
+ sortOrder,
184
+ groupBy
185
+ } = params;
186
+
187
+ // Convert simplified log types to Unity log types
188
+ let expandedLogTypes = [];
189
+
190
+ if (!logTypes || logTypes.length === 0 || logTypes.includes('All')) {
191
+ // Default to all types
192
+ expandedLogTypes = ['Log', 'Warning', 'Error', 'Exception', 'Assert'];
193
+ } else {
194
+ // Expand each simplified type to Unity types
195
+ logTypes.forEach(type => {
196
+ switch(type) {
197
+ case 'Info':
198
+ expandedLogTypes.push('Log');
199
+ break;
200
+ case 'Warning':
201
+ expandedLogTypes.push('Warning');
202
+ break;
203
+ case 'Error':
204
+ // Error includes all error-related types
205
+ expandedLogTypes.push('Error', 'Exception', 'Assert');
206
+ break;
207
+ case 'All': expandedLogTypes.push('Log', 'Warning', 'Error', 'Exception', 'Assert');
208
+ break;
209
+ }
210
+ });
211
+
212
+ // Remove duplicates
213
+ expandedLogTypes = [...new Set(expandedLogTypes)];
214
+ }
215
+
216
+ // Ensure connection to Unity
217
+ if (!this.unityConnection.isConnected()) {
218
+ await this.unityConnection.connect();
219
+ }
220
+
221
+ // Prepare command parameters
222
+ const commandParams = {
223
+ count,
224
+ logTypes: expandedLogTypes,
225
+ includeStackTrace,
226
+ format,
227
+ sortOrder,
228
+ groupBy
229
+ };
230
+
231
+ // Add optional parameters
232
+ if (filterText !== undefined) {
233
+ commandParams.filterText = filterText;
234
+ }
235
+ if (sinceTimestamp !== undefined) {
236
+ commandParams.sinceTimestamp = sinceTimestamp;
237
+ }
238
+ if (untilTimestamp !== undefined) {
239
+ commandParams.untilTimestamp = untilTimestamp;
240
+ }
241
+
242
+ // Send command to Unity
243
+ const response = await this.unityConnection.sendCommand('read_console', commandParams);
244
+
245
+ // Handle Unity response
246
+ if (response.success === false) {
247
+ throw new Error(response.error || 'Failed to read logs');
248
+ }
249
+
250
+ // Build result object
251
+ const result = {
252
+ logs: response.logs || [],
253
+ count: response.count || 0,
254
+ totalCaptured: response.totalCaptured || 0
255
+ };
256
+
257
+ // Include optional fields if available
258
+ if (response.filteredCount !== undefined) {
259
+ result.filteredCount = response.filteredCount;
260
+ }
261
+ if (response.statistics !== undefined) {
262
+ result.statistics = response.statistics;
263
+ }
264
+ if (response.groupedLogs !== undefined) {
265
+ result.groupedLogs = response.groupedLogs;
266
+ }
267
+ if (response.format !== undefined) {
268
+ result.format = response.format;
269
+ }
270
+ if (response.groupBy !== undefined) {
271
+ result.groupBy = response.groupBy;
272
+ }
273
+
274
+ return result;
275
+ }
276
+ }
@@ -0,0 +1,160 @@
1
+ import { BaseToolHandler } from '../base/BaseToolHandler.js';
2
+
3
+ /**
4
+ * Handles Unity layer management operations
5
+ */
6
+ export class LayerManagementToolHandler extends BaseToolHandler {
7
+ constructor(unityConnection) {
8
+ super(
9
+ 'manage_layers',
10
+ 'Manage project layers: add/remove/list and convert (by name/index).',
11
+ {
12
+ type: 'object',
13
+ properties: {
14
+ action: {
15
+ type: 'string',
16
+ enum: ['add', 'remove', 'get', 'get_by_name', 'get_by_index'],
17
+ description: 'Operation: add, remove, get, get_by_name, or get_by_index.'
18
+ },
19
+ layerName: {
20
+ type: 'string',
21
+ description: 'Layer name (required for add/remove/get_by_name). Letters/numbers/space/_ only.'
22
+ },
23
+ layerIndex: {
24
+ type: 'number',
25
+ minimum: 0,
26
+ maximum: 31,
27
+ description: 'Layer index (0-31). Required for get_by_index.'
28
+ }
29
+ },
30
+ required: ['action']
31
+ }
32
+ );
33
+ this.unityConnection = unityConnection;
34
+ this.RESERVED_LAYERS = ['Default', 'TransparentFX', 'Ignore Raycast', 'Water', 'UI'];
35
+ }
36
+
37
+ /**
38
+ * Validate the parameters for the layer management operation
39
+ */
40
+ validate(params) {
41
+ const { action, layerName, layerIndex } = params;
42
+
43
+ // Check action is provided
44
+ if (!action) {
45
+ throw new Error('action is required');
46
+ }
47
+
48
+ // Validate action is one of the allowed values
49
+ const allowedActions = ['add', 'remove', 'get', 'get_by_name', 'get_by_index'];
50
+ if (!allowedActions.includes(action)) {
51
+ throw new Error(`action must be one of: ${allowedActions.join(', ')}`);
52
+ }
53
+
54
+ // Validate based on action
55
+ switch (action) {
56
+ case 'add':
57
+ case 'remove':
58
+ if (layerName === undefined || layerName === null) {
59
+ throw new Error(`layerName is required for ${action} action`);
60
+ }
61
+ if (layerName === '') {
62
+ throw new Error('layerName cannot be empty');
63
+ }
64
+ if (!this.isValidLayerName(layerName)) {
65
+ throw new Error('layerName contains invalid characters');
66
+ }
67
+ if (action === 'add' && this.RESERVED_LAYERS.includes(layerName)) {
68
+ throw new Error('layerName is reserved');
69
+ }
70
+ break;
71
+
72
+ case 'get_by_name':
73
+ if (layerName === undefined || layerName === null) {
74
+ throw new Error('layerName is required for get_by_name action');
75
+ }
76
+ break;
77
+
78
+ case 'get_by_index':
79
+ if (layerIndex === undefined || layerIndex === null) {
80
+ throw new Error('layerIndex is required for get_by_index action');
81
+ }
82
+ if (layerIndex < 0 || layerIndex > 31) {
83
+ throw new Error('layerIndex must be between 0 and 31');
84
+ }
85
+ break;
86
+ }
87
+
88
+ // Call parent validation last
89
+ super.validate(params);
90
+ }
91
+
92
+ /**
93
+ * Check if layer name contains only valid characters
94
+ */
95
+ isValidLayerName(layerName) {
96
+ // Layer names should only contain letters, numbers, spaces, and underscores
97
+ const validPattern = /^[a-zA-Z0-9\s_]+$/;
98
+ return validPattern.test(layerName);
99
+ }
100
+
101
+ /**
102
+ * Execute the layer management command
103
+ */
104
+ async execute(params) {
105
+ // Ensure connected
106
+ if (!this.unityConnection.isConnected()) {
107
+ await this.unityConnection.connect();
108
+ }
109
+
110
+ const result = await this.unityConnection.sendCommand('manage_layers', params);
111
+
112
+ if (result.error) {
113
+ throw new Error(result.error);
114
+ }
115
+
116
+ return result;
117
+ }
118
+
119
+ /**
120
+ * Get examples of how to use this tool
121
+ */
122
+ getExamples() {
123
+ return {
124
+ getLayers: {
125
+ description: 'Get all layers with indices',
126
+ params: {
127
+ action: 'get'
128
+ }
129
+ },
130
+ addLayer: {
131
+ description: 'Add a new layer',
132
+ params: {
133
+ action: 'add',
134
+ layerName: 'Enemy'
135
+ }
136
+ },
137
+ removeLayer: {
138
+ description: 'Remove an existing layer',
139
+ params: {
140
+ action: 'remove',
141
+ layerName: 'Enemy'
142
+ }
143
+ },
144
+ getLayerByName: {
145
+ description: 'Get layer index by name',
146
+ params: {
147
+ action: 'get_by_name',
148
+ layerName: 'Player'
149
+ }
150
+ },
151
+ getLayerByIndex: {
152
+ description: 'Get layer name by index',
153
+ params: {
154
+ action: 'get_by_index',
155
+ layerIndex: 8
156
+ }
157
+ }
158
+ };
159
+ }
160
+ }