unreal-engine-mcp-server 0.5.0 → 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 (188) 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 +2 -4
  6. package/.github/workflows/release-drafter.yml +3 -2
  7. package/.github/workflows/release.yml +3 -3
  8. package/CHANGELOG.md +109 -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/automation/bridge.d.ts +1 -2
  14. package/dist/automation/bridge.js +24 -23
  15. package/dist/automation/connection-manager.d.ts +1 -0
  16. package/dist/automation/connection-manager.js +10 -0
  17. package/dist/automation/message-handler.js +5 -4
  18. package/dist/automation/request-tracker.d.ts +4 -0
  19. package/dist/automation/request-tracker.js +11 -3
  20. package/dist/config.d.ts +0 -1
  21. package/dist/config.js +0 -1
  22. package/dist/constants.d.ts +4 -0
  23. package/dist/constants.js +4 -0
  24. package/dist/graphql/loaders.d.ts +64 -0
  25. package/dist/graphql/loaders.js +117 -0
  26. package/dist/graphql/resolvers.d.ts +3 -3
  27. package/dist/graphql/resolvers.js +33 -30
  28. package/dist/graphql/server.js +3 -1
  29. package/dist/graphql/types.d.ts +2 -0
  30. package/dist/index.d.ts +2 -0
  31. package/dist/index.js +13 -2
  32. package/dist/server-setup.d.ts +0 -1
  33. package/dist/server-setup.js +0 -40
  34. package/dist/tools/actors.d.ts +58 -24
  35. package/dist/tools/actors.js +22 -6
  36. package/dist/tools/assets.d.ts +19 -71
  37. package/dist/tools/assets.js +28 -22
  38. package/dist/tools/base-tool.d.ts +4 -4
  39. package/dist/tools/base-tool.js +1 -1
  40. package/dist/tools/blueprint.d.ts +45 -61
  41. package/dist/tools/blueprint.js +43 -14
  42. package/dist/tools/consolidated-tool-definitions.js +2 -1
  43. package/dist/tools/consolidated-tool-handlers.js +96 -110
  44. package/dist/tools/dynamic-handler-registry.d.ts +11 -9
  45. package/dist/tools/dynamic-handler-registry.js +17 -95
  46. package/dist/tools/editor.d.ts +19 -193
  47. package/dist/tools/editor.js +11 -2
  48. package/dist/tools/environment.d.ts +8 -14
  49. package/dist/tools/foliage.d.ts +18 -143
  50. package/dist/tools/foliage.js +4 -2
  51. package/dist/tools/handlers/actor-handlers.d.ts +1 -1
  52. package/dist/tools/handlers/actor-handlers.js +14 -13
  53. package/dist/tools/handlers/asset-handlers.js +454 -454
  54. package/dist/tools/handlers/sequence-handlers.d.ts +1 -1
  55. package/dist/tools/handlers/sequence-handlers.js +24 -13
  56. package/dist/tools/introspection.d.ts +1 -1
  57. package/dist/tools/introspection.js +1 -1
  58. package/dist/tools/landscape.d.ts +16 -116
  59. package/dist/tools/landscape.js +7 -3
  60. package/dist/tools/level.d.ts +22 -103
  61. package/dist/tools/level.js +26 -18
  62. package/dist/tools/lighting.d.ts +54 -7
  63. package/dist/tools/lighting.js +9 -5
  64. package/dist/tools/materials.d.ts +1 -1
  65. package/dist/tools/materials.js +5 -1
  66. package/dist/tools/niagara.js +37 -2
  67. package/dist/tools/performance.d.ts +0 -1
  68. package/dist/tools/performance.js +0 -1
  69. package/dist/tools/physics.js +5 -1
  70. package/dist/tools/sequence.d.ts +24 -24
  71. package/dist/tools/sequence.js +13 -0
  72. package/dist/tools/ui.d.ts +0 -2
  73. package/dist/types/automation-responses.d.ts +115 -0
  74. package/dist/types/automation-responses.js +2 -0
  75. package/dist/types/responses.d.ts +249 -0
  76. package/dist/types/responses.js +2 -0
  77. package/dist/types/tool-interfaces.d.ts +135 -135
  78. package/dist/types/tool-types.d.ts +2 -0
  79. package/dist/unreal-bridge.js +4 -4
  80. package/dist/utils/command-validator.js +7 -5
  81. package/dist/utils/error-handler.d.ts +24 -2
  82. package/dist/utils/error-handler.js +58 -23
  83. package/dist/utils/normalize.d.ts +7 -4
  84. package/dist/utils/normalize.js +12 -10
  85. package/dist/utils/path-security.d.ts +2 -0
  86. package/dist/utils/path-security.js +24 -0
  87. package/dist/utils/response-factory.d.ts +4 -4
  88. package/dist/utils/response-factory.js +15 -21
  89. package/dist/utils/response-validator.js +88 -73
  90. package/dist/utils/unreal-command-queue.d.ts +2 -0
  91. package/dist/utils/unreal-command-queue.js +8 -1
  92. package/docs/Migration-Guide-v0.5.0.md +1 -9
  93. package/docs/handler-mapping.md +4 -2
  94. package/docs/testing-guide.md +2 -2
  95. package/package.json +12 -6
  96. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSubsystem.cpp +298 -33
  97. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AnimationHandlers.cpp +7 -8
  98. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintGraphHandlers.cpp +229 -319
  99. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers.cpp +98 -0
  100. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EffectHandlers.cpp +24 -0
  101. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EnvironmentHandlers.cpp +96 -0
  102. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LightingHandlers.cpp +52 -5
  103. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ProcessRequest.cpp +5 -268
  104. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequenceHandlers.cpp +57 -2
  105. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpConnectionManager.cpp +0 -1
  106. package/scripts/run-all-tests.mjs +25 -20
  107. package/server.json +3 -2
  108. package/src/automation/bridge.ts +27 -25
  109. package/src/automation/connection-manager.ts +18 -0
  110. package/src/automation/message-handler.ts +33 -8
  111. package/src/automation/request-tracker.ts +39 -7
  112. package/src/config.ts +1 -1
  113. package/src/constants.ts +7 -0
  114. package/src/graphql/loaders.ts +244 -0
  115. package/src/graphql/resolvers.ts +47 -49
  116. package/src/graphql/server.ts +3 -1
  117. package/src/graphql/types.ts +3 -0
  118. package/src/index.ts +15 -2
  119. package/src/resources/assets.ts +5 -4
  120. package/src/server/tool-registry.ts +3 -3
  121. package/src/server-setup.ts +3 -37
  122. package/src/tools/actors.ts +77 -44
  123. package/src/tools/animation.ts +1 -0
  124. package/src/tools/assets.ts +76 -65
  125. package/src/tools/base-tool.ts +3 -3
  126. package/src/tools/blueprint.ts +170 -104
  127. package/src/tools/consolidated-tool-definitions.ts +2 -1
  128. package/src/tools/consolidated-tool-handlers.ts +129 -150
  129. package/src/tools/dynamic-handler-registry.ts +22 -140
  130. package/src/tools/editor.ts +43 -29
  131. package/src/tools/environment.ts +21 -27
  132. package/src/tools/foliage.ts +28 -25
  133. package/src/tools/handlers/actor-handlers.ts +16 -17
  134. package/src/tools/handlers/asset-handlers.ts +484 -484
  135. package/src/tools/handlers/sequence-handlers.ts +85 -62
  136. package/src/tools/introspection.ts +7 -7
  137. package/src/tools/landscape.ts +34 -28
  138. package/src/tools/level.ts +100 -80
  139. package/src/tools/lighting.ts +25 -20
  140. package/src/tools/materials.ts +9 -3
  141. package/src/tools/niagara.ts +44 -2
  142. package/src/tools/performance.ts +1 -2
  143. package/src/tools/physics.ts +7 -1
  144. package/src/tools/sequence.ts +42 -26
  145. package/src/tools/ui.ts +1 -3
  146. package/src/types/automation-responses.ts +119 -0
  147. package/src/types/responses.ts +355 -0
  148. package/src/types/tool-interfaces.ts +135 -135
  149. package/src/types/tool-types.ts +4 -0
  150. package/src/unreal-bridge.ts +71 -26
  151. package/src/utils/command-validator.ts +47 -5
  152. package/src/utils/error-handler.ts +128 -45
  153. package/src/utils/normalize.test.ts +162 -0
  154. package/src/utils/normalize.ts +38 -16
  155. package/src/utils/path-security.ts +43 -0
  156. package/src/utils/response-factory.ts +29 -24
  157. package/src/utils/response-validator.ts +103 -87
  158. package/src/utils/safe-json.test.ts +90 -0
  159. package/src/utils/unreal-command-queue.ts +13 -1
  160. package/src/utils/validation.test.ts +184 -0
  161. package/tests/test-animation.mjs +358 -33
  162. package/tests/test-asset-graph.mjs +311 -0
  163. package/tests/test-audio.mjs +314 -116
  164. package/tests/test-behavior-tree.mjs +327 -144
  165. package/tests/test-blueprint-graph.mjs +343 -12
  166. package/tests/test-control-editor.mjs +85 -53
  167. package/tests/test-graphql.mjs +58 -8
  168. package/tests/test-input.mjs +349 -0
  169. package/tests/test-inspect.mjs +291 -61
  170. package/tests/test-landscape.mjs +304 -48
  171. package/tests/test-lighting.mjs +428 -0
  172. package/tests/test-manage-level.mjs +70 -51
  173. package/tests/test-performance.mjs +539 -0
  174. package/tests/test-sequence.mjs +82 -46
  175. package/tests/test-system.mjs +72 -33
  176. package/tests/test-wasm.mjs +98 -8
  177. package/vitest.config.ts +35 -0
  178. package/.github/release-drafter.yml +0 -148
  179. package/dist/prompts/index.d.ts +0 -21
  180. package/dist/prompts/index.js +0 -217
  181. package/dist/tools/blueprint/helpers.d.ts +0 -29
  182. package/dist/tools/blueprint/helpers.js +0 -182
  183. package/src/prompts/index.ts +0 -249
  184. package/src/tools/blueprint/helpers.ts +0 -189
  185. package/tests/test-blueprint-events.mjs +0 -35
  186. package/tests/test-extra-tools.mjs +0 -38
  187. package/tests/test-render.mjs +0 -33
  188. package/tests/test-search-assets.mjs +0 -66
