unreal-engine-mcp-server 0.5.0 → 0.5.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 (139) hide show
  1. package/.env.example +1 -1
  2. package/.github/release-drafter-config.yml +51 -0
  3. package/.github/workflows/greetings.yml +5 -1
  4. package/.github/workflows/labeler.yml +2 -1
  5. package/.github/workflows/publish-mcp.yml +1 -0
  6. package/.github/workflows/release-drafter.yml +1 -1
  7. package/.github/workflows/release.yml +3 -3
  8. package/CHANGELOG.md +71 -0
  9. package/CONTRIBUTING.md +1 -1
  10. package/GEMINI.md +115 -0
  11. package/Public/Plugin_setup_guide.mp4 +0 -0
  12. package/README.md +166 -200
  13. package/dist/config.d.ts +0 -1
  14. package/dist/config.js +0 -1
  15. package/dist/constants.d.ts +4 -0
  16. package/dist/constants.js +4 -0
  17. package/dist/graphql/loaders.d.ts +64 -0
  18. package/dist/graphql/loaders.js +117 -0
  19. package/dist/graphql/resolvers.d.ts +3 -3
  20. package/dist/graphql/resolvers.js +33 -30
  21. package/dist/graphql/server.js +3 -1
  22. package/dist/graphql/types.d.ts +2 -0
  23. package/dist/index.d.ts +2 -0
  24. package/dist/index.js +13 -2
  25. package/dist/server-setup.d.ts +0 -1
  26. package/dist/server-setup.js +0 -40
  27. package/dist/tools/actors.d.ts +40 -24
  28. package/dist/tools/actors.js +8 -2
  29. package/dist/tools/assets.d.ts +19 -71
  30. package/dist/tools/assets.js +28 -22
  31. package/dist/tools/base-tool.d.ts +4 -4
  32. package/dist/tools/base-tool.js +1 -1
  33. package/dist/tools/blueprint.d.ts +33 -61
  34. package/dist/tools/consolidated-tool-handlers.js +96 -110
  35. package/dist/tools/dynamic-handler-registry.d.ts +11 -9
  36. package/dist/tools/dynamic-handler-registry.js +17 -95
  37. package/dist/tools/editor.d.ts +19 -193
  38. package/dist/tools/editor.js +8 -0
  39. package/dist/tools/environment.d.ts +8 -14
  40. package/dist/tools/foliage.d.ts +18 -143
  41. package/dist/tools/foliage.js +4 -2
  42. package/dist/tools/handlers/actor-handlers.js +0 -5
  43. package/dist/tools/handlers/asset-handlers.js +454 -454
  44. package/dist/tools/landscape.d.ts +16 -116
  45. package/dist/tools/landscape.js +7 -3
  46. package/dist/tools/level.d.ts +22 -103
  47. package/dist/tools/level.js +24 -16
  48. package/dist/tools/lighting.js +5 -1
  49. package/dist/tools/materials.js +5 -1
  50. package/dist/tools/niagara.js +37 -2
  51. package/dist/tools/performance.d.ts +0 -1
  52. package/dist/tools/performance.js +0 -1
  53. package/dist/tools/physics.js +5 -1
  54. package/dist/tools/sequence.d.ts +24 -24
  55. package/dist/tools/sequence.js +13 -0
  56. package/dist/tools/ui.d.ts +0 -2
  57. package/dist/types/automation-responses.d.ts +115 -0
  58. package/dist/types/automation-responses.js +2 -0
  59. package/dist/types/responses.d.ts +249 -0
  60. package/dist/types/responses.js +2 -0
  61. package/dist/types/tool-interfaces.d.ts +135 -135
  62. package/dist/utils/command-validator.js +3 -2
  63. package/dist/utils/path-security.d.ts +2 -0
  64. package/dist/utils/path-security.js +24 -0
  65. package/dist/utils/response-factory.d.ts +4 -4
  66. package/dist/utils/response-factory.js +15 -21
  67. package/docs/Migration-Guide-v0.5.0.md +1 -9
  68. package/docs/testing-guide.md +2 -2
  69. package/package.json +12 -6
  70. package/scripts/run-all-tests.mjs +25 -20
  71. package/server.json +3 -2
  72. package/src/config.ts +1 -1
  73. package/src/constants.ts +7 -0
  74. package/src/graphql/loaders.ts +244 -0
  75. package/src/graphql/resolvers.ts +47 -49
  76. package/src/graphql/server.ts +3 -1
  77. package/src/graphql/types.ts +3 -0
  78. package/src/index.ts +15 -2
  79. package/src/resources/assets.ts +5 -4
  80. package/src/server-setup.ts +3 -37
  81. package/src/tools/actors.ts +36 -28
  82. package/src/tools/animation.ts +1 -0
  83. package/src/tools/assets.ts +74 -63
  84. package/src/tools/base-tool.ts +3 -3
  85. package/src/tools/blueprint.ts +59 -59
  86. package/src/tools/consolidated-tool-handlers.ts +129 -150
  87. package/src/tools/dynamic-handler-registry.ts +22 -140
  88. package/src/tools/editor.ts +39 -26
  89. package/src/tools/environment.ts +21 -27
  90. package/src/tools/foliage.ts +28 -25
  91. package/src/tools/handlers/actor-handlers.ts +2 -8
  92. package/src/tools/handlers/asset-handlers.ts +484 -484
  93. package/src/tools/handlers/sequence-handlers.ts +1 -1
  94. package/src/tools/landscape.ts +34 -28
  95. package/src/tools/level.ts +96 -76
  96. package/src/tools/lighting.ts +6 -1
  97. package/src/tools/materials.ts +8 -2
  98. package/src/tools/niagara.ts +44 -2
  99. package/src/tools/performance.ts +1 -2
  100. package/src/tools/physics.ts +7 -1
  101. package/src/tools/sequence.ts +41 -25
  102. package/src/tools/ui.ts +0 -2
  103. package/src/types/automation-responses.ts +119 -0
  104. package/src/types/responses.ts +355 -0
  105. package/src/types/tool-interfaces.ts +135 -135
  106. package/src/utils/command-validator.ts +3 -2
  107. package/src/utils/normalize.test.ts +162 -0
  108. package/src/utils/path-security.ts +43 -0
  109. package/src/utils/response-factory.ts +29 -24
  110. package/src/utils/safe-json.test.ts +90 -0
  111. package/src/utils/validation.test.ts +184 -0
  112. package/tests/test-animation.mjs +358 -33
  113. package/tests/test-asset-graph.mjs +311 -0
  114. package/tests/test-audio.mjs +314 -116
  115. package/tests/test-behavior-tree.mjs +327 -144
  116. package/tests/test-blueprint-graph.mjs +343 -12
  117. package/tests/test-control-editor.mjs +85 -53
  118. package/tests/test-graphql.mjs +58 -8
  119. package/tests/test-input.mjs +349 -0
  120. package/tests/test-inspect.mjs +291 -61
  121. package/tests/test-landscape.mjs +304 -48
  122. package/tests/test-lighting.mjs +428 -0
  123. package/tests/test-manage-level.mjs +70 -51
  124. package/tests/test-performance.mjs +539 -0
  125. package/tests/test-sequence.mjs +82 -46
  126. package/tests/test-system.mjs +72 -33
  127. package/tests/test-wasm.mjs +98 -8
  128. package/vitest.config.ts +35 -0
  129. package/.github/release-drafter.yml +0 -148
  130. package/dist/prompts/index.d.ts +0 -21
  131. package/dist/prompts/index.js +0 -217
  132. package/dist/tools/blueprint/helpers.d.ts +0 -29
  133. package/dist/tools/blueprint/helpers.js +0 -182
  134. package/src/prompts/index.ts +0 -249
  135. package/src/tools/blueprint/helpers.ts +0 -189
  136. package/tests/test-blueprint-events.mjs +0 -35
  137. package/tests/test-extra-tools.mjs +0 -38
  138. package/tests/test-render.mjs +0 -33
  139. package/tests/test-search-assets.mjs +0 -66
