unreal-engine-mcp-server 0.5.1 → 0.5.2

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 (75) hide show
  1. package/.github/workflows/publish-mcp.yml +1 -4
  2. package/.github/workflows/release-drafter.yml +2 -1
  3. package/CHANGELOG.md +38 -0
  4. package/dist/automation/bridge.d.ts +1 -2
  5. package/dist/automation/bridge.js +24 -23
  6. package/dist/automation/connection-manager.d.ts +1 -0
  7. package/dist/automation/connection-manager.js +10 -0
  8. package/dist/automation/message-handler.js +5 -4
  9. package/dist/automation/request-tracker.d.ts +4 -0
  10. package/dist/automation/request-tracker.js +11 -3
  11. package/dist/tools/actors.d.ts +19 -1
  12. package/dist/tools/actors.js +15 -5
  13. package/dist/tools/assets.js +1 -1
  14. package/dist/tools/blueprint.d.ts +12 -0
  15. package/dist/tools/blueprint.js +43 -14
  16. package/dist/tools/consolidated-tool-definitions.js +2 -1
  17. package/dist/tools/editor.js +3 -2
  18. package/dist/tools/handlers/actor-handlers.d.ts +1 -1
  19. package/dist/tools/handlers/actor-handlers.js +14 -8
  20. package/dist/tools/handlers/sequence-handlers.d.ts +1 -1
  21. package/dist/tools/handlers/sequence-handlers.js +24 -13
  22. package/dist/tools/introspection.d.ts +1 -1
  23. package/dist/tools/introspection.js +1 -1
  24. package/dist/tools/level.js +3 -3
  25. package/dist/tools/lighting.d.ts +54 -7
  26. package/dist/tools/lighting.js +4 -4
  27. package/dist/tools/materials.d.ts +1 -1
  28. package/dist/types/tool-types.d.ts +2 -0
  29. package/dist/unreal-bridge.js +4 -4
  30. package/dist/utils/command-validator.js +6 -5
  31. package/dist/utils/error-handler.d.ts +24 -2
  32. package/dist/utils/error-handler.js +58 -23
  33. package/dist/utils/normalize.d.ts +7 -4
  34. package/dist/utils/normalize.js +12 -10
  35. package/dist/utils/response-validator.js +88 -73
  36. package/dist/utils/unreal-command-queue.d.ts +2 -0
  37. package/dist/utils/unreal-command-queue.js +8 -1
  38. package/docs/handler-mapping.md +4 -2
  39. package/package.json +1 -1
  40. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSubsystem.cpp +298 -33
  41. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AnimationHandlers.cpp +7 -8
  42. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintGraphHandlers.cpp +229 -319
  43. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers.cpp +98 -0
  44. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EffectHandlers.cpp +24 -0
  45. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EnvironmentHandlers.cpp +96 -0
  46. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LightingHandlers.cpp +52 -5
  47. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ProcessRequest.cpp +5 -268
  48. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequenceHandlers.cpp +57 -2
  49. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpConnectionManager.cpp +0 -1
  50. package/server.json +3 -3
  51. package/src/automation/bridge.ts +27 -25
  52. package/src/automation/connection-manager.ts +18 -0
  53. package/src/automation/message-handler.ts +33 -8
  54. package/src/automation/request-tracker.ts +39 -7
  55. package/src/server/tool-registry.ts +3 -3
  56. package/src/tools/actors.ts +44 -19
  57. package/src/tools/assets.ts +3 -3
  58. package/src/tools/blueprint.ts +115 -49
  59. package/src/tools/consolidated-tool-definitions.ts +2 -1
  60. package/src/tools/editor.ts +4 -3
  61. package/src/tools/handlers/actor-handlers.ts +14 -9
  62. package/src/tools/handlers/sequence-handlers.ts +86 -63
  63. package/src/tools/introspection.ts +7 -7
  64. package/src/tools/level.ts +6 -6
  65. package/src/tools/lighting.ts +19 -19
  66. package/src/tools/materials.ts +1 -1
  67. package/src/tools/sequence.ts +1 -1
  68. package/src/tools/ui.ts +1 -1
  69. package/src/types/tool-types.ts +4 -0
  70. package/src/unreal-bridge.ts +71 -26
  71. package/src/utils/command-validator.ts +46 -5
  72. package/src/utils/error-handler.ts +128 -45
  73. package/src/utils/normalize.ts +38 -16
  74. package/src/utils/response-validator.ts +103 -87
  75. package/src/utils/unreal-command-queue.ts +13 -1
