blueprint-extractor-mcp 3.2.0 → 3.3.0

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.
@@ -482,4 +482,92 @@ export const exampleCatalog = {
482
482
  },
483
483
  ],
484
484
  },
485
+ state_tree_bindings: {
486
+ summary: 'Wire task outputs to task inputs using property path bindings. Extract an existing StateTree to discover structIds and property names, then use binding operations to set up data flow.',
487
+ recommended_flow: [
488
+ 'extract_asset (discover structIds and property names)',
489
+ 'modify_state_tree (add_binding / set_bindings)',
490
+ 'extract_asset (verify bindings)',
491
+ 'save_assets',
492
+ ],
493
+ examples: [
494
+ {
495
+ title: 'add_single_binding',
496
+ tool: 'modify_state_tree',
497
+ arguments: {
498
+ asset_path: '/Game/AI/ST_Character',
499
+ operation: 'add_binding',
500
+ payload: {
501
+ sourcePath: {
502
+ structId: 'EAB9611F4B07D7E2C25A948AFC790A50',
503
+ segments: [{ name: 'SelectedGestureTag' }],
504
+ },
505
+ targetPath: {
506
+ structId: 'F2A3B44C4D08E6A1C25A948AFC790A50',
507
+ segments: [{ name: 'MontageTag' }],
508
+ },
509
+ },
510
+ },
511
+ context: { description: 'Wire SelectRandomGesture output → PlayMontage input. structIds from extract_asset output.' },
512
+ },
513
+ {
514
+ title: 'set_all_bindings',
515
+ tool: 'modify_state_tree',
516
+ arguments: {
517
+ asset_path: '/Game/AI/ST_Character',
518
+ operation: 'set_bindings',
519
+ payload: {
520
+ propertyBindings: [
521
+ {
522
+ sourcePath: { segments: [{ name: 'SelectedTag' }] },
523
+ targetPath: { segments: [{ name: 'MontageTag' }] },
524
+ },
525
+ {
526
+ sourcePath: { segments: [{ name: 'Duration' }] },
527
+ targetPath: { segments: [{ name: 'WaitTime' }] },
528
+ },
529
+ ],
530
+ },
531
+ },
532
+ context: { description: 'Replace all bindings on the StateTree with a new set.' },
533
+ },
534
+ {
535
+ title: 'remove_binding_by_target',
536
+ tool: 'modify_state_tree',
537
+ arguments: {
538
+ asset_path: '/Game/AI/ST_Character',
539
+ operation: 'remove_binding',
540
+ payload: {
541
+ targetPath: { segments: [{ name: 'MontageTag' }] },
542
+ },
543
+ },
544
+ context: { description: 'Remove all bindings targeting the MontageTag property.' },
545
+ },
546
+ {
547
+ title: 'create_with_bindings',
548
+ tool: 'create_state_tree',
549
+ arguments: {
550
+ asset_path: '/Game/AI/ST_NewWithBindings',
551
+ payload: {
552
+ schema: '/Script/GameplayStateTreeModule.StateTreeComponentSchema',
553
+ states: [{
554
+ name: 'Root',
555
+ type: 'State',
556
+ tasks: [
557
+ { nodeStructType: '/Script/MyMod.STCSelectGesture', name: 'SelectGesture' },
558
+ { nodeStructType: '/Script/MyMod.STCPlayMontage', name: 'PlayMontage' },
559
+ ],
560
+ }],
561
+ bindings: {
562
+ propertyBindings: [{
563
+ sourcePath: { segments: [{ name: 'SelectedGestureTag' }] },
564
+ targetPath: { segments: [{ name: 'MontageTag' }] },
565
+ }],
566
+ },
567
+ },
568
+ },
569
+ context: { description: 'Create a StateTree with tasks and bindings in a single call.' },
570
+ },
571
+ ],
572
+ },
485
573
  };
@@ -36,6 +36,10 @@ export function enrichLiveCodingResult(result, changedPaths = [], lastExternalBu
36
36
  warnings.push('Live Coding cannot add, remove, or reorder UPROPERTYs or change class layouts. '
37
37
  + 'Use compile_project_code + restart_editor for class layout changes.');
38
38
  }
39
+ if (result.noOp && typeof result.compileResult === 'string' && result.compileResult.toLowerCase() === 'nochanges') {
40
+ warnings.push('Live Coding cannot compile newly added source files — it only detects changes to already-compiled .obj files. '
41
+ + 'Use compile_project_code for new files, then restart_editor to load the new module.');
42
+ }
39
43
  const fallbackRecommended = canFallbackFromLiveCoding(result);
