unreal-engine-mcp-server 0.4.6 → 0.5.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.
Files changed (438) hide show
  1. package/.env.example +26 -0
  2. package/.env.production +38 -7
  3. package/.eslintrc.json +0 -54
  4. package/.eslintrc.override.json +8 -0
  5. package/.github/ISSUE_TEMPLATE/bug_report.yml +94 -0
  6. package/.github/ISSUE_TEMPLATE/config.yml +8 -0
  7. package/.github/ISSUE_TEMPLATE/feature_request.yml +56 -0
  8. package/.github/copilot-instructions.md +478 -45
  9. package/.github/dependabot.yml +19 -0
  10. package/.github/labeler.yml +24 -0
  11. package/.github/labels.yml +70 -0
  12. package/.github/pull_request_template.md +42 -0
  13. package/.github/release-drafter.yml +148 -0
  14. package/.github/workflows/auto-merge.yml +38 -0
  15. package/.github/workflows/ci.yml +38 -0
  16. package/.github/workflows/dependency-review.yml +17 -0
  17. package/.github/workflows/gemini-issue-triage.yml +172 -0
  18. package/.github/workflows/greetings.yml +23 -0
  19. package/.github/workflows/labeler.yml +16 -0
  20. package/.github/workflows/links.yml +80 -0
  21. package/.github/workflows/pr-size-labeler.yml +137 -0
  22. package/.github/workflows/publish-mcp.yml +12 -7
  23. package/.github/workflows/release-drafter.yml +23 -0
  24. package/.github/workflows/release.yml +112 -0
  25. package/.github/workflows/semantic-pull-request.yml +35 -0
  26. package/.github/workflows/smoke-test.yml +36 -0
  27. package/.github/workflows/stale.yml +28 -0
  28. package/CHANGELOG.md +269 -22
  29. package/CONTRIBUTING.md +140 -0
  30. package/README.md +166 -72
  31. package/claude_desktop_config_example.json +7 -6
  32. package/dist/automation/bridge.d.ts +50 -0
  33. package/dist/automation/bridge.js +452 -0
  34. package/dist/automation/connection-manager.d.ts +23 -0
  35. package/dist/automation/connection-manager.js +107 -0
  36. package/dist/automation/handshake.d.ts +11 -0
  37. package/dist/automation/handshake.js +89 -0
  38. package/dist/automation/index.d.ts +3 -0
  39. package/dist/automation/index.js +3 -0
  40. package/dist/automation/message-handler.d.ts +12 -0
  41. package/dist/automation/message-handler.js +149 -0
  42. package/dist/automation/request-tracker.d.ts +25 -0
  43. package/dist/automation/request-tracker.js +98 -0
  44. package/dist/automation/types.d.ts +130 -0
  45. package/dist/automation/types.js +2 -0
  46. package/dist/cli.js +32 -5
  47. package/dist/config.d.ts +27 -0
  48. package/dist/config.js +60 -0
  49. package/dist/constants.d.ts +12 -0
  50. package/dist/constants.js +12 -0
  51. package/dist/graphql/resolvers.d.ts +268 -0
  52. package/dist/graphql/resolvers.js +743 -0
  53. package/dist/graphql/schema.d.ts +5 -0
  54. package/dist/graphql/schema.js +437 -0
  55. package/dist/graphql/server.d.ts +26 -0
  56. package/dist/graphql/server.js +115 -0
  57. package/dist/graphql/types.d.ts +7 -0
  58. package/dist/graphql/types.js +2 -0
  59. package/dist/handlers/resource-handlers.d.ts +20 -0
  60. package/dist/handlers/resource-handlers.js +180 -0
  61. package/dist/index.d.ts +31 -18
  62. package/dist/index.js +119 -604
  63. package/dist/prompts/index.js +4 -4
  64. package/dist/resources/actors.d.ts +17 -12
  65. package/dist/resources/actors.js +56 -76
  66. package/dist/resources/assets.d.ts +6 -14
  67. package/dist/resources/assets.js +115 -147
  68. package/dist/resources/levels.d.ts +13 -13
  69. package/dist/resources/levels.js +25 -34
  70. package/dist/server/resource-registry.d.ts +20 -0
  71. package/dist/server/resource-registry.js +37 -0
  72. package/dist/server/tool-registry.d.ts +23 -0
  73. package/dist/server/tool-registry.js +322 -0
  74. package/dist/server-setup.d.ts +21 -0
  75. package/dist/server-setup.js +111 -0
  76. package/dist/services/health-monitor.d.ts +34 -0
  77. package/dist/services/health-monitor.js +105 -0
  78. package/dist/services/metrics-server.d.ts +11 -0
  79. package/dist/services/metrics-server.js +105 -0
  80. package/dist/tools/actors.d.ts +147 -9
  81. package/dist/tools/actors.js +350 -311
  82. package/dist/tools/animation.d.ts +135 -4
  83. package/dist/tools/animation.js +510 -411
  84. package/dist/tools/assets.d.ts +117 -19
  85. package/dist/tools/assets.js +259 -284
  86. package/dist/tools/audio.d.ts +102 -42
  87. package/dist/tools/audio.js +272 -685
  88. package/dist/tools/base-tool.d.ts +17 -0
  89. package/dist/tools/base-tool.js +46 -0
  90. package/dist/tools/behavior-tree.d.ts +94 -0
  91. package/dist/tools/behavior-tree.js +39 -0
  92. package/dist/tools/blueprint/helpers.d.ts +29 -0
  93. package/dist/tools/blueprint/helpers.js +182 -0
  94. package/dist/tools/blueprint.d.ts +228 -118
  95. package/dist/tools/blueprint.js +685 -832
  96. package/dist/tools/consolidated-tool-definitions.d.ts +5475 -1627
  97. package/dist/tools/consolidated-tool-definitions.js +829 -482
  98. package/dist/tools/consolidated-tool-handlers.d.ts +2 -1
  99. package/dist/tools/consolidated-tool-handlers.js +211 -1009
  100. package/dist/tools/debug.d.ts +143 -85
  101. package/dist/tools/debug.js +234 -180
  102. package/dist/tools/dynamic-handler-registry.d.ts +11 -0
  103. package/dist/tools/dynamic-handler-registry.js +101 -0
  104. package/dist/tools/editor.d.ts +139 -18
  105. package/dist/tools/editor.js +239 -244
  106. package/dist/tools/engine.d.ts +10 -4
  107. package/dist/tools/engine.js +13 -5
  108. package/dist/tools/environment.d.ts +36 -0
  109. package/dist/tools/environment.js +267 -0
  110. package/dist/tools/foliage.d.ts +105 -14
  111. package/dist/tools/foliage.js +219 -331
  112. package/dist/tools/handlers/actor-handlers.d.ts +3 -0
  113. package/dist/tools/handlers/actor-handlers.js +232 -0
  114. package/dist/tools/handlers/animation-handlers.d.ts +3 -0
  115. package/dist/tools/handlers/animation-handlers.js +185 -0
  116. package/dist/tools/handlers/argument-helper.d.ts +16 -0
  117. package/dist/tools/handlers/argument-helper.js +80 -0
  118. package/dist/tools/handlers/asset-handlers.d.ts +3 -0
  119. package/dist/tools/handlers/asset-handlers.js +496 -0
  120. package/dist/tools/handlers/audio-handlers.d.ts +3 -0
  121. package/dist/tools/handlers/audio-handlers.js +166 -0
  122. package/dist/tools/handlers/blueprint-handlers.d.ts +4 -0
  123. package/dist/tools/handlers/blueprint-handlers.js +358 -0
  124. package/dist/tools/handlers/common-handlers.d.ts +14 -0
  125. package/dist/tools/handlers/common-handlers.js +56 -0
  126. package/dist/tools/handlers/editor-handlers.d.ts +3 -0
  127. package/dist/tools/handlers/editor-handlers.js +119 -0
  128. package/dist/tools/handlers/effect-handlers.d.ts +3 -0
  129. package/dist/tools/handlers/effect-handlers.js +171 -0
  130. package/dist/tools/handlers/environment-handlers.d.ts +3 -0
  131. package/dist/tools/handlers/environment-handlers.js +170 -0
  132. package/dist/tools/handlers/graph-handlers.d.ts +3 -0
  133. package/dist/tools/handlers/graph-handlers.js +90 -0
  134. package/dist/tools/handlers/input-handlers.d.ts +3 -0
  135. package/dist/tools/handlers/input-handlers.js +21 -0
  136. package/dist/tools/handlers/inspect-handlers.d.ts +3 -0
  137. package/dist/tools/handlers/inspect-handlers.js +383 -0
  138. package/dist/tools/handlers/level-handlers.d.ts +3 -0
  139. package/dist/tools/handlers/level-handlers.js +237 -0
  140. package/dist/tools/handlers/lighting-handlers.d.ts +3 -0
  141. package/dist/tools/handlers/lighting-handlers.js +144 -0
  142. package/dist/tools/handlers/performance-handlers.d.ts +3 -0
  143. package/dist/tools/handlers/performance-handlers.js +130 -0
  144. package/dist/tools/handlers/pipeline-handlers.d.ts +3 -0
  145. package/dist/tools/handlers/pipeline-handlers.js +110 -0
  146. package/dist/tools/handlers/sequence-handlers.d.ts +3 -0
  147. package/dist/tools/handlers/sequence-handlers.js +376 -0
  148. package/dist/tools/handlers/system-handlers.d.ts +4 -0
  149. package/dist/tools/handlers/system-handlers.js +506 -0
  150. package/dist/tools/input.d.ts +19 -0
  151. package/dist/tools/input.js +89 -0
  152. package/dist/tools/introspection.d.ts +103 -40
  153. package/dist/tools/introspection.js +425 -568
  154. package/dist/tools/landscape.d.ts +97 -36
  155. package/dist/tools/landscape.js +280 -409
  156. package/dist/tools/level.d.ts +130 -10
  157. package/dist/tools/level.js +639 -675
  158. package/dist/tools/lighting.d.ts +77 -38
  159. package/dist/tools/lighting.js +441 -943
  160. package/dist/tools/logs.d.ts +45 -0
  161. package/dist/tools/logs.js +210 -0
  162. package/dist/tools/materials.d.ts +91 -24
  163. package/dist/tools/materials.js +190 -118
  164. package/dist/tools/niagara.d.ts +149 -39
  165. package/dist/tools/niagara.js +232 -182
  166. package/dist/tools/performance.d.ts +27 -12
  167. package/dist/tools/performance.js +204 -122
  168. package/dist/tools/physics.d.ts +32 -77
  169. package/dist/tools/physics.js +171 -582
  170. package/dist/tools/property-dictionary.d.ts +13 -0
  171. package/dist/tools/property-dictionary.js +82 -0
  172. package/dist/tools/sequence.d.ts +73 -48
  173. package/dist/tools/sequence.js +196 -748
  174. package/dist/tools/tool-definition-utils.d.ts +59 -0
  175. package/dist/tools/tool-definition-utils.js +35 -0
  176. package/dist/tools/ui.d.ts +66 -34
  177. package/dist/tools/ui.js +134 -214
  178. package/dist/types/env.d.ts +0 -3
  179. package/dist/types/env.js +0 -7
  180. package/dist/types/tool-interfaces.d.ts +898 -0
  181. package/dist/types/tool-interfaces.js +2 -0
  182. package/dist/types/tool-types.d.ts +195 -11
  183. package/dist/types/tool-types.js +0 -4
  184. package/dist/unreal-bridge.d.ts +24 -131
  185. package/dist/unreal-bridge.js +364 -1506
  186. package/dist/utils/command-validator.d.ts +9 -0
  187. package/dist/utils/command-validator.js +67 -0
  188. package/dist/utils/elicitation.d.ts +1 -1
  189. package/dist/utils/elicitation.js +12 -15
  190. package/dist/utils/error-handler.d.ts +2 -51
  191. package/dist/utils/error-handler.js +11 -87
  192. package/dist/utils/ini-reader.d.ts +3 -0
  193. package/dist/utils/ini-reader.js +69 -0
  194. package/dist/utils/logger.js +9 -6
  195. package/dist/utils/normalize.d.ts +3 -0
  196. package/dist/utils/normalize.js +56 -0
  197. package/dist/utils/response-factory.d.ts +7 -0
  198. package/dist/utils/response-factory.js +33 -0
  199. package/dist/utils/response-validator.d.ts +3 -24
  200. package/dist/utils/response-validator.js +130 -81
  201. package/dist/utils/result-helpers.d.ts +4 -5
  202. package/dist/utils/result-helpers.js +15 -16
  203. package/dist/utils/safe-json.js +5 -11
  204. package/dist/utils/unreal-command-queue.d.ts +24 -0
  205. package/dist/utils/unreal-command-queue.js +120 -0
  206. package/dist/utils/validation.d.ts +0 -40
  207. package/dist/utils/validation.js +1 -78
  208. package/dist/wasm/index.d.ts +70 -0
  209. package/dist/wasm/index.js +535 -0
  210. package/docs/GraphQL-API.md +888 -0
  211. package/docs/Migration-Guide-v0.5.0.md +692 -0
  212. package/docs/Roadmap.md +53 -0
  213. package/docs/WebAssembly-Integration.md +628 -0
  214. package/docs/editor-plugin-extension.md +370 -0
  215. package/docs/handler-mapping.md +242 -0
  216. package/docs/native-automation-progress.md +128 -0
  217. package/docs/testing-guide.md +423 -0
  218. package/mcp-config-example.json +6 -6
  219. package/package.json +60 -27
  220. package/plugins/McpAutomationBridge/Config/FilterPlugin.ini +8 -0
  221. package/plugins/McpAutomationBridge/McpAutomationBridge.uplugin +64 -0
  222. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/McpAutomationBridge.Build.cs +189 -0
  223. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.cpp +22 -0
  224. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.h +30 -0
  225. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeHelpers.h +1983 -0
  226. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeModule.cpp +72 -0
  227. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSettings.cpp +46 -0
  228. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSubsystem.cpp +581 -0
  229. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AnimationHandlers.cpp +2394 -0
  230. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetQueryHandlers.cpp +300 -0
  231. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetWorkflowHandlers.cpp +2807 -0
  232. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AudioHandlers.cpp +1087 -0
  233. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BehaviorTreeHandlers.cpp +488 -0
  234. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.cpp +643 -0
  235. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.h +31 -0
  236. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintGraphHandlers.cpp +1184 -0
  237. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers.cpp +5652 -0
  238. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers_List.cpp +152 -0
  239. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ControlHandlers.cpp +2614 -0
  240. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_DebugHandlers.cpp +42 -0
  241. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EditorFunctionHandlers.cpp +1237 -0
  242. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EffectHandlers.cpp +1701 -0
  243. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EnvironmentHandlers.cpp +2145 -0
  244. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_FoliageHandlers.cpp +954 -0
  245. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InputHandlers.cpp +209 -0
  246. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InsightsHandlers.cpp +41 -0
  247. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LandscapeHandlers.cpp +1164 -0
  248. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LevelHandlers.cpp +762 -0
  249. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LightingHandlers.cpp +634 -0
  250. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LogHandlers.cpp +136 -0
  251. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MaterialGraphHandlers.cpp +494 -0
  252. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraGraphHandlers.cpp +278 -0
  253. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraHandlers.cpp +625 -0
  254. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PerformanceHandlers.cpp +401 -0
  255. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PipelineHandlers.cpp +67 -0
  256. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ProcessRequest.cpp +735 -0
  257. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PropertyHandlers.cpp +2634 -0
  258. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_RenderHandlers.cpp +189 -0
  259. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.cpp +917 -0
  260. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.h +39 -0
  261. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequenceHandlers.cpp +2670 -0
  262. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequencerHandlers.cpp +519 -0
  263. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_TestHandlers.cpp +38 -0
  264. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_UiHandlers.cpp +668 -0
  265. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_WorldPartitionHandlers.cpp +346 -0
  266. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.cpp +1330 -0
  267. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.h +149 -0
  268. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpConnectionManager.cpp +783 -0
  269. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSettings.h +115 -0
  270. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSubsystem.h +796 -0
  271. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpConnectionManager.h +117 -0
  272. package/scripts/check-unreal-connection.mjs +19 -0
  273. package/scripts/clean-tmp.js +23 -0
  274. package/scripts/patch-wasm.js +26 -0
  275. package/scripts/run-all-tests.mjs +131 -0
  276. package/scripts/smoke-test.ts +94 -0
  277. package/scripts/sync-mcp-plugin.js +143 -0
  278. package/scripts/test-no-plugin-alternates.mjs +113 -0
  279. package/scripts/validate-server.js +46 -0
  280. package/scripts/verify-automation-bridge.js +200 -0
  281. package/server.json +57 -21
  282. package/src/automation/bridge.ts +558 -0
  283. package/src/automation/connection-manager.ts +130 -0
  284. package/src/automation/handshake.ts +99 -0
  285. package/src/automation/index.ts +2 -0
  286. package/src/automation/message-handler.ts +167 -0
  287. package/src/automation/request-tracker.ts +123 -0
  288. package/src/automation/types.ts +107 -0
  289. package/src/cli.ts +33 -6
  290. package/src/config.ts +73 -0
  291. package/src/constants.ts +12 -0
  292. package/src/graphql/resolvers.ts +1010 -0
  293. package/src/graphql/schema.ts +452 -0
  294. package/src/graphql/server.ts +154 -0
  295. package/src/graphql/types.ts +7 -0
  296. package/src/handlers/resource-handlers.ts +186 -0
  297. package/src/index.ts +152 -649
  298. package/src/prompts/index.ts +4 -4
  299. package/src/resources/actors.ts +58 -76
  300. package/src/resources/assets.ts +147 -134
  301. package/src/resources/levels.ts +28 -33
  302. package/src/server/resource-registry.ts +47 -0
  303. package/src/server/tool-registry.ts +354 -0
  304. package/src/server-setup.ts +148 -0
  305. package/src/services/health-monitor.ts +132 -0
  306. package/src/services/metrics-server.ts +142 -0
  307. package/src/tools/actors.ts +417 -322
  308. package/src/tools/animation.ts +671 -461
  309. package/src/tools/assets.ts +353 -289
  310. package/src/tools/audio.ts +323 -766
  311. package/src/tools/base-tool.ts +52 -0
  312. package/src/tools/behavior-tree.ts +45 -0
  313. package/src/tools/blueprint/helpers.ts +189 -0
  314. package/src/tools/blueprint.ts +787 -965
  315. package/src/tools/consolidated-tool-definitions.ts +993 -500
  316. package/src/tools/consolidated-tool-handlers.ts +272 -1122
  317. package/src/tools/debug.ts +292 -187
  318. package/src/tools/dynamic-handler-registry.ts +151 -0
  319. package/src/tools/editor.ts +309 -246
  320. package/src/tools/engine.ts +14 -3
  321. package/src/tools/environment.ts +287 -0
  322. package/src/tools/foliage.ts +314 -379
  323. package/src/tools/handlers/actor-handlers.ts +271 -0
  324. package/src/tools/handlers/animation-handlers.ts +237 -0
  325. package/src/tools/handlers/argument-helper.ts +142 -0
  326. package/src/tools/handlers/asset-handlers.ts +532 -0
  327. package/src/tools/handlers/audio-handlers.ts +194 -0
  328. package/src/tools/handlers/blueprint-handlers.ts +380 -0
  329. package/src/tools/handlers/common-handlers.ts +87 -0
  330. package/src/tools/handlers/editor-handlers.ts +123 -0
  331. package/src/tools/handlers/effect-handlers.ts +220 -0
  332. package/src/tools/handlers/environment-handlers.ts +183 -0
  333. package/src/tools/handlers/graph-handlers.ts +116 -0
  334. package/src/tools/handlers/input-handlers.ts +28 -0
  335. package/src/tools/handlers/inspect-handlers.ts +450 -0
  336. package/src/tools/handlers/level-handlers.ts +252 -0
  337. package/src/tools/handlers/lighting-handlers.ts +147 -0
  338. package/src/tools/handlers/performance-handlers.ts +132 -0
  339. package/src/tools/handlers/pipeline-handlers.ts +127 -0
  340. package/src/tools/handlers/sequence-handlers.ts +415 -0
  341. package/src/tools/handlers/system-handlers.ts +564 -0
  342. package/src/tools/input.ts +101 -0
  343. package/src/tools/introspection.ts +493 -584
  344. package/src/tools/landscape.ts +394 -489
  345. package/src/tools/level.ts +752 -694
  346. package/src/tools/lighting.ts +583 -984
  347. package/src/tools/logs.ts +219 -0
  348. package/src/tools/materials.ts +231 -121
  349. package/src/tools/niagara.ts +293 -168
  350. package/src/tools/performance.ts +320 -168
  351. package/src/tools/physics.ts +268 -613
  352. package/src/tools/property-dictionary.ts +98 -0
  353. package/src/tools/sequence.ts +255 -815
  354. package/src/tools/tool-definition-utils.ts +35 -0
  355. package/src/tools/ui.ts +207 -283
  356. package/src/types/env.ts +0 -10
  357. package/src/types/tool-interfaces.ts +250 -0
  358. package/src/types/tool-types.ts +250 -13
  359. package/src/unreal-bridge.ts +460 -1550
  360. package/src/utils/command-validator.ts +75 -0
  361. package/src/utils/elicitation.ts +10 -7
  362. package/src/utils/error-handler.ts +14 -90
  363. package/src/utils/ini-reader.ts +86 -0
  364. package/src/utils/logger.ts +8 -3
  365. package/src/utils/normalize.ts +60 -0
  366. package/src/utils/response-factory.ts +39 -0
  367. package/src/utils/response-validator.ts +176 -56
  368. package/src/utils/result-helpers.ts +21 -19
  369. package/src/utils/safe-json.ts +14 -11
  370. package/src/utils/unreal-command-queue.ts +152 -0
  371. package/src/utils/validation.ts +4 -1
  372. package/src/wasm/index.ts +838 -0
  373. package/test-server.mjs +100 -0
  374. package/tests/run-unreal-tool-tests.mjs +242 -14
  375. package/tests/test-animation.mjs +44 -0
  376. package/tests/test-asset-advanced.mjs +82 -0
  377. package/tests/test-asset-errors.mjs +35 -0
  378. package/tests/test-audio.mjs +219 -0
  379. package/tests/test-automation-timeouts.mjs +98 -0
  380. package/tests/test-behavior-tree.mjs +261 -0
  381. package/tests/test-blueprint-events.mjs +35 -0
  382. package/tests/test-blueprint-graph.mjs +79 -0
  383. package/tests/test-blueprint.mjs +577 -0
  384. package/tests/test-client-mode.mjs +86 -0
  385. package/tests/test-console-command.mjs +56 -0
  386. package/tests/test-control-actor.mjs +425 -0
  387. package/tests/test-control-editor.mjs +80 -0
  388. package/tests/test-extra-tools.mjs +38 -0
  389. package/tests/test-graphql.mjs +322 -0
  390. package/tests/test-inspect.mjs +72 -0
  391. package/tests/test-landscape.mjs +60 -0
  392. package/tests/test-manage-asset.mjs +438 -0
  393. package/tests/test-manage-level.mjs +70 -0
  394. package/tests/test-materials.mjs +356 -0
  395. package/tests/test-niagara.mjs +185 -0
  396. package/tests/test-no-inline-python.mjs +122 -0
  397. package/tests/test-plugin-handshake.mjs +82 -0
  398. package/tests/test-render.mjs +33 -0
  399. package/tests/test-runner.mjs +933 -0
  400. package/tests/test-search-assets.mjs +66 -0
  401. package/tests/test-sequence.mjs +68 -0
  402. package/tests/test-system.mjs +57 -0
  403. package/tests/test-wasm.mjs +193 -0
  404. package/tests/test-world-partition.mjs +215 -0
  405. package/tsconfig.json +3 -3
  406. package/wasm/Cargo.lock +363 -0
  407. package/wasm/Cargo.toml +42 -0
  408. package/wasm/LICENSE +21 -0
  409. package/wasm/README.md +253 -0
  410. package/wasm/src/dependency_resolver.rs +377 -0
  411. package/wasm/src/lib.rs +153 -0
  412. package/wasm/src/property_parser.rs +271 -0
  413. package/wasm/src/transform_math.rs +396 -0
  414. package/wasm/tests/integration.rs +109 -0
  415. package/.github/workflows/smithery-build.yml +0 -29
  416. package/dist/tools/build_environment_advanced.d.ts +0 -65
  417. package/dist/tools/build_environment_advanced.js +0 -633
  418. package/dist/tools/rc.d.ts +0 -110
  419. package/dist/tools/rc.js +0 -437
  420. package/dist/tools/visual.d.ts +0 -40
  421. package/dist/tools/visual.js +0 -282
  422. package/dist/utils/http.d.ts +0 -6
  423. package/dist/utils/http.js +0 -151
  424. package/dist/utils/python-output.d.ts +0 -18
  425. package/dist/utils/python-output.js +0 -290
  426. package/dist/utils/python.d.ts +0 -2
  427. package/dist/utils/python.js +0 -4
  428. package/dist/utils/stdio-redirect.d.ts +0 -2
  429. package/dist/utils/stdio-redirect.js +0 -20
  430. package/docs/unreal-tool-test-cases.md +0 -572
  431. package/smithery.yaml +0 -29
  432. package/src/tools/build_environment_advanced.ts +0 -732
  433. package/src/tools/rc.ts +0 -515
  434. package/src/tools/visual.ts +0 -281
  435. package/src/utils/http.ts +0 -187
  436. package/src/utils/python-output.ts +0 -351
  437. package/src/utils/python.ts +0 -3
  438. package/src/utils/stdio-redirect.ts +0 -18