@@ -4,27 +4,57 @@ import { Logger } from '../utils/logger.js';
4
4
  import { validateAssetParams, concurrencyDelay } from '../utils/validation.js';
5
5
  import { coerceString } from '../utils/result-helpers.js';
6
6
 
7
+ /** Response from automation actions */
8
+ interface ActionResponse extends StandardActionResponse {
9
+ result?: Record<string, unknown>;
10
+ requestId?: string;
11
+ blueprint?: string;
12
+ blueprintPath?: string;
13
+ component?: string;
14
+ componentName?: string;
15
+ componentType?: string;
16
+ componentClass?: string;
17
+ found?: string;
18
+ checked?: string[];
19
+ path?: string;
20
+ nodes?: unknown[];
21
+ graphName?: string;
22
+ }
23
+
24
+ /** Response type guard */
25
+ interface ActionResponseInput {
26
+ success?: boolean;
27
+ message?: string;
28
+ error?: string;
29
+ result?: unknown;
30
+ requestId?: string;
31
+ [key: string]: unknown;
32
+ }
33
+
7
34
  export class BlueprintTools extends BaseTool implements IBlueprintTools {
8
35
  private log = new Logger('BlueprintTools');
9
36
  private pluginBlueprintActionsAvailable: boolean | null = null;
10
37
 
11
- private async sendAction(action: string, payload: Record<string, unknown> = {}, options?: { timeoutMs?: number; waitForEvent?: boolean; waitForEventTimeoutMs?: number }) {
38
+ private async sendAction(action: string, payload: Record<string, unknown> = {}, options?: { timeoutMs?: number; waitForEvent?: boolean; waitForEventTimeoutMs?: number }): Promise<ActionResponse> {
12
39
  const envDefault = Number(process.env.MCP_AUTOMATION_REQUEST_TIMEOUT_MS ?? '120000');
13
40
  const defaultTimeout = Number.isFinite(envDefault) && envDefault > 0 ? envDefault : 120000;
14
41
  const finalTimeout = typeof options?.timeoutMs === 'number' && options?.timeoutMs > 0 ? options.timeoutMs : defaultTimeout;
15
42
  try {
16
- const response: any = await this.sendAutomationRequest(action, payload, { timeoutMs: finalTimeout, waitForEvent: !!options?.waitForEvent, waitForEventTimeoutMs: options?.waitForEventTimeoutMs });
43
+ const response = await this.sendAutomationRequest(action, payload, { timeoutMs: finalTimeout, waitForEvent: !!options?.waitForEvent, waitForEventTimeoutMs: options?.waitForEventTimeoutMs }) as ActionResponseInput;
17
44
  const success = response && response.success !== false;
18
- const result = response.result ?? response;
19
- return { success, message: response.message ?? undefined, error: response.success === false ? (response.error ?? response.message) : undefined, result, requestId: response.requestId } as any;
20
- } catch (err: any) {
21
- return { success: false, error: String(err), message: String(err) } as const;
45
+ const result = (response.result ?? response) as Record<string, unknown>;
46
+ return { success, message: response.message ?? undefined, error: response.success === false ? (response.error ?? response.message) : undefined, result, requestId: response.requestId };
47
+ } catch (err: unknown) {
48
+ const errMsg = err instanceof Error ? err.message : String(err);
49
+ return { success: false, error: errMsg, message: errMsg };
22
50
  }
23
51
  }
24
52
 
25
- private isUnknownActionResponse(res: any): boolean {
53
+ private isUnknownActionResponse(res: ActionResponse | StandardActionResponse | null | undefined): boolean {
26
54
  if (!res) return false;
27
- const txt = String((res.error ?? res.message ?? '')).toLowerCase();
55
+ const errStr = typeof res.error === 'string' ? res.error : '';
56
+ const msgStr = typeof res.message === 'string' ? res.message : '';
57
+ const txt = (errStr || msgStr).toLowerCase();
28
58
  // Only treat specific error codes as "not implemented"
29
59
  return txt.includes('unknown_action') || txt.includes('unknown automation action') || txt.includes('not_implemented') || txt === 'unknown_plugin_action';
30
60
  }
@@ -84,8 +114,8 @@ export class BlueprintTools extends BaseTool implements IBlueprintTools {
84
114
  this.pluginBlueprintActionsAvailable = false;
85
115
  return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement blueprint_create' } as const;
86
116
  }
87
- return res as any;
88
- } catch (err: any) {
117
+ return res;
118
+ } catch (err: unknown) {
89
119
  // ... (unchanged catch block)
90
120
  const errTxt = String(err ?? '');
91
121
  const isTimeout = errTxt.includes('Request timed out') || errTxt.includes('-32001') || errTxt.toLowerCase().includes('timeout');
@@ -94,7 +124,7 @@ export class BlueprintTools extends BaseTool implements IBlueprintTools {
94
124
  }
95
125
  return { success: false, error: String(err), message: String(err) } as const;
96
126
  }
97
- } catch (err: any) {
127
+ } catch (err: unknown) {
98
128
  return { success: false, error: String(err), message: String(err) };
99
129
  }
100
130
  }