40
44
  const reason = deriveLiveCodingFallbackReason(result);
41
45
  return {
@@ -47,13 +47,40 @@ export async function callSubsystemJson(client, method, params, options) {
47
47
  }
48
48
  return parsed;
49
49
  }
50
+ /**
51
+ * Extracts the best available error message from a UE failure response,
52
+ * checking message, errorMessage, error, diagnostics[], and errors[] fields
53
+ * before falling back to listing the available response keys.
54
+ */
55
+ function extractFailureMessage(parsed) {
56
+ if (typeof parsed.message === 'string' && parsed.message.length > 0)
57
+ return parsed.message;
58
+ if (typeof parsed.errorMessage === 'string' && parsed.errorMessage.length > 0)
59
+ return parsed.errorMessage;
60
+ if (typeof parsed.error === 'string' && parsed.error.length > 0)
61
+ return parsed.error;
62
+ if (Array.isArray(parsed.diagnostics) && parsed.diagnostics.length > 0) {
63
+ const messages = parsed.diagnostics
64
+ .filter((d) => typeof d === 'object' && d !== null && typeof d.message === 'string')
65
+ .map((d) => d.message);
66
+ if (messages.length > 0)
67
+ return messages.join('; ');
68
+ }
69
+ if (Array.isArray(parsed.errors) && parsed.errors.length > 0) {
70
+ const first = parsed.errors[0];
71
+ return typeof first === 'string' ? first : JSON.stringify(first);
72
+ }
73
+ const keys = Object.keys(parsed).join(', ');
74
+ return `Operation returned success:false with no diagnostic details. Response keys: [${keys}]`;
75
+ }
50
76
  export function jsonToolSuccess(parsed, options = {}) {
51
77
  const structuredContent = isRecord(parsed) ? parsed : { data: parsed };
52
78
  // Guard: if the UE response indicates failure, route through error path.
53
79
  // This prevents tool handlers from accidentally passing error payloads as successes.
54
80
  if (isRecord(parsed) && parsed.success === false) {
81
+ const errorText = extractFailureMessage(parsed);
55
82
  return {
56
- content: [{ type: 'text', text: `Error: ${typeof parsed.message === 'string' ? parsed.message : 'Operation failed'}` }],
83
+ content: [{ type: 'text', text: `Error: ${errorText}` }],
57
84
  structuredContent,
58
85
  isError: true,
59
86
  };
@@ -51,6 +51,7 @@ export interface CompileProjectCodeResult {
51
51
  stdout?: string;
52
52
  stderr?: string;
53
53
  uhtCacheFilesDeleted?: string[];
54
+ compilationSucceeded: boolean;
54
55
  errorCategory?: BuildErrorCategory;
55
56
  errorSummary?: string;
56
57
  lockedFiles?: string[];
@@ -104,6 +105,10 @@ export interface ProjectControllerLike {
104
105
  readonly liveCodingSupported: boolean;
105
106
  classifyChangedPaths(changedPaths: string[], forceRebuild?: boolean): SyncStrategyPlan;
106
107
  compileProjectCode(request: CompileProjectCodeRequest): Promise<CompileProjectCodeResult>;
108
+ killEditorProcess(): Promise<{
109
+ killed: boolean;
110
+ error?: string;
111
+ }>;
107
112
  launchEditor(request: LaunchEditorRequest): Promise<LaunchEditorResult>;
108
113
  waitForEditorRestart(probeConnection: ProbeConnection, options?: {
109
114
  disconnectTimeoutMs?: number;
@@ -131,6 +136,10 @@ export declare class ProjectController implements ProjectControllerLike {
131
136
  get liveCodingSupported(): boolean;
132
137
  classifyChangedPaths(changedPaths: string[], forceRebuild?: boolean): SyncStrategyPlan;
133
138
  compileProjectCode(request: CompileProjectCodeRequest): Promise<CompileProjectCodeResult>;
139
+ killEditorProcess(): Promise<{
140
+ killed: boolean;
141
+ error?: string;
142
+ }>;
134
143
  launchEditor(request: LaunchEditorRequest): Promise<LaunchEditorResult>;
135
144
  waitForEditorRestart(probeConnection: ProbeConnection, options?: {
136
145
  disconnectTimeoutMs?: number;
@@ -1,4 +1,4 @@
1
- import { spawn } from 'node:child_process';
1
+ import { execSync, spawn } from 'node:child_process';
2
2
  import { access, readdir, unlink } from 'node:fs/promises';
3
3
  import { constants as fsConstants } from 'node:fs';
4
4
  import { dirname, resolve } from 'node:path';
@@ -358,6 +358,7 @@ export class ProjectController {
358
358
  },
359
359
  durationMs: Date.now() - startedAt,
360
360
  exitCode: completed.exitCode,
361
+ compilationSucceeded: completed.exitCode === 0,
361
362
  restartRequired: true,
362
363
  restartReasons: ['external_build_completed'],
363
364
  outputIncluded: includeOutput,
@@ -380,9 +381,26 @@ export class ProjectController {
380
381
  if (classification.lockedFiles.length > 0) {
381
382
  result.lockedFiles = classification.lockedFiles;
382
383
  }
384
+ // Compilation itself succeeded if the failure is only at the link/lock stage
385
+ result.compilationSucceeded = classification.errorCategory === 'locked_file'
386
+ || classification.errorCategory === 'link';
383
387
  }
384
388
  return result;
385
389
  }
390
+ async killEditorProcess() {
391
+ try {
392
+ if (this.platform === 'win32') {
393
+ execSync('taskkill /F /IM "UnrealEditor.exe"', { timeout: 10000 });
394
+ }
395
+ else {
396
+ execSync('pkill -9 -f UnrealEditor', { timeout: 10000 });
397
+ }
398
+ return { killed: true };
399
+ }
400
+ catch (err) {
401
+ return { killed: false, error: String(err) };
402
+ }
403
+ }
386
404
  async launchEditor(request) {
387
405
  const engineRoot = request.engineRoot ?? this.env.UE_ENGINE_ROOT;
388
406
  const projectPath = request.projectPath ?? this.env.UE_PROJECT_PATH;
@@ -1,7 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import { exampleCatalog } from './catalogs/example-catalog.js';
3
3
  import { collectRelatedResources, collectToolExampleFamilies as collectToolExampleFamiliesFromCatalog, summarizeOutputSchema, summarizeSchemaFields, } from './helpers/tool-help.js';
4
- import { AnimMontageMutationOperationSchema, AnimationNotifySelectorSchema, AnimSequenceMutationOperationSchema, BehaviorTreeMutationOperationSchema, BehaviorTreeNodeSelectorSchema, BlackboardKeySchema, BlackboardMutationOperationSchema, BlendParameterSchema, BlendSpaceMutationOperationSchema, BlendSpaceSampleSchema, BlueprintGraphMutationOperationSchema, BlueprintMemberMutationOperationSchema, BuildConfigurationSchema, BuildPlatformSchema, CurveChannelSchema, CurveKeyDeleteSchema, CurveKeyUpsertSchema, CurveTableModeSchema, CurveTableRowSchema, CurveTypeSchema, DataTableRowSchema, ExtractAssetTypeSchema, EnhancedInputValueTypeSchema, FontImportItemSchema, ImportJobListSchema, ImportJobSchema, ImportPayloadSchema, InputMappingSchema, JsonObjectSchema, MaterialConnectionSelectorFieldsSchema, MaterialFontParameterSchema, MaterialFunctionAssetKindSchema, MaterialGraphOperationKindSchema, MaterialGraphOperationSchema, MaterialGraphPayloadSchema, MaterialLayerStackSchema, MaterialNodePositionSchema, MaterialScalarParameterSchema, MaterialStaticSwitchParameterSchema, MaterialTextureParameterSchema, MaterialVectorParameterSchema, MeshImportPayloadSchema, StateTreeEditorNodeSelectorSchema, StateTreeMutationOperationSchema, StateTreeStateSelectorSchema, StateTreeTransitionSelectorSchema, StateTreeBindingSchema, TextureImportPayloadSchema, UserDefinedEnumEntrySchema, UserDefinedEnumMutationOperationSchema, UserDefinedStructFieldSchema, UserDefinedStructMutationOperationSchema, WidgetBlueprintMutationOperationSchema, WidgetNodeSchema, WidgetSelectorFieldsSchema, WindowFontApplicationSchema, } from './schemas/tool-inputs.js';
4
+ import { AnimMontageMutationOperationSchema, AnimationNotifySelectorSchema, AnimSequenceMutationOperationSchema, BehaviorTreeMutationOperationSchema, BehaviorTreeNodeSelectorSchema, BlackboardKeySchema, BlackboardMutationOperationSchema, BlendParameterSchema, BlendSpaceMutationOperationSchema, BlendSpaceSampleSchema, BlueprintGraphMutationOperationSchema, BlueprintMemberMutationOperationSchema, BuildConfigurationSchema, BuildPlatformSchema, CurveChannelSchema, CurveKeyDeleteSchema, CurveKeyUpsertSchema, CurveTableModeSchema, CurveTableRowSchema, CurveTypeSchema, DataTableRowSchema, ExtractAssetTypeSchema, EnhancedInputValueTypeSchema, FontImportItemSchema, ImportJobListSchema, ImportJobSchema, ImportPayloadSchema, InputMappingSchema, JsonObjectSchema, MaterialConnectionSelectorFieldsSchema, MaterialFontParameterSchema, MaterialFunctionAssetKindSchema, MaterialGraphOperationKindSchema, MaterialGraphOperationSchema, MaterialGraphPayloadSchema, MaterialLayerStackSchema, MaterialNodePositionSchema, MaterialScalarParameterSchema, MaterialStaticSwitchParameterSchema, MaterialTextureParameterSchema, MaterialVectorParameterSchema, MeshImportPayloadSchema, StateTreeEditorNodeSelectorSchema, StateTreeMutationOperationSchema, StateTreeStateSelectorSchema, StateTreeTransitionSelectorSchema, StateTreeBindingsObjectSchema, TextureImportPayloadSchema, UserDefinedEnumEntrySchema, UserDefinedEnumMutationOperationSchema, UserDefinedStructFieldSchema, UserDefinedStructMutationOperationSchema, WidgetBlueprintMutationOperationSchema, WidgetNodeSchema, WidgetSelectorFieldsSchema, WindowFontApplicationSchema, } from './schemas/tool-inputs.js';
5
5
  import { applyWindowUiChangesResultSchema, AutomationRunListSchema, automationRunSchema, CaptureResultSchema, CascadeResultSchema, CleanupCapturesResultSchema, CompareCaptureResultSchema, CompareMotionCaptureBundleResultSchema, CreateModifyWidgetAnimationResultSchema, ExtractWidgetAnimationResultSchema, ListCapturesResultSchema, motionCaptureModeSchema, MotionCaptureBundleResultSchema, widgetAnimationCheckpointSchema, } from './schemas/tool-results.js';
6
6
  import { registerAnimationAuthoringTools } from './tools/animation-authoring.js';
7
7
  import { registerAutomationRunTools } from './tools/automation-runs.js';
@@ -166,7 +166,7 @@ export function registerServerTools({ server, client, projectController, automat
166
166
  stateTreeStateSelectorSchema: StateTreeStateSelectorSchema,
167
167
  stateTreeEditorNodeSelectorSchema: StateTreeEditorNodeSelectorSchema,
168
168
  stateTreeTransitionSelectorSchema: StateTreeTransitionSelectorSchema,
169
- stateTreeBindingSchema: StateTreeBindingSchema,
169
+ stateTreeBindingsObjectSchema: StateTreeBindingsObjectSchema,
170
170
  });
171
171
  registerAnimationAuthoringTools({
172
172
  server,
@@ -64,7 +64,8 @@ export function registerStaticDocResources(server) {
64
64
  '- UserDefinedEnum: entry selector by name; operations replace_entries, rename_entry, remove_entry, reorder_entries.',
65
65
  '- Blackboard: key selector by entryName; operations replace_keys, patch_key, remove_key, set_parent.',
66
66
  '- BehaviorTree: node selector by nodePath; operations replace_tree, patch_node, patch_attachment, set_blackboard.',
67
- '- StateTree: selectors by stateId/statePath, editorNodeId, or transitionId; operations replace_tree, patch_state, patch_editor_node, patch_transition, set_schema.',
67
+ '- StateTree: selectors by stateId/statePath, editorNodeId, or transitionId; operations replace_tree, patch_state, patch_editor_node, patch_transition, set_schema, set_bindings, add_binding, remove_binding. Blueprint-wrapped nodes (StateTreeBlueprintTaskWrapper / StateTreeBlueprintConditionWrapper) require instanceObjectClass for the backing instance to exist at runtime.',
68
+ '- StateTree property bindings wire task/evaluator outputs to task inputs. Use extract_asset to discover structIds and property names. Each binding has sourcePath and targetPath with segments: [{name, arrayIndex?, instanceStruct?}] and optional structId. Operations: set_bindings (replace all), add_binding (append), remove_binding (by targetPath). In replace_tree/create: include as { bindings: { propertyBindings: [...] } }.',
68
69
  '- AnimSequence: notify selector by notifyId/notifyGuid with notifyIndex or track metadata as fallback; operations replace_notifies, patch_notify, replace_sync_markers, replace_curve_metadata.',
69
70
  '- AnimMontage: notify selector by notifyId/notifyGuid with notifyIndex or track metadata as fallback; operations replace_notifies, patch_notify, replace_sections, replace_slots.',
70
71
  '- BlendSpace: sample selector by sampleIndex; operations replace_samples, patch_sample, set_axes.',