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
@@ -0,0 +1,643 @@
1
+ #include "McpAutomationBridge_BlueprintCreationHandlers.h"
2
+ #include "HAL/PlatformTime.h"
3
+ #include "McpAutomationBridgeGlobals.h"
4
+ #include "McpAutomationBridgeHelpers.h"
5
+ #include "McpAutomationBridgeSubsystem.h"
6
+ #include "Misc/ScopeExit.h"
7
+ #include "Misc/ScopeLock.h"
8
+
9
+ #if WITH_EDITOR
10
+ #include "AssetRegistry/AssetRegistryModule.h"
11
+ #include "AssetToolsModule.h"
12
+ #include "Components/ActorComponent.h"
13
+ #include "Components/StaticMeshComponent.h"
14
+ #include "EditorAssetLibrary.h"
15
+ #include "Engine/SCS_Node.h"
16
+ #include "Engine/SimpleConstructionScript.h"
17
+ #include "Factories/BlueprintFactory.h"
18
+ #include "GameFramework/Actor.h"
19
+ #include "GameFramework/Character.h"
20
+ #include "GameFramework/Pawn.h"
21
+ #include "Kismet2/BlueprintEditorUtils.h"
22
+ #include "Kismet2/KismetEditorUtilities.h"
23
+ #include "UObject/UObjectIterator.h"
24
+ #include "UObject/UnrealType.h"
25
+
26
+ // Respect build-rule's PublicDefinitions: if the build rule set
27
+ // MCP_HAS_SUBOBJECT_DATA_SUBSYSTEM=1 then include the subsystem headers.
28
+ #if defined(MCP_HAS_SUBOBJECT_DATA_SUBSYSTEM) && \
29
+ (MCP_HAS_SUBOBJECT_DATA_SUBSYSTEM == 1)
30
+ #if defined(__has_include)
31
+ #if __has_include("Subsystems/SubobjectDataSubsystem.h")
32
+ #include "Subsystems/SubobjectDataSubsystem.h"
33
+ #elif __has_include("SubobjectDataSubsystem.h")
34
+ #include "SubobjectDataSubsystem.h"
35
+ #elif __has_include("SubobjectData/SubobjectDataSubsystem.h")
36
+ #include "SubobjectData/SubobjectDataSubsystem.h"
37
+ #endif
38
+ #else
39
+ #include "SubobjectDataSubsystem.h"
40
+ #endif
41
+ #elif !defined(MCP_HAS_SUBOBJECT_DATA_SUBSYSTEM)
42
+ // If the build-rule did not define the macro, perform header probing here
43
+ // to discover whether the engine exposes SubobjectData headers.
44
+ #if defined(__has_include)
45
+ #if __has_include("Subsystems/SubobjectDataSubsystem.h")
46
+ #include "Subsystems/SubobjectDataSubsystem.h"
47
+ #define MCP_HAS_SUBOBJECT_DATA_SUBSYSTEM 1
48
+ #elif __has_include("SubobjectDataSubsystem.h")
49
+ #include "SubobjectDataSubsystem.h"
50
+ #define MCP_HAS_SUBOBJECT_DATA_SUBSYSTEM 1
51
+ #elif __has_include("SubobjectData/SubobjectDataSubsystem.h")
52
+ #include "SubobjectData/SubobjectDataSubsystem.h"
53
+ #define MCP_HAS_SUBOBJECT_DATA_SUBSYSTEM 1
54
+ #else
55
+ #define MCP_HAS_SUBOBJECT_DATA_SUBSYSTEM 0
56
+ #endif
57
+ #else
58
+ #define MCP_HAS_SUBOBJECT_DATA_SUBSYSTEM 0
59
+ #endif
60
+ #else
61
+ #define MCP_HAS_SUBOBJECT_DATA_SUBSYSTEM 0
62
+ #endif
63
+
64
+ #endif // WITH_EDITOR
65
+
66
+ /**
67
+ * @brief Probes subobject handles for a temporary blueprint and returns gathered handles to the requester.
68
+ *
69
+ * Creates a temporary probe Blueprint, attempts to gather subobject handles via the SubobjectDataSubsystem when available, and falls back to enumerating SimpleConstructionScript nodes when the subsystem is not available. In non-editor builds, sends a NOT_IMPLEMENTED response.
70
+ *
71
+ * @param Self Pointer to the MCP Automation Bridge subsystem handling the request.
72
+ * @param RequestId Identifier for the incoming request; used when sending the response.
73
+ * @param LocalPayload JSON payload for the probe request (may include "componentClass").
74
+ * @param RequestingSocket WebSocket of the requesting client; may be null for non-socket invocations.
75
+ * @return true if the request was handled (a response was sent).
76
+ */
77
+ bool FBlueprintCreationHandlers::HandleBlueprintProbeSubobjectHandle(
78
+ UMcpAutomationBridgeSubsystem *Self, const FString &RequestId,
79
+ const TSharedPtr<FJsonObject> &LocalPayload,
80
+ TSharedPtr<FMcpBridgeWebSocket> RequestingSocket) {
81
+ check(Self);
82
+ // Local extraction
83
+ FString ComponentClass;
84
+ LocalPayload->TryGetStringField(TEXT("componentClass"), ComponentClass);
85
+ if (ComponentClass.IsEmpty())
86
+ ComponentClass = TEXT("StaticMeshComponent");
87
+
88
+ #if WITH_EDITOR
89
+ UE_LOG(LogMcpAutomationBridgeSubsystem, Log,
90
+ TEXT("HandleBlueprintAction: blueprint_probe_subobject_handle start "
91
+ "RequestId=%s componentClass=%s"),
92
+ *RequestId, *ComponentClass);
93
+
94
+ auto CleanupProbeAsset = [](UBlueprint *ProbeBP) {
95
+ #if WITH_EDITOR
96
+ if (ProbeBP) {
97
+ const FString AssetPath = ProbeBP->GetPathName();
98
+ if (!UEditorAssetLibrary::DeleteLoadedAsset(ProbeBP)) {
99
+ UE_LOG(LogMcpAutomationBridgeSubsystem, Warning,
100
+ TEXT("Failed to delete loaded probe asset: %s"), *AssetPath);
101
+ }
102
+
103
+ if (!AssetPath.IsEmpty() &&
104
+ UEditorAssetLibrary::DoesAssetExist(AssetPath)) {
105
+ if (!UEditorAssetLibrary::DeleteAsset(AssetPath)) {
106
+ UE_LOG(LogMcpAutomationBridgeSubsystem, Error,
107
+ TEXT("Failed to delete probe asset file: %s"), *AssetPath);
108
+ }
109
+ }
110
+ }
111
+ #endif
112
+ };
113
+
114
+ const FString ProbeFolder = TEXT("/Game/Temp/MCPProbe");
115
+ const FString ProbeName = FString::Printf(
116
+ TEXT("MCP_Probe_%s"), *FGuid::NewGuid().ToString(EGuidFormats::Digits));
117
+ UBlueprint *CreatedBP = nullptr;
118
+ {
119
+ UBlueprintFactory *Factory = NewObject<UBlueprintFactory>();
120
+ FAssetToolsModule &AssetToolsModule =
121
+ FModuleManager::LoadModuleChecked<FAssetToolsModule>(
122
+ TEXT("AssetTools"));
123
+ UObject *NewObj = AssetToolsModule.Get().CreateAsset(
124
+ ProbeName, ProbeFolder, UBlueprint::StaticClass(), Factory);
125
+ if (!NewObj) {
126
+ TSharedPtr<FJsonObject> Err = MakeShared<FJsonObject>();
127
+ Err->SetStringField(TEXT("componentClass"), ComponentClass);
128
+ Err->SetStringField(TEXT("error"),
129
+ TEXT("Failed to create probe blueprint asset"));
130
+ UE_LOG(LogMcpAutomationBridgeSubsystem, Warning,
131
+ TEXT("blueprint_probe_subobject_handle: asset creation failed"));
132
+ Self->SendAutomationResponse(RequestingSocket, RequestId, false,
133
+ TEXT("Failed to create probe blueprint"),
134
+ Err, TEXT("PROBE_CREATE_FAILED"));
135
+ return true;
136
+ }
137
+ CreatedBP = Cast<UBlueprint>(NewObj);
138
+ if (!CreatedBP) {
139
+ TSharedPtr<FJsonObject> Err = MakeShared<FJsonObject>();
140
+ Err->SetStringField(TEXT("componentClass"), ComponentClass);
141
+ Err->SetStringField(TEXT("error"),
142
+ TEXT("Probe asset was not a Blueprint"));
143
+ UE_LOG(
144
+ LogMcpAutomationBridgeSubsystem, Warning,
145
+ TEXT(
146
+ "blueprint_probe_subobject_handle: created asset not blueprint"));
147
+ Self->SendAutomationResponse(
148
+ RequestingSocket, RequestId, false,
149
+ TEXT("Probe asset created was not a Blueprint"), Err,
150
+ TEXT("PROBE_CREATE_FAILED"));
151
+ CleanupProbeAsset(CreatedBP);
152
+ return true;
153
+ }
154
+ FAssetRegistryModule &Arm =
155
+ FModuleManager::LoadModuleChecked<FAssetRegistryModule>(
156
+ TEXT("AssetRegistry"));
157
+ Arm.Get().AssetCreated(CreatedBP);
158
+ }
159
+
160
+ TSharedPtr<FJsonObject> ResultObj = MakeShared<FJsonObject>();
161
+ ResultObj->SetStringField(TEXT("componentClass"), ComponentClass);
162
+ ResultObj->SetBoolField(TEXT("success"), false);
163
+ ResultObj->SetBoolField(TEXT("subsystemAvailable"), false);
164
+
165
+ #if MCP_HAS_SUBOBJECT_DATA_SUBSYSTEM
166
+ if (USubobjectDataSubsystem *Subsystem =
167
+ (GEngine ? GEngine->GetEngineSubsystem<USubobjectDataSubsystem>()
168
+ : nullptr)) {
169
+ ResultObj->SetBoolField(TEXT("subsystemAvailable"), true);
170
+
171
+ TArray<FSubobjectDataHandle> GatheredHandles;
172
+ Subsystem->K2_GatherSubobjectDataForBlueprint(CreatedBP, GatheredHandles);
173
+
174
+ TArray<TSharedPtr<FJsonValue>> HandleJsonArr;
175
+ const UScriptStruct *HandleStruct = FSubobjectDataHandle::StaticStruct();
176
+ for (int32 Index = 0; Index < GatheredHandles.Num(); ++Index) {
177
+ const FSubobjectDataHandle &Handle = GatheredHandles[Index];
178
+ FString Repr;
179
+ if (HandleStruct) {
180
+ Repr = FString::Printf(TEXT("%s@%p"), *HandleStruct->GetName(),
181
+ (void *)&Handle);
182
+ } else {
183
+ Repr = FString::Printf(TEXT("<subobject_handle_%d>"), Index);
184
+ }
185
+ HandleJsonArr.Add(MakeShared<FJsonValueString>(Repr));
186
+ }
187
+ ResultObj->SetArrayField(TEXT("gatheredHandles"), HandleJsonArr);
188
+ ResultObj->SetBoolField(TEXT("success"), true);
189
+
190
+ CleanupProbeAsset(CreatedBP);
191
+ Self->SendAutomationResponse(RequestingSocket, RequestId, true,
192
+ TEXT("Native probe completed"), ResultObj,
193
+ FString());
194
+ return true;
195
+ }
196
+ #endif
197
+
198
+ // Subsystem unavailable – fallback to simple SCS enumeration
199
+ ResultObj->SetBoolField(TEXT("subsystemAvailable"), false);
200
+ TArray<TSharedPtr<FJsonValue>> HandleJsonArr;
201
+ if (CreatedBP && CreatedBP->SimpleConstructionScript) {
202
+ const TArray<USCS_Node *> &Nodes =
203
+ CreatedBP->SimpleConstructionScript->GetAllNodes();
204
+ for (USCS_Node *Node : Nodes) {
205
+ if (!Node || !Node->GetVariableName().IsValid())
206
+ continue;
207
+ HandleJsonArr.Add(MakeShared<FJsonValueString>(FString::Printf(
208
+ TEXT("scs://%s"), *Node->GetVariableName().ToString())));
209
+ }
210
+ }
211
+ if (HandleJsonArr.Num() == 0) {
212
+ HandleJsonArr.Add(
213
+ MakeShared<FJsonValueString>(TEXT("<probe_handle_stub>")));
214
+ }
215
+ ResultObj->SetArrayField(TEXT("gatheredHandles"), HandleJsonArr);
216
+ ResultObj->SetBoolField(TEXT("success"), true);
217
+
218
+ CleanupProbeAsset(CreatedBP);
219
+ Self->SendAutomationResponse(RequestingSocket, RequestId, true,
220
+ TEXT("Fallback probe completed"), ResultObj,
221
+ FString());
222
+ return true;
223
+ #else
224
+ Self->SendAutomationResponse(RequestingSocket, RequestId, false,
225
+ TEXT("Blueprint probe requires editor build."),
226
+ nullptr, TEXT("NOT_IMPLEMENTED"));
227
+ return true;
228
+ #endif // WITH_EDITOR
229
+ }
230
+
231
+ /**
232
+ * @brief Applies JSON-defined property values to a UObject, recursively handling nested object properties.
233
+ *
234
+ * For each entry in Properties, this function looks up a property on TargetObj by name and sets it:
235
+ * - If the property is an object property and the JSON value is an object, the function recurses into that child object.
236
+ * - Otherwise JSON primitives (string, number, boolean) are converted to text and applied using ImportText_Direct.
237
+ * Unknown property names are silently ignored and no errors are thrown.
238
+ *
239
+ * @param TargetObj The UObject to modify. No action is performed if null.
240
+ * @param Properties JSON object mapping property names to values; nested JSON objects map to subobjects/components.
241
+ */
242
+ static void ApplyPropertiesToObject(UObject *TargetObj,
243
+ const TSharedPtr<FJsonObject> &Properties) {
244
+ if (!TargetObj || !Properties.IsValid()) {
245
+ return;
246
+ }
247
+
248
+ for (const auto &Pair : Properties->Values) {
249
+ FProperty *Property = TargetObj->GetClass()->FindPropertyByName(*Pair.Key);
250
+ if (!Property) {
251
+ continue;
252
+ }
253
+
254
+ // 1. Handle Object Properties (Recursion for Components/Subobjects)
255
+ if (FObjectProperty *ObjProp = CastField<FObjectProperty>(Property)) {
256
+ if (Pair.Value->Type == EJson::Object) {
257
+ // This is likely a component or subobject property we want to edit
258
+ // inline
259
+ UObject *ChildObj =
260
+ ObjProp->GetObjectPropertyValue_InContainer(TargetObj);
261
+ if (ChildObj) {
262
+ ApplyPropertiesToObject(ChildObj, Pair.Value->AsObject());
263
+ }
264
+ continue;
265
+ }
266
+ }
267
+
268
+ // 2. Handle generic property setting via text import
269
+ FString TextValue;
270
+
271
+ if (Pair.Value->Type == EJson::String) {
272
+ TextValue = Pair.Value->AsString();
273
+ } else if (Pair.Value->Type == EJson::Number) {
274
+ double Val = Pair.Value->AsNumber();
275
+ // Heuristic: check if target is integer to avoid floating point syntax
276
+ // issues if any
277
+ if (Property->IsA<FIntProperty>() || Property->IsA<FInt64Property>() ||
278
+ Property->IsA<FByteProperty>()) {
279
+ TextValue = FString::Printf(TEXT("%lld"), (long long)Val);
280
+ } else {
281
+ TextValue = FString::SanitizeFloat(Val);
282
+ }
283
+ } else if (Pair.Value->Type == EJson::Boolean) {
284
+ TextValue = Pair.Value->AsBool() ? TEXT("True") : TEXT("False");
285
+ }
286
+
287
+ if (!TextValue.IsEmpty()) {
288
+ Property->ImportText_Direct(
289
+ *TextValue, Property->ContainerPtrToValuePtr<void>(TargetObj),
290
+ TargetObj, 0);
291
+ }
292
+ }
293
+ }
294
+
295
+ /**
296
+ * @brief Create a new Blueprint asset from the provided payload and send a completion response to the requester (coalesces concurrent requests for the same path).
297
+ *
298
+ * Expected payload fields:
299
+ * - "name" (string, required): asset name.
300
+ * - "savePath" (string, optional, default "/Game"): destination folder.
301
+ * - "parentClass" (string, optional): class path or name used as the Blueprint parent.
302
+ * - "blueprintType" (string, optional): hint like "actor", "pawn", or "character" used when parent class is not resolved.
303
+ * - "properties" (object, optional): JSON object of CDO properties to apply to the generated class default object.
304
+ * - "waitForCompletion" (bool, optional): whether the caller intends to wait for completion (affects coalescing behavior).
305
+ *
306
+ * Behavior notes:
307
+ * - Multiple concurrent requests that target the same SavePath/Name are coalesced so all waiters receive the same completion result.
308
+ * - In editor builds, attempts to create the Blueprint (or returns an existing asset if present), applies optional CDO properties, registers the asset with the Asset Registry, and attempts to ensure asset availability (save/scan).
309
+ * - In non-editor builds, responds with NOT_IMPLEMENTED indicating blueprint creation requires an editor build.
310
+ *
311
+ * @param Self Subsystem instance used to send responses and perform subsystem operations.
312
+ * @param RequestId Identifier for the request; included in the completion response.
313
+ * @param LocalPayload JSON payload describing the blueprint to create (see Expected payload fields above).
314
+ * @param RequestingSocket Optional socket to which the immediate response should be sent; coalesced waiters will also be notified.
315
+ * @return true if the request was handled and a response was sent to the requester (or coalesced waiters).
316
+ */
317
+ bool FBlueprintCreationHandlers::HandleBlueprintCreate(
318
+ UMcpAutomationBridgeSubsystem *Self, const FString &RequestId,
319
+ const TSharedPtr<FJsonObject> &LocalPayload,
320
+ TSharedPtr<FMcpBridgeWebSocket> RequestingSocket) {
321
+ check(Self);
322
+ UE_LOG(LogMcpAutomationBridgeSubsystem, Log,
323
+ TEXT("HandleBlueprintCreate ENTRY: RequestId=%s"), *RequestId);
324
+
325
+ FString Name;
326
+ LocalPayload->TryGetStringField(TEXT("name"), Name);
327
+ if (Name.TrimStartAndEnd().IsEmpty()) {
328
+ Self->SendAutomationResponse(RequestingSocket, RequestId, false,
329
+ TEXT("blueprint_create requires a name."),
330
+ nullptr, TEXT("INVALID_ARGUMENT"));
331
+ return true;
332
+ }
333
+ FString SavePath;
334
+ LocalPayload->TryGetStringField(TEXT("savePath"), SavePath);
335
+ if (SavePath.TrimStartAndEnd().IsEmpty())
336
+ SavePath = TEXT("/Game");
337
+ FString ParentClassSpec;
338
+ LocalPayload->TryGetStringField(TEXT("parentClass"), ParentClassSpec);
339
+ FString BlueprintTypeSpec;
340
+ LocalPayload->TryGetStringField(TEXT("blueprintType"), BlueprintTypeSpec);
341
+ const double Now = FPlatformTime::Seconds();
342
+ const FString CreateKey = FString::Printf(TEXT("%s/%s"), *SavePath, *Name);
343
+
344
+ // Check if client wants to wait for completion
345
+ bool bWaitForCompletion = false;
346
+ LocalPayload->TryGetBoolField(TEXT("waitForCompletion"), bWaitForCompletion);
347
+ UE_LOG(
348
+ LogMcpAutomationBridgeSubsystem, Log,
349
+ TEXT("HandleBlueprintCreate: name=%s, savePath=%s, waitForCompletion=%s"),
350
+ *Name, *SavePath, bWaitForCompletion ? TEXT("true") : TEXT("false"));
351
+
352
+ // Track in-flight requests regardless so all waiters receive completion
353
+ {
354
+ FScopeLock Lock(&GBlueprintCreateMutex);
355
+ if (GBlueprintCreateInflight.Contains(CreateKey)) {
356
+ GBlueprintCreateInflight[CreateKey].Add(
357
+ TPair<FString, TSharedPtr<FMcpBridgeWebSocket>>(RequestId,
358
+ RequestingSocket));
359
+ UE_LOG(LogMcpAutomationBridgeSubsystem, Log,
360
+ TEXT("HandleBlueprintCreate: Coalescing request %s for %s"),
361
+ *RequestId, *CreateKey);
362
+ return true;
363
+ }
364
+
365
+ GBlueprintCreateInflight.Add(
366
+ CreateKey, TArray<TPair<FString, TSharedPtr<FMcpBridgeWebSocket>>>());
367
+ GBlueprintCreateInflightTs.Add(CreateKey, Now);
368
+ GBlueprintCreateInflight[CreateKey].Add(
369
+ TPair<FString, TSharedPtr<FMcpBridgeWebSocket>>(RequestId,
370
+ RequestingSocket));
371
+ }
372
+
373
+ #if WITH_EDITOR
374
+ // Perform real creation (editor only)
375
+ UE_LOG(LogMcpAutomationBridgeSubsystem, Log,
376
+ TEXT("HandleBlueprintCreate: Starting blueprint creation "
377
+ "(WITH_EDITOR=1)"));
378
+ UBlueprint *CreatedBlueprint = nullptr;
379
+ FString CreatedNormalizedPath;
380
+ FString CreationError;
381
+
382
+ // Check if asset already exists to avoid "Overwrite" dialogs which can crash
383
+ // the editor/driver
384
+ FString PreExistingNormalized;
385
+ FString PreExistingError;
386
+ if (UBlueprint *PreExistingBP = LoadBlueprintAsset(
387
+ CreateKey, PreExistingNormalized, PreExistingError)) {
388
+ CreatedBlueprint = PreExistingBP;
389
+ CreatedNormalizedPath = !PreExistingNormalized.TrimStartAndEnd().IsEmpty()
390
+ ? PreExistingNormalized
391
+ : PreExistingBP->GetPathName();
392
+ if (CreatedNormalizedPath.Contains(TEXT("."))) {
393
+ CreatedNormalizedPath =
394
+ CreatedNormalizedPath.Left(CreatedNormalizedPath.Find(TEXT(".")));
395
+ }
396
+
397
+ TSharedPtr<FJsonObject> ResultPayload = MakeShared<FJsonObject>();
398
+ ResultPayload->SetStringField(TEXT("path"), CreatedNormalizedPath);
399
+ ResultPayload->SetStringField(TEXT("assetPath"),
400
+ PreExistingBP->GetPathName());
401
+ ResultPayload->SetBoolField(TEXT("saved"), true);
402
+
403
+ FScopeLock Lock(&GBlueprintCreateMutex);
404
+ if (TArray<TPair<FString, TSharedPtr<FMcpBridgeWebSocket>>> *Subs =
405
+ GBlueprintCreateInflight.Find(CreateKey)) {
406
+ for (const TPair<FString, TSharedPtr<FMcpBridgeWebSocket>> &Pair :
407
+ *Subs) {
408
+ Self->SendAutomationResponse(Pair.Value, Pair.Key, true,
409
+ TEXT("Blueprint already exists"),
410
+ ResultPayload, FString());
411
+ }
412
+ GBlueprintCreateInflight.Remove(CreateKey);
413
+ GBlueprintCreateInflightTs.Remove(CreateKey);
414
+ UE_LOG(LogMcpAutomationBridgeSubsystem, Log,
415
+ TEXT("blueprint_create RequestId=%s completed (existing blueprint "
416
+ "found early)."),
417
+ *RequestId);
418
+ } else {
419
+ Self->SendAutomationResponse(RequestingSocket, RequestId, true,
420
+ TEXT("Blueprint already exists"),
421
+ ResultPayload, FString());
422
+ }
423
+
424
+ return true;
425
+ }
426
+
427
+ UBlueprintFactory *Factory = NewObject<UBlueprintFactory>();
428
+ UClass *ResolvedParent = nullptr;
429
+ if (!ParentClassSpec.IsEmpty()) {
430
+ if (ParentClassSpec.StartsWith(TEXT("/Script/"))) {
431
+ ResolvedParent = LoadClass<UObject>(nullptr, *ParentClassSpec);
432
+ } else {
433
+ ResolvedParent = FindObject<UClass>(nullptr, *ParentClassSpec);
434
+ // Avoid calling StaticLoadClass on a bare short name like "Actor" which
435
+ // can generate engine warnings (e.g., "Class None.Actor"). For short
436
+ // names, try common /Script prefixes instead.
437
+ const bool bLooksPathLike = ParentClassSpec.Contains(TEXT("/")) ||
438
+ ParentClassSpec.Contains(TEXT("."));
439
+ if (!ResolvedParent && bLooksPathLike) {
440
+ ResolvedParent =
441
+ StaticLoadClass(UObject::StaticClass(), nullptr, *ParentClassSpec);
442
+ }
443
+ if (!ResolvedParent && !bLooksPathLike) {
444
+ const TArray<FString> PrefixGuesses = {
445
+ FString::Printf(TEXT("/Script/Engine.%s"), *ParentClassSpec),
446
+ FString::Printf(TEXT("/Script/GameFramework.%s"), *ParentClassSpec),
447
+ FString::Printf(TEXT("/Script/CoreUObject.%s"), *ParentClassSpec)};
448
+ for (const FString &Guess : PrefixGuesses) {
449
+ UClass *Loaded = FindObject<UClass>(nullptr, *Guess);
450
+ if (!Loaded) {
451
+ Loaded = StaticLoadClass(UObject::StaticClass(), nullptr, *Guess);
452
+ }
453
+ if (Loaded) {
454
+ ResolvedParent = Loaded;
455
+ break;
456
+ }
457
+ }
458
+ }
459
+ if (!ResolvedParent) {
460
+ for (TObjectIterator<UClass> It; It; ++It) {
461
+ UClass *C = *It;
462
+ if (!C)
463
+ continue;
464
+ if (C->GetName().Equals(ParentClassSpec, ESearchCase::IgnoreCase)) {
465
+ ResolvedParent = C;
466
+ break;
467
+ }
468
+ }
469
+ }
470
+ }
471
+ }
472
+ if (!ResolvedParent && !BlueprintTypeSpec.IsEmpty()) {
473
+ const FString LowerType = BlueprintTypeSpec.ToLower();
474
+ if (LowerType == TEXT("actor"))
475
+ ResolvedParent = AActor::StaticClass();
476
+ else if (LowerType == TEXT("pawn"))
477
+ ResolvedParent = APawn::StaticClass();
478
+ else if (LowerType == TEXT("character"))
479
+ ResolvedParent = ACharacter::StaticClass();
480
+ }
481
+ if (ResolvedParent)
482
+ Factory->ParentClass = ResolvedParent;
483
+ else
484
+ Factory->ParentClass = AActor::StaticClass();
485
+
486
+ FAssetToolsModule &AssetToolsModule =
487
+ FModuleManager::LoadModuleChecked<FAssetToolsModule>(TEXT("AssetTools"));
488
+ UObject *NewObj = AssetToolsModule.Get().CreateAsset(
489
+ Name, SavePath, UBlueprint::StaticClass(), Factory);
490
+ if (NewObj) {
491
+ UE_LOG(LogMcpAutomationBridgeSubsystem, Log,
492
+ TEXT("CreateAsset returned object: name=%s path=%s class=%s"),
493
+ *NewObj->GetName(), *NewObj->GetPathName(),
494
+ *NewObj->GetClass()->GetName());
495
+ }
496
+
497
+ CreatedBlueprint = Cast<UBlueprint>(NewObj);
498
+
499
+ // Apply optional CDO properties immediately if provided
500
+ if (CreatedBlueprint && CreatedBlueprint->GeneratedClass) {
501
+ const TSharedPtr<FJsonObject> *PropertiesPtr;
502
+ if (LocalPayload->TryGetObjectField(TEXT("properties"), PropertiesPtr)) {
503
+ UObject *CDO = CreatedBlueprint->GeneratedClass->GetDefaultObject();
504
+ if (CDO) {
505
+ ApplyPropertiesToObject(CDO, *PropertiesPtr);
506
+ // Mark dirty
507
+ CreatedBlueprint->Modify();
508
+ }
509
+ }
510
+ }
511
+
512
+ if (!CreatedBlueprint) {
513
+ // If creation failed, check whether a Blueprint already exists at the
514
+ // target path. AssetTools will return nullptr when an asset with the
515
+ // same name already exists; in that case we should treat this as an
516
+ // idempotent success instead of a hard failure.
517
+ FString ExistingNormalized;
518
+ FString ExistingError;
519
+ UBlueprint *ExistingBP =
520
+ LoadBlueprintAsset(CreateKey, ExistingNormalized, ExistingError);
521
+ if (ExistingBP) {
522
+ CreatedBlueprint = ExistingBP;
523
+ CreatedNormalizedPath = !ExistingNormalized.TrimStartAndEnd().IsEmpty()
524
+ ? ExistingNormalized
525
+ : ExistingBP->GetPathName();
526
+ if (CreatedNormalizedPath.Contains(TEXT("."))) {
527
+ CreatedNormalizedPath =
528
+ CreatedNormalizedPath.Left(CreatedNormalizedPath.Find(TEXT(".")));
529
+ }
530
+
531
+ TSharedPtr<FJsonObject> ResultPayload = MakeShared<FJsonObject>();
532
+ ResultPayload->SetStringField(TEXT("path"), CreatedNormalizedPath);
533
+ ResultPayload->SetStringField(TEXT("assetPath"),
534
+ ExistingBP->GetPathName());
535
+ ResultPayload->SetBoolField(TEXT("saved"), true);
536
+
537
+ FScopeLock Lock(&GBlueprintCreateMutex);
538
+ if (TArray<TPair<FString, TSharedPtr<FMcpBridgeWebSocket>>> *Subs =
539
+ GBlueprintCreateInflight.Find(CreateKey)) {
540
+ for (const TPair<FString, TSharedPtr<FMcpBridgeWebSocket>> &Pair :
541
+ *Subs) {
542
+ Self->SendAutomationResponse(Pair.Value, Pair.Key, true,
543
+ TEXT("Blueprint already exists"),
544
+ ResultPayload, FString());
545
+ }
546
+ GBlueprintCreateInflight.Remove(CreateKey);
547
+ GBlueprintCreateInflightTs.Remove(CreateKey);
548
+ UE_LOG(LogMcpAutomationBridgeSubsystem, Log,
549
+ TEXT("blueprint_create RequestId=%s completed (existing "
550
+ "blueprint)."),
551
+ *RequestId);
552
+ } else {
553
+ Self->SendAutomationResponse(RequestingSocket, RequestId, true,
554
+ TEXT("Blueprint already exists"),
555
+ ResultPayload, FString());
556
+ }
557
+
558
+ return true;
559
+ }
560
+
561
+ CreationError =
562
+ FString::Printf(TEXT("Created asset is not a Blueprint: %s"),
563
+ NewObj ? *NewObj->GetPathName() : TEXT("<null>"));
564
+
565
+ {
566
+ FScopeLock Lock(&GBlueprintCreateMutex);
567
+ if (TArray<TPair<FString, TSharedPtr<FMcpBridgeWebSocket>>> *Subs =
568
+ GBlueprintCreateInflight.Find(CreateKey)) {
569
+ for (const TPair<FString, TSharedPtr<FMcpBridgeWebSocket>> &Pair :
570
+ *Subs) {
571
+ Self->SendAutomationResponse(Pair.Value, Pair.Key, false,
572
+ CreationError, nullptr,
573
+ TEXT("CREATE_FAILED"));
574
+ }
575
+ GBlueprintCreateInflight.Remove(CreateKey);
576
+ GBlueprintCreateInflightTs.Remove(CreateKey);
577
+ } else {
578
+ Self->SendAutomationResponse(RequestingSocket, RequestId, false,
579
+ CreationError, nullptr,
580
+ TEXT("CREATE_FAILED"));
581
+ }
582
+ }
583
+ return true;
584
+ }
585
+
586
+ CreatedNormalizedPath = CreatedBlueprint->GetPathName();
587
+ if (CreatedNormalizedPath.Contains(TEXT(".")))
588
+ CreatedNormalizedPath =
589
+ CreatedNormalizedPath.Left(CreatedNormalizedPath.Find(TEXT(".")));
590
+ FAssetRegistryModule &AssetRegistryModule =
591
+ FModuleManager::LoadModuleChecked<FAssetRegistryModule>(
592
+ TEXT("AssetRegistry"));
593
+ AssetRegistryModule.AssetCreated(CreatedBlueprint);
594
+
595
+ TSharedPtr<FJsonObject> ResultPayload = MakeShared<FJsonObject>();
596
+ ResultPayload->SetStringField(TEXT("path"), CreatedNormalizedPath);
597
+ ResultPayload->SetStringField(TEXT("assetPath"),
598
+ CreatedBlueprint->GetPathName());
599
+ ResultPayload->SetBoolField(TEXT("saved"), true);
600
+ FScopeLock Lock(&GBlueprintCreateMutex);
601
+ if (TArray<TPair<FString, TSharedPtr<FMcpBridgeWebSocket>>> *Subs =
602
+ GBlueprintCreateInflight.Find(CreateKey)) {
603
+ for (const TPair<FString, TSharedPtr<FMcpBridgeWebSocket>> &Pair : *Subs) {
604
+ Self->SendAutomationResponse(Pair.Value, Pair.Key, true,
605
+ TEXT("Blueprint created"), ResultPayload,
606
+ FString());
607
+ }
608
+ GBlueprintCreateInflight.Remove(CreateKey);
609
+ GBlueprintCreateInflightTs.Remove(CreateKey);
610
+ UE_LOG(LogMcpAutomationBridgeSubsystem, Log,
611
+ TEXT("blueprint_create RequestId=%s completed (coalesced)."),
612
+ *RequestId);
613
+ } else {
614
+ Self->SendAutomationResponse(RequestingSocket, RequestId, true,
615
+ TEXT("Blueprint created"), ResultPayload,
616
+ FString());
617
+ }
618
+
619
+ TWeakObjectPtr<UBlueprint> WeakCreatedBp = CreatedBlueprint;
620
+ if (WeakCreatedBp.IsValid()) {
621
+ UBlueprint *BP = WeakCreatedBp.Get();
622
+ #if WITH_EDITOR
623
+ // Force immediate save and registry scan to ensure availability
624
+ SaveLoadedAssetThrottled(BP, -1.0, true);
625
+ ScanPathSynchronous(BP->GetOutermost()->GetName());
626
+ #endif
627
+ }
628
+
629
+ UE_LOG(LogMcpAutomationBridgeSubsystem, Log,
630
+ TEXT("HandleBlueprintCreate EXIT: RequestId=%s created successfully"),
631
+ *RequestId);
632
+ return true;
633
+ #else
634
+ UE_LOG(LogMcpAutomationBridgeSubsystem, Warning,
635
+ TEXT("HandleBlueprintCreate: WITH_EDITOR not defined - cannot create "
636
+ "blueprints"));
637
+ Self->SendAutomationResponse(
638
+ RequestingSocket, RequestId, false,
639
+ TEXT("Blueprint creation requires editor build."), nullptr,
640
+ TEXT("NOT_IMPLEMENTED"));
641
+ return true;
642
+ #endif
643
+ }
@@ -0,0 +1,31 @@
1
+ #pragma once
2
+
3
+ #include "CoreMinimal.h"
4
+ #include "Dom/JsonObject.h"
5
+
6
+ class UMcpAutomationBridgeSubsystem;
7
+ class FMcpBridgeWebSocket;
8
+
9
+ /**
10
+ * Handle a request to create or instantiate a Blueprint asset or Blueprint-derived object described in the payload.
11
+ * @param Self Pointer to the MCP automation bridge subsystem processing the request.
12
+ * @param RequestId Correlation identifier for the incoming request/response.
13
+ * @param LocalPayload JSON object containing the blueprint creation parameters and metadata.
14
+ * @param RequestingSocket WebSocket that originated the request (may be null).
15
+ * @returns `true` if the request was parsed and processing was initiated successfully, `false` otherwise.
16
+ */
17
+
18
+ /**
19
+ * Handle a request that probes for a handle or reference to a subobject inside a Blueprint instance.
20
+ * @param Self Pointer to the MCP automation bridge subsystem processing the request.
21
+ * @param RequestId Correlation identifier for the incoming request/response.
22
+ * @param LocalPayload JSON object containing the probe parameters (target blueprint instance and subobject identifiers).
23
+ * @param RequestingSocket WebSocket that originated the request (may be null).
24
+ * @returns `true` if the probe request was parsed and processing was initiated successfully, `false` otherwise.
25
+ */
26
+ class FBlueprintCreationHandlers
27
+ {
28
+ public:
29
+ static bool HandleBlueprintCreate(UMcpAutomationBridgeSubsystem* Self, const FString& RequestId, const TSharedPtr<FJsonObject>& LocalPayload, TSharedPtr<FMcpBridgeWebSocket> RequestingSocket);
30
+ static bool HandleBlueprintProbeSubobjectHandle(UMcpAutomationBridgeSubsystem* Self, const FString& RequestId, const TSharedPtr<FJsonObject>& LocalPayload, TSharedPtr<FMcpBridgeWebSocket> RequestingSocket);
31
+ };