@@ -112,12 +142,12 @@ export class BlueprintTools extends BaseTool implements IBlueprintTools {
112
142
  return op;
113
143
  });
114
144
 
115
- const payload: any = { blueprintPath, operations };
145
+ const payload: Record<string, unknown> = { blueprintPath, operations };
116
146
  if (typeof params.compile === 'boolean') payload.compile = params.compile;
117
147
  if (typeof params.save === 'boolean') payload.save = params.save;
118
148
  const res = await this.sendAction('blueprint_modify_scs', payload, { timeoutMs: params.timeoutMs, waitForEvent: !!params.waitForCompletion, waitForEventTimeoutMs: params.waitForCompletionTimeoutMs });
119
149
 
120
- if (res && res.result && typeof res.result === 'object' && (res.result as any).error === 'SCS_UNAVAILABLE') {
150
+ if (res && res.result && typeof res.result === 'object' && res.result?.error === 'SCS_UNAVAILABLE') {
121
151
  this.pluginBlueprintActionsAvailable = false;
122
152
  return { success: false, error: 'SCS_UNAVAILABLE', message: 'Plugin does not support construction script modification (blueprint_modify_scs)' } as const;
123
153
  }
@@ -144,23 +174,23 @@ export class BlueprintTools extends BaseTool implements IBlueprintTools {
144
174
  const svcResult = await this.modifyConstructionScript({ blueprintPath: primary, operations: [op], compile: params.compile, save: params.save, timeoutMs: params.timeoutMs, waitForCompletion: params.waitForCompletion, waitForCompletionTimeoutMs: params.waitForCompletionTimeoutMs });
145
175
  if (svcResult && svcResult.success) {
146
176
  this.pluginBlueprintActionsAvailable = true;
147
- return { ...(svcResult as any), component: sanitizedComponentName, componentName: sanitizedComponentName, componentType: componentClass, componentClass, blueprintPath: svcResult.blueprintPath ?? primary } as const;
177
+ return { ...svcResult, component: sanitizedComponentName, componentName: sanitizedComponentName, componentType: componentClass, componentClass, blueprintPath: svcResult.blueprintPath ?? primary } as const;
148
178
  }
149
179
  if (svcResult && (this.isUnknownActionResponse(svcResult) || (svcResult.error && svcResult.error === 'SCS_UNAVAILABLE'))) {
150
180
  this.pluginBlueprintActionsAvailable = false;
151
181
  return { success: false, error: 'SCS_UNAVAILABLE', message: 'Plugin does not support construction script modification (blueprint_modify_scs)' } as const;
152
182
  }
153
- return svcResult as any;
154
- } catch (err: any) {
183
+ return svcResult;
184
+ } catch (err: unknown) {
155
185
  return { success: false, error: String(err) };
156
186
  }
157
187
  }