@@ -8,8 +8,8 @@ export interface StandardActionResponse<T = any> {
8
8
  success: boolean;
9
9
  data?: T;
10
10
  warnings?: string[];
11
- error?: {
12
- code: string;
11
+ error?: string | {
12
+ code?: string;
13
13
  message: string;
14
14
  [key: string]: unknown;
15
15
  } | null;
@@ -53,48 +53,48 @@ export interface SourceControlState {
53
53
  }
54
54
 
55
55
  export interface IAssetTools {
56
- importAsset(params: { sourcePath: string; destinationPath: string; overwrite?: boolean; save?: boolean }): Promise<any>;
57
- createFolder(path: string): Promise<any>;
58
- duplicateAsset(params: { sourcePath: string; destinationPath: string; overwrite?: boolean }): Promise<any>;
59
- renameAsset(params: { sourcePath: string; destinationPath: string }): Promise<any>;
60
- moveAsset(params: { sourcePath: string; destinationPath: string }): Promise<any>;
61
- deleteAssets(params: { paths: string[]; fixupRedirectors?: boolean; timeoutMs?: number }): Promise<any>;
62
- searchAssets(params: { classNames?: string[]; packagePaths?: string[]; recursivePaths?: boolean; recursiveClasses?: boolean; limit?: number }): Promise<any>;
63
- saveAsset(assetPath: string): Promise<any>;
64
- findByTag(params: { tag: string; value?: string }): Promise<any>;
65
- getDependencies(params: { assetPath: string; recursive?: boolean }): Promise<any>;
66
- getMetadata(params: { assetPath: string }): Promise<any>;
67
- getSourceControlState(params: { assetPath: string }): Promise<SourceControlState | any>;
68
- analyzeGraph(params: { assetPath: string; maxDepth?: number }): Promise<any>;
69
- createThumbnail(params: { assetPath: string; width?: number; height?: number }): Promise<any>;
70
- setTags(params: { assetPath: string; tags: string[] }): Promise<any>;
71
- generateReport(params: { directory: string; reportType?: string; outputPath?: string }): Promise<any>;
72
- validate(params: { assetPath: string }): Promise<any>;
73
- generateLODs(params: { assetPath: string; lodCount: number; reductionSettings?: Record<string, unknown> }): Promise<any>;
56
+ importAsset(params: { sourcePath: string; destinationPath: string; overwrite?: boolean; save?: boolean }): Promise<StandardActionResponse>;
57
+ createFolder(path: string): Promise<StandardActionResponse>;
58
+ duplicateAsset(params: { sourcePath: string; destinationPath: string; overwrite?: boolean }): Promise<StandardActionResponse>;
59
+ renameAsset(params: { sourcePath: string; destinationPath: string }): Promise<StandardActionResponse>;
60
+ moveAsset(params: { sourcePath: string; destinationPath: string }): Promise<StandardActionResponse>;
61
+ deleteAssets(params: { paths: string[]; fixupRedirectors?: boolean; timeoutMs?: number }): Promise<StandardActionResponse>;
62
+ searchAssets(params: { classNames?: string[]; packagePaths?: string[]; recursivePaths?: boolean; recursiveClasses?: boolean; limit?: number }): Promise<StandardActionResponse>;
63
+ saveAsset(assetPath: string): Promise<StandardActionResponse>;
64
+ findByTag(params: { tag: string; value?: string }): Promise<StandardActionResponse>;
65
+ getDependencies(params: { assetPath: string; recursive?: boolean }): Promise<StandardActionResponse>;
66
+ getMetadata(params: { assetPath: string }): Promise<StandardActionResponse>;
67
+ getSourceControlState(params: { assetPath: string }): Promise<SourceControlState | StandardActionResponse>;
68
+ analyzeGraph(params: { assetPath: string; maxDepth?: number }): Promise<StandardActionResponse>;
69
+ createThumbnail(params: { assetPath: string; width?: number; height?: number }): Promise<StandardActionResponse>;
70
+ setTags(params: { assetPath: string; tags: string[] }): Promise<StandardActionResponse>;
71
+ generateReport(params: { directory: string; reportType?: string; outputPath?: string }): Promise<StandardActionResponse>;
72
+ validate(params: { assetPath: string }): Promise<StandardActionResponse>;
73
+ generateLODs(params: { assetPath: string; lodCount: number; reductionSettings?: Record<string, unknown> }): Promise<StandardActionResponse>;
74
74
  }
75
75
 
76
76
  export interface ISequenceTools {
77
- create(params: { name: string; path?: string; timeoutMs?: number }): Promise<any>;
78
- open(params: { path: string }): Promise<any>;
79
- addCamera(params: { spawnable?: boolean; path?: string }): Promise<any>;
80
- addActor(params: { actorName: string; createBinding?: boolean; path?: string }): Promise<any>;
81
- addActors(params: { actorNames: string[]; path?: string }): Promise<any>;
82
- removeActors(params: { actorNames: string[]; path?: string }): Promise<any>;
83
- getBindings(params: { path?: string }): Promise<any>;
84
- addSpawnableFromClass(params: { className: string; path?: string }): Promise<any>;
85
- play(params: { path?: string; startTime?: number; loopMode?: 'once' | 'loop' | 'pingpong' }): Promise<any>;
86
- pause(params?: { path?: string }): Promise<any>;
87
- stop(params?: { path?: string }): Promise<any>;
88
- setSequenceProperties(params: { path?: string; frameRate?: number; lengthInFrames?: number; playbackStart?: number; playbackEnd?: number }): Promise<any>;
89
- getSequenceProperties(params: { path?: string }): Promise<any>;
90
- setPlaybackSpeed(params: { speed: number; path?: string }): Promise<any>;
91
- list(params: { path?: string }): Promise<any>;
92
- duplicate(params: { path: string; destinationPath: string }): Promise<any>;
93
- rename(params: { path: string; newName: string }): Promise<any>;
94
- deleteSequence(params: { path: string }): Promise<any>;
95
- getMetadata(params: { path: string }): Promise<any>;
96
- listTracks(params: { path: string }): Promise<any>;
97
- setWorkRange(params: { path?: string; start: number; end: number }): Promise<any>;
77
+ create(params: { name: string; path?: string; timeoutMs?: number }): Promise<StandardActionResponse>;
78
+ open(params: { path: string }): Promise<StandardActionResponse>;
79
+ addCamera(params: { spawnable?: boolean; path?: string }): Promise<StandardActionResponse>;
80
+ addActor(params: { actorName: string; createBinding?: boolean; path?: string }): Promise<StandardActionResponse>;
81
+ addActors(params: { actorNames: string[]; path?: string }): Promise<StandardActionResponse>;
82
+ removeActors(params: { actorNames: string[]; path?: string }): Promise<StandardActionResponse>;
83
+ getBindings(params: { path?: string }): Promise<StandardActionResponse>;
84
+ addSpawnableFromClass(params: { className: string; path?: string }): Promise<StandardActionResponse>;
85
+ play(params: { path?: string; startTime?: number; loopMode?: 'once' | 'loop' | 'pingpong' }): Promise<StandardActionResponse>;
86
+ pause(params?: { path?: string }): Promise<StandardActionResponse>;
87
+ stop(params?: { path?: string }): Promise<StandardActionResponse>;
88
+ setSequenceProperties(params: { path?: string; frameRate?: number; lengthInFrames?: number; playbackStart?: number; playbackEnd?: number }): Promise<StandardActionResponse>;
89
+ getSequenceProperties(params: { path?: string }): Promise<StandardActionResponse>;
90
+ setPlaybackSpeed(params: { speed: number; path?: string }): Promise<StandardActionResponse>;
91
+ list(params: { path?: string }): Promise<StandardActionResponse>;
92
+ duplicate(params: { path: string; destinationPath: string }): Promise<StandardActionResponse>;
93
+ rename(params: { path: string; newName: string }): Promise<StandardActionResponse>;
94
+ deleteSequence(params: { path: string }): Promise<StandardActionResponse>;
95
+ getMetadata(params: { path: string }): Promise<StandardActionResponse>;
96
+ listTracks(params: { path: string }): Promise<StandardActionResponse>;
97
+ setWorkRange(params: { path?: string; start: number; end: number }): Promise<StandardActionResponse>;
98
98
  }
99
99
 
100
100
  export interface IAssetResources {
@@ -102,118 +102,118 @@ export interface IAssetResources {
102
102
  }
103
103
 
104
104
  export interface IBlueprintTools {
105
- createBlueprint(params: { name: string; blueprintType?: string; savePath?: string; parentClass?: string; properties?: Record<string, unknown>; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<any>;
106
- modifyConstructionScript(params: { blueprintPath: string; operations: any[]; compile?: boolean; save?: boolean; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<any>;
107
- addComponent(params: { blueprintName: string; componentType: string; componentName: string; attachTo?: string; transform?: Record<string, unknown>; properties?: Record<string, unknown>; compile?: boolean; save?: boolean; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<any>;
108
- waitForBlueprint(blueprintRef: string | string[], timeoutMs?: number): Promise<any>;
109
- getBlueprint(params: { blueprintName: string; timeoutMs?: number }): Promise<any>;
110
- getBlueprintInfo(params: { blueprintPath: string; timeoutMs?: number }): Promise<any>;
111
- probeSubobjectDataHandle(opts?: { componentClass?: string }): Promise<any>;
112
- setBlueprintDefault(params: { blueprintName: string; propertyName: string; value: unknown }): Promise<any>;
113
- addVariable(params: { blueprintName: string; variableName: string; variableType: string; defaultValue?: any; category?: string; isReplicated?: boolean; isPublic?: boolean; variablePinType?: Record<string, unknown>; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<any>;
114
- removeVariable(params: { blueprintName: string; variableName: string; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<any>;
115
- renameVariable(params: { blueprintName: string; oldName: string; newName: string; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<any>;
116
- addEvent(params: { blueprintName: string; eventType: string; customEventName?: string; parameters?: Array<{ name: string; type: string }>; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<any>;
117
- removeEvent(params: { blueprintName: string; eventName: string; customEventName?: string; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<any>;
118
- addFunction(params: { blueprintName: string; functionName: string; inputs?: Array<{ name: string; type: string }>; outputs?: Array<{ name: string; type: string }>; isPublic?: boolean; category?: string; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<any>;
119
- setVariableMetadata(params: { blueprintName: string; variableName: string; metadata: Record<string, unknown>; timeoutMs?: number }): Promise<any>;
120
- renameVariable(params: { blueprintName: string; oldName: string; newName: string; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<any>;
121
- addConstructionScript(params: { blueprintName: string; scriptName: string; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<any>;
122
- compileBlueprint(params: { blueprintName: string; saveAfterCompile?: boolean }): Promise<any>;
123
- getBlueprintSCS(params: { blueprintPath: string; timeoutMs?: number }): Promise<any>;
124
- addSCSComponent(params: { blueprintPath: string; componentClass: string; componentName: string; parentComponent?: string; meshPath?: string; materialPath?: string; timeoutMs?: number }): Promise<any>;
125
- removeSCSComponent(params: { blueprintPath: string; componentName: string; timeoutMs?: number }): Promise<any>;
126
- reparentSCSComponent(params: { blueprintPath: string; componentName: string; newParent: string; timeoutMs?: number }): Promise<any>;
127
- setSCSComponentTransform(params: { blueprintPath: string; componentName: string; location?: [number, number, number]; rotation?: [number, number, number]; scale?: [number, number, number]; timeoutMs?: number }): Promise<any>;
128
- setSCSComponentProperty(params: { blueprintPath: string; componentName: string; propertyName: string; propertyValue: any; timeoutMs?: number }): Promise<any>;
129
- addNode(params: { blueprintName: string; nodeType: string; graphName?: string; functionName?: string; variableName?: string; nodeName?: string; eventName?: string; memberClass?: string; posX?: number; posY?: number; timeoutMs?: number }): Promise<any>;
130
- connectPins(params: { blueprintName: string; sourceNodeGuid: string; targetNodeGuid: string; sourcePinName?: string; targetPinName?: string; timeoutMs?: number }): Promise<any>;
105
+ createBlueprint(params: { name: string; blueprintType?: string; savePath?: string; parentClass?: string; properties?: Record<string, unknown>; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<StandardActionResponse>;
106
+ modifyConstructionScript(params: { blueprintPath: string; operations: any[]; compile?: boolean; save?: boolean; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<StandardActionResponse>;
107
+ addComponent(params: { blueprintName: string; componentType: string; componentName: string; attachTo?: string; transform?: Record<string, unknown>; properties?: Record<string, unknown>; compile?: boolean; save?: boolean; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<StandardActionResponse>;
108
+ waitForBlueprint(blueprintRef: string | string[], timeoutMs?: number): Promise<StandardActionResponse>;
109
+ getBlueprint(params: { blueprintName: string; timeoutMs?: number }): Promise<StandardActionResponse>;
110
+ getBlueprintInfo(params: { blueprintPath: string; timeoutMs?: number }): Promise<StandardActionResponse>;
111
+ probeSubobjectDataHandle(opts?: { componentClass?: string }): Promise<StandardActionResponse>;
112
+ setBlueprintDefault(params: { blueprintName: string; propertyName: string; value: unknown }): Promise<StandardActionResponse>;
113
+ addVariable(params: { blueprintName: string; variableName: string; variableType: string; defaultValue?: any; category?: string; isReplicated?: boolean; isPublic?: boolean; variablePinType?: Record<string, unknown>; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<StandardActionResponse>;
114
+ removeVariable(params: { blueprintName: string; variableName: string; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<StandardActionResponse>;
115
+ renameVariable(params: { blueprintName: string; oldName: string; newName: string; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<StandardActionResponse>;
116
+ addEvent(params: { blueprintName: string; eventType: string; customEventName?: string; parameters?: Array<{ name: string; type: string }>; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<StandardActionResponse>;
117
+ removeEvent(params: { blueprintName: string; eventName: string; customEventName?: string; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<StandardActionResponse>;
118
+ addFunction(params: { blueprintName: string; functionName: string; inputs?: Array<{ name: string; type: string }>; outputs?: Array<{ name: string; type: string }>; isPublic?: boolean; category?: string; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<StandardActionResponse>;
119
+ setVariableMetadata(params: { blueprintName: string; variableName: string; metadata: Record<string, unknown>; timeoutMs?: number }): Promise<StandardActionResponse>;
120
+ renameVariable(params: { blueprintName: string; oldName: string; newName: string; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<StandardActionResponse>;
121
+ addConstructionScript(params: { blueprintName: string; scriptName: string; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<StandardActionResponse>;
122
+ compileBlueprint(params: { blueprintName: string; saveAfterCompile?: boolean }): Promise<StandardActionResponse>;
123
+ getBlueprintSCS(params: { blueprintPath: string; timeoutMs?: number }): Promise<StandardActionResponse>;
124
+ addSCSComponent(params: { blueprintPath: string; componentClass: string; componentName: string; parentComponent?: string; meshPath?: string; materialPath?: string; timeoutMs?: number }): Promise<StandardActionResponse>;
125
+ removeSCSComponent(params: { blueprintPath: string; componentName: string; timeoutMs?: number }): Promise<StandardActionResponse>;
126
+ reparentSCSComponent(params: { blueprintPath: string; componentName: string; newParent: string; timeoutMs?: number }): Promise<StandardActionResponse>;
127
+ setSCSComponentTransform(params: { blueprintPath: string; componentName: string; location?: [number, number, number]; rotation?: [number, number, number]; scale?: [number, number, number]; timeoutMs?: number }): Promise<StandardActionResponse>;
128
+ setSCSComponentProperty(params: { blueprintPath: string; componentName: string; propertyName: string; propertyValue: any; timeoutMs?: number }): Promise<StandardActionResponse>;
129
+ addNode(params: { blueprintName: string; nodeType: string; graphName?: string; functionName?: string; variableName?: string; nodeName?: string; eventName?: string; memberClass?: string; posX?: number; posY?: number; timeoutMs?: number }): Promise<StandardActionResponse>;
130
+ connectPins(params: { blueprintName: string; sourceNodeGuid: string; targetNodeGuid: string; sourcePinName?: string; targetPinName?: string; timeoutMs?: number }): Promise<StandardActionResponse>;
131
131
  }
132
132
 
133
133
  export interface ILevelTools {
134
- listLevels(): Promise<any>;
135
- getLevelSummary(levelPath?: string): Promise<any>;
134
+ listLevels(): Promise<StandardActionResponse>;
135
+ getLevelSummary(levelPath?: string): Promise<StandardActionResponse>;
136
136
  registerLight(levelPath: string | undefined, info: { name: string; type: string; details?: Record<string, unknown> }): void;
137
- exportLevel(params: { levelPath?: string; exportPath: string; note?: string; timeoutMs?: number }): Promise<any>;
138
- importLevel(params: { packagePath: string; destinationPath?: string; streaming?: boolean; timeoutMs?: number }): Promise<any>;
139
- saveLevelAs(params: { sourcePath?: string; targetPath: string }): Promise<any>;
140
- deleteLevels(params: { levelPaths: string[] }): Promise<any>;
141
- loadLevel(params: { levelPath: string; streaming?: boolean; position?: [number, number, number] }): Promise<any>;
142
- saveLevel(params: { levelName?: string; savePath?: string }): Promise<any>;
143
- createLevel(params: { levelName: string; template?: 'Empty' | 'Default' | 'VR' | 'TimeOfDay'; savePath?: string }): Promise<any>;
144
- addSubLevel(params: { parentLevel?: string; subLevelPath: string; streamingMethod?: 'Blueprint' | 'AlwaysLoaded' }): Promise<any>;
145
- streamLevel(params: { levelPath?: string; levelName?: string; shouldBeLoaded: boolean; shouldBeVisible?: boolean; position?: [number, number, number] }): Promise<any>;
146
- setupWorldComposition(params: { enableComposition: boolean; tileSize?: number; distanceStreaming?: boolean; streamingDistance?: number }): Promise<any>;
147
- editLevelBlueprint(params: { eventType: 'BeginPlay' | 'EndPlay' | 'Tick' | 'Custom'; customEventName?: string; nodes?: Array<{ nodeType: string; position: [number, number]; connections?: string[] }> }): Promise<any>;
148
- createSubLevel(params: { name: string; type: 'Persistent' | 'Streaming' | 'Lighting' | 'Gameplay'; parent?: string }): Promise<any>;
149
- setWorldSettings(params: { gravity?: number; worldScale?: number; gameMode?: string; defaultPawn?: string; killZ?: number }): Promise<any>;
150
- setLevelBounds(params: { min: [number, number, number]; max: [number, number, number] }): Promise<any>;
151
- buildNavMesh(params: { rebuildAll?: boolean; selectedOnly?: boolean }): Promise<any>;
152
- setLevelVisibility(params: { levelName: string; visible: boolean }): Promise<any>;
153
- setWorldOrigin(params: { location: [number, number, number] }): Promise<any>;
154
- createStreamingVolume(params: { levelName: string; position: [number, number, number]; size: [number, number, number]; streamingDistance?: number }): Promise<any>;
155
- setLevelLOD(params: { levelName: string; lodLevel: number; distance: number }): Promise<any>;
137
+ exportLevel(params: { levelPath?: string; exportPath: string; note?: string; timeoutMs?: number }): Promise<StandardActionResponse>;
138
+ importLevel(params: { packagePath: string; destinationPath?: string; streaming?: boolean; timeoutMs?: number }): Promise<StandardActionResponse>;
139
+ saveLevelAs(params: { sourcePath?: string; targetPath: string }): Promise<StandardActionResponse>;
140
+ deleteLevels(params: { levelPaths: string[] }): Promise<StandardActionResponse>;
141
+ loadLevel(params: { levelPath: string; streaming?: boolean; position?: [number, number, number] }): Promise<StandardActionResponse>;
142
+ saveLevel(params: { levelName?: string; savePath?: string }): Promise<StandardActionResponse>;
143
+ createLevel(params: { levelName: string; template?: 'Empty' | 'Default' | 'VR' | 'TimeOfDay'; savePath?: string }): Promise<StandardActionResponse>;
144
+ addSubLevel(params: { parentLevel?: string; subLevelPath: string; streamingMethod?: 'Blueprint' | 'AlwaysLoaded' }): Promise<StandardActionResponse>;
145
+ streamLevel(params: { levelPath?: string; levelName?: string; shouldBeLoaded: boolean; shouldBeVisible?: boolean; position?: [number, number, number] }): Promise<StandardActionResponse>;
146
+ setupWorldComposition(params: { enableComposition: boolean; tileSize?: number; distanceStreaming?: boolean; streamingDistance?: number }): Promise<StandardActionResponse>;
147
+ editLevelBlueprint(params: { eventType: 'BeginPlay' | 'EndPlay' | 'Tick' | 'Custom'; customEventName?: string; nodes?: Array<{ nodeType: string; position: [number, number]; connections?: string[] }> }): Promise<StandardActionResponse>;
148
+ createSubLevel(params: { name: string; type: 'Persistent' | 'Streaming' | 'Lighting' | 'Gameplay'; parent?: string }): Promise<StandardActionResponse>;
149
+ setWorldSettings(params: { gravity?: number; worldScale?: number; gameMode?: string; defaultPawn?: string; killZ?: number }): Promise<StandardActionResponse>;
150
+ setLevelBounds(params: { min: [number, number, number]; max: [number, number, number] }): Promise<StandardActionResponse>;
151
+ buildNavMesh(params: { rebuildAll?: boolean; selectedOnly?: boolean }): Promise<StandardActionResponse>;
152
+ setLevelVisibility(params: { levelName: string; visible: boolean }): Promise<StandardActionResponse>;
153
+ setWorldOrigin(params: { location: [number, number, number] }): Promise<StandardActionResponse>;
154
+ createStreamingVolume(params: { levelName: string; position: [number, number, number]; size: [number, number, number]; streamingDistance?: number }): Promise<StandardActionResponse>;
155
+ setLevelLOD(params: { levelName: string; lodLevel: number; distance: number }): Promise<StandardActionResponse>;
156
156
  }
157
157
 
158
158
  export interface IEditorTools {
159
159
  isInPIE(): Promise<boolean>;
160
160
  ensureNotInPIE(): Promise<void>;
161
- playInEditor(timeoutMs?: number): Promise<any>;
162
- stopPlayInEditor(): Promise<any>;
163
- pausePlayInEditor(): Promise<any>;
164
- pauseInEditor(): Promise<any>;
165
- buildLighting(): Promise<any>;
166
- setViewportCamera(location?: { x: number; y: number; z: number } | [number, number, number] | null | undefined, rotation?: { pitch: number; yaw: number; roll: number } | [number, number, number] | null | undefined): Promise<any>;
167
- setCameraSpeed(speed: number): Promise<any>;
168
- setFOV(fov: number): Promise<any>;
169
- takeScreenshot(filename?: string, resolution?: string): Promise<any>;
170
- resumePlayInEditor(): Promise<any>;
171
- stepPIEFrame(steps?: number): Promise<any>;
172
- startRecording(options?: { filename?: string; frameRate?: number; durationSeconds?: number; metadata?: Record<string, unknown> }): Promise<any>;
173
- stopRecording(): Promise<any>;
174
- createCameraBookmark(name: string): Promise<any>;
175
- jumpToCameraBookmark(name: string): Promise<any>;
176
- setEditorPreferences(category: string | undefined, preferences: Record<string, unknown>): Promise<any>;
177
- setViewportResolution(width: number, height: number): Promise<any>;
178
- executeConsoleCommand(command: string): Promise<any>;
161
+ playInEditor(timeoutMs?: number): Promise<StandardActionResponse>;
162
+ stopPlayInEditor(): Promise<StandardActionResponse>;
163
+ pausePlayInEditor(): Promise<StandardActionResponse>;
164
+ pauseInEditor(): Promise<StandardActionResponse>;
165
+ buildLighting(): Promise<StandardActionResponse>;
166
+ setViewportCamera(location?: { x: number; y: number; z: number } | [number, number, number] | null | undefined, rotation?: { pitch: number; yaw: number; roll: number } | [number, number, number] | null | undefined): Promise<StandardActionResponse>;
167
+ setCameraSpeed(speed: number): Promise<StandardActionResponse>;
168
+ setFOV(fov: number): Promise<StandardActionResponse>;
169
+ takeScreenshot(filename?: string, resolution?: string): Promise<StandardActionResponse>;
170
+ resumePlayInEditor(): Promise<StandardActionResponse>;
171
+ stepPIEFrame(steps?: number): Promise<StandardActionResponse>;
172
+ startRecording(options?: { filename?: string; frameRate?: number; durationSeconds?: number; metadata?: Record<string, unknown> }): Promise<StandardActionResponse>;
173
+ stopRecording(): Promise<StandardActionResponse>;
174
+ createCameraBookmark(name: string): Promise<StandardActionResponse>;
175
+ jumpToCameraBookmark(name: string): Promise<StandardActionResponse>;
176
+ setEditorPreferences(category: string | undefined, preferences: Record<string, unknown>): Promise<StandardActionResponse>;
177
+ setViewportResolution(width: number, height: number): Promise<StandardActionResponse>;
178
+ executeConsoleCommand(command: string): Promise<StandardActionResponse>;
179
179
  }
180
180
 
181
181
  export interface IEnvironmentTools {
182
- setTimeOfDay(hour: unknown): Promise<any>;
183
- setSunIntensity(intensity: unknown): Promise<any>;
184
- setSkylightIntensity(intensity: unknown): Promise<any>;
185
- exportSnapshot(params: { path?: unknown; filename?: unknown }): Promise<any>;
186
- importSnapshot(params: { path?: unknown; filename?: unknown }): Promise<any>;
187
- cleanup(params?: { names?: unknown }): Promise<any>;
182
+ setTimeOfDay(hour: unknown): Promise<StandardActionResponse>;
183
+ setSunIntensity(intensity: unknown): Promise<StandardActionResponse>;
184
+ setSkylightIntensity(intensity: unknown): Promise<StandardActionResponse>;
185
+ exportSnapshot(params: { path?: unknown; filename?: unknown }): Promise<StandardActionResponse>;
186
+ importSnapshot(params: { path?: unknown; filename?: unknown }): Promise<StandardActionResponse>;
187
+ cleanup(params?: { names?: unknown }): Promise<StandardActionResponse>;
188
188
  }
189
189
 
190
190
  export interface ILandscapeTools {
191
- createLandscape(params: { name: string; location?: [number, number, number]; sizeX?: number; sizeY?: number; quadsPerSection?: number; sectionsPerComponent?: number; componentCount?: number; materialPath?: string; enableWorldPartition?: boolean; runtimeGrid?: string; isSpatiallyLoaded?: boolean; dataLayers?: string[] }): Promise<any>;
192
- sculptLandscape(params: { landscapeName: string; tool: string; brushSize?: number; brushFalloff?: number; strength?: number; location?: [number, number, number]; radius?: number }): Promise<any>;
193
- paintLandscape(params: { landscapeName: string; layerName: string; position: [number, number, number]; brushSize?: number; strength?: number; targetValue?: number; radius?: number; density?: number }): Promise<any>;
194
- createProceduralTerrain(params: { name: string; location?: [number, number, number]; subdivisions?: number; heightFunction?: string; material?: string; settings?: Record<string, unknown> }): Promise<any>;
195
- createLandscapeGrassType(params: { name: string; meshPath: string; density?: number; minScale?: number; maxScale?: number; path?: string; staticMesh?: string }): Promise<any>;
196
- setLandscapeMaterial(params: { landscapeName: string; materialPath: string }): Promise<any>;
197
- modifyHeightmap(params: { landscapeName: string; heightData: number[]; minX: number; minY: number; maxX: number; maxY: number; updateNormals?: boolean }): Promise<any>;
191
+ createLandscape(params: { name: string; location?: [number, number, number]; sizeX?: number; sizeY?: number; quadsPerSection?: number; sectionsPerComponent?: number; componentCount?: number; materialPath?: string; enableWorldPartition?: boolean; runtimeGrid?: string; isSpatiallyLoaded?: boolean; dataLayers?: string[] }): Promise<StandardActionResponse>;
192
+ sculptLandscape(params: { landscapeName: string; tool: string; brushSize?: number; brushFalloff?: number; strength?: number; location?: [number, number, number]; radius?: number }): Promise<StandardActionResponse>;
193
+ paintLandscape(params: { landscapeName: string; layerName: string; position: [number, number, number]; brushSize?: number; strength?: number; targetValue?: number; radius?: number; density?: number }): Promise<StandardActionResponse>;
194
+ createProceduralTerrain(params: { name: string; location?: [number, number, number]; subdivisions?: number; heightFunction?: string; material?: string; settings?: Record<string, unknown> }): Promise<StandardActionResponse>;
195
+ createLandscapeGrassType(params: { name: string; meshPath: string; density?: number; minScale?: number; maxScale?: number; path?: string; staticMesh?: string }): Promise<StandardActionResponse>;
196
+ setLandscapeMaterial(params: { landscapeName: string; materialPath: string }): Promise<StandardActionResponse>;
197
+ modifyHeightmap(params: { landscapeName: string; heightData: number[]; minX: number; minY: number; maxX: number; maxY: number; updateNormals?: boolean }): Promise<StandardActionResponse>;
198
198
  }
199
199
 
200
200
  export interface IFoliageTools {
201
- addFoliageType(params: { name: string; meshPath: string; density?: number; radius?: number; minScale?: number; maxScale?: number; alignToNormal?: boolean; randomYaw?: boolean; groundSlope?: number }): Promise<any>;
202
- addFoliage(params: { foliageType: string; locations: Array<{ x: number; y: number; z: number }> }): Promise<any>;
203
- paintFoliage(params: { foliageType: string; position: [number, number, number]; brushSize?: number; paintDensity?: number; eraseMode?: boolean }): Promise<any>;
204
- createProceduralFoliage(params: { name: string; bounds?: { location: { x: number; y: number; z: number }; size: { x: number; y: number; z: number } }; foliageTypes?: Array<{ meshPath: string; density: number; minScale?: number; maxScale?: number; alignToNormal?: boolean; randomYaw?: boolean }>; volumeName?: string; position?: [number, number, number]; size?: [number, number, number]; seed?: number; tileSize?: number }): Promise<any>;
205
- addFoliageInstances(params: { foliageType: string; transforms: Array<{ location: [number, number, number]; rotation?: [number, number, number]; scale?: [number, number, number] }> }): Promise<any>;
206
- getFoliageInstances(params: { foliageType?: string }): Promise<any>;
207
- removeFoliage(params: { foliageType?: string; removeAll?: boolean }): Promise<any>;
208
- createInstancedMesh(params: { name: string; meshPath: string; instances: Array<{ position: [number, number, number]; rotation?: [number, number, number]; scale?: [number, number, number] }>; enableCulling?: boolean; cullDistance?: number }): Promise<any>;
209
- setFoliageLOD(params: { foliageType: string; lodDistances?: number[]; screenSize?: number[] }): Promise<any>;
210
- setFoliageCollision(params: { foliageType: string; collisionEnabled?: boolean; collisionProfile?: string; generateOverlapEvents?: boolean }): Promise<any>;
211
- createGrassSystem(params: { name: string; grassTypes: Array<{ meshPath: string; density: number; minScale?: number; maxScale?: number }>; windStrength?: number; windSpeed?: number }): Promise<any>;
212
- removeFoliageInstances(params: { foliageType: string; position: [number, number, number]; radius: number }): Promise<any>;
213
- selectFoliageInstances(params: { foliageType: string; position?: [number, number, number]; radius?: number; selectAll?: boolean }): Promise<any>;
214
- updateFoliageInstances(params: { foliageType: string; updateTransforms?: boolean; updateMesh?: boolean; newMeshPath?: string }): Promise<any>;
215
- createFoliageSpawner(params: { name: string; spawnArea: 'Landscape' | 'StaticMesh' | 'BSP' | 'Foliage' | 'All'; excludeAreas?: Array<[number, number, number, number]> }): Promise<any>;
216
- optimizeFoliage(params: { mergeInstances?: boolean; generateClusters?: boolean; clusterSize?: number; reduceDrawCalls?: boolean }): Promise<any>;
201
+ addFoliageType(params: { name: string; meshPath: string; density?: number; radius?: number; minScale?: number; maxScale?: number; alignToNormal?: boolean; randomYaw?: boolean; groundSlope?: number }): Promise<StandardActionResponse>;
202
+ addFoliage(params: { foliageType: string; locations: Array<{ x: number; y: number; z: number }> }): Promise<StandardActionResponse>;
203
+ paintFoliage(params: { foliageType: string; position: [number, number, number]; brushSize?: number; paintDensity?: number; eraseMode?: boolean }): Promise<StandardActionResponse>;
204
+ createProceduralFoliage(params: { name: string; bounds?: { location: { x: number; y: number; z: number }; size: { x: number; y: number; z: number } }; foliageTypes?: Array<{ meshPath: string; density: number; minScale?: number; maxScale?: number; alignToNormal?: boolean; randomYaw?: boolean }>; volumeName?: string; position?: [number, number, number]; size?: [number, number, number]; seed?: number; tileSize?: number }): Promise<StandardActionResponse>;
205
+ addFoliageInstances(params: { foliageType: string; transforms: Array<{ location: [number, number, number]; rotation?: [number, number, number]; scale?: [number, number, number] }> }): Promise<StandardActionResponse>;
206
+ getFoliageInstances(params: { foliageType?: string }): Promise<StandardActionResponse>;
207
+ removeFoliage(params: { foliageType?: string; removeAll?: boolean }): Promise<StandardActionResponse>;
208
+ createInstancedMesh(params: { name: string; meshPath: string; instances: Array<{ position: [number, number, number]; rotation?: [number, number, number]; scale?: [number, number, number] }>; enableCulling?: boolean; cullDistance?: number }): Promise<StandardActionResponse>;
209
+ setFoliageLOD(params: { foliageType: string; lodDistances?: number[]; screenSize?: number[] }): Promise<StandardActionResponse>;
210
+ setFoliageCollision(params: { foliageType: string; collisionEnabled?: boolean; collisionProfile?: string; generateOverlapEvents?: boolean }): Promise<StandardActionResponse>;
211
+ createGrassSystem(params: { name: string; grassTypes: Array<{ meshPath: string; density: number; minScale?: number; maxScale?: number }>; windStrength?: number; windSpeed?: number }): Promise<StandardActionResponse>;
212
+ removeFoliageInstances(params: { foliageType: string; position: [number, number, number]; radius: number }): Promise<StandardActionResponse>;
213
+ selectFoliageInstances(params: { foliageType: string; position?: [number, number, number]; radius?: number; selectAll?: boolean }): Promise<StandardActionResponse>;
214
+ updateFoliageInstances(params: { foliageType: string; updateTransforms?: boolean; updateMesh?: boolean; newMeshPath?: string }): Promise<StandardActionResponse>;
215
+ createFoliageSpawner(params: { name: string; spawnArea: 'Landscape' | 'StaticMesh' | 'BSP' | 'Foliage' | 'All'; excludeAreas?: Array<[number, number, number, number]> }): Promise<StandardActionResponse>;
216
+ optimizeFoliage(params: { mergeInstances?: boolean; generateClusters?: boolean; clusterSize?: number; reduceDrawCalls?: boolean }): Promise<StandardActionResponse>;
217
217
  }
218
218
 
219
219
  export interface ITools {
@@ -9,6 +9,10 @@ export interface BaseToolResponse {
9
9
  message?: string;
10
10
  error?: string;
11
11
  warning?: string;
12
+ /** Whether this error is retriable (e.g., connection failures) */
13
+ retriable?: boolean;
14
+ /** Scope/context for the error (e.g., 'tool-call/manage_asset') */
15
+ scope?: string;
12
16
  }
13
17
 
14
18
  // Asset Management Types
@@ -5,14 +5,59 @@ import { DEFAULT_AUTOMATION_HOST, DEFAULT_AUTOMATION_PORT } from './constants.js
5
5
  import { UnrealCommandQueue } from './utils/unreal-command-queue.js';
6
6
  import { CommandValidator } from './utils/command-validator.js';
7
7
 
8
+ /** Connection event payload for automation bridge events */
9
+ interface ConnectionEventInfo {
10
+ host?: string;
11
+ port?: number;
12
+ reason?: string;
13
+ error?: string;
14
+ [key: string]: unknown;
15
+ }
16
+
17
+ /** Result object from automation requests */
18
+ interface AutomationResult {
19
+ value?: unknown;
20
+ propertyValue?: unknown;
21
+ message?: string;
22
+ warnings?: string[];
23
+ [key: string]: unknown;
24
+ }
25
+
26
+ /** Subsystems feature flags */
27
+ interface SubsystemFlags {
28
+ unrealEditor?: boolean;
29
+ levelEditor?: boolean;
30
+ editorActor?: boolean;
31
+ [key: string]: unknown;
32
+ }
33
+
34
+ /** Engine version result */
35
+ interface EngineVersionResult {
36
+ version?: string;
37
+ major?: number;
38
+ minor?: number;
39
+ patch?: number;
40
+ isUE56OrAbove?: boolean;
41
+ [key: string]: unknown;
42
+ }
43
+
44
+ /** Console command response */
45
+ interface ConsoleCommandResponse {
46
+ success?: boolean;
47
+ message?: string;
48
+ error?: string;
49
+ transport?: string;
50
+ [key: string]: unknown;
51
+ }
52
+
8
53
  export class UnrealBridge {
9
54
  private log = new Logger('UnrealBridge');
10
55
  private connected = false;
11
56
  private automationBridge?: AutomationBridge;
12
57
  private automationBridgeListeners?: {
13
- connected: (info: any) => void;
14
- disconnected: (info: any) => void;
15
- handshakeFailed: (info: any) => void;
58
+ connected: (info: ConnectionEventInfo) => void;
59
+ disconnected: (info: ConnectionEventInfo) => void;
60
+ handshakeFailed: (info: ConnectionEventInfo) => void;
16
61
  };
17
62
 
18
63
  // Command queue for throttling
@@ -35,17 +80,17 @@ export class UnrealBridge {
35
80
  return;
36
81
  }
37
82
 
38
- const onConnected = (info: any) => {
83
+ const onConnected = (info: ConnectionEventInfo) => {
39
84
  this.connected = true;
40
85
  this.log.debug('Automation bridge connected', info);
41
86
  };
42
87
 
43
- const onDisconnected = (info: any) => {
88
+ const onDisconnected = (info: ConnectionEventInfo) => {
44
89
  this.connected = false;
45
90
  this.log.debug('Automation bridge disconnected', info);
46
91
  };
47
92
 
48
- const onHandshakeFailed = (info: any) => {
93
+ const onHandshakeFailed = (info: ConnectionEventInfo) => {
49
94
  this.connected = false;
50
95
  this.log.warn('Automation bridge handshake failed', info);
51
96
  };
@@ -279,13 +324,13 @@ export class UnrealBridge {
279
324
  );
280
325
 
281
326
  const success = response.success !== false;
282
- const rawResult =
327
+ const rawResult: AutomationResult | undefined =
283
328
  response.result && typeof response.result === 'object'
284
329
  ? { ...(response.result as Record<string, unknown>) }
285
- : response.result;
330
+ : undefined;
286
331
  const value =
287
- (rawResult as any)?.value ??
288
- (rawResult as any)?.propertyValue ??
332
+ rawResult?.value ??
333
+ rawResult?.propertyValue ??
289
334
  (success ? rawResult : undefined);
290
335
 
291
336
  if (success) {
@@ -297,8 +342,8 @@ export class UnrealBridge {
297
342
  propertyValue: value,
298
343
  transport: 'automation_bridge',
299
344
  message: response.message,
300
- warnings: Array.isArray((rawResult as any)?.warnings)
301
- ? (rawResult as any).warnings
345
+ warnings: Array.isArray(rawResult?.warnings)
346
+ ? rawResult.warnings
302
347
  : undefined,
303
348
  raw: rawResult,
304
349
  bridge: {
@@ -389,10 +434,10 @@ export class UnrealBridge {
389
434
  );
390
435
 
391
436
  const success = response.success !== false;
392
- const rawResult =
437
+ const rawResult: AutomationResult | undefined =
393
438
  response.result && typeof response.result === 'object'
394
439
  ? { ...(response.result as Record<string, unknown>) }
395
- : response.result;
440
+ : undefined;
396
441
 
397
442
  if (success) {
398
443
  return {
@@ -401,7 +446,7 @@ export class UnrealBridge {
401
446
  propertyName,
402
447
  message:
403
448
  response.message ||
404
- (typeof (rawResult as any)?.message === 'string' ? (rawResult as any).message : undefined),
449
+ (typeof rawResult?.message === 'string' ? rawResult.message : undefined),
405
450
  transport: 'automation_bridge',
406
451
  raw: rawResult,
407
452
  bridge: {
@@ -469,14 +514,14 @@ export class UnrealBridge {
469
514
  throw new Error('Automation bridge not connected');
470
515
  }
471
516
 
472
- const pluginResp: any = await this.automationBridge.sendAutomationRequest(
517
+ const pluginResp: ConsoleCommandResponse = await this.automationBridge.sendAutomationRequest(
473
518
  'console_command',
474
519
  { command: cmdTrimmed },
475
520
  { timeoutMs: 30000 }
476
521
  );
477
522
 
478
523
  if (pluginResp && pluginResp.success) {
479
- return { ...(pluginResp as any), transport: 'automation_bridge' };
524
+ return { ...pluginResp, transport: 'automation_bridge' };
480
525
  }
481
526
 
482
527
  const errMsg = pluginResp?.message || pluginResp?.error || 'Plugin execution failed';
@@ -553,14 +598,14 @@ export class UnrealBridge {
553
598
 
554
599
  const bridge = this.getAutomationBridge();
555
600
  try {
556
- const resp: any = await bridge.sendAutomationRequest(
601
+ const resp = await bridge.sendAutomationRequest(
557
602
  'system_control',
558
603
  { action: 'get_engine_version' },
559
604
  { timeoutMs: 15000 }
560
605
  );
561
- const raw = resp && typeof resp.result === 'object'
562
- ? (resp.result as any)
563
- : (resp?.result ?? resp ?? {});
606
+ const raw: EngineVersionResult = resp && typeof resp.result === 'object'
607
+ ? (resp.result as Record<string, unknown>)
608
+ : (resp?.result as Record<string, unknown>) ?? resp ?? {};
564
609
  const version = typeof raw.version === 'string' ? raw.version : 'unknown';
565
610
  const major = typeof raw.major === 'number' ? raw.major : 0;
566
611
  const minor = typeof raw.minor === 'number' ? raw.minor : 0;
@@ -596,16 +641,16 @@ export class UnrealBridge {
596
641
 
597
642
  const bridge = this.getAutomationBridge();
598
643
  try {
599
- const resp: any = await bridge.sendAutomationRequest(
644
+ const resp = await bridge.sendAutomationRequest(
600
645
  'system_control',
601
646
  { action: 'get_feature_flags' },
602
647
  { timeoutMs: 15000 }
603
648
  );
604
649
  const raw = resp && typeof resp.result === 'object'
605
- ? (resp.result as any)
606
- : (resp?.result ?? resp ?? {});
607
- const subs = raw && typeof raw.subsystems === 'object'
608
- ? (raw.subsystems as any)
650
+ ? (resp.result as Record<string, unknown>)
651
+ : (resp?.result as Record<string, unknown>) ?? resp ?? {};
652
+ const subs: SubsystemFlags = raw && typeof raw.subsystems === 'object'
653
+ ? (raw.subsystems as SubsystemFlags)
609
654
  : {};
610
655
  return {
611
656
  subsystems: {
@@ -1,27 +1,58 @@
1
+ /**
2
+ * Validates console commands before execution to prevent dangerous operations.
3
+ * Blocks crash-inducing commands, shell injection, and Python execution.
4
+ */
1
5
  export class CommandValidator {
6
+ /**
7
+ * Commands that can crash the engine or cause severe instability.
8
+ * These are blocked unconditionally.
9
+ */
2
10
  private static readonly DANGEROUS_COMMANDS = [
3
- 'quit', 'exit', 'delete', 'destroy', 'kill', 'crash',
11
+ // Engine termination commands
12
+ 'quit', 'exit', 'kill', 'crash',
13
+ // Crash-inducing commands
14
+ 'r.gpucrash', 'r.crash', 'debug crash', 'forcecrash', 'debug break',
15
+ 'assert false', 'check(false)',
16
+ // View buffer commands that can crash on some hardware
4
17
  'viewmode visualizebuffer basecolor',
5
18
  'viewmode visualizebuffer worldnormal',
6
- 'r.gpucrash',
7
- 'buildpaths', // Can cause access violation if nav system not initialized
8
- 'rebuildnavigation' // Can also crash without proper nav setup
19
+ // Heavy operations that can cause access violations if systems not initialized
20
+ 'buildpaths', 'rebuildnavigation',
21
+ // Heavy debug commands that can stall or crash
22
+ 'obj garbage', 'obj list', 'memreport',
23
+ // Potentially destructive without proper setup
24
+ 'delete', 'destroy'
9
25
  ];
10
26
 
27
+ /**
28
+ * Tokens that indicate shell injection or external system access attempts.
29
+ * Any command containing these is blocked.
30
+ */
11
31
  private static readonly FORBIDDEN_TOKENS = [
32
+ // Shell commands (Windows/Unix)
12
33
  'rm ', 'rm-', 'del ', 'format ', 'shutdown', 'reboot',
13
34
  'rmdir', 'mklink', 'copy ', 'move ', 'start "', 'system(',
35
+ // Python injection attempts
14
36
  'import os', 'import subprocess', 'subprocess.', 'os.system',
15
37
  'exec(', 'eval(', '__import__', 'import sys', 'import importlib',
16
- 'with open', 'open('
38
+ 'with open', 'open(', 'write(', 'read('
17
39
  ];
18
40
 
41
+ /**
42
+ * Patterns that indicate obviously invalid commands.
43
+ * Used to warn about likely typos or invalid input.
44
+ */
19
45
  private static readonly INVALID_PATTERNS = [
20
46
  /^\d+$/, // Just numbers
21
47
  /^invalid_command/i,
22
48
  /^this_is_not_a_valid/i,
23
49
  ];
24
50
 
51
+ /**
52
+ * Validates a console command for safety before execution.
53
+ * @param command - The console command string to validate
54
+ * @throws Error if the command is dangerous, contains forbidden tokens, or is invalid
55
+ */
25
56
  static validate(command: string): void {
26
57
  if (!command || typeof command !== 'string') {
27
58
  throw new Error('Invalid command: must be a non-empty string');
@@ -55,11 +86,22 @@ export class CommandValidator {
55
86
  }
56
87
  }
57
88
 
89
+ /**
90
+ * Check if a command looks like an obviously invalid or mistyped command.
91
+ * @param command - The command to check
92
+ * @returns true if the command matches known invalid patterns
93
+ */
58
94
  static isLikelyInvalid(command: string): boolean {
59
95
  const cmdTrimmed = command.trim();
60
96
  return this.INVALID_PATTERNS.some(pattern => pattern.test(cmdTrimmed));
61
97
  }
62
98
 
99
+ /**
100
+ * Get the priority level of a command for throttling purposes.
101
+ * Lower numbers indicate heavier operations that need more throttling.
102
+ * @param command - The command to evaluate
103
+ * @returns Priority level (1=heavy, 5=medium, 7=default, 8-9=light)
104
+ */
63
105
  static getPriority(command: string): number {
64
106
  if (command.includes('BuildLighting') || command.includes('BuildPaths')) {
65
107
  return 1; // Heavy operation