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.
- package/.github/workflows/publish-mcp.yml +1 -4
- package/.github/workflows/release-drafter.yml +2 -1
- package/CHANGELOG.md +38 -0
- package/dist/automation/bridge.d.ts +1 -2
- package/dist/automation/bridge.js +24 -23
- package/dist/automation/connection-manager.d.ts +1 -0
- package/dist/automation/connection-manager.js +10 -0
- package/dist/automation/message-handler.js +5 -4
- package/dist/automation/request-tracker.d.ts +4 -0
- package/dist/automation/request-tracker.js +11 -3
- package/dist/tools/actors.d.ts +19 -1
- package/dist/tools/actors.js +15 -5
- package/dist/tools/assets.js +1 -1
- package/dist/tools/blueprint.d.ts +12 -0
- package/dist/tools/blueprint.js +43 -14
- package/dist/tools/consolidated-tool-definitions.js +2 -1
- package/dist/tools/editor.js +3 -2
- package/dist/tools/handlers/actor-handlers.d.ts +1 -1
- package/dist/tools/handlers/actor-handlers.js +14 -8
- package/dist/tools/handlers/sequence-handlers.d.ts +1 -1
- package/dist/tools/handlers/sequence-handlers.js +24 -13
- package/dist/tools/introspection.d.ts +1 -1
- package/dist/tools/introspection.js +1 -1
- package/dist/tools/level.js +3 -3
- package/dist/tools/lighting.d.ts +54 -7
- package/dist/tools/lighting.js +4 -4
- package/dist/tools/materials.d.ts +1 -1
- package/dist/types/tool-types.d.ts +2 -0
- package/dist/unreal-bridge.js +4 -4
- package/dist/utils/command-validator.js +6 -5
- package/dist/utils/error-handler.d.ts +24 -2
- package/dist/utils/error-handler.js +58 -23
- package/dist/utils/normalize.d.ts +7 -4
- package/dist/utils/normalize.js +12 -10
- package/dist/utils/response-validator.js +88 -73
- package/dist/utils/unreal-command-queue.d.ts +2 -0
- package/dist/utils/unreal-command-queue.js +8 -1
- package/docs/handler-mapping.md +4 -2
- package/package.json +1 -1
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSubsystem.cpp +298 -33
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AnimationHandlers.cpp +7 -8
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintGraphHandlers.cpp +229 -319
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers.cpp +98 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EffectHandlers.cpp +24 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EnvironmentHandlers.cpp +96 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LightingHandlers.cpp +52 -5
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ProcessRequest.cpp +5 -268
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequenceHandlers.cpp +57 -2
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpConnectionManager.cpp +0 -1
- package/server.json +3 -3
- package/src/automation/bridge.ts +27 -25
- package/src/automation/connection-manager.ts +18 -0
- package/src/automation/message-handler.ts +33 -8
- package/src/automation/request-tracker.ts +39 -7
- package/src/server/tool-registry.ts +3 -3
- package/src/tools/actors.ts +44 -19
- package/src/tools/assets.ts +3 -3
- package/src/tools/blueprint.ts +115 -49
- package/src/tools/consolidated-tool-definitions.ts +2 -1
- package/src/tools/editor.ts +4 -3
- package/src/tools/handlers/actor-handlers.ts +14 -9
- package/src/tools/handlers/sequence-handlers.ts +86 -63
- package/src/tools/introspection.ts +7 -7
- package/src/tools/level.ts +6 -6
- package/src/tools/lighting.ts +19 -19
- package/src/tools/materials.ts +1 -1
- package/src/tools/sequence.ts +1 -1
- package/src/tools/ui.ts +1 -1
- package/src/types/tool-types.ts +4 -0
- package/src/unreal-bridge.ts +71 -26
- package/src/utils/command-validator.ts +46 -5
- package/src/utils/error-handler.ts +128 -45
- package/src/utils/normalize.ts +38 -16
- package/src/utils/response-validator.ts +103 -87
- package/src/utils/unreal-command-queue.ts +13 -1
package/src/tools/blueprint.ts
CHANGED
|
@@ -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
|
|
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 }
|
|
20
|
-
} catch (err:
|
|
21
|
-
|
|
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:
|
|
53
|
+
private isUnknownActionResponse(res: ActionResponse | StandardActionResponse | null | undefined): boolean {
|
|
26
54
|
if (!res) return false;
|
|
27
|
-
const
|
|
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
|
|
88
|
-
} catch (err:
|
|
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:
|
|
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:
|
|
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' &&
|
|
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 { ...
|
|
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
|
|
154
|
-
} catch (err:
|
|
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: [] }
|
|
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' }
|
|
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 }
|
|
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' }
|
|
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' }
|
|
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 }
|
|
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 }
|
|
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:
|
|
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:
|
|
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 })
|
|
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:
|
|
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:
|
|
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:
|
|
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 (
|
|
467
|
-
this.log.warn?.(`addSCSComponent reported warning: ${
|
|
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:
|
|
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 (
|
|
498
|
-
this.log.warn?.(`removeSCSComponent reported warning: ${
|
|
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:
|
|
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 (
|
|
538
|
-
this.log.warn?.(`reparentSCSComponent reported warning: ${
|
|
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:
|
|
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 (
|
|
583
|
-
this.log.warn?.(`setSCSComponentTransform reported warning: ${
|
|
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:
|
|
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 (
|
|
632
|
-
this.log.warn?.(`setSCSComponentProperty reported warning: ${
|
|
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:
|
|
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
|
|
666
|
-
graphName: (pluginResp.result as
|
|
667
|
-
}
|
|
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:
|
|
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:
|
|
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
|
},
|
package/src/tools/editor.ts
CHANGED
|
@@ -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:
|
|
51
|
+
} catch (err: unknown) {
|
|
52
52
|
// If it's a timeout, return error instead of falling back
|
|
53
|
-
|
|
54
|
-
|
|
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
|
|
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
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
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
|
}
|