158
188
 
159
189
  async waitForBlueprint(blueprintRef: string | string[], timeoutMs?: number): Promise<StandardActionResponse> {
160
190
  const candidates = Array.isArray(blueprintRef) ? blueprintRef : this.buildCandidates(blueprintRef as string | undefined);
161
- if (!candidates || candidates.length === 0) return { success: false, error: 'Invalid blueprint reference', checked: [] } as any;
191
+ if (!candidates || candidates.length === 0) return { success: false, error: 'Invalid blueprint reference', checked: [] };
162
192
  if (this.pluginBlueprintActionsAvailable === false) {
163
- return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement blueprint_exists' } as any;
193
+ return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement blueprint_exists' };
164
194
  }
165
195
 
166
196
  const start = Date.now();
@@ -175,11 +205,11 @@ export class BlueprintTools extends BaseTool implements IBlueprintTools {
175
205
  const r = await this.sendAction('blueprint_exists', { blueprintCandidates: [candidate], requestedPath: candidate }, { timeoutMs: Math.min(perCheck, tot) });
176
206
  if (r && r.success && r.result && (r.result.exists === true || r.result.found)) {
177
207
  this.pluginBlueprintActionsAvailable = true;
178
- return { success: true, found: r.result.found ?? candidate } as any;
208
+ return { success: true, found: r.result.found ?? candidate };
179
209
  }
180
210
  if (r && r.success === false && this.isUnknownActionResponse(r)) {
181
211
  this.pluginBlueprintActionsAvailable = false;
182
- return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement blueprint_exists' } as any;
212
+ return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement blueprint_exists' };
183
213
  }
184
214
  } catch (_e) {
185
215
  // ignore and try next candidate
@@ -188,9 +218,9 @@ export class BlueprintTools extends BaseTool implements IBlueprintTools {
188
218
  await new Promise((r) => setTimeout(r, 1000));
189
219
  }
190
220
  if (this.pluginBlueprintActionsAvailable === null) {
191
- return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin availability unknown; blueprint_exists not implemented by plugin' } as any;
221
+ return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin availability unknown; blueprint_exists not implemented by plugin' };
192
222
  }
193
- return { success: false, error: `Timeout waiting for blueprint after ${tot}ms`, checked: candidates } as any;
223
+ return { success: false, error: `Timeout waiting for blueprint after ${tot}ms`, checked: candidates };
194
224
  }
195
225
 
196
226
  async getBlueprint(params: { blueprintName: string; timeoutMs?: number; }): Promise<StandardActionResponse> {
@@ -201,7 +231,7 @@ export class BlueprintTools extends BaseTool implements IBlueprintTools {
201
231
  const pluginResp = await this.sendAction('blueprint_get', { blueprintCandidates: candidates, requestedPath: primary }, { timeoutMs: params.timeoutMs });
202
232
  if (pluginResp && pluginResp.success) {
203
233
  if (pluginResp && typeof pluginResp === 'object') {
204
- return { ...pluginResp, blueprint: pluginResp.result, blueprintPath: primary } as any;
234
+ return { ...pluginResp, blueprint: pluginResp.result, blueprintPath: primary };
205
235
  }
206
236
  return pluginResp;
207
237
  }
@@ -209,7 +239,7 @@ export class BlueprintTools extends BaseTool implements IBlueprintTools {
209
239
  return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement blueprint_get' } as const;
210
240
  }
211
241
  return { success: false, error: pluginResp?.error ?? 'BLUEPRINT_GET_FAILED', message: pluginResp?.message ?? 'Failed to get blueprint via automation bridge' } as const;
212
- } catch (err: any) {
242
+ } catch (err: unknown) {
213
243
  return { success: false, error: String(err), message: String(err) } as const;
214
244
  }
215
245
  }
@@ -243,13 +273,13 @@ export class BlueprintTools extends BaseTool implements IBlueprintTools {
243
273
  }
244
274
 
245
275
  return { success: false, error: resp?.error ?? 'BLUEPRINT_GET_FAILED', message: resp?.message ?? 'Failed to get blueprint via automation bridge' } as const;
246
- } catch (err: any) {
276
+ } catch (err: unknown) {
247
277
  return { success: false, error: String(err), message: String(err) } as const;
248
278
  }
249
279
  }
250
280
 
251
281
  async probeSubobjectDataHandle(opts: { componentClass?: string } = {}): Promise<StandardActionResponse> {
252
- return await this.sendAction('blueprint_probe_subobject_handle', { componentClass: opts.componentClass }) as any;
282
+ return await this.sendAction('blueprint_probe_subobject_handle', { componentClass: opts.componentClass });
253
283
  }
254
284
 
255
285
  async setBlueprintDefault(params: { blueprintName: string; propertyName: string; value: unknown }): Promise<StandardActionResponse> {
@@ -331,7 +361,7 @@ export class BlueprintTools extends BaseTool implements IBlueprintTools {
331
361
  return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement blueprint_remove_event' } as const;
332
362
  }
333
363
  return { success: false, error: pluginResp?.error ?? 'BLUEPRINT_REMOVE_EVENT_FAILED', message: pluginResp?.message ?? 'Failed to remove event via automation bridge' } as const;
334
- } catch (err: any) {
364
+ } catch (err: unknown) {
335
365
  return { success: false, error: String(err), message: String(err) } as const;
336
366
  }
337
367
  }
@@ -393,7 +423,7 @@ export class BlueprintTools extends BaseTool implements IBlueprintTools {
393
423
  return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement blueprint_compile' } as const;
394
424
  }
395
425
  return { success: false, error: pluginResp?.error ?? 'BLUEPRINT_COMPILE_FAILED', message: pluginResp?.message ?? 'Failed to compile blueprint via automation bridge' } as const;
396
- } catch (err: any) {
426
+ } catch (err: unknown) {
397
427
  return { success: false, error: String(err) };
398
428
  }
399
429
  }
@@ -414,7 +444,7 @@ export class BlueprintTools extends BaseTool implements IBlueprintTools {
414
444
  return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement get_blueprint_scs' } as const;
415
445
  }
416
446
  return { success: false, error: pluginResp?.error ?? 'GET_SCS_FAILED', message: pluginResp?.message ?? 'Failed to get SCS via automation bridge' } as const;
417
- } catch (err: any) {
447
+ } catch (err: unknown) {
418
448
  return { success: false, error: String(err), message: String(err) } as const;
419
449
  }
420
450
  }
@@ -463,8 +493,8 @@ export class BlueprintTools extends BaseTool implements IBlueprintTools {
463
493
  const pluginResp = await this.sendAction('add_scs_component', payload, { timeoutMs: params.timeoutMs });
464
494
 
465
495
  if (pluginResp && pluginResp.success === false) {
466
- if ((pluginResp as any).message) {
467
- this.log.warn?.(`addSCSComponent reported warning: ${(pluginResp as any).message}`);
496
+ if (pluginResp?.message) {
497
+ this.log.warn?.(`addSCSComponent reported warning: ${pluginResp?.message}`);
468
498
  }
469
499
  }
470
500
  if (pluginResp && pluginResp.success) return pluginResp as any;
@@ -472,7 +502,7 @@ export class BlueprintTools extends BaseTool implements IBlueprintTools {
472
502
  return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement add_scs_component' } as const;
473
503
  }
474
504
  return { success: false, error: pluginResp?.error ?? 'ADD_SCS_COMPONENT_FAILED', message: pluginResp?.message ?? 'Failed to add SCS component via automation bridge' } as const;
475
- } catch (err: any) {
505
+ } catch (err: unknown) {
476
506
  return { success: false, error: String(err), message: String(err) } as const;
477
507
  }
478
508
  }
@@ -494,8 +524,8 @@ export class BlueprintTools extends BaseTool implements IBlueprintTools {
494
524
  { timeoutMs: params.timeoutMs });
495
525
 
496
526
  if (pluginResp && pluginResp.success === false) {
497
- if ((pluginResp as any).message) {
498
- this.log.warn?.(`removeSCSComponent reported warning: ${(pluginResp as any).message}`);
527
+ if (pluginResp?.message) {
528
+ this.log.warn?.(`removeSCSComponent reported warning: ${pluginResp?.message}`);
499
529
  }
500
530
  }
501
531
  if (pluginResp && pluginResp.success) return pluginResp as any;
@@ -503,7 +533,7 @@ export class BlueprintTools extends BaseTool implements IBlueprintTools {
503
533
  return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement remove_scs_component' } as const;
504
534
  }
505
535
  return { success: false, error: pluginResp?.error ?? 'REMOVE_SCS_COMPONENT_FAILED', message: pluginResp?.message ?? 'Failed to remove SCS component via automation bridge' } as const;
506
- } catch (err: any) {
536
+ } catch (err: unknown) {
507
537
  return { success: false, error: String(err), message: String(err) } as const;
508
538
  }
509
539
  }
@@ -534,8 +564,8 @@ export class BlueprintTools extends BaseTool implements IBlueprintTools {
534
564
  { timeoutMs: params.timeoutMs });
535
565
 
536
566
  if (pluginResp && pluginResp.success === false) {
537
- if ((pluginResp as any).message) {
538
- this.log.warn?.(`reparentSCSComponent reported warning: ${(pluginResp as any).message}`);
567
+ if (pluginResp?.message) {
568
+ this.log.warn?.(`reparentSCSComponent reported warning: ${pluginResp?.message}`);
539
569
  }
540
570
  }
541
571
  if (pluginResp && pluginResp.success) return pluginResp as any;
@@ -543,7 +573,7 @@ export class BlueprintTools extends BaseTool implements IBlueprintTools {
543
573
  return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement reparent_scs_component' } as const;
544
574
  }
545
575
  return { success: false, error: pluginResp?.error ?? 'REPARENT_SCS_COMPONENT_FAILED', message: pluginResp?.message ?? 'Failed to reparent SCS component via automation bridge' } as const;
546
- } catch (err: any) {
576
+ } catch (err: unknown) {
547
577
  return { success: false, error: String(err), message: String(err) } as const;
548
578
  }
549
579
  }
@@ -579,8 +609,8 @@ export class BlueprintTools extends BaseTool implements IBlueprintTools {
579
609
  const pluginResp = await this.sendAction('set_scs_component_transform', payload, { timeoutMs: params.timeoutMs });
580
610
 
581
611
  if (pluginResp && pluginResp.success === false) {
582
- if ((pluginResp as any).message) {
583
- this.log.warn?.(`setSCSComponentTransform reported warning: ${(pluginResp as any).message}`);
612
+ if (pluginResp?.message) {
613
+ this.log.warn?.(`setSCSComponentTransform reported warning: ${pluginResp?.message}`);
584
614
  }
585
615
  }
586
616
  if (pluginResp && pluginResp.success) return pluginResp as any;
@@ -588,7 +618,7 @@ export class BlueprintTools extends BaseTool implements IBlueprintTools {
588
618
  return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement set_scs_component_transform' } as const;
589
619
  }
590
620
  return { success: false, error: pluginResp?.error ?? 'SET_SCS_TRANSFORM_FAILED', message: pluginResp?.message ?? 'Failed to set SCS component transform via automation bridge' } as const;
591
- } catch (err: any) {
621
+ } catch (err: unknown) {
592
622
  return { success: false, error: String(err), message: String(err) } as const;
593
623
  }
594
624
  }
@@ -628,8 +658,8 @@ export class BlueprintTools extends BaseTool implements IBlueprintTools {
628
658
  { timeoutMs: params.timeoutMs });
629
659
 