@@ -1,7 +1,16 @@
1
-
2
1
  import { BaseTool } from './base-tool.js';
3
- import { IAssetTools } from '../types/tool-interfaces.js';
2
+ import { IAssetTools, StandardActionResponse, SourceControlState } from '../types/tool-interfaces.js';
4
3
  import { wasmIntegration } from '../wasm/index.js';
4
+ import { Logger } from '../utils/logger.js';
5
+ import { AssetResponse } from '../types/automation-responses.js';
6
+ import { sanitizePath } from '../utils/path-security.js';
7
+ import {
8
+ DEFAULT_ASSET_OP_TIMEOUT_MS,
9
+ EXTENDED_ASSET_OP_TIMEOUT_MS,
10
+ LONG_RUNNING_OP_TIMEOUT_MS
11
+ } from '../constants.js';
12
+
13
+ const log = new Logger('AssetTools');
5
14
 
6
15
  export class AssetTools extends BaseTool implements IAssetTools {
7
16
  private normalizeAssetPath(path: string): string {
@@ -18,106 +27,105 @@ export class AssetTools extends BaseTool implements IAssetTools {
18
27
  }
19
28
 
20
29
  // Remove double slashes just in case
21
- return normalized.replace(/\/+/g, '/');
30
+ normalized = normalized.replace(/\/+/g, '/');
31
+
32
+ // Security check
33
+ return sanitizePath(normalized);
22
34
  }
23
35
 
24
- async importAsset(params: { sourcePath: string; destinationPath: string; overwrite?: boolean; save?: boolean }) {
25
- const res = await this.sendRequest('manage_asset', {
36
+ async importAsset(params: { sourcePath: string; destinationPath: string; overwrite?: boolean; save?: boolean }): Promise<StandardActionResponse> {
37
+ const res = await this.sendRequest<AssetResponse>('manage_asset', {
26
38
  ...params,
27
39
  subAction: 'import'
28
- }, 'manage_asset', { timeoutMs: 120000 });
40
+ }, 'manage_asset', { timeoutMs: EXTENDED_ASSET_OP_TIMEOUT_MS });
29
41
  if (res && res.success) {
30
42
  return { ...res, asset: this.normalizeAssetPath(params.destinationPath), source: params.sourcePath };
31
43
  }
32
44
  return res;
33
45
  }
34
46
 
35
- async duplicateAsset(params: { sourcePath: string; destinationPath: string; overwrite?: boolean }) {
47
+ async duplicateAsset(params: { sourcePath: string; destinationPath: string; overwrite?: boolean }): Promise<StandardActionResponse> {
36
48
  const sourcePath = this.normalizeAssetPath(params.sourcePath);
37
49
  const destinationPath = this.normalizeAssetPath(params.destinationPath);
38
50
 
39
- const res = await this.sendRequest('manage_asset', {
51
+ const res = await this.sendRequest<AssetResponse>('manage_asset', {
40
52
  sourcePath,
41
53
  destinationPath,
42
54
  overwrite: params.overwrite ?? false,
43
55
  subAction: 'duplicate'
44
- }, 'manage_asset', { timeoutMs: 60000 });
56
+ }, 'manage_asset', { timeoutMs: DEFAULT_ASSET_OP_TIMEOUT_MS });
45
57
  if (res && res.success) {
46
58
  return { ...res, asset: destinationPath, source: sourcePath };
47
59
  }
48
60
  return res;
49
61
  }
50
62
 
51
- async renameAsset(params: { sourcePath: string; destinationPath: string }) {
63
+ async renameAsset(params: { sourcePath: string; destinationPath: string }): Promise<StandardActionResponse> {
52
64
  const sourcePath = this.normalizeAssetPath(params.sourcePath);
53
65
  const destinationPath = this.normalizeAssetPath(params.destinationPath);
54
66
 
55
- const res = await this.sendRequest('manage_asset', {
67
+ const res = await this.sendRequest<AssetResponse>('manage_asset', {
56
68
  sourcePath,
57
69
  destinationPath,
58
70
  subAction: 'rename'
59
- }, 'manage_asset', { timeoutMs: 60000 });
71
+ }, 'manage_asset', { timeoutMs: DEFAULT_ASSET_OP_TIMEOUT_MS });
60
72
  if (res && res.success) {
61
73
  return { ...res, asset: destinationPath, oldName: sourcePath };
62
74
  }
63
75
  return res;
64
76
  }
65
77
 
66
- async moveAsset(params: { sourcePath: string; destinationPath: string }) {
78
+ async moveAsset(params: { sourcePath: string; destinationPath: string }): Promise<StandardActionResponse> {
67
79
  const sourcePath = this.normalizeAssetPath(params.sourcePath);
68
80
  const destinationPath = this.normalizeAssetPath(params.destinationPath);
69
81
 
70
- const res = await this.sendRequest('manage_asset', {
82
+ const res = await this.sendRequest<AssetResponse>('manage_asset', {
71
83
  sourcePath,
72
84
  destinationPath,
73
85
  subAction: 'move'
74
- }, 'manage_asset', { timeoutMs: 60000 });
86
+ }, 'manage_asset', { timeoutMs: DEFAULT_ASSET_OP_TIMEOUT_MS });
75
87
  if (res && res.success) {
76
88
  return { ...res, asset: destinationPath, from: sourcePath };
77
89
  }
78
90
  return res;
79
91
  }
80
92
 
81
- async findByTag(params: { tag: string; value?: string }) {
93
+ async findByTag(params: { tag: string; value?: string }): Promise<StandardActionResponse> {
82
94
  // tag searches don't usually involve paths, but if they did we'd normalize.
83
95
  // preserving existing logic for findByTag as it takes 'tag' and 'value'.
84
- return this.sendRequest('asset_query', {
96
+ return this.sendRequest<AssetResponse>('asset_query', {
85
97
  ...params,
86
98
  subAction: 'find_by_tag'
87
- }, 'asset_query', { timeoutMs: 60000 });
99
+ }, 'asset_query', { timeoutMs: DEFAULT_ASSET_OP_TIMEOUT_MS });
88
100
  }
89
101
 
90
- async deleteAssets(params: { paths: string[]; fixupRedirectors?: boolean; timeoutMs?: number }) {
102
+ async deleteAssets(params: { paths: string[]; fixupRedirectors?: boolean; timeoutMs?: number }): Promise<StandardActionResponse> {
91
103
  const assetPaths = (Array.isArray(params.paths) ? params.paths : [])
92
104
  .map(p => this.normalizeAssetPath(p));
93
105
 
94
- // Bulk delete maps to 'manage_asset' subAction 'bulk_delete' or 'delete'
95
- // C++ 'HandleDeleteAssets' handles single delete, 'HandleBulkDeleteAssets' handles bulk.
96
- // Let's use 'bulk_delete' if we have multiple, or 'delete' for consistency?
97
- // C++ HandleAssetAction dispatches 'bulk_delete' to HandleBulkDeleteAssets.
98
- return this.sendRequest('manage_asset', {
106
+ return this.sendRequest<AssetResponse>('manage_asset', {
99
107
  assetPaths,
100
108
  fixupRedirectors: params.fixupRedirectors,
101
109
  subAction: 'delete'
102
- }, 'manage_asset', { timeoutMs: params.timeoutMs || 120000 });
110
+ }, 'manage_asset', { timeoutMs: params.timeoutMs || EXTENDED_ASSET_OP_TIMEOUT_MS });
103
111
  }
104
112
 
105
- async searchAssets(params: { classNames?: string[]; packagePaths?: string[]; recursivePaths?: boolean; recursiveClasses?: boolean; limit?: number }) {
113
+ async searchAssets(params: { classNames?: string[]; packagePaths?: string[]; recursivePaths?: boolean; recursiveClasses?: boolean; limit?: number }): Promise<StandardActionResponse> {
106
114
  // Normalize package paths if provided
107
115
  const packagePaths = params.packagePaths
108
116
  ? params.packagePaths.map(p => this.normalizeAssetPath(p))
109
117
  : ['/Game'];
110
118
 
111
119
  // Route via asset_query action with subAction 'search_assets'
112
- const response = await this.sendRequest('asset_query', {
120
+ const response = await this.sendRequest<AssetResponse>('asset_query', {
113
121
  ...params,
114
122
  packagePaths,
115
123
  subAction: 'search_assets'
116
- }, 'asset_query', { timeoutMs: 60000 });
124
+ }, 'asset_query', { timeoutMs: DEFAULT_ASSET_OP_TIMEOUT_MS });
117
125
 
118
126
  if (!response.success) {
119
- const errorMsg = response.error || `Failed to search assets. Raw response: ${JSON.stringify(response)}`;
120
- return { success: false, error: errorMsg };
127
+ const errorMsg = typeof response.error === 'string' ? response.error : JSON.stringify(response.error);
128
+ return { success: false, error: errorMsg || 'Failed to search assets' };
121
129
  }
122
130
 
123
131
  const assetsRaw = response.assets || response.data || response.result;
@@ -131,7 +139,7 @@ export class AssetTools extends BaseTool implements IAssetTools {
131
139
  };
132
140
  }
133
141
 
134
- async saveAsset(assetPath: string) {
142
+ async saveAsset(assetPath: string): Promise<StandardActionResponse> {
135
143
  const normalizedPath = this.normalizeAssetPath(assetPath);
136
144
  try {
137
145
  // Try Automation Bridge first
@@ -149,7 +157,7 @@ export class AssetTools extends BaseTool implements IAssetTools {
149
157
  // Let's stick to the existing fallback logic but maybe fix the command if known?
150
158
  // Since 'save_asset' is not in Subsystem.cpp, it fails.
151
159
  // Let's rely on executeEditorFunction below.
152
- { timeoutMs: 60000 }
160
+ { timeoutMs: DEFAULT_ASSET_OP_TIMEOUT_MS }
153
161
  );
154
162
 
155
163
  if (response && response.success !== false) {
@@ -160,8 +168,11 @@ export class AssetTools extends BaseTool implements IAssetTools {
160
168
  ...response
161
169
  };
162
170
  }
163
- } catch (_err) {
164
- // Fall through to executeEditorFunction
171
+ } catch (primaryError) {
172
+ // Log the primary method failure before trying fallback
173
+ // This helps debugging when both methods fail
174
+ log.debug('saveAsset primary method failed, trying fallback', primaryError);
175
+ // Fall through to executeEditorFunction fallback
165
176
  }
166
177
  }
167
178
 
@@ -174,43 +185,43 @@ export class AssetTools extends BaseTool implements IAssetTools {
174
185
 
175
186
  return { success: false, error: (res as any)?.error ?? 'Failed to save asset' };
176
187
  } catch (err) {
177
- return { success: false, error: `Failed to save asset: ${err}` };
188
+ return { success: false, error: `Failed to save asset: ${err} ` };
178
189
  }
179
190
  }
180
191
 
181
- async createFolder(folderPath: string) {
192
+ async createFolder(folderPath: string): Promise<StandardActionResponse> {
182
193
  // Folders are paths too
183
194
  const path = this.normalizeAssetPath(folderPath);
184
- return this.sendRequest('manage_asset', {
195
+ return this.sendRequest<AssetResponse>('manage_asset', {
185
196
  path,
186
197
  subAction: 'create_folder'
187
- }, 'manage_asset', { timeoutMs: 60000 });
198
+ }, 'manage_asset', { timeoutMs: DEFAULT_ASSET_OP_TIMEOUT_MS });
188
199
  }
189
200
 
190
- async getDependencies(params: { assetPath: string; recursive?: boolean }) {
201
+ async getDependencies(params: { assetPath: string; recursive?: boolean }): Promise<StandardActionResponse> {
191
202
  // get_dependencies is typically an asset query or managed asset action?
192
203
  // HandleAssetAction has 'get_dependencies' dispatch.
193
- return this.sendRequest('manage_asset', {
204
+ return this.sendRequest<AssetResponse>('manage_asset', {
194
205
  ...params,
195
206
  assetPath: this.normalizeAssetPath(params.assetPath),
196
207
  subAction: 'get_dependencies'
197
208
  }, 'manage_asset');
198
209
  }
199
210
 
200
- async getSourceControlState(params: { assetPath: string }) {
211
+ async getSourceControlState(params: { assetPath: string }): Promise<SourceControlState | StandardActionResponse> {
201
212
  // Source control state usually via 'asset_query' or 'manage_asset'?
202
213
  // It's not in HandleAssetAction explicitly, maybe 'asset_query' subAction?
203
214
  // Let's check AssetQueryHandlers.cpp or AssetWorkflowHandlers.cpp dispatch.
204
215
  // Assuming 'asset_query' supports it (original code used asset_query).
205
- return this.sendRequest('asset_query', {
216
+ return this.sendRequest<AssetResponse>('asset_query', {
206
217
  ...params,
207
218
  assetPath: this.normalizeAssetPath(params.assetPath),
208
219
  subAction: 'get_source_control_state'
209
220
  }, 'asset_query');
210
221
  }
211
222
 
212
- async getMetadata(params: { assetPath: string }) {
213
- const response = await this.sendRequest('manage_asset', {
223
+ async getMetadata(params: { assetPath: string }): Promise<StandardActionResponse> {
224
+ const response = await this.sendRequest<AssetResponse>('manage_asset', {
214
225
  ...params,
215
226
  assetPath: this.normalizeAssetPath(params.assetPath),
216
227
  subAction: 'get_metadata'
@@ -227,17 +238,17 @@ export class AssetTools extends BaseTool implements IAssetTools {
227
238
  };
228
239
  }
229
240
 
230
- async analyzeGraph(params: { assetPath: string; maxDepth?: number }) {
241
+ async analyzeGraph(params: { assetPath: string; maxDepth?: number }): Promise<StandardActionResponse> {
231
242
  const maxDepth = params.maxDepth ?? 3;
232
243
  const assetPath = this.normalizeAssetPath(params.assetPath);
233
244
 
234
245
  try {
235
246
  // Offload the heavy graph traversal to C++
236
- const response: any = await this.sendRequest('manage_asset', {
247
+ const response: any = await this.sendRequest<AssetResponse>('manage_asset', {
237
248
  assetPath,
238
249
  maxDepth,
239
250
  subAction: 'get_asset_graph'
240
- }, 'manage_asset', { timeoutMs: 60000 });
251
+ }, 'manage_asset', { timeoutMs: DEFAULT_ASSET_OP_TIMEOUT_MS });
241
252
 
242
253
  if (!response.success || !response.graph) {
243
254
  return { success: false, error: response.error || 'Failed to retrieve asset graph from engine' };
@@ -300,43 +311,43 @@ export class AssetTools extends BaseTool implements IAssetTools {
300
311
  analysis
301
312
  };
302
313
  } catch (e: any) {
303
- return { success: false, error: `Analysis failed: ${e.message}` };
314
+ return { success: false, error: `Analysis failed: ${e.message} ` };
304
315
  }
305
316
  }
306
317
 
307
- async createThumbnail(params: { assetPath: string; width?: number; height?: number }) {
308
- return this.sendRequest('manage_asset', {
318
+ async createThumbnail(params: { assetPath: string; width?: number; height?: number }): Promise<StandardActionResponse> {
319
+ return this.sendRequest<AssetResponse>('manage_asset', {
309
320
  ...params,
310
321
  assetPath: this.normalizeAssetPath(params.assetPath),
311
322
  subAction: 'generate_thumbnail'
312
- }, 'manage_asset', { timeoutMs: 60000 });
323
+ }, 'manage_asset', { timeoutMs: DEFAULT_ASSET_OP_TIMEOUT_MS });
313
324
  }
314
325
 
315
- async setTags(params: { assetPath: string; tags: string[] }) {
316
- return this.sendRequest('manage_asset', {
326
+ async setTags(params: { assetPath: string; tags: string[] }): Promise<StandardActionResponse> {
327
+ return this.sendRequest<AssetResponse>('manage_asset', {
317
328
  ...params,
318
329
  assetPath: this.normalizeAssetPath(params.assetPath),
319
330
  subAction: 'set_tags'
320
- }, 'manage_asset', { timeoutMs: 60000 });
331
+ }, 'manage_asset', { timeoutMs: DEFAULT_ASSET_OP_TIMEOUT_MS });
321
332
  }
322
333
 
323
- async generateReport(params: { directory: string; reportType?: string; outputPath?: string }) {
324
- return this.sendRequest('manage_asset', {
334
+ async generateReport(params: { directory: string; reportType?: string; outputPath?: string }): Promise<StandardActionResponse> {
335
+ return this.sendRequest<AssetResponse>('manage_asset', {
325
336
  ...params,
326
337
  directory: this.normalizeAssetPath(params.directory),
327
338
  subAction: 'generate_report'
328
- }, 'manage_asset', { timeoutMs: 300000 });
339
+ }, 'manage_asset', { timeoutMs: LONG_RUNNING_OP_TIMEOUT_MS });
329
340
  }
330
341
 
331
- async validate(params: { assetPath: string }) {
332
- return this.sendRequest('manage_asset', {
342
+ async validate(params: { assetPath: string }): Promise<StandardActionResponse> {
343
+ return this.sendRequest<AssetResponse>('manage_asset', {
333
344
  ...params,
334
345
  assetPath: this.normalizeAssetPath(params.assetPath),
335
346
  subAction: 'validate'
336
- }, 'manage_asset', { timeoutMs: 300000 });
347
+ }, 'manage_asset', { timeoutMs: LONG_RUNNING_OP_TIMEOUT_MS });
337
348
  }
338
349
 
339
- async generateLODs(params: { assetPath: string; lodCount: number }) {
350
+ async generateLODs(params: { assetPath: string; lodCount: number }): Promise<StandardActionResponse> {
340
351
  const assetPath = this.normalizeAssetPath(String(params.assetPath ?? '').trim());
341
352
  const lodCountRaw = Number(params.lodCount);
342
353
 
@@ -354,7 +365,7 @@ export class AssetTools extends BaseTool implements IAssetTools {
354
365
  assetPaths: [assetPath],
355
366
  numLODs: lodCount,
356
367
  subAction: 'generate_lods'
357
- }, { timeoutMs: 120000 });
368
+ }, { timeoutMs: EXTENDED_ASSET_OP_TIMEOUT_MS });
358
369
 
359
370
  if (!response || response.success === false) {
360
371
  return {
@@ -376,7 +387,7 @@ export class AssetTools extends BaseTool implements IAssetTools {
376
387
  } catch (error) {
377
388
  return {
378
389
  success: false,
379
- error: `Failed to generate LODs: ${error instanceof Error ? error.message : String(error)}`
390
+ error: `Failed to generate LODs: ${error instanceof Error ? error.message : String(error)} `
380
391
  };
381
392
  }
382
393
  }
@@ -9,7 +9,7 @@ export abstract class BaseTool implements IBaseTool {
9
9
  return this.bridge.getAutomationBridge();
10
10
  }
11
11
 
12
- protected async sendRequest(action: string, params: Record<string, unknown>, toolName: string = 'unknown_tool', options?: { timeoutMs?: number }): Promise<any> {
12
+ protected async sendRequest<T = unknown>(action: string, params: Record<string, unknown>, toolName: string = 'unknown_tool', options?: { timeoutMs?: number }): Promise<T> {
13
13
  const automation = this.getAutomationBridge();
14
14
 
15
15
  // Basic validation
@@ -39,10 +39,10 @@ export abstract class BaseTool implements IBaseTool {
39
39
  throw new Error(errorMessage);
40
40
  }
41
41
 
42
- return response.result ?? response;
42
+ return (response.result ?? response) as T;
43
43
  }
44
44
 
45
- protected async sendAutomationRequest(action: string, params: Record<string, unknown> = {}, options?: { timeoutMs?: number; waitForEvent?: boolean; waitForEventTimeoutMs?: number }): Promise<any> {
45
+ protected async sendAutomationRequest<T = unknown>(action: string, params: Record<string, unknown> = {}, options?: { timeoutMs?: number; waitForEvent?: boolean; waitForEventTimeoutMs?: number }): Promise<T> {
46
46
  const automation = this.getAutomationBridge();
47
47
  if (!automation.isConnected()) {
48
48
  throw new Error('Automation bridge not connected');