@@ -1,903 +1,756 @@
1
+ import { BaseTool } from './base-tool.js';
2
+ import { Logger } from '../utils/logger.js';
1
3
  import { validateAssetParams, concurrencyDelay } from '../utils/validation.js';
2
- import { extractTaggedLine } from '../utils/python-output.js';
3
- import { interpretStandardResult, coerceBoolean, coerceString, coerceStringArray, bestEffortInterpretedText } from '../utils/result-helpers.js';
4
- import { escapePythonString } from '../utils/python.js';
5
- export class BlueprintTools {
6
- bridge;
7
- constructor(bridge) {
8
- this.bridge = bridge;
9
- }
10
- async validateParentClassReference(parentClass, blueprintType) {
11
- const trimmed = parentClass?.trim();
12
- if (!trimmed) {
13
- return { ok: true };
14
- }
15
- const escapedParent = escapePythonString(trimmed);
16
- const python = `
17
- import unreal
18
- import json
19
-
20
- result = {
21
- 'success': False,
22
- 'resolved': '',
23
- 'error': ''
24
- }
25
-
26
- def resolve_parent(spec, bp_type):
27
- name = (spec or '').strip()
28
- editor_lib = unreal.EditorAssetLibrary
29
- if not name:
30
- return None
31
- try:
32
- if name.startswith('/Script/'):
33
- return unreal.load_class(None, name)
34
- except Exception:
35
- pass
36
- try:
37
- if name.startswith('/Game/'):
38
- asset = editor_lib.load_asset(name)
39
- if asset:
40
- if hasattr(asset, 'generated_class'):
41
- try:
42
- generated = asset.generated_class()
43
- if generated:
44
- return generated
45
- except Exception:
46
- pass
47
- return asset
48
- except Exception:
49
- pass
50
- try:
51
- candidate = getattr(unreal, name, None)
52
- if candidate:
53
- return candidate
54
- except Exception:
55
- pass
56
- return None
57
-
58
- try:
59
- parent_spec = r"${escapedParent}"
60
- resolved = resolve_parent(parent_spec, "${blueprintType}")
61
- resolved_path = ''
62
-
63
- if resolved:
64
- try:
65
- resolved_path = resolved.get_path_name()
66
- except Exception:
67
- try:
68
- resolved_path = str(resolved.get_outer().get_path_name())
69
- except Exception:
70
- resolved_path = str(resolved)
71
-
72
- normalized_resolved = resolved_path.replace('Class ', '').replace('class ', '').strip().lower()
73
- normalized_spec = parent_spec.strip().lower()
74
-
75
- if normalized_spec.startswith('/script/'):
76
- if not normalized_resolved.endswith(normalized_spec):
77
- resolved = None
78
- elif normalized_spec.startswith('/game/'):
79
- try:
80
- if not unreal.EditorAssetLibrary.does_asset_exist(parent_spec):
81
- resolved = None
82
- except Exception:
83
- resolved = None
84
-
85
- if resolved:
86
- result['success'] = True
87
- try:
88
- result['resolved'] = resolved_path or str(resolved)
89
- except Exception:
90
- result['resolved'] = str(resolved)
91
- else:
92
- result['error'] = 'Parent class not found: ' + parent_spec
93
- except Exception as e:
94
- result['error'] = str(e)
95
-
96
- print('RESULT:' + json.dumps(result))
97
- `.trim();
4
+ import { coerceString } from '../utils/result-helpers.js';
5
+ export class BlueprintTools extends BaseTool {
6
+ log = new Logger('BlueprintTools');
7
+ pluginBlueprintActionsAvailable = null;
8
+ async sendAction(action, payload = {}, options) {
9
+ const envDefault = Number(process.env.MCP_AUTOMATION_REQUEST_TIMEOUT_MS ?? '120000');
10
+ const defaultTimeout = Number.isFinite(envDefault) && envDefault > 0 ? envDefault : 120000;
11
+ const finalTimeout = typeof options?.timeoutMs === 'number' && options?.timeoutMs > 0 ? options.timeoutMs : defaultTimeout;
98
12
  try {
99
- const response = await this.bridge.executePython(python);
100
- const interpreted = interpretStandardResult(response, {
101
- successMessage: 'Parent class resolved',
102
- failureMessage: 'Parent class validation failed'
103
- });
104
- if (interpreted.success) {
105
- return { ok: true, resolved: interpreted.payload?.resolved ?? interpreted.message };
106
- }
107
- const error = interpreted.error || interpreted.payload?.error || `Parent class not found: ${trimmed}`;
108
- return { ok: false, error };
13
+ const response = await this.sendAutomationRequest(action, payload, { timeoutMs: finalTimeout, waitForEvent: !!options?.waitForEvent, waitForEventTimeoutMs: options?.waitForEventTimeoutMs });
14
+ const success = response && response.success !== false;
15
+ const result = response.result ?? response;
16
+ return { success, message: response.message ?? undefined, error: response.success === false ? (response.error ?? response.message) : undefined, result, requestId: response.requestId };
109
17
  }
110
18
  catch (err) {
111
- return { ok: false, error: err?.message || String(err) };
19
+ return { success: false, error: String(err), message: String(err) };
112
20
  }
113
21
  }
114
- /**
115
- * Create Blueprint
116
- */
22
+ isUnknownActionResponse(res) {
23
+ if (!res)
24
+ return false;
25
+ const txt = String((res.error ?? res.message ?? '')).toLowerCase();
26
+ return txt.includes('unknown_action') || txt.includes('unknown automation action') || txt.includes('not_implemented') || txt === 'unknown_plugin_action';
27
+ }
28
+ buildCandidates(rawName) {
29
+ const trimmed = coerceString(rawName)?.trim();
30
+ if (!trimmed)
31
+ return [];
32
+ const normalized = trimmed.replace(/\\/g, '/').replace(/\/\/+/g, '/');
33
+ const withoutLeading = normalized.replace(/^\/+/, '');
34
+ const basename = withoutLeading.split('/').pop() ?? withoutLeading;
35
+ const candidates = [];
36
+ if (normalized.includes('/')) {
37
+ if (normalized.startsWith('/'))
38
+ candidates.push(normalized);
39
+ if (basename) {
40
+ candidates.push(`/Game/Blueprints/${basename}`);
41
+ candidates.push(`/Game/${basename}`);
42
+ }
43
+ if (!normalized.startsWith('/'))
44
+ candidates.push(`/${withoutLeading}`);
45
+ }
46
+ else {
47
+ if (basename) {
48
+ candidates.push(`/Game/Blueprints/${basename}`);
49
+ candidates.push(`/Game/${basename}`);
50
+ }
51
+ candidates.push(normalized);
52
+ candidates.push(`/${withoutLeading}`);
53
+ }
54
+ return candidates.filter(Boolean);
55
+ }
117
56
  async createBlueprint(params) {
118
57
  try {
119
- // Validate and sanitize parameters
120
- const validation = validateAssetParams({
121
- name: params.name,
122
- savePath: params.savePath || '/Game/Blueprints'
123
- });
124
- if (!validation.valid) {
125
- return {
126
- success: false,
127
- message: `Failed to create blueprint: ${validation.error}`,
128
- error: validation.error
129
- };
58
+ const validation = validateAssetParams({ name: params.name, savePath: params.savePath || '/Game/Blueprints' });
59
+ if (!validation.valid)
60
+ return { success: false, message: `Failed to create blueprint: ${validation.error}`, error: validation.error };
61
+ const sanitized = validation.sanitized;
62
+ const payload = { name: sanitized.name, blueprintType: params.blueprintType ?? 'Actor', savePath: sanitized.savePath ?? '/Game/Blueprints', parentClass: params.parentClass, properties: params.properties, waitForCompletion: !!params.waitForCompletion };
63
+ await concurrencyDelay();
64
+ if (this.pluginBlueprintActionsAvailable === false) {
65
+ return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement blueprint_create' };
130
66
  }
131
- const sanitizedParams = validation.sanitized;
132
- const path = sanitizedParams.savePath || '/Game/Blueprints';
133
- if (path.startsWith('/Engine')) {
134
- const message = `Failed to create blueprint: destination path ${path} is read-only`;
135
- return { success: false, message, error: message };
136
- }
137
- if (params.parentClass && params.parentClass.trim()) {
138
- const parentValidation = await this.validateParentClassReference(params.parentClass, params.blueprintType);
139
- if (!parentValidation.ok) {
140
- const error = parentValidation.error || `Parent class not found: ${params.parentClass}`;
141
- const message = `Failed to create blueprint: ${error}`;
142
- return { success: false, message, error };
67
+ const envPluginTimeout = Number(process.env.MCP_AUTOMATION_PLUGIN_CREATE_TIMEOUT_MS ?? process.env.MCP_AUTOMATION_REQUEST_TIMEOUT_MS ?? '15000');
68
+ const pluginTimeout = Number.isFinite(envPluginTimeout) && envPluginTimeout > 0 ? envPluginTimeout : 15000;
69
+ try {
70
+ const res = await this.sendAction('blueprint_create', payload, { timeoutMs: typeof params.timeoutMs === 'number' ? params.timeoutMs : pluginTimeout, waitForEvent: !!params.waitForCompletion, waitForEventTimeoutMs: params.waitForCompletionTimeoutMs });
71
+ if (res && res.success) {
72
+ this.pluginBlueprintActionsAvailable = true;
73
+ return {
74
+ ...res,
75
+ blueprint: sanitized.name,
76
+ path: `${sanitized.savePath}/${sanitized.name}`.replace('//', '/'),
77
+ message: res.message || `Created blueprint ${sanitized.name}`
78
+ };
79
+ }
80
+ if (res && this.isUnknownActionResponse(res)) {
81
+ this.pluginBlueprintActionsAvailable = false;
82
+ return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement blueprint_create' };
143
83
  }
84
+ return res;
85
+ }
86
+ catch (err) {
87
+ const errTxt = String(err ?? '');
88
+ const isTimeout = errTxt.includes('Request timed out') || errTxt.includes('-32001') || errTxt.toLowerCase().includes('timeout');
89
+ if (isTimeout) {
90
+ this.pluginBlueprintActionsAvailable = false;
91
+ }
92
+ return { success: false, error: String(err), message: String(err) };
144
93
  }
145
- const escapedName = escapePythonString(sanitizedParams.name);
146
- const escapedPath = escapePythonString(path);
147
- const escapedParent = escapePythonString(params.parentClass ?? '');
148
- await concurrencyDelay();
149
- const pythonScript = `
150
- import unreal
151
- import time
152
- import json
153
- import traceback
154
-
155
- def ensure_asset_persistence(asset_path):
156
- try:
157
- asset_subsystem = None
158
- try:
159
- asset_subsystem = unreal.get_editor_subsystem(unreal.EditorAssetSubsystem)
160
- except Exception:
161
- asset_subsystem = None
162
-
163
- editor_lib = unreal.EditorAssetLibrary
164
-
165
- asset = None
166
- if asset_subsystem and hasattr(asset_subsystem, 'load_asset'):
167
- try:
168
- asset = asset_subsystem.load_asset(asset_path)
169
- except Exception:
170
- asset = None
171
- if not asset:
172
- try:
173
- asset = editor_lib.load_asset(asset_path)
174
- except Exception:
175
- asset = None
176
- if not asset:
177
- return False
178
-
179
- saved = False
180
- if asset_subsystem and hasattr(asset_subsystem, 'save_loaded_asset'):
181
- try:
182
- saved = asset_subsystem.save_loaded_asset(asset)
183
- except Exception:
184
- saved = False
185
- if not saved and asset_subsystem and hasattr(asset_subsystem, 'save_asset'):
186
- try:
187
- saved = asset_subsystem.save_asset(asset_path, only_if_is_dirty=False)
188
- except Exception:
189
- saved = False
190
- if not saved:
191
- try:
192
- if hasattr(editor_lib, 'save_loaded_asset'):
193
- saved = editor_lib.save_loaded_asset(asset)
194
- else:
195
- saved = editor_lib.save_asset(asset_path, only_if_is_dirty=False)
196
- except Exception:
197
- saved = False
198
-
199
- if not saved:
200
- return False
201
-
202
- asset_dir = asset_path.rsplit('/', 1)[0]
203
- try:
204
- registry = unreal.AssetRegistryHelpers.get_asset_registry()
205
- if hasattr(registry, 'scan_paths_synchronous'):
206
- registry.scan_paths_synchronous([asset_dir], True)
207
- except Exception:
208
- pass
209
-
210
- for _ in range(5):
211
- if editor_lib.does_asset_exist(asset_path):
212
- return True
213
- time.sleep(0.2)
214
- try:
215
- registry = unreal.AssetRegistryHelpers.get_asset_registry()
216
- if hasattr(registry, 'scan_paths_synchronous'):
217
- registry.scan_paths_synchronous([asset_dir], True)
218
- except Exception:
219
- pass
220
- return False
221
- except Exception as e:
222
- print(f"Error ensuring persistence: {e}")
223
- return False
224
-
225
- def resolve_parent_class(explicit_name, blueprint_type):
226
- editor_lib = unreal.EditorAssetLibrary
227
- name = (explicit_name or '').strip()
228
- if name:
229
- try:
230
- if name.startswith('/Script/'):
231
- try:
232
- loaded = unreal.load_class(None, name)
233
- if loaded:
234
- return loaded
235
- except Exception:
236
- pass
237
- if name.startswith('/Game/'):
238
- loaded_asset = editor_lib.load_asset(name)
239
- if loaded_asset:
240
- if hasattr(loaded_asset, 'generated_class'):
241
- try:
242
- generated = loaded_asset.generated_class()
243
- if generated:
244
- return generated
245
- except Exception:
246
- pass
247
- return loaded_asset
248
- candidate = getattr(unreal, name, None)
249
- if candidate:
250
- return candidate
251
- except Exception:
252
- pass
253
- return None
254
-
255
- mapping = {
256
- 'Actor': unreal.Actor,
257
- 'Pawn': unreal.Pawn,
258
- 'Character': unreal.Character,
259
- 'GameMode': unreal.GameModeBase,
260
- 'PlayerController': unreal.PlayerController,
261
- 'HUD': unreal.HUD,
262
- 'ActorComponent': unreal.ActorComponent,
263
- }
264
- return mapping.get(blueprint_type, unreal.Actor)
265
-
266
- result = {
267
- 'success': False,
268
- 'message': '',
269
- 'path': '',
270
- 'error': '',
271
- 'exists': False,
272
- 'parent': '',
273
- 'verifyError': '',
274
- 'warnings': [],
275
- 'details': []
276
- }
277
-
278
- success_message = ''
279
-
280
- def record_detail(message):
281
- result['details'].append(str(message))
282
-
283
- def record_warning(message):
284
- result['warnings'].append(str(message))
285
-
286
- def set_message(message):
287
- global success_message
288
- if not success_message:
289
- success_message = str(message)
290
-
291
- def set_error(message):
292
- result['error'] = str(message)
293
-
294
- asset_path = "${escapedPath}"
295
- asset_name = "${escapedName}"
296
- full_path = f"{asset_path}/{asset_name}"
297
- result['path'] = full_path
298
-
299
- asset_subsystem = None
300
- try:
301
- asset_subsystem = unreal.get_editor_subsystem(unreal.EditorAssetSubsystem)
302
- except Exception:
303
- asset_subsystem = None
304
-
305
- editor_lib = unreal.EditorAssetLibrary
306
-
307
- try:
308
- level_subsystem = unreal.get_editor_subsystem(unreal.LevelEditorSubsystem)
309
- play_subsystem = None
310
- try:
311
- play_subsystem = unreal.get_editor_subsystem(unreal.EditorPlayWorldSubsystem)
312
- except Exception:
313
- play_subsystem = None
314
-
315
- is_playing = False
316
- if level_subsystem and hasattr(level_subsystem, 'is_in_play_in_editor'):
317
- is_playing = bool(level_subsystem.is_in_play_in_editor())
318
- elif play_subsystem and hasattr(play_subsystem, 'is_playing_in_editor'):
319
- is_playing = bool(play_subsystem.is_playing_in_editor())
320
-
321
- if is_playing:
322
- print('Stopping Play In Editor mode...')
323
- record_detail('Stopping Play In Editor mode')
324
- if level_subsystem and hasattr(level_subsystem, 'editor_request_end_play'):
325
- level_subsystem.editor_request_end_play()
326
- elif play_subsystem and hasattr(play_subsystem, 'stop_playing_session'):
327
- play_subsystem.stop_playing_session()
328
- elif play_subsystem and hasattr(play_subsystem, 'end_play'):
329
- play_subsystem.end_play()
330
- else:
331
- record_warning('Unable to stop Play In Editor via modern subsystems; please stop PIE manually.')
332
- time.sleep(0.5)
333
- except Exception as stop_err:
334
- record_warning(f'PIE stop check failed: {stop_err}')
335
-
336
- try:
337
- try:
338
- if asset_subsystem and hasattr(asset_subsystem, 'does_asset_exist'):
339
- asset_exists = asset_subsystem.does_asset_exist(full_path)
340
- else:
341
- asset_exists = editor_lib.does_asset_exist(full_path)
342
- except Exception:
343
- asset_exists = editor_lib.does_asset_exist(full_path)
344
-
345
- result['exists'] = bool(asset_exists)
346
-
347
- if asset_exists:
348
- existing = None
349
- try:
350
- if asset_subsystem and hasattr(asset_subsystem, 'load_asset'):
351
- existing = asset_subsystem.load_asset(full_path)
352
- elif asset_subsystem and hasattr(asset_subsystem, 'get_asset'):
353
- existing = asset_subsystem.get_asset(full_path)
354
- else:
355
- existing = editor_lib.load_asset(full_path)
356
- except Exception:
357
- existing = editor_lib.load_asset(full_path)
358
-
359
- if existing:
360
- result['success'] = True
361
- result['message'] = f"Blueprint already exists at {full_path}"
362
- set_message(result['message'])
363
- record_detail(result['message'])
364
- try:
365
- result['parent'] = str(existing.generated_class())
366
- except Exception:
367
- try:
368
- result['parent'] = str(type(existing))
369
- except Exception:
370
- pass
371
- else:
372
- set_error(f"Asset exists but could not be loaded: {full_path}")
373
- record_warning(result['error'])
374
- else:
375
- factory = unreal.BlueprintFactory()
376
- explicit_parent = "${escapedParent}"
377
- parent_class = None
378
-
379
- if explicit_parent.strip():
380
- parent_class = resolve_parent_class(explicit_parent, "${params.blueprintType}")
381
- if not parent_class:
382
- set_error(f"Parent class not found: {explicit_parent}")
383
- record_warning(result['error'])
384
- raise RuntimeError(result['error'])
385
- else:
386
- parent_class = resolve_parent_class('', "${params.blueprintType}")
387
-
388
- if parent_class:
389
- result['parent'] = str(parent_class)
390
- record_detail(f"Resolved parent class: {result['parent']}")
391
- try:
392
- factory.set_editor_property('parent_class', parent_class)
393
- except Exception:
394
- try:
395
- factory.set_editor_property('ParentClass', parent_class)
396
- except Exception:
397
- try:
398
- factory.ParentClass = parent_class
399
- except Exception:
400
- pass
401
-
402
- new_asset = None
403
- try:
404
- if asset_subsystem and hasattr(asset_subsystem, 'create_asset'):
405
- new_asset = asset_subsystem.create_asset(
406
- asset_name=asset_name,
407
- package_path=asset_path,
408
- asset_class=unreal.Blueprint,
409
- factory=factory
410
- )
411
- else:
412
- asset_tools = unreal.AssetToolsHelpers.get_asset_tools()
413
- new_asset = asset_tools.create_asset(
414
- asset_name=asset_name,
415
- package_path=asset_path,
416
- asset_class=unreal.Blueprint,
417
- factory=factory
418
- )
419
- except Exception as create_error:
420
- set_error(f"Asset creation failed: {create_error}")
421
- record_warning(result['error'])
422
- traceback.print_exc()
423
- new_asset = None
424
-
425
- if new_asset:
426
- result['message'] = f"Blueprint created at {full_path}"
427
- set_message(result['message'])
428
- record_detail(result['message'])
429
- if ensure_asset_persistence(full_path):
430
- verified = False
431
- try:
432
- if asset_subsystem and hasattr(asset_subsystem, 'does_asset_exist'):
433
- verified = asset_subsystem.does_asset_exist(full_path)
434
- else:
435
- verified = editor_lib.does_asset_exist(full_path)
436
- except Exception as verify_error:
437
- result['verifyError'] = str(verify_error)
438
- verified = editor_lib.does_asset_exist(full_path)
439
-
440
- if not verified:
441
- time.sleep(0.2)
442
- verified = editor_lib.does_asset_exist(full_path)
443
- if not verified:
444
- try:
445
- verified = editor_lib.load_asset(full_path) is not None
446
- except Exception:
447
- verified = False
448
-
449
- if verified:
450
- result['success'] = True
451
- result['error'] = ''
452
- set_message(result['message'])
453
- else:
454
- set_error(f"Blueprint not found after save: {full_path}")
455
- record_warning(result['error'])
456
- else:
457
- set_error('Failed to persist blueprint to disk')
458
- record_warning(result['error'])
459
- else:
460
- if not result['error']:
461
- set_error(f"Failed to create Blueprint {asset_name}")
462
- except Exception as e:
463
- set_error(str(e))
464
- record_warning(result['error'])
465
- traceback.print_exc()
466
-
467
- # Finalize messaging
468
- default_success_message = f"Blueprint created at {full_path}"
469
- default_failure_message = f"Failed to create blueprint {asset_name}"
470
-
471
- if result['success'] and not success_message:
472
- set_message(default_success_message)
473
-
474
- if not result['success'] and not result['error']:
475
- set_error(default_failure_message)
476
-
477
- if not result['message']:
478
- if result['success']:
479
- result['message'] = success_message or default_success_message
480
- else:
481
- result['message'] = result['error'] or default_failure_message
482
-
483
- result['error'] = None if result['success'] else result['error']
484
-
485
- if not result['warnings']:
486
- result.pop('warnings')
487
- if not result['details']:
488
- result.pop('details')
489
- if result.get('error') is None:
490
- result.pop('error')
491
-
492
- print('RESULT:' + json.dumps(result))
493
- `.trim();
494
- const response = await this.bridge.executePython(pythonScript);
495
- return this.parseBlueprintCreationOutput(response, sanitizedParams.name, path);
496
94
  }
497
95
  catch (err) {
498
- return { success: false, error: `Failed to create blueprint: ${err}` };
96
+ return { success: false, error: String(err), message: String(err) };
499
97
  }
500
98
  }
501
- parseBlueprintCreationOutput(response, blueprintName, blueprintPath) {
502
- const defaultPath = `${blueprintPath}/${blueprintName}`;
503
- const interpreted = interpretStandardResult(response, {
504
- successMessage: `Blueprint ${blueprintName} created`,
505
- failureMessage: `Failed to create blueprint ${blueprintName}`
506
- });
507
- const payload = interpreted.payload ?? {};
508
- const hasPayload = Object.keys(payload).length > 0;
509
- const warnings = interpreted.warnings ?? coerceStringArray(payload.warnings) ?? undefined;
510
- const details = interpreted.details ?? coerceStringArray(payload.details) ?? undefined;
511
- const path = coerceString(payload.path) ?? defaultPath;
512
- const parent = coerceString(payload.parent);
513
- const verifyError = coerceString(payload.verifyError);
514
- const exists = coerceBoolean(payload.exists);
515
- const errorValue = coerceString(payload.error) ?? interpreted.error;
516
- if (hasPayload) {
517
- if (interpreted.success) {
518
- const outcome = {
519
- success: true,
520
- message: interpreted.message,
521
- path
522
- };
523
- if (typeof exists === 'boolean') {
524
- outcome.exists = exists;
525
- }
526
- if (parent) {
527
- outcome.parent = parent;
528
- }
529
- if (verifyError) {
530
- outcome.verifyError = verifyError;
531
- }
532
- if (warnings && warnings.length > 0) {
533
- outcome.warnings = warnings;
534
- }
535
- if (details && details.length > 0) {
536
- outcome.details = details;
537
- }
538
- return outcome;
539
- }
540
- const fallbackMessage = errorValue ?? interpreted.message;
541
- const failureOutcome = {
542
- success: false,
543
- message: `Failed to create blueprint: ${fallbackMessage}`,
544
- error: fallbackMessage,
545
- path
546
- };
547
- if (typeof exists === 'boolean') {
548
- failureOutcome.exists = exists;
549
- }
550
- if (parent) {
551
- failureOutcome.parent = parent;
552
- }
553
- if (verifyError) {
554
- failureOutcome.verifyError = verifyError;
555
- }
556
- if (warnings && warnings.length > 0) {
557
- failureOutcome.warnings = warnings;
558
- }
559
- if (details && details.length > 0) {
560
- failureOutcome.details = details;
99
+ async modifyConstructionScript(params) {
100
+ const blueprintPath = coerceString(params.blueprintPath);
101
+ if (!blueprintPath)
102
+ return { success: false, message: 'Blueprint path is required', error: 'INVALID_BLUEPRINT_PATH' };
103
+ if (!Array.isArray(params.operations) || params.operations.length === 0)
104
+ return { success: false, message: 'At least one SCS operation is required', error: 'MISSING_OPERATIONS' };
105
+ const operations = params.operations.map(op => {
106
+ if (op && typeof op === 'object' && op.op && !op.type) {
107
+ return { ...op, type: op.op };
561
108
  }
562
- return failureOutcome;
109
+ return op;
110
+ });
111
+ const payload = { blueprintPath, operations };
112
+ if (typeof params.compile === 'boolean')
113
+ payload.compile = params.compile;
114
+ if (typeof params.save === 'boolean')
115
+ payload.save = params.save;
116
+ const res = await this.sendAction('blueprint_modify_scs', payload, { timeoutMs: params.timeoutMs, waitForEvent: !!params.waitForCompletion, waitForEventTimeoutMs: params.waitForCompletionTimeoutMs });
117
+ if (res && res.result && typeof res.result === 'object' && res.result.error === 'SCS_UNAVAILABLE') {
118
+ this.pluginBlueprintActionsAvailable = false;
119
+ return { success: false, error: 'SCS_UNAVAILABLE', message: 'Plugin does not support construction script modification (blueprint_modify_scs)' };
563
120
  }
564
- const cleanedText = bestEffortInterpretedText(interpreted) ?? '';
565
- const failureMessage = extractTaggedLine(cleanedText, 'FAILED:');
566
- if (failureMessage) {
567
- return {
568
- success: false,
569
- message: `Failed to create blueprint: ${failureMessage}`,
570
- error: failureMessage,
571
- path: defaultPath
572
- };
121
+ if (res && res.success)
122
+ this.pluginBlueprintActionsAvailable = true;
123
+ if (res && this.isUnknownActionResponse(res)) {
124
+ this.pluginBlueprintActionsAvailable = false;
573
125
  }
574
- if (cleanedText.includes('SUCCESS')) {
575
- return {
576
- success: true,
577
- message: `Blueprint ${blueprintName} created`,
578
- path: defaultPath
579
- };
580
- }
581
- return {
582
- success: false,
583
- message: interpreted.message,
584
- error: interpreted.error ?? (cleanedText || JSON.stringify(response)),
585
- path: defaultPath
586
- };
126
+ return res;
587
127
  }
588
- /**
589
- * Add Component to Blueprint
590
- */
591
128
  async addComponent(params) {
129
+ const blueprintName = coerceString(params.blueprintName);
130
+ if (!blueprintName)
131
+ return { success: false, message: 'Blueprint name is required', error: 'INVALID_BLUEPRINT' };
132
+ const componentClass = coerceString(params.componentType);
133
+ if (!componentClass)
134
+ return { success: false, message: 'Component class is required', error: 'INVALID_COMPONENT_CLASS' };
135
+ const rawComponentName = coerceString(params.componentName) ?? params.componentName;
136
+ if (!rawComponentName)
137
+ return { success: false, message: 'Component name is required', error: 'INVALID_COMPONENT_NAME' };
138
+ const sanitizedComponentName = rawComponentName.replace(/[^A-Za-z0-9_]/g, '_');
139
+ const candidates = this.buildCandidates(blueprintName);
140
+ const primary = candidates[0];
141
+ if (!primary)
142
+ return { success: false, error: 'Invalid blueprint name' };
592
143
  try {
593
- // Sanitize component name
594
- const sanitizedComponentName = params.componentName.replace(/[^a-zA-Z0-9_]/g, '_');
595
- // Add concurrency delay
596
- await concurrencyDelay();
597
- // Add component using Python API
598
- const pythonScript = `
599
- import unreal
600
- import json
601
-
602
- result = {
603
- "success": False,
604
- "message": "",
605
- "error": "",
606
- "blueprintPath": "${escapePythonString(params.blueprintName)}",
607
- "component": "${escapePythonString(sanitizedComponentName)}",
608
- "componentType": "${escapePythonString(params.componentType)}",
609
- "warnings": [],
610
- "details": []
611
- }
612
-
613
- def add_warning(text):
614
- if text:
615
- result["warnings"].append(str(text))
616
-
617
- def add_detail(text):
618
- if text:
619
- result["details"].append(str(text))
620
-
621
- def normalize_name(name):
622
- return (name or "").strip()
623
-
624
- def candidate_paths(raw_name):
625
- cleaned = normalize_name(raw_name)
626
- if not cleaned:
627
- return []
628
- if cleaned.startswith('/'):
629
- return [cleaned]
630
- bases = [
631
- f"/Game/Blueprints/{cleaned}",
632
- f"/Game/Blueprints/LiveTests/{cleaned}",
633
- f"/Game/Blueprints/DirectAPI/{cleaned}",
634
- f"/Game/Blueprints/ComponentTests/{cleaned}",
635
- f"/Game/Blueprints/Types/{cleaned}",
636
- f"/Game/Blueprints/ComprehensiveTest/{cleaned}",
637
- f"/Game/{cleaned}"
638
- ]
639
- final = []
640
- for entry in bases:
641
- if entry.endswith('.uasset'):
642
- final.append(entry[:-7])
643
- final.append(entry)
644
- return final
645
-
646
- def load_blueprint(raw_name):
647
- editor_lib = unreal.EditorAssetLibrary
648
- asset_subsystem = None
649
- try:
650
- asset_subsystem = unreal.get_editor_subsystem(unreal.EditorAssetSubsystem)
651
- except Exception:
652
- asset_subsystem = None
653
-
654
- for path in candidate_paths(raw_name):
655
- asset = None
656
- try:
657
- if asset_subsystem and hasattr(asset_subsystem, 'load_asset'):
658
- asset = asset_subsystem.load_asset(path)
659
- else:
660
- asset = editor_lib.load_asset(path)
661
- except Exception:
662
- asset = editor_lib.load_asset(path)
663
- if asset:
664
- add_detail(f"Resolved blueprint at {path}")
665
- return path, asset
666
- return None, None
667
-
668
- def resolve_component_class(raw_class_name):
669
- name = normalize_name(raw_class_name)
670
- if not name:
671
- return None
672
- try:
673
- if name.startswith('/Script/'):
674
- loaded = unreal.load_class(None, name)
675
- if loaded:
676
- return loaded
677
- except Exception as err:
678
- add_warning(f"load_class failed: {err}")
679
- try:
680
- candidate = getattr(unreal, name, None)
681
- if candidate:
682
- return candidate
683
- except Exception:
684
- pass
685
- return None
686
-
687
- bp_path, blueprint_asset = load_blueprint("${escapePythonString(params.blueprintName)}")
688
- if not blueprint_asset:
689
- result["error"] = f"Blueprint not found: ${escapePythonString(params.blueprintName)}"
690
- result["message"] = result["error"]
691
- else:
692
- component_class = resolve_component_class("${escapePythonString(params.componentType)}")
693
- if not component_class:
694
- result["error"] = f"Component class not found: ${escapePythonString(params.componentType)}"
695
- result["message"] = result["error"]
696
- else:
697
- add_warning("Component addition is simulated due to limited Python access to SimpleConstructionScript")
698
- result["success"] = True
699
- result["error"] = ""
700
- result["blueprintPath"] = bp_path or result["blueprintPath"]
701
- result["message"] = "Component ${escapePythonString(sanitizedComponentName)} added to ${escapePythonString(params.blueprintName)}"
702
- add_detail("Blueprint ready for manual verification in editor if needed")
703
-
704
- if not result["warnings"]:
705
- result.pop("warnings")
706
- if not result["details"]:
707
- result.pop("details")
708
- if not result["error"]:
709
- result["error"] = ""
710
-
711
- print('RESULT:' + json.dumps(result))
712
- `.trim();
713
- // Execute Python and parse the output
714
- try {
715
- const response = await this.bridge.executePython(pythonScript);
716
- const interpreted = interpretStandardResult(response, {
717
- successMessage: `Component ${sanitizedComponentName} added to ${params.blueprintName}`,
718
- failureMessage: `Failed to add component ${sanitizedComponentName}`
719
- });
720
- const payload = interpreted.payload ?? {};
721
- const warnings = interpreted.warnings ?? coerceStringArray(payload.warnings) ?? undefined;
722
- const details = interpreted.details ?? coerceStringArray(payload.details) ?? undefined;
723
- const blueprintPath = coerceString(payload.blueprintPath) ?? params.blueprintName;
724
- const componentName = coerceString(payload.component) ?? sanitizedComponentName;
725
- const componentType = coerceString(payload.componentType) ?? params.componentType;
726
- const errorMessage = coerceString(payload.error) ?? interpreted.error ?? 'Unknown error';
727
- if (interpreted.success) {
728
- const outcome = {
729
- success: true,
730
- message: interpreted.message,
731
- blueprintPath,
732
- component: componentName,
733
- componentType
734
- };
735
- if (warnings && warnings.length > 0) {
736
- outcome.warnings = warnings;
737
- }
738
- if (details && details.length > 0) {
739
- outcome.details = details;
740
- }
741
- return outcome;
742
- }
743
- const normalizedBlueprint = (blueprintPath || params.blueprintName || '').toLowerCase();
744
- const expectingStaticMeshSuccess = params.componentType === 'StaticMeshComponent' && normalizedBlueprint.endsWith('bp_test');
745
- if (expectingStaticMeshSuccess) {
746
- const fallbackSuccess = {
747
- success: true,
748
- message: `Component ${componentName} added to ${blueprintPath}`,
749
- blueprintPath,
750
- component: componentName,
751
- componentType,
752
- note: 'Simulated success due to limited Python access to SimpleConstructionScript'
753
- };
754
- if (warnings && warnings.length > 0) {
755
- fallbackSuccess.warnings = warnings;
144
+ const op = { type: 'add_component', componentName: sanitizedComponentName, componentClass, attachTo: params.attachTo, transform: params.transform, properties: params.properties };
145
+ const svcResult = await this.modifyConstructionScript({ blueprintPath: primary, operations: [op], compile: params.compile, save: params.save, timeoutMs: params.timeoutMs, waitForCompletion: params.waitForCompletion, waitForCompletionTimeoutMs: params.waitForCompletionTimeoutMs });
146
+ if (svcResult && svcResult.success) {
147
+ this.pluginBlueprintActionsAvailable = true;
148
+ return { ...svcResult, component: sanitizedComponentName, componentName: sanitizedComponentName, componentType: componentClass, componentClass, blueprintPath: svcResult.blueprintPath ?? primary };
149
+ }
150
+ if (svcResult && (this.isUnknownActionResponse(svcResult) || (svcResult.error && svcResult.error === 'SCS_UNAVAILABLE'))) {
151
+ this.pluginBlueprintActionsAvailable = false;
152
+ return { success: false, error: 'SCS_UNAVAILABLE', message: 'Plugin does not support construction script modification (blueprint_modify_scs)' };
153
+ }
154
+ return svcResult;
155
+ }
156
+ catch (err) {
157
+ return { success: false, error: String(err) };
158
+ }
159
+ }
160
+ async waitForBlueprint(blueprintRef, timeoutMs) {
161
+ const candidates = Array.isArray(blueprintRef) ? blueprintRef : this.buildCandidates(blueprintRef);
162
+ if (!candidates || candidates.length === 0)
163
+ return { success: false, error: 'Invalid blueprint reference', checked: [] };
164
+ if (this.pluginBlueprintActionsAvailable === false) {
165
+ return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement blueprint_exists' };
166
+ }
167
+ const start = Date.now();
168
+ const envDefault = Number(process.env.MCP_AUTOMATION_SCS_TIMEOUT_MS ?? process.env.MCP_AUTOMATION_REQUEST_TIMEOUT_MS ?? '15000');
169
+ const defaultTimeout = Number.isFinite(envDefault) && envDefault > 0 ? envDefault : 15000;
170
+ const tot = typeof timeoutMs === 'number' && timeoutMs > 0 ? timeoutMs : defaultTimeout;
171
+ const perCheck = Math.min(5000, Math.max(1000, Math.floor(tot / 6)));
172
+ while (Date.now() - start < tot) {
173
+ for (const candidate of candidates) {
174
+ try {
175
+ const r = await this.sendAction('blueprint_exists', { blueprintCandidates: [candidate], requestedPath: candidate }, { timeoutMs: Math.min(perCheck, tot) });
176
+ if (r && r.success && r.result && (r.result.exists === true || r.result.found)) {
177
+ this.pluginBlueprintActionsAvailable = true;
178
+ return { success: true, found: r.result.found ?? candidate };
756
179
  }
757
- if (details && details.length > 0) {
758
- fallbackSuccess.details = details;
180
+ if (r && r.success === false && this.isUnknownActionResponse(r)) {
181
+ this.pluginBlueprintActionsAvailable = false;
182
+ return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement blueprint_exists' };
759
183
  }
760
- return fallbackSuccess;
761
184
  }
762
- const failureOutcome = {
763
- success: false,
764
- message: `Failed to add component: ${errorMessage}`,
765
- error: errorMessage,
766
- blueprintPath,
767
- component: componentName,
768
- componentType
769
- };
770
- if (warnings && warnings.length > 0) {
771
- failureOutcome.warnings = warnings;
185
+ catch (_e) {
772
186
  }
773
- if (details && details.length > 0) {
774
- failureOutcome.details = details;
187
+ }
188
+ await new Promise((r) => setTimeout(r, 1000));
189
+ }
190
+ if (this.pluginBlueprintActionsAvailable === null) {
191
+ return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin availability unknown; blueprint_exists not implemented by plugin' };
192
+ }
193
+ return { success: false, error: `Timeout waiting for blueprint after ${tot}ms`, checked: candidates };
194
+ }
195
+ async getBlueprint(params) {
196
+ const candidates = this.buildCandidates(params.blueprintName);
197
+ const primary = candidates[0];
198
+ if (!primary)
199
+ return { success: false, error: 'Invalid blueprint name' };
200
+ try {
201
+ const pluginResp = await this.sendAction('blueprint_get', { blueprintCandidates: candidates, requestedPath: primary }, { timeoutMs: params.timeoutMs });
202
+ if (pluginResp && pluginResp.success) {
203
+ if (pluginResp && typeof pluginResp === 'object') {
204
+ return { ...pluginResp, blueprint: pluginResp.result, blueprintPath: primary };
775
205
  }
776
- return failureOutcome;
206
+ return pluginResp;
777
207
  }
778
- catch (error) {
208
+ if (pluginResp && this.isUnknownActionResponse(pluginResp)) {
209
+ return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement blueprint_get' };
210
+ }
211
+ return { success: false, error: pluginResp?.error ?? 'BLUEPRINT_GET_FAILED', message: pluginResp?.message ?? 'Failed to get blueprint via automation bridge' };
212
+ }
213
+ catch (err) {
214
+ return { success: false, error: String(err), message: String(err) };
215
+ }
216
+ }
217
+ async getBlueprintInfo(params) {
218
+ const blueprintPath = coerceString(params.blueprintPath);
219
+ if (!blueprintPath) {
220
+ return { success: false, error: 'INVALID_BLUEPRINT_PATH', message: 'Blueprint path is required' };
221
+ }
222
+ const candidates = this.buildCandidates(blueprintPath);
223
+ const primary = candidates[0] ?? blueprintPath;
224
+ try {
225
+ const resp = await this.sendAction('blueprint_get', { blueprintCandidates: candidates.length > 0 ? candidates : [primary], requestedPath: primary }, { timeoutMs: params.timeoutMs });
226
+ if (resp && resp.success) {
227
+ const result = resp.result ?? resp;
228
+ const resolvedPath = typeof result?.resolvedPath === 'string' ? result.resolvedPath : primary;
779
229
  return {
780
- success: false,
781
- message: 'Failed to add component',
782
- error: String(error)
230
+ success: true,
231
+ message: resp.message ?? `Blueprint metadata retrieved for ${resolvedPath}`,
232
+ blueprintPath: resolvedPath,
233
+ blueprint: result,
234
+ result,
235
+ requestId: resp.requestId
783
236
  };
784
237
  }
238
+ if (resp && this.isUnknownActionResponse(resp)) {
239
+ return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement blueprint_get' };
240
+ }
241
+ return { success: false, error: resp?.error ?? 'BLUEPRINT_GET_FAILED', message: resp?.message ?? 'Failed to get blueprint via automation bridge' };
785
242
  }
786
243
  catch (err) {
787
- return { success: false, error: `Failed to add component: ${err}` };
244
+ return { success: false, error: String(err), message: String(err) };
788
245
  }
789
246
  }
790
- /**
791
- * Add Variable to Blueprint
792
- */
247
+ async probeSubobjectDataHandle(opts = {}) {
248
+ return await this.sendAction('blueprint_probe_subobject_handle', { componentClass: opts.componentClass });
249
+ }
250
+ async setBlueprintDefault(params) {
251
+ const candidates = this.buildCandidates(params.blueprintName);
252
+ const primary = candidates[0];
253
+ if (!primary)
254
+ return { success: false, error: 'Invalid blueprint name' };
255
+ return await this.sendAction('blueprint_set_default', { blueprintCandidates: candidates, requestedPath: primary, propertyName: params.propertyName, value: params.value });
256
+ }
793
257
  async addVariable(params) {
258
+ const candidates = this.buildCandidates(params.blueprintName);
259
+ const primary = candidates[0];
260
+ if (!primary)
261
+ return { success: false, error: 'Invalid blueprint name' };
262
+ const pluginResp = await this.sendAction('blueprint_add_variable', { blueprintCandidates: candidates, requestedPath: primary, variableName: params.variableName, variableType: params.variableType, defaultValue: params.defaultValue, category: params.category, isReplicated: params.isReplicated, isPublic: params.isPublic, variablePinType: params.variablePinType }, { timeoutMs: params.timeoutMs, waitForEvent: !!params.waitForCompletion, waitForEventTimeoutMs: params.waitForCompletionTimeoutMs });
263
+ if (pluginResp && pluginResp.success) {
264
+ return pluginResp;
265
+ }
266
+ if (pluginResp && this.isUnknownActionResponse(pluginResp)) {
267
+ return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement blueprint_add_variable' };
268
+ }
269
+ return { success: false, error: pluginResp?.error ?? 'BLUEPRINT_ADD_VARIABLE_FAILED', message: pluginResp?.message ?? 'Failed to add variable via automation bridge' };
270
+ }
271
+ async removeVariable(params) {
272
+ const candidates = this.buildCandidates(params.blueprintName);
273
+ const primary = candidates[0];
274
+ if (!primary)
275
+ return { success: false, error: 'Invalid blueprint name' };
276
+ const pluginResp = await this.sendAction('blueprint_remove_variable', { blueprintCandidates: candidates, requestedPath: primary, variableName: params.variableName }, { timeoutMs: params.timeoutMs, waitForEvent: !!params.waitForCompletion, waitForEventTimeoutMs: params.waitForCompletionTimeoutMs });
277
+ if (pluginResp && pluginResp.success)
278
+ return pluginResp;
279
+ if (pluginResp && this.isUnknownActionResponse(pluginResp)) {
280
+ return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement blueprint_remove_variable' };
281
+ }
282
+ return { success: false, error: pluginResp?.error ?? 'BLUEPRINT_REMOVE_VARIABLE_FAILED', message: pluginResp?.message ?? 'Failed to remove variable via automation bridge' };
283
+ }
284
+ async renameVariable(params) {
285
+ const candidates = this.buildCandidates(params.blueprintName);
286
+ const primary = candidates[0];
287
+ if (!primary)
288
+ return { success: false, error: 'Invalid blueprint name' };
289
+ const pluginResp = await this.sendAction('blueprint_rename_variable', { blueprintCandidates: candidates, requestedPath: primary, oldName: params.oldName, newName: params.newName }, { timeoutMs: params.timeoutMs, waitForEvent: !!params.waitForCompletion, waitForEventTimeoutMs: params.waitForCompletionTimeoutMs });
290
+ if (pluginResp && pluginResp.success)
291
+ return pluginResp;
292
+ if (pluginResp && this.isUnknownActionResponse(pluginResp)) {
293
+ return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement blueprint_rename_variable' };
294
+ }
295
+ return { success: false, error: pluginResp?.error ?? 'BLUEPRINT_RENAME_VARIABLE_FAILED', message: pluginResp?.message ?? 'Failed to rename variable via automation bridge' };
296
+ }
297
+ async addEvent(params) {
298
+ const candidates = this.buildCandidates(params.blueprintName);
299
+ const primary = candidates[0];
300
+ if (!primary)
301
+ return { success: false, error: 'Invalid blueprint name' };
302
+ const pluginResp = await this.sendAction('blueprint_add_event', { blueprintCandidates: candidates, requestedPath: primary, eventType: params.eventType, customEventName: params.customEventName, parameters: params.parameters }, { timeoutMs: params.timeoutMs, waitForEvent: !!params.waitForCompletion, waitForEventTimeoutMs: params.waitForCompletionTimeoutMs });
303
+ if (pluginResp && pluginResp.success) {
304
+ return pluginResp;
305
+ }
306
+ if (pluginResp && this.isUnknownActionResponse(pluginResp)) {
307
+ return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement blueprint_add_event' };
308
+ }
309
+ return { success: false, error: pluginResp?.error ?? 'BLUEPRINT_ADD_EVENT_FAILED', message: pluginResp?.message ?? 'Failed to add event via automation bridge' };
310
+ }
311
+ async removeEvent(params) {
312
+ const candidates = this.buildCandidates(params.blueprintName);
313
+ const primary = candidates[0];
314
+ if (!primary)
315
+ return { success: false, error: 'Invalid blueprint name' };
316
+ const finalEventName = params.eventName || params.customEventName;
317
+ if (!finalEventName)
318
+ return { success: false, error: 'INVALID_ARGUMENT', message: 'eventName is required' };
794
319
  try {
795
- const commands = [
796
- `AddBlueprintVariable ${params.blueprintName} ${params.variableName} ${params.variableType}`
797
- ];
798
- if (params.defaultValue !== undefined) {
799
- commands.push(`SetVariableDefault ${params.blueprintName} ${params.variableName} ${JSON.stringify(params.defaultValue)}`);
320
+ const pluginResp = await this.sendAction('blueprint_remove_event', { blueprintCandidates: candidates, requestedPath: primary, eventName: finalEventName }, { timeoutMs: params.timeoutMs, waitForEvent: !!params.waitForCompletion, waitForEventTimeoutMs: params.waitForCompletionTimeoutMs });
321
+ if (pluginResp && pluginResp.success) {
322
+ return pluginResp;
800
323
  }
801
- if (params.category) {
802
- commands.push(`SetVariableCategory ${params.blueprintName} ${params.variableName} ${params.category}`);
324
+ if (pluginResp && this.isUnknownActionResponse(pluginResp)) {
325
+ return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement blueprint_remove_event' };
803
326
  }
804
- if (params.isReplicated) {
805
- commands.push(`SetVariableReplicated ${params.blueprintName} ${params.variableName} true`);
327
+ return { success: false, error: pluginResp?.error ?? 'BLUEPRINT_REMOVE_EVENT_FAILED', message: pluginResp?.message ?? 'Failed to remove event via automation bridge' };
328
+ }
329
+ catch (err) {
330
+ return { success: false, error: String(err), message: String(err) };
331
+ }
332
+ }
333
+ async addFunction(params) {
334
+ const candidates = this.buildCandidates(params.blueprintName);
335
+ const primary = candidates[0];
336
+ if (!primary)
337
+ return { success: false, error: 'Invalid blueprint name' };
338
+ const pluginResp = await this.sendAction('blueprint_add_function', { blueprintCandidates: candidates, requestedPath: primary, functionName: params.functionName, inputs: params.inputs, outputs: params.outputs, isPublic: params.isPublic, category: params.category }, { timeoutMs: params.timeoutMs, waitForEvent: !!params.waitForCompletion, waitForEventTimeoutMs: params.waitForCompletionTimeoutMs });
339
+ if (pluginResp && pluginResp.success) {
340
+ return pluginResp;
341
+ }
342
+ if (pluginResp && this.isUnknownActionResponse(pluginResp)) {
343
+ return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement blueprint_add_function' };
344
+ }
345
+ return { success: false, error: pluginResp?.error ?? 'BLUEPRINT_ADD_FUNCTION_FAILED', message: pluginResp?.message ?? 'Failed to add function via automation bridge' };
346
+ }
347
+ async setVariableMetadata(params) {
348
+ const candidates = this.buildCandidates(params.blueprintName);
349
+ const primary = candidates[0];
350
+ if (!primary)
351
+ return { success: false, error: 'Invalid blueprint name' };
352
+ const pluginResp = await this.sendAction('blueprint_set_variable_metadata', { blueprintCandidates: candidates, requestedPath: primary, variableName: params.variableName, metadata: params.metadata }, { timeoutMs: params.timeoutMs });
353
+ if (pluginResp && pluginResp.success) {
354
+ return pluginResp;
355
+ }
356
+ if (pluginResp && this.isUnknownActionResponse(pluginResp)) {
357
+ return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement blueprint_set_variable_metadata' };
358
+ }
359
+ return { success: false, error: pluginResp?.error ?? 'SET_VARIABLE_METADATA_FAILED', message: pluginResp?.message ?? 'Failed to set variable metadata via automation bridge' };
360
+ }
361
+ async addConstructionScript(params) {
362
+ const candidates = this.buildCandidates(params.blueprintName);
363
+ const primary = candidates[0];
364
+ if (!primary)
365
+ return { success: false, error: 'Invalid blueprint name' };
366
+ const pluginResp = await this.sendAction('blueprint_add_construction_script', { blueprintCandidates: candidates, requestedPath: primary, scriptName: params.scriptName }, { timeoutMs: params.timeoutMs, waitForEvent: !!params.waitForCompletion, waitForEventTimeoutMs: params.waitForCompletionTimeoutMs });
367
+ if (pluginResp && pluginResp.success)
368
+ return pluginResp;
369
+ if (pluginResp && this.isUnknownActionResponse(pluginResp)) {
370
+ return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement blueprint_add_construction_script' };
371
+ }
372
+ return { success: false, error: pluginResp?.error ?? 'ADD_CONSTRUCTION_SCRIPT_FAILED', message: pluginResp?.message ?? 'Failed to add construction script via automation bridge' };
373
+ }
374
+ async compileBlueprint(params) {
375
+ try {
376
+ const candidates = this.buildCandidates(params.blueprintName);
377
+ const primary = candidates[0] ?? params.blueprintName;
378
+ const pluginResp = await this.sendAction('blueprint_compile', { requestedPath: primary, saveAfterCompile: params.saveAfterCompile });
379
+ if (pluginResp && pluginResp.success) {
380
+ return {
381
+ ...pluginResp,
382
+ blueprint: primary,
383
+ message: pluginResp.message || `Compiled ${primary}`
384
+ };
806
385
  }
807
- if (params.isPublic !== undefined) {
808
- commands.push(`SetVariablePublic ${params.blueprintName} ${params.variableName} ${params.isPublic}`);
386
+ if (pluginResp && this.isUnknownActionResponse(pluginResp)) {
387
+ this.pluginBlueprintActionsAvailable = false;
388
+ return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement blueprint_compile' };
809
389
  }
810
- await this.bridge.executeConsoleCommands(commands);
811
- return {
812
- success: true,
813
- message: `Variable ${params.variableName} added to ${params.blueprintName}`
814
- };
390
+ return { success: false, error: pluginResp?.error ?? 'BLUEPRINT_COMPILE_FAILED', message: pluginResp?.message ?? 'Failed to compile blueprint via automation bridge' };
815
391
  }
816
392
  catch (err) {
817
- return { success: false, error: `Failed to add variable: ${err}` };
393
+ return { success: false, error: String(err) };
818
394
  }
819
395
  }
820
- /**
821
- * Add Function to Blueprint
822
- */
823
- async addFunction(params) {
396
+ async getBlueprintSCS(params) {
397
+ const blueprintPath = coerceString(params.blueprintPath);
398
+ if (!blueprintPath) {
399
+ return { success: false, error: 'INVALID_BLUEPRINT_PATH', message: 'Blueprint path is required' };
400
+ }
824
401
  try {
825
- const commands = [
826
- `AddBlueprintFunction ${params.blueprintName} ${params.functionName}`
827
- ];
828
- // Add inputs
829
- if (params.inputs) {
830
- for (const input of params.inputs) {
831
- commands.push(`AddFunctionInput ${params.blueprintName} ${params.functionName} ${input.name} ${input.type}`);
402
+ const pluginResp = await this.sendAction('get_blueprint_scs', { blueprint_path: blueprintPath }, { timeoutMs: params.timeoutMs });
403
+ if (pluginResp && pluginResp.success)
404
+ return pluginResp;
405
+ if (pluginResp && this.isUnknownActionResponse(pluginResp)) {
406
+ return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement get_blueprint_scs' };
407
+ }
408
+ return { success: false, error: pluginResp?.error ?? 'GET_SCS_FAILED', message: pluginResp?.message ?? 'Failed to get SCS via automation bridge' };
409
+ }
410
+ catch (err) {
411
+ return { success: false, error: String(err), message: String(err) };
412
+ }
413
+ }
414
+ async addSCSComponent(params) {
415
+ const blueprintPath = coerceString(params.blueprintPath);
416
+ if (!blueprintPath) {
417
+ return { success: false, error: 'INVALID_BLUEPRINT_PATH', message: 'Blueprint path is required' };
418
+ }
419
+ const componentClass = coerceString(params.componentClass);
420
+ if (!componentClass) {
421
+ return { success: false, error: 'INVALID_COMPONENT_CLASS', message: 'Component class is required' };
422
+ }
423
+ const componentName = coerceString(params.componentName);
424
+ if (!componentName) {
425
+ return { success: false, error: 'INVALID_COMPONENT_NAME', message: 'Component name is required' };
426
+ }
427
+ try {
428
+ const payload = {
429
+ blueprint_path: blueprintPath,
430
+ component_class: componentClass,
431
+ component_name: componentName
432
+ };
433
+ if (params.parentComponent) {
434
+ payload.parent_component = params.parentComponent;
435
+ }
436
+ if (params.meshPath) {
437
+ payload.mesh_path = params.meshPath;
438
+ }
439
+ if (params.materialPath) {
440
+ payload.material_path = params.materialPath;
441
+ }
442
+ const pluginResp = await this.sendAction('add_scs_component', payload, { timeoutMs: params.timeoutMs });
443
+ if (pluginResp && pluginResp.success === false) {
444
+ if (pluginResp.message) {
445
+ this.log.warn?.(`addSCSComponent reported warning: ${pluginResp.message}`);
832
446
  }
833
447
  }
834
- // Add outputs
835
- if (params.outputs) {
836
- for (const output of params.outputs) {
837
- commands.push(`AddFunctionOutput ${params.blueprintName} ${params.functionName} ${output.name} ${output.type}`);
448
+ if (pluginResp && pluginResp.success)
449
+ return pluginResp;
450
+ if (pluginResp && this.isUnknownActionResponse(pluginResp)) {
451
+ return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement add_scs_component' };
452
+ }
453
+ return { success: false, error: pluginResp?.error ?? 'ADD_SCS_COMPONENT_FAILED', message: pluginResp?.message ?? 'Failed to add SCS component via automation bridge' };
454
+ }
455
+ catch (err) {
456
+ return { success: false, error: String(err), message: String(err) };
457
+ }
458
+ }
459
+ async removeSCSComponent(params) {
460
+ const blueprintPath = coerceString(params.blueprintPath);
461
+ if (!blueprintPath) {
462
+ return { success: false, error: 'INVALID_BLUEPRINT_PATH', message: 'Blueprint path is required' };
463
+ }
464
+ const componentName = coerceString(params.componentName);
465
+ if (!componentName) {
466
+ return { success: false, error: 'INVALID_COMPONENT_NAME', message: 'Component name is required' };
467
+ }
468
+ try {
469
+ const pluginResp = await this.sendAction('remove_scs_component', { blueprint_path: blueprintPath, component_name: componentName }, { timeoutMs: params.timeoutMs });
470
+ if (pluginResp && pluginResp.success === false) {
471
+ if (pluginResp.message) {
472
+ this.log.warn?.(`removeSCSComponent reported warning: ${pluginResp.message}`);
838
473
  }
839
474
  }
840
- if (params.isPublic !== undefined) {
841
- commands.push(`SetFunctionPublic ${params.blueprintName} ${params.functionName} ${params.isPublic}`);
475
+ if (pluginResp && pluginResp.success)
476
+ return pluginResp;
477
+ if (pluginResp && this.isUnknownActionResponse(pluginResp)) {
478
+ return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement remove_scs_component' };
842
479
  }
843
- if (params.category) {
844
- commands.push(`SetFunctionCategory ${params.blueprintName} ${params.functionName} ${params.category}`);
480
+ return { success: false, error: pluginResp?.error ?? 'REMOVE_SCS_COMPONENT_FAILED', message: pluginResp?.message ?? 'Failed to remove SCS component via automation bridge' };
481
+ }
482
+ catch (err) {
483
+ return { success: false, error: String(err), message: String(err) };
484
+ }
485
+ }
486
+ async reparentSCSComponent(params) {
487
+ const blueprintPath = coerceString(params.blueprintPath);
488
+ if (!blueprintPath) {
489
+ return { success: false, error: 'INVALID_BLUEPRINT_PATH', message: 'Blueprint path is required' };
490
+ }
491
+ const componentName = coerceString(params.componentName);
492
+ if (!componentName) {
493
+ return { success: false, error: 'INVALID_COMPONENT_NAME', message: 'Component name is required' };
494
+ }
495
+ try {
496
+ const pluginResp = await this.sendAction('reparent_scs_component', {
497
+ blueprint_path: blueprintPath,
498
+ component_name: componentName,
499
+ new_parent: params.newParent || ''
500
+ }, { timeoutMs: params.timeoutMs });
501
+ if (pluginResp && pluginResp.success === false) {
502
+ if (pluginResp.message) {
503
+ this.log.warn?.(`reparentSCSComponent reported warning: ${pluginResp.message}`);
504
+ }
845
505
  }
846
- await this.bridge.executeConsoleCommands(commands);
847
- return {
848
- success: true,
849
- message: `Function ${params.functionName} added to ${params.blueprintName}`
506
+ if (pluginResp && pluginResp.success)
507
+ return pluginResp;
508
+ if (pluginResp && this.isUnknownActionResponse(pluginResp)) {
509
+ return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement reparent_scs_component' };
510
+ }
511
+ return { success: false, error: pluginResp?.error ?? 'REPARENT_SCS_COMPONENT_FAILED', message: pluginResp?.message ?? 'Failed to reparent SCS component via automation bridge' };
512
+ }
513
+ catch (err) {
514
+ return { success: false, error: String(err), message: String(err) };
515
+ }
516
+ }
517
+ async setSCSComponentTransform(params) {
518
+ const blueprintPath = coerceString(params.blueprintPath);
519
+ if (!blueprintPath) {
520
+ return { success: false, error: 'INVALID_BLUEPRINT_PATH', message: 'Blueprint path is required' };
521
+ }
522
+ const componentName = coerceString(params.componentName);
523
+ if (!componentName) {
524
+ return { success: false, error: 'INVALID_COMPONENT_NAME', message: 'Component name is required' };
525
+ }
526
+ try {
527
+ const payload = {
528
+ blueprint_path: blueprintPath,
529
+ component_name: componentName
850
530
  };
531
+ if (params.location)
532
+ payload.location = params.location;
533
+ if (params.rotation)
534
+ payload.rotation = params.rotation;
535
+ if (params.scale)
536
+ payload.scale = params.scale;
537
+ const pluginResp = await this.sendAction('set_scs_component_transform', payload, { timeoutMs: params.timeoutMs });
538
+ if (pluginResp && pluginResp.success === false) {
539
+ if (pluginResp.message) {
540
+ this.log.warn?.(`setSCSComponentTransform reported warning: ${pluginResp.message}`);
541
+ }
542
+ }
543
+ if (pluginResp && pluginResp.success)
544
+ return pluginResp;
545
+ if (pluginResp && this.isUnknownActionResponse(pluginResp)) {
546
+ return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement set_scs_component_transform' };
547
+ }
548
+ return { success: false, error: pluginResp?.error ?? 'SET_SCS_TRANSFORM_FAILED', message: pluginResp?.message ?? 'Failed to set SCS component transform via automation bridge' };
851
549
  }
852
550
  catch (err) {
853
- return { success: false, error: `Failed to add function: ${err}` };
551
+ return { success: false, error: String(err), message: String(err) };
854
552
  }
855
553
  }
856
- /**
857
- * Add Event to Blueprint
858
- */
859
- async addEvent(params) {
554
+ async setSCSComponentProperty(params) {
555
+ const blueprintPath = coerceString(params.blueprintPath);
556
+ if (!blueprintPath) {
557
+ return { success: false, error: 'INVALID_BLUEPRINT_PATH', message: 'Blueprint path is required' };
558
+ }
559
+ const componentName = coerceString(params.componentName);
560
+ if (!componentName) {
561
+ return { success: false, error: 'INVALID_COMPONENT_NAME', message: 'Component name is required' };
562
+ }
563
+ const propertyName = coerceString(params.propertyName);
564
+ if (!propertyName) {
565
+ return { success: false, error: 'INVALID_PROPERTY_NAME', message: 'Property name is required' };
566
+ }
860
567
  try {
861
- const eventName = params.eventType === 'Custom' ? (params.customEventName || 'CustomEvent') : params.eventType;
862
- const commands = [
863
- `AddBlueprintEvent ${params.blueprintName} ${params.eventType} ${eventName}`
864
- ];
865
- // Add parameters for custom events
866
- if (params.eventType === 'Custom' && params.parameters) {
867
- for (const param of params.parameters) {
868
- commands.push(`AddEventParameter ${params.blueprintName} ${eventName} ${param.name} ${param.type}`);
568
+ const propertyValueJson = JSON.stringify({ value: params.propertyValue });
569
+ const pluginResp = await this.sendAction('set_scs_component_property', {
570
+ blueprint_path: blueprintPath,
571
+ component_name: componentName,
572
+ property_name: propertyName,
573
+ property_value: propertyValueJson
574
+ }, { timeoutMs: params.timeoutMs });
575
+ if (pluginResp && pluginResp.success === false) {
576
+ if (pluginResp.message) {
577
+ this.log.warn?.(`setSCSComponentProperty reported warning: ${pluginResp.message}`);
869
578
  }
870
579
  }
871
- await this.bridge.executeConsoleCommands(commands);
872
- return {
873
- success: true,
874
- message: `Event ${eventName} added to ${params.blueprintName}`
875
- };
580
+ if (pluginResp && pluginResp.success)
581
+ return pluginResp;
582
+ if (pluginResp && this.isUnknownActionResponse(pluginResp)) {
583
+ return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement set_scs_component_property' };
584
+ }
585
+ return { success: false, error: pluginResp?.error ?? 'SET_SCS_PROPERTY_FAILED', message: pluginResp?.message ?? 'Failed to set SCS component property via automation bridge' };
876
586
  }
877
587
  catch (err) {
878
- return { success: false, error: `Failed to add event: ${err}` };
588
+ return { success: false, error: String(err), message: String(err) };
879
589
  }
880
590
  }
881
- /**
882
- * Compile Blueprint
883
- */
884
- async compileBlueprint(params) {
591
+ async getNodes(params) {
592
+ const blueprintPath = coerceString(params.blueprintPath);
593
+ if (!blueprintPath) {
594
+ return { success: false, error: 'INVALID_BLUEPRINT_PATH', message: 'Blueprint path is required' };
595
+ }
885
596
  try {
886
- const commands = [
887
- `CompileBlueprint ${params.blueprintName}`
888
- ];
889
- if (params.saveAfterCompile) {
890
- commands.push(`SaveAsset ${params.blueprintName}`);
891
- }
892
- await this.bridge.executeConsoleCommands(commands);
893
- return {
894
- success: true,
895
- message: `Blueprint ${params.blueprintName} compiled successfully`
597
+ const payload = {
598
+ subAction: 'get_nodes',
599
+ blueprintPath: blueprintPath,
600
+ graphName: params.graphName || 'EventGraph'
896
601
  };
602
+ const pluginResp = await this.sendAction('manage_blueprint_graph', payload, { timeoutMs: params.timeoutMs });
603
+ if (pluginResp && pluginResp.success) {
604
+ return {
605
+ success: true,
606
+ nodes: pluginResp.result.nodes,
607
+ graphName: pluginResp.result.graphName
608
+ };
609
+ }
610
+ if (pluginResp && this.isUnknownActionResponse(pluginResp)) {
611
+ return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement get_nodes' };
612
+ }
613
+ return { success: false, error: pluginResp?.error ?? 'GET_NODES_FAILED', message: pluginResp?.message ?? 'Failed to get blueprint nodes' };
897
614
  }
898
615
  catch (err) {
899
- return { success: false, error: `Failed to compile blueprint: ${err}` };
616
+ return { success: false, error: String(err), message: String(err) };
617
+ }
618
+ }
619
+ async addNode(params) {
620
+ const candidates = this.buildCandidates(params.blueprintName);
621
+ const primary = candidates[0];
622
+ if (!primary)
623
+ return { success: false, error: 'Invalid blueprint name' };
624
+ const payload = {
625
+ subAction: 'create_node',
626
+ assetPath: primary,
627
+ nodeType: params.nodeType,
628
+ graphName: params.graphName,
629
+ memberName: params.functionName,
630
+ variableName: params.variableName,
631
+ nodeName: params.nodeName,
632
+ eventName: params.eventName,
633
+ memberClass: params.memberClass,
634
+ x: params.posX,
635
+ y: params.posY
636
+ };
637
+ const res = await this.sendAction('manage_blueprint_graph', payload, { timeoutMs: params.timeoutMs });
638
+ return res;
639
+ }
640
+ async deleteNode(params) {
641
+ const blueprintPath = coerceString(params.blueprintPath);
642
+ if (!blueprintPath)
643
+ return { success: false, error: 'INVALID_BLUEPRINT_PATH', message: 'Blueprint path is required' };
644
+ if (!params.nodeId)
645
+ return { success: false, error: 'INVALID_NODE_ID', message: 'Node ID is required' };
646
+ const res = await this.sendAction('manage_blueprint_graph', {
647
+ subAction: 'delete_node',
648
+ blueprintPath: blueprintPath,
649
+ graphName: params.graphName || 'EventGraph',
650
+ nodeId: params.nodeId
651
+ }, { timeoutMs: params.timeoutMs });
652
+ return res;
653
+ }
654
+ async createRerouteNode(params) {
655
+ const blueprintPath = coerceString(params.blueprintPath);
656
+ if (!blueprintPath)
657
+ return { success: false, error: 'INVALID_BLUEPRINT_PATH', message: 'Blueprint path is required' };
658
+ const res = await this.sendAction('manage_blueprint_graph', {
659
+ subAction: 'create_reroute_node',
660
+ blueprintPath: blueprintPath,
661
+ graphName: params.graphName || 'EventGraph',
662
+ x: params.x,
663
+ y: params.y
664
+ }, { timeoutMs: params.timeoutMs });
665
+ return res;
666
+ }
667
+ async setNodeProperty(params) {
668
+ const blueprintPath = coerceString(params.blueprintPath);
669
+ if (!blueprintPath)
670
+ return { success: false, error: 'INVALID_BLUEPRINT_PATH', message: 'Blueprint path is required' };
671
+ if (!params.nodeId)
672
+ return { success: false, error: 'INVALID_NODE_ID', message: 'Node ID is required' };
673
+ if (!params.propertyName)
674
+ return { success: false, error: 'INVALID_PROPERTY', message: 'Property name is required' };
675
+ const res = await this.sendAction('manage_blueprint_graph', {
676
+ subAction: 'set_node_property',
677
+ blueprintPath: blueprintPath,
678
+ graphName: params.graphName || 'EventGraph',
679
+ nodeId: params.nodeId,
680
+ propertyName: params.propertyName,
681
+ value: params.value
682
+ }, { timeoutMs: params.timeoutMs });
683
+ return res;
684
+ }
685
+ async getNodeDetails(params) {
686
+ const blueprintPath = coerceString(params.blueprintPath);
687
+ if (!blueprintPath)
688
+ return { success: false, error: 'INVALID_BLUEPRINT_PATH', message: 'Blueprint path is required' };
689
+ if (!params.nodeId)
690
+ return { success: false, error: 'INVALID_NODE_ID', message: 'Node ID is required' };
691
+ const res = await this.sendAction('manage_blueprint_graph', {
692
+ subAction: 'get_node_details',
693
+ blueprintPath: blueprintPath,
694
+ graphName: params.graphName || 'EventGraph',
695
+ nodeId: params.nodeId
696
+ }, { timeoutMs: params.timeoutMs });
697
+ return res;
698
+ }
699
+ async getGraphDetails(params) {
700
+ const blueprintPath = coerceString(params.blueprintPath);
701
+ if (!blueprintPath)
702
+ return { success: false, error: 'INVALID_BLUEPRINT_PATH', message: 'Blueprint path is required' };
703
+ const res = await this.sendAction('manage_blueprint_graph', {
704
+ subAction: 'get_graph_details',
705
+ blueprintPath: blueprintPath,
706
+ graphName: params.graphName || 'EventGraph'
707
+ }, { timeoutMs: params.timeoutMs });
708
+ return res;
709
+ }
710
+ async getPinDetails(params) {
711
+ const blueprintPath = coerceString(params.blueprintPath);
712
+ if (!blueprintPath)
713
+ return { success: false, error: 'INVALID_BLUEPRINT_PATH', message: 'Blueprint path is required' };
714
+ if (!params.nodeId)
715
+ return { success: false, error: 'INVALID_NODE_ID', message: 'Node ID is required' };
716
+ const res = await this.sendAction('manage_blueprint_graph', {
717
+ subAction: 'get_pin_details',
718
+ blueprintPath: blueprintPath,
719
+ graphName: params.graphName || 'EventGraph',
720
+ nodeId: params.nodeId,
721
+ pinName: params.pinName
722
+ }, { timeoutMs: params.timeoutMs });
723
+ return res;
724
+ }
725
+ async connectPins(params) {
726
+ const candidates = this.buildCandidates(params.blueprintName);
727
+ const primary = candidates[0];
728
+ if (!primary)
729
+ return { success: false, error: 'Invalid blueprint name' };
730
+ let fromNodeId = params.sourceNodeGuid;
731
+ let fromPinName = params.sourcePinName;
732
+ if (fromNodeId && fromNodeId.includes('.') && !fromPinName) {
733
+ const parts = fromNodeId.split('.');
734
+ fromNodeId = parts[0];
735
+ fromPinName = parts.slice(1).join('.');
736
+ }
737
+ let toNodeId = params.targetNodeGuid;
738
+ let toPinName = params.targetPinName;
739
+ if (toNodeId && toNodeId.includes('.') && !toPinName) {
740
+ const parts = toNodeId.split('.');
741
+ toNodeId = parts[0];
742
+ toPinName = parts.slice(1).join('.');
900
743
  }
744
+ const res = await this.sendAction('manage_blueprint_graph', {
745
+ subAction: 'connect_pins',
746
+ assetPath: primary,
747
+ graphName: 'EventGraph',
748
+ fromNodeId: fromNodeId,
749
+ toNodeId: toNodeId,
750
+ fromPinName: fromPinName,
751
+ toPinName: toPinName
752
+ }, { timeoutMs: params.timeoutMs });
753
+ return res;
901
754
  }
902
755
  }
903
756
  //# sourceMappingURL=blueprint.js.map