630
660
  if (pluginResp && pluginResp.success === false) {
631
- if ((pluginResp as any).message) {
632
- this.log.warn?.(`setSCSComponentProperty reported warning: ${(pluginResp as any).message}`);
661
+ if (pluginResp?.message) {
662
+ this.log.warn?.(`setSCSComponentProperty reported warning: ${pluginResp?.message}`);
633
663
  }
634
664
  }
635
665
  if (pluginResp && pluginResp.success) return pluginResp as any;
@@ -637,7 +667,7 @@ export class BlueprintTools extends BaseTool implements IBlueprintTools {
637
667
  return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement set_scs_component_property' } as const;
638
668
  }
639
669
  return { success: false, error: pluginResp?.error ?? 'SET_SCS_PROPERTY_FAILED', message: pluginResp?.message ?? 'Failed to set SCS component property via automation bridge' } as const;
640
- } catch (err: any) {
670
+ } catch (err: unknown) {
641
671
  return { success: false, error: String(err), message: String(err) } as const;
642
672
  }
643
673
  }
@@ -662,15 +692,15 @@ export class BlueprintTools extends BaseTool implements IBlueprintTools {
662
692
  if (pluginResp && pluginResp.success) {
663
693
  return {
664
694
  success: true,
665
- nodes: (pluginResp.result as any).nodes,
666
- graphName: (pluginResp.result as any).graphName
667
- } as any;
695
+ nodes: (pluginResp.result as Record<string, unknown>).nodes,
696
+ graphName: (pluginResp.result as Record<string, unknown>).graphName
697
+ };
668
698
  }
669
699
  if (pluginResp && this.isUnknownActionResponse(pluginResp)) {
670
700
  return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement get_nodes' } as const;
671
701
  }
672
702
  return { success: false, error: pluginResp?.error ?? 'GET_NODES_FAILED', message: pluginResp?.message ?? 'Failed to get blueprint nodes' } as const;
673
- } catch (err: any) {
703
+ } catch (err: unknown) {
674
704
  return { success: false, error: String(err), message: String(err) } as const;
675
705
  }
676
706
  }
