@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,692 @@
1
+ import { BaseToolHandler } from '../base/BaseToolHandler.js';
2
+
3
+ /**
4
+ * Handler for capturing screenshots from Unity Editor
5
+ */
6
+ export class CaptureScreenshotToolHandler extends BaseToolHandler {
7
+ constructor(unityConnection) {
8
+ super(
9
+ 'capture_screenshot',
10
+ 'Capture Game/Scene/Window/Explorer screenshots. Output path is fixed to <workspace>/.unity/capture/. For LLM use, prefer explorer mode (auto-framing, clarity). Use encodeAsBase64=true only for immediate analysis, and keep resolution minimal.',
11
+ {
12
+ type: 'object',
13
+ properties: {
14
+ captureMode: {
15
+ type: 'string',
16
+ enum: ['game', 'scene', 'window', 'explorer'],
17
+ description: 'Capture mode selection:\n- "game": Game View (player\'s perspective, what the user sees)\n- "scene": Scene View (editor\'s 3D workspace view)\n- "explorer": AI/LLM exploration mode (optimized view for understanding scene content)\n- "window": Specific Unity Editor window by name'
18
+ },
19
+ width: {
20
+ type: 'number',
21
+ description: 'Screenshot width in pixels (0 = use current view size). Range: 1-8192. Not applicable for explorer mode (use explorerSettings.camera.width instead)'
22
+ },
23
+ height: {
24
+ type: 'number',
25
+ description: 'Screenshot height in pixels (0 = use current view size). Range: 1-8192. Not applicable for explorer mode (use explorerSettings.camera.height instead)'
26
+ },
27
+ includeUI: {
28
+ type: 'boolean',
29
+ description: 'Whether to include UI overlay elements in Game View captures. Set to false for clean gameplay screenshots (default: true)'
30
+ },
31
+ windowName: {
32
+ type: 'string',
33
+ description: 'Name of the Unity Editor window to capture (required when captureMode is "window"). Examples: "Console", "Inspector", "Hierarchy", "Project"'
34
+ },
35
+ encodeAsBase64: {
36
+ type: 'boolean',
37
+ description: 'Return screenshot data as base64 string for immediate processing/analysis without file I/O (default: false)'
38
+ },
39
+ explorerSettings: {
40
+ type: 'object',
41
+ description: 'Configuration for explorer mode - AI/LLM optimized capture settings',
42
+ properties: {
43
+ target: {
44
+ type: 'object',
45
+ description: 'Defines what to focus on in the scene',
46
+ properties: {
47
+ type: {
48
+ type: 'string',
49
+ enum: ['gameObject', 'tag', 'area', 'position'],
50
+ description: 'Target selection method:\n- "gameObject": Focus on a specific GameObject by name\n- "tag": Focus on all objects with a specific tag\n- "area": Focus on a circular area defined by center and radius\n- "position": Manual camera positioning without auto-framing'
51
+ },
52
+ name: {
53
+ type: 'string',
54
+ description: 'GameObject name to target (required for "gameObject" type). Example: "Player", "MainCamera", "Enemy_01"'
55
+ },
56
+ tag: {
57
+ type: 'string',
58
+ description: 'Tag to search for GameObjects (required for "tag" type). Example: "Enemy", "Collectible", "Player"'
59
+ },
60
+ center: {
61
+ type: 'object',
62
+ description: 'Center position for area targeting (required for "area" type)',
63
+ properties: {
64
+ x: { type: 'number', description: 'X coordinate in world space' },
65
+ y: { type: 'number', description: 'Y coordinate in world space' },
66
+ z: { type: 'number', description: 'Z coordinate in world space' }
67
+ }
68
+ },
69
+ radius: {
70
+ type: 'number',
71
+ description: 'Radius around center for area targeting (used with "area" type). Defines the circular area to capture'
72
+ },
73
+ includeChildren: {
74
+ type: 'boolean',
75
+ description: 'Include child objects when calculating the target bounds for framing (default: true)'
76
+ }
77
+ }
78
+ },
79
+ camera: {
80
+ type: 'object',
81
+ description: 'Camera configuration for the capture',
82
+ properties: {
83
+ position: {
84
+ type: 'object',
85
+ description: 'Manual camera position in world space (overrides auto-framing)',
86
+ properties: {
87
+ x: { type: 'number', description: 'X position in world units' },
88
+ y: { type: 'number', description: 'Y position in world units' },
89
+ z: { type: 'number', description: 'Z position in world units' }
90
+ }
91
+ },
92
+ lookAt: {
93
+ type: 'object',
94
+ description: 'Point for camera to look at in world space',
95
+ properties: {
96
+ x: { type: 'number', description: 'X coordinate to look at' },
97
+ y: { type: 'number', description: 'Y coordinate to look at' },
98
+ z: { type: 'number', description: 'Z coordinate to look at' }
99
+ }
100
+ },
101
+ rotation: {
102
+ type: 'object',
103
+ description: 'Camera rotation in Euler angles (alternative to lookAt)',
104
+ properties: {
105
+ x: { type: 'number', description: 'X rotation (pitch) in degrees' },
106
+ y: { type: 'number', description: 'Y rotation (yaw) in degrees' },
107
+ z: { type: 'number', description: 'Z rotation (roll) in degrees' }
108
+ }
109
+ },
110
+ fieldOfView: {
111
+ type: 'number',
112
+
113
+ description: 'Camera field of view in degrees (1-179). Lower values zoom in, higher values zoom out'
114
+ },
115
+ nearClip: {
116
+ type: 'number',
117
+ description: 'Near clipping plane distance. Objects closer than this won\'t be rendered (default: 0.3)'
118
+ },
119
+ farClip: {
120
+ type: 'number',
121
+ description: 'Far clipping plane distance. Objects farther than this won\'t be rendered'
122
+ },
123
+ autoFrame: {
124
+ type: 'boolean',
125
+ description: 'Automatically position camera to frame the target. Disable for manual positioning (default: true)'
126
+ },
127
+ padding: {
128
+ type: 'number',
129
+ description: 'Padding around target when auto-framing (0-1). 0 = tight fit, 1 = lots of space (default: 0.2)'
130
+ },
131
+ offset: {
132
+ type: 'object',
133
+ description: 'Additional offset from calculated auto-frame position',
134
+ properties: {
135
+ x: { type: 'number', description: 'X offset in world units' },
136
+ y: { type: 'number', description: 'Y offset in world units' },
137
+ z: { type: 'number', description: 'Z offset in world units' }
138
+ }
139
+ },
140
+ width: {
141
+ type: 'number',
142
+ description: 'Capture resolution width in pixels (1-8192)'
143
+ },
144
+ height: {
145
+ type: 'number',
146
+ description: 'Capture resolution height in pixels (1-8192)'
147
+ }
148
+ }
149
+ },
150
+ display: {
151
+ type: 'object',
152
+ description: 'Visual display settings for the capture',
153
+ properties: {
154
+ backgroundColor: {
155
+ type: 'object',
156
+ description: 'Background color for the capture (RGBA values 0-1)',
157
+ properties: {
158
+ r: { type: 'number', description: 'Red component (0-1, default: 0.2)' },
159
+ g: { type: 'number', description: 'Green component (0-1, default: 0.2)' },
160
+ b: { type: 'number', description: 'Blue component (0-1, default: 0.2)' },
161
+ a: { type: 'number', description: 'Alpha/opacity (0-1, default: 1)' }
162
+ }
163
+ },
164
+ layers: {
165
+ type: 'array',
166
+ items: { type: 'string' },
167
+ description: 'Unity layer names to include in rendering. If not specified, all layers are included. Examples: ["Default", "UI", "Player"]'
168
+ },
169
+ showGizmos: {
170
+ type: 'boolean',
171
+ description: 'Show editor gizmos (transform handles, icons) in the capture (default: false)'
172
+ },
173
+ showColliders: {
174
+ type: 'boolean',
175
+ description: 'Visualize physics colliders as wireframes (default: false)'
176
+ },
177
+ showBounds: {
178
+ type: 'boolean',
179
+ description: 'Show bounding boxes around objects (default: false)'
180
+ },
181
+ highlightTarget: {
182
+ type: 'boolean',
183
+ description: 'Highlight the target object(s) with an outline or color (default: false)'
184
+ }
185
+ }
186
+ }
187
+ }
188
+ }
189
+ },
190
+ required: []
191
+ }
192
+ );
193
+
194
+ this.unityConnection = unityConnection;
195
+ }
196
+
197
+ /**
198
+ * Validates the input parameters
199
+ * @param {Object} params - The input parameters
200
+ * @throws {Error} If validation fails
201
+ */
202
+ validate(params) {
203
+ const { captureMode = 'game', windowName, outputPath, explorerSettings } = params;
204
+
205
+ // Validate capture mode
206
+ if (!['game', 'scene', 'window', 'explorer'].includes(captureMode)) {
207
+ throw new Error(
208
+ 'Invalid captureMode. Must be one of: "game" (Game View), "scene" (Scene View), "window" (specific editor window), or "explorer" (AI/LLM exploration mode)'
209
+ );
210
+ }
211
+
212
+ // Window mode requires windowName
213
+ if (captureMode === 'window' && !windowName) {
214
+ throw new Error(
215
+ 'windowName is required when captureMode is "window". ' +
216
+ 'Specify the name of the Unity Editor window to capture (e.g., "Console", "Inspector", "Hierarchy")'
217
+ );
218
+ }
219
+
220
+ // Explorer mode validation
221
+ if (captureMode === 'explorer' && explorerSettings) {
222
+ const { target, camera, display } = explorerSettings;
223
+
224
+ // Validate target settings
225
+ if (target) {
226
+ const { type } = target;
227
+ if (type && !['gameObject', 'tag', 'area', 'position'].includes(type)) {
228
+ throw new Error(
229
+ 'Invalid target.type for explorer mode. Must be one of:\n' +
230
+ '- "gameObject": Focus on a specific GameObject by name\n' +
231
+ '- "tag": Focus on all objects with a specific tag\n' +
232
+ '- "area": Focus on a circular area (center + radius)\n' +
233
+ '- "position": Manual camera positioning'
234
+ );
235
+ }
236
+
237
+ if (type === 'gameObject' && !target.name) {
238
+ throw new Error(
239
+ 'target.name is required when target.type is "gameObject". ' +
240
+ 'Specify the name of the GameObject to focus on (e.g., "Player", "MainCamera")'
241
+ );
242
+ }
243
+
244
+ if (type === 'tag' && !target.tag) {
245
+ throw new Error(
246
+ 'target.tag is required when target.type is "tag". ' +
247
+ 'Specify the tag to search for (e.g., "Enemy", "Collectible")'
248
+ );
249
+ }
250
+
251
+ if (type === 'area') {
252
+ if (!target.center) {
253
+ throw new Error(
254
+ 'target.center is required when target.type is "area". ' +
255
+ 'Specify the center position as {x, y, z} in world coordinates'
256
+ );
257
+ }
258
+ if (target.radius === undefined || target.radius <= 0) {
259
+ throw new Error(
260
+ 'target.radius must be a positive number when target.type is "area". ' +
261
+ 'Specify the radius of the area to capture'
262
+ );
263
+ }
264
+ }
265
+ }
266
+
267
+ // Validate camera settings
268
+ if (camera) {
269
+ if (camera.fieldOfView !== undefined) {
270
+ if (camera.fieldOfView < 1 || camera.fieldOfView > 179) {
271
+ throw new Error(
272
+ 'camera.fieldOfView must be between 1 and 179 degrees. ' +
273
+ 'Lower values (20-40) provide zoom effect, standard is 60, wide angle is 90+'
274
+ );
275
+ }
276
+ }
277
+
278
+ if (camera.padding !== undefined) {
279
+ if (camera.padding < 0 || camera.padding > 1) {
280
+ throw new Error(
281
+ 'camera.padding must be between 0 and 1. ' +
282
+ '0 = tight framing, 0.2 = normal padding, 0.5+ = lots of surrounding space'
283
+ );
284
+ }
285
+ }
286
+
287
+ if (camera.width !== undefined) {
288
+ if (camera.width < 1 || camera.width > 8192) {
289
+ throw new Error(
290
+ 'camera.width must be between 1 and 8192 pixels. ' +
291
+ 'Common values: 1920 (Full HD), 2560 (2K), 3840 (4K)'
292
+ );
293
+ }
294
+ }
295
+
296
+ if (camera.height !== undefined) {
297
+ if (camera.height < 1 || camera.height > 8192) {
298
+ throw new Error(
299
+ 'camera.height must be between 1 and 8192 pixels. ' +
300
+ 'Common values: 1080 (Full HD), 1440 (2K), 2160 (4K)'
301
+ );
302
+ }
303
+ }
304
+
305
+ if (camera.nearClip !== undefined && camera.nearClip <= 0) {
306
+ throw new Error(
307
+ 'camera.nearClip must be a positive number. ' +
308
+ 'Typically 0.01 to 1.0 for close-up views, 0.3 is default'
309
+ );
310
+ }
311
+
312
+ if (camera.farClip !== undefined && camera.farClip <= 0) {
313
+ throw new Error(
314
+ 'camera.farClip must be a positive number. ' +
315
+ 'Should be larger than nearClip. Default is 1000, reduce for performance'
316
+ );
317
+ }
318
+
319
+ if (camera.nearClip !== undefined && camera.farClip !== undefined) {
320
+ if (camera.nearClip >= camera.farClip) {
321
+ throw new Error(
322
+ 'camera.nearClip must be less than camera.farClip. ' +
323
+ 'nearClip defines the closest visible distance, farClip the farthest'
324
+ );
325
+ }
326
+ }
327
+ }
328
+
329
+ // Validate display settings
330
+ if (display) {
331
+ if (display.backgroundColor) {
332
+ const { r, g, b, a } = display.backgroundColor;
333
+ const validateColorComponent = (value, name) => {
334
+ if (value !== undefined && (value < 0 || value > 1)) {
335
+ throw new Error(
336
+ `backgroundColor.${name} must be between 0 and 1. ` +
337
+ 'Color components use normalized values (0 = black/transparent, 1 = full intensity)'
338
+ );
339
+ }
340
+ };
341
+ validateColorComponent(r, 'r');
342
+ validateColorComponent(g, 'g');
343
+ validateColorComponent(b, 'b');
344
+ validateColorComponent(a, 'a');
345
+ }
346
+
347
+ if (display.layers && !Array.isArray(display.layers)) {
348
+ throw new Error(
349
+ 'display.layers must be an array of layer names. ' +
350
+ 'Example: ["Default", "UI", "Player"]'
351
+ );
352
+ }
353
+ }
354
+ }
355
+
356
+ // Validate output path if provided
357
+ if (outputPath) {
358
+ const validExtensions = ['.png', '.jpg', '.jpeg'];
359
+ const hasValidExtension = validExtensions.some(ext => outputPath.toLowerCase().endsWith(ext));
360
+
361
+ if (!hasValidExtension) {
362
+ throw new Error(
363
+ 'outputPath must end with .png, .jpg, or .jpeg. ' +
364
+ 'PNG is recommended for best quality, JPG for smaller file size'
365
+ );
366
+ }
367
+
368
+ if (!outputPath.startsWith('Assets/')) {
369
+ throw new Error(
370
+ 'outputPath must be within the Assets folder. ' +
371
+ 'Example: "Assets/Screenshots/capture.png"'
372
+ );
373
+ }
374
+
375
+ // Warn about potential path issues
376
+ if (outputPath.includes('\\')) {
377
+ throw new Error(
378
+ 'outputPath should use forward slashes (/) not backslashes (\\). ' +
379
+ 'Example: "Assets/Screenshots/capture.png"'
380
+ );
381
+ }
382
+ }
383
+
384
+ // Validate dimensions for non-explorer modes
385
+ if (captureMode !== 'explorer') {
386
+ if (params.width !== undefined) {
387
+ if (params.width < 0 || params.width > 8192) {
388
+ throw new Error(
389
+ 'width must be between 0 and 8192 pixels. ' +
390
+ '0 = use current view size, common values: 1920, 2560, 3840'
391
+ );
392
+ }
393
+ }
394
+
395
+ if (params.height !== undefined) {
396
+ if (params.height < 0 || params.height > 8192) {
397
+ throw new Error(
398
+ 'height must be between 0 and 8192 pixels. ' +
399
+ '0 = use current view size, common values: 1080, 1440, 2160'
400
+ );
401
+ }
402
+ }
403
+ } else if (params.width !== undefined || params.height !== undefined) {
404
+ throw new Error(
405
+ 'For explorer mode, set resolution using explorerSettings.camera.width and explorerSettings.camera.height instead of top-level width/height'
406
+ );
407
+ }
408
+ }
409
+
410
+ /**
411
+ * Executes the screenshot capture
412
+ * @param {Object} params - The validated input parameters
413
+ * @returns {Promise<Object>} The screenshot capture result
414
+ */
415
+ async execute(params) {
416
+ // Ensure connection to Unity
417
+ if (!this.unityConnection.isConnected()) {
418
+ await this.unityConnection.connect();
419
+ }
420
+
421
+ // Send capture command to Unity
422
+ const { WORKSPACE_ROOT } = await import('../../core/config.js');
423
+ const response = await this.unityConnection.sendCommand('capture_screenshot', { ...(params||{}), workspaceRoot: WORKSPACE_ROOT });
424
+
425
+ // Handle Unity response
426
+ if (response.error) {
427
+ throw new Error(response.error);
428
+ }
429
+
430
+ // Build result object
431
+ const result = {
432
+ path: response.path,
433
+ width: response.width,
434
+ height: response.height,
435
+ captureMode: response.captureMode,
436
+ fileSize: response.fileSize,
437
+ message: response.message || 'Screenshot captured successfully'
438
+ };
439
+
440
+ // Include optional fields if present
441
+ if (response.includeUI !== undefined) {
442
+ result.includeUI = response.includeUI;
443
+ }
444
+
445
+ if (response.cameraPosition) {
446
+ result.cameraPosition = response.cameraPosition;
447
+ }
448
+
449
+ if (response.cameraRotation) {
450
+ result.cameraRotation = response.cameraRotation;
451
+ }
452
+
453
+ if (response.base64Data) {
454
+ result.base64Data = response.base64Data;
455
+ }
456
+
457
+ return result;
458
+ }
459
+
460
+ /**
461
+ * Gets example usage for this tool
462
+ * @returns {Object} Example usage scenarios
463
+ */
464
+ getExamples() {
465
+ return {
466
+ // Basic capture examples
467
+ quickGameCapture: {
468
+ description: 'Quick capture of current game view for debugging',
469
+ params: {
470
+ captureMode: 'game'
471
+ }
472
+ },
473
+ captureForAnalysis: {
474
+ description: 'Capture game view as base64 for immediate AI analysis',
475
+ params: {
476
+ captureMode: 'game',
477
+ encodeAsBase64: true,
478
+ includeUI: false
479
+ }
480
+ },
481
+ highResSceneCapture: {
482
+ description: 'High-resolution scene view capture for documentation',
483
+ params: {
484
+ captureMode: 'scene',
485
+ width: 3840,
486
+ height: 2160,
487
+ outputPath: 'Assets/Screenshots/scene_4k.png'
488
+ }
489
+ },
490
+
491
+ // Explorer mode examples for AI/LLM usage
492
+ analyzePlayer: {
493
+ description: 'AI/LLM: Analyze the player character and its surroundings',
494
+ params: {
495
+ captureMode: 'explorer',
496
+ encodeAsBase64: true,
497
+ explorerSettings: {
498
+ target: {
499
+ type: 'gameObject',
500
+ name: 'Player',
501
+ includeChildren: true
502
+ },
503
+ camera: {
504
+ autoFrame: true,
505
+ padding: 0.3,
506
+ fieldOfView: 45
507
+ },
508
+ display: {
509
+ backgroundColor: { r: 0.15, g: 0.15, b: 0.2, a: 1 },
510
+ highlightTarget: true
511
+ }
512
+ }
513
+ }
514
+ },
515
+ inspectUILayout: {
516
+ description: 'AI/LLM: Inspect UI canvas layout and elements',
517
+ params: {
518
+ captureMode: 'explorer',
519
+ encodeAsBase64: true,
520
+ explorerSettings: {
521
+ target: {
522
+ type: 'gameObject',
523
+ name: 'Canvas',
524
+ includeChildren: true
525
+ },
526
+ camera: {
527
+ autoFrame: true,
528
+ padding: 0.1
529
+ },
530
+ display: {
531
+ layers: ['UI'],
532
+ backgroundColor: { r: 0.1, g: 0.1, b: 0.1, a: 1 }
533
+ }
534
+ }
535
+ }
536
+ },
537
+ findAllEnemies: {
538
+ description: 'AI/LLM: Locate and analyze all enemies in the scene',
539
+ params: {
540
+ captureMode: 'explorer',
541
+ encodeAsBase64: true,
542
+ explorerSettings: {
543
+ target: {
544
+ type: 'tag',
545
+ tag: 'Enemy'
546
+ },
547
+ camera: {
548
+ autoFrame: true,
549
+ padding: 0.4,
550
+ fieldOfView: 60
551
+ },
552
+ display: {
553
+ highlightTarget: true,
554
+ showBounds: true
555
+ }
556
+ }
557
+ }
558
+ },
559
+ inspectGameArea: {
560
+ description: 'AI/LLM: Overview of a specific game area or zone',
561
+ params: {
562
+ captureMode: 'explorer',
563
+ encodeAsBase64: true,
564
+ explorerSettings: {
565
+ target: {
566
+ type: 'area',
567
+ center: { x: 0, y: 0, z: 0 },
568
+ radius: 30
569
+ },
570
+ camera: {
571
+ position: { x: 20, y: 30, z: -20 },
572
+ lookAt: { x: 0, y: 0, z: 0 },
573
+ fieldOfView: 50
574
+ },
575
+ display: {
576
+ layers: ['Default', 'Terrain', 'Buildings'],
577
+ showColliders: false
578
+ }
579
+ }
580
+ }
581
+ },
582
+ debugPhysics: {
583
+ description: 'AI/LLM: Debug physics setup with colliders visible',
584
+ params: {
585
+ captureMode: 'explorer',
586
+ encodeAsBase64: true,
587
+ explorerSettings: {
588
+ target: {
589
+ type: 'gameObject',
590
+ name: 'PhysicsTestObject',
591
+ includeChildren: true
592
+ },
593
+ camera: {
594
+ autoFrame: true,
595
+ padding: 0.5
596
+ },
597
+ display: {
598
+ showColliders: true,
599
+ showBounds: true,
600
+ backgroundColor: { r: 0.05, g: 0.05, b: 0.1, a: 1 }
601
+ }
602
+ }
603
+ }
604
+ },
605
+ birdEyeView: {
606
+ description: 'AI/LLM: Top-down bird\'s eye view of the entire level',
607
+ params: {
608
+ captureMode: 'explorer',
609
+ encodeAsBase64: true,
610
+ explorerSettings: {
611
+ camera: {
612
+ position: { x: 0, y: 100, z: 0 },
613
+ rotation: { x: 90, y: 0, z: 0 },
614
+ fieldOfView: 60,
615
+ farClip: 200
616
+ },
617
+ display: {
618
+ backgroundColor: { r: 0.53, g: 0.81, b: 0.92, a: 1 }
619
+ }
620
+ }
621
+ }
622
+ },
623
+
624
+ // Window capture examples
625
+ captureConsole: {
626
+ description: 'Capture Unity console for error debugging',
627
+ params: {
628
+ captureMode: 'window',
629
+ windowName: 'Console',
630
+ outputPath: 'Assets/Screenshots/console_errors.png'
631
+ }
632
+ },
633
+ captureInspector: {
634
+ description: 'Capture inspector window to document component settings',
635
+ params: {
636
+ captureMode: 'window',
637
+ windowName: 'Inspector'
638
+ }
639
+ },
640
+
641
+ // Advanced explorer examples
642
+ cinematicShot: {
643
+ description: 'AI/LLM: Cinematic angle shot of a specific scene moment',
644
+ params: {
645
+ captureMode: 'explorer',
646
+ explorerSettings: {
647
+ target: {
648
+ type: 'gameObject',
649
+ name: 'MainCharacter'
650
+ },
651
+ camera: {
652
+ autoFrame: false,
653
+ position: { x: 5, y: 2, z: -8 },
654
+ lookAt: { x: 0, y: 1.5, z: 0 },
655
+ fieldOfView: 35,
656
+ width: 2560,
657
+ height: 1440
658
+ },
659
+ display: {
660
+ backgroundColor: { r: 0.02, g: 0.02, b: 0.05, a: 1 },
661
+ showGizmos: false
662
+ }
663
+ },
664
+ outputPath: 'Assets/Screenshots/cinematic_shot.png'
665
+ }
666
+ },
667
+ multiObjectAnalysis: {
668
+ description: 'AI/LLM: Analyze multiple tagged objects in one view',
669
+ params: {
670
+ captureMode: 'explorer',
671
+ encodeAsBase64: true,
672
+ explorerSettings: {
673
+ target: {
674
+ type: 'tag',
675
+ tag: 'Interactive'
676
+ },
677
+ camera: {
678
+ autoFrame: true,
679
+ padding: 0.5,
680
+ fieldOfView: 70
681
+ },
682
+ display: {
683
+ highlightTarget: true,
684
+ showBounds: true,
685
+ layers: ['Default', 'Interactive']
686
+ }
687
+ }
688
+ }
689
+ }
690
+ };
691
+ }
692
+ }