@@ -694,7 +724,7 @@ export class BlueprintTools extends BaseTool implements IBlueprintTools {
694
724
  if (!primary) return { success: false as const, error: 'Invalid blueprint name' };
695
725
 
696
726
  // Fix: C++ expects 'manage_blueprint_graph' with 'subAction' = 'create_node'
697
- const payload: any = {
727
+ const payload: Record<string, unknown> = {
698
728
  subAction: 'create_node',
699
729
  assetPath: primary, // C++ expects 'assetPath' or 'blueprintPath'
700
730
  nodeType: params.nodeType,
@@ -830,6 +860,42 @@ export class BlueprintTools extends BaseTool implements IBlueprintTools {
830
860
  return res as any;
831
861
  }
832
862
 
863
+ async listNodeTypes(params: {
864
+ blueprintPath?: string;
865
+ timeoutMs?: number;
866
+ } = {}): Promise<StandardActionResponse> {
867
+ const res = await this.sendAction('manage_blueprint_graph', {
868
+ subAction: 'list_node_types',
869
+ blueprintPath: params.blueprintPath || '/Game/Temp',
870
+ graphName: 'EventGraph'
871
+ }, { timeoutMs: params.timeoutMs });
872
+ return res as any;
873
+ }
874
+
875
+ async setPinDefaultValue(params: {
876
+ blueprintPath: string;
877
+ nodeId: string;
878
+ pinName: string;
879
+ value: string;
880
+ graphName?: string;
881
+ timeoutMs?: number;
882
+ }): Promise<StandardActionResponse> {
883
+ const blueprintPath = coerceString(params.blueprintPath);
884
+ if (!blueprintPath) return { success: false, error: 'INVALID_BLUEPRINT_PATH', message: 'Blueprint path is required' } as const;
885
+ if (!params.nodeId) return { success: false, error: 'INVALID_NODE_ID', message: 'Node ID is required' } as const;
886
+ if (!params.pinName) return { success: false, error: 'INVALID_PIN_NAME', message: 'Pin name is required' } as const;
887
+
888
+ const res = await this.sendAction('manage_blueprint_graph', {
889
+ subAction: 'set_pin_default_value',
890
+ blueprintPath: blueprintPath,
891
+ graphName: params.graphName || 'EventGraph',
892
+ nodeId: params.nodeId,
893
+ pinName: params.pinName,
894
+ value: params.value
895
+ }, { timeoutMs: params.timeoutMs });
896
+ return res as any;
897
+ }
898
+
833
899
 
834
900
  async connectPins(params: {
835
901
  blueprintName: string;
@@ -1046,7 +1046,8 @@ Supported actions: create_node, delete_node, connect_pins, break_pin_links, set_
1046
1046
  type: 'string',
1047
1047
  enum: [
1048
1048
  'create_node', 'delete_node', 'connect_pins', 'break_pin_links', 'set_node_property',
1049
- 'create_reroute_node', 'get_node_details', 'get_graph_details', 'get_pin_details'
1049
+ 'create_reroute_node', 'get_node_details', 'get_graph_details', 'get_pin_details',
1050
+ 'list_node_types', 'set_pin_default_value'
1050
1051
  ],
1051
1052
  description: 'Action'
1052
1053
  },
@@ -48,10 +48,11 @@ export class EditorTools extends BaseTool implements IEditorTools {
48
48
  return { success: true, message: response.message || 'PIE started' };
49
49
  }
50
50
  return { success: false, error: response?.error || response?.message || 'Failed to start PIE' };
51
- } catch (err: any) {
51
+ } catch (err: unknown) {
52
52
  // If it's a timeout, return error instead of falling back
53
- if (err.message && /time.*out/i.test(err.message)) {
54
- return { success: false, error: `Timeout waiting for PIE to start: ${err.message}` };
53
+ const errMsg = err instanceof Error ? err.message : String(err);
54
+ if (errMsg && /time.*out/i.test(errMsg)) {
55
+ return { success: false, error: `Timeout waiting for PIE to start: ${errMsg}` };
55
56
  }
56
57
 
57
58
  // Fallback to console commands if automation bridge is unavailable or fails (non-timeout)
@@ -1,7 +1,7 @@
1
- import { cleanObject } from '../../utils/safe-json.js';
2
1
  import { ITools } from '../../types/tool-interfaces.js';
3
2
  import { executeAutomationRequest } from './common-handlers.js';
4
3
  import { normalizeArgs } from './argument-helper.js';
4
+ import { ResponseFactory } from '../../utils/response-factory.js';
5
5
 
6
6
  type ActorActionHandler = (args: any, tools: ITools) => Promise<any>;
7
7
 
@@ -39,11 +39,11 @@ const handlers: Record<string, ActorActionHandler> = {
39
39
  // failure so tests can exercise timeout handling deterministically
40
40
  // without relying on editor performance.
41
41
  if (typeof timeoutMs === 'number' && timeoutMs > 0 && timeoutMs < 200) {
42
- return cleanObject({
42
+ return {
43
43
  success: false,
44
44
  error: `Timeout too small for spawn operation: ${timeoutMs}ms`,
45
45
  message: 'Timeout too small for spawn operation'
46
- });
46
+ };
47
47
  }
48
48
 
49
49
  // For SplineActor alias, add SplineComponent automatically
@@ -255,11 +255,16 @@ const handlers: Record<string, ActorActionHandler> = {
255
255
  };
256
256
 
257
257
  export async function handleActorTools(action: string, args: any, tools: ITools) {
258
- const handler = handlers[action];
259
- if (handler) {
260
- const res = await handler(args, tools);
261
- return cleanObject(res);
258
+ try {
259
+ const handler = handlers[action];
260
+ if (handler) {
261
+ const res = await handler(args, tools);
262
+ return ResponseFactory.success(res);
263
+ }
264
+ // Fallback to direct bridge call or error
265
+ const res = await executeAutomationRequest(tools, 'control_actor', args);
266
+ return ResponseFactory.success(res);
267
+ } catch (error) {
268
+ return ResponseFactory.error(error);
262
269
  }
263
- // Fallback to direct bridge call or error
264
- return executeAutomationRequest(tools, 'control_actor', args);
265
270
  }