unreal-engine-mcp-server 0.4.7 → 0.5.1

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 (454) 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-config.yml +51 -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 +27 -0
  19. package/.github/workflows/labeler.yml +17 -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 +13 -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 +338 -31
  29. package/CONTRIBUTING.md +140 -0
  30. package/GEMINI.md +115 -0
  31. package/Public/Plugin_setup_guide.mp4 +0 -0
  32. package/README.md +189 -128
  33. package/claude_desktop_config_example.json +7 -6
  34. package/dist/automation/bridge.d.ts +50 -0
  35. package/dist/automation/bridge.js +452 -0
  36. package/dist/automation/connection-manager.d.ts +23 -0
  37. package/dist/automation/connection-manager.js +107 -0
  38. package/dist/automation/handshake.d.ts +11 -0
  39. package/dist/automation/handshake.js +89 -0
  40. package/dist/automation/index.d.ts +3 -0
  41. package/dist/automation/index.js +3 -0
  42. package/dist/automation/message-handler.d.ts +12 -0
  43. package/dist/automation/message-handler.js +149 -0
  44. package/dist/automation/request-tracker.d.ts +25 -0
  45. package/dist/automation/request-tracker.js +98 -0
  46. package/dist/automation/types.d.ts +130 -0
  47. package/dist/automation/types.js +2 -0
  48. package/dist/cli.js +32 -5
  49. package/dist/config.d.ts +26 -0
  50. package/dist/config.js +59 -0
  51. package/dist/constants.d.ts +16 -0
  52. package/dist/constants.js +16 -0
  53. package/dist/graphql/loaders.d.ts +64 -0
  54. package/dist/graphql/loaders.js +117 -0
  55. package/dist/graphql/resolvers.d.ts +268 -0
  56. package/dist/graphql/resolvers.js +746 -0
  57. package/dist/graphql/schema.d.ts +5 -0
  58. package/dist/graphql/schema.js +437 -0
  59. package/dist/graphql/server.d.ts +26 -0
  60. package/dist/graphql/server.js +117 -0
  61. package/dist/graphql/types.d.ts +9 -0
  62. package/dist/graphql/types.js +2 -0
  63. package/dist/handlers/resource-handlers.d.ts +20 -0
  64. package/dist/handlers/resource-handlers.js +180 -0
  65. package/dist/index.d.ts +33 -18
  66. package/dist/index.js +130 -619
  67. package/dist/resources/actors.d.ts +17 -12
  68. package/dist/resources/actors.js +56 -76
  69. package/dist/resources/assets.d.ts +6 -14
  70. package/dist/resources/assets.js +115 -147
  71. package/dist/resources/levels.d.ts +13 -13
  72. package/dist/resources/levels.js +25 -34
  73. package/dist/server/resource-registry.d.ts +20 -0
  74. package/dist/server/resource-registry.js +37 -0
  75. package/dist/server/tool-registry.d.ts +23 -0
  76. package/dist/server/tool-registry.js +322 -0
  77. package/dist/server-setup.d.ts +20 -0
  78. package/dist/server-setup.js +71 -0
  79. package/dist/services/health-monitor.d.ts +34 -0
  80. package/dist/services/health-monitor.js +105 -0
  81. package/dist/services/metrics-server.d.ts +11 -0
  82. package/dist/services/metrics-server.js +105 -0
  83. package/dist/tools/actors.d.ts +163 -9
  84. package/dist/tools/actors.js +356 -311
  85. package/dist/tools/animation.d.ts +135 -4
  86. package/dist/tools/animation.js +510 -411
  87. package/dist/tools/assets.d.ts +75 -29
  88. package/dist/tools/assets.js +265 -284
  89. package/dist/tools/audio.d.ts +102 -42
  90. package/dist/tools/audio.js +272 -685
  91. package/dist/tools/base-tool.d.ts +17 -0
  92. package/dist/tools/base-tool.js +46 -0
  93. package/dist/tools/behavior-tree.d.ts +94 -0
  94. package/dist/tools/behavior-tree.js +39 -0
  95. package/dist/tools/blueprint.d.ts +208 -126
  96. package/dist/tools/blueprint.js +685 -832
  97. package/dist/tools/consolidated-tool-definitions.d.ts +5462 -1781
  98. package/dist/tools/consolidated-tool-definitions.js +829 -496
  99. package/dist/tools/consolidated-tool-handlers.d.ts +2 -1
  100. package/dist/tools/consolidated-tool-handlers.js +198 -1027
  101. package/dist/tools/debug.d.ts +143 -85
  102. package/dist/tools/debug.js +234 -180
  103. package/dist/tools/dynamic-handler-registry.d.ts +13 -0
  104. package/dist/tools/dynamic-handler-registry.js +23 -0
  105. package/dist/tools/editor.d.ts +30 -83
  106. package/dist/tools/editor.js +247 -244
  107. package/dist/tools/engine.d.ts +10 -4
  108. package/dist/tools/engine.js +13 -5
  109. package/dist/tools/environment.d.ts +30 -0
  110. package/dist/tools/environment.js +267 -0
  111. package/dist/tools/foliage.d.ts +65 -99
  112. package/dist/tools/foliage.js +221 -331
  113. package/dist/tools/handlers/actor-handlers.d.ts +3 -0
  114. package/dist/tools/handlers/actor-handlers.js +227 -0
  115. package/dist/tools/handlers/animation-handlers.d.ts +3 -0
  116. package/dist/tools/handlers/animation-handlers.js +185 -0
  117. package/dist/tools/handlers/argument-helper.d.ts +16 -0
  118. package/dist/tools/handlers/argument-helper.js +80 -0
  119. package/dist/tools/handlers/asset-handlers.d.ts +3 -0
  120. package/dist/tools/handlers/asset-handlers.js +496 -0
  121. package/dist/tools/handlers/audio-handlers.d.ts +3 -0
  122. package/dist/tools/handlers/audio-handlers.js +166 -0
  123. package/dist/tools/handlers/blueprint-handlers.d.ts +4 -0
  124. package/dist/tools/handlers/blueprint-handlers.js +358 -0
  125. package/dist/tools/handlers/common-handlers.d.ts +14 -0
  126. package/dist/tools/handlers/common-handlers.js +56 -0
  127. package/dist/tools/handlers/editor-handlers.d.ts +3 -0
  128. package/dist/tools/handlers/editor-handlers.js +119 -0
  129. package/dist/tools/handlers/effect-handlers.d.ts +3 -0
  130. package/dist/tools/handlers/effect-handlers.js +171 -0
  131. package/dist/tools/handlers/environment-handlers.d.ts +3 -0
  132. package/dist/tools/handlers/environment-handlers.js +170 -0
  133. package/dist/tools/handlers/graph-handlers.d.ts +3 -0
  134. package/dist/tools/handlers/graph-handlers.js +90 -0
  135. package/dist/tools/handlers/input-handlers.d.ts +3 -0
  136. package/dist/tools/handlers/input-handlers.js +21 -0
  137. package/dist/tools/handlers/inspect-handlers.d.ts +3 -0
  138. package/dist/tools/handlers/inspect-handlers.js +383 -0
  139. package/dist/tools/handlers/level-handlers.d.ts +3 -0
  140. package/dist/tools/handlers/level-handlers.js +237 -0
  141. package/dist/tools/handlers/lighting-handlers.d.ts +3 -0
  142. package/dist/tools/handlers/lighting-handlers.js +144 -0
  143. package/dist/tools/handlers/performance-handlers.d.ts +3 -0
  144. package/dist/tools/handlers/performance-handlers.js +130 -0
  145. package/dist/tools/handlers/pipeline-handlers.d.ts +3 -0
  146. package/dist/tools/handlers/pipeline-handlers.js +110 -0
  147. package/dist/tools/handlers/sequence-handlers.d.ts +3 -0
  148. package/dist/tools/handlers/sequence-handlers.js +376 -0
  149. package/dist/tools/handlers/system-handlers.d.ts +4 -0
  150. package/dist/tools/handlers/system-handlers.js +506 -0
  151. package/dist/tools/input.d.ts +19 -0
  152. package/dist/tools/input.js +89 -0
  153. package/dist/tools/introspection.d.ts +103 -40
  154. package/dist/tools/introspection.js +425 -568
  155. package/dist/tools/landscape.d.ts +54 -93
  156. package/dist/tools/landscape.js +284 -409
  157. package/dist/tools/level.d.ts +66 -27
  158. package/dist/tools/level.js +647 -675
  159. package/dist/tools/lighting.d.ts +77 -38
  160. package/dist/tools/lighting.js +445 -943
  161. package/dist/tools/logs.d.ts +3 -3
  162. package/dist/tools/logs.js +5 -57
  163. package/dist/tools/materials.d.ts +91 -24
  164. package/dist/tools/materials.js +194 -118
  165. package/dist/tools/niagara.d.ts +149 -39
  166. package/dist/tools/niagara.js +267 -182
  167. package/dist/tools/performance.d.ts +27 -13
  168. package/dist/tools/performance.js +203 -122
  169. package/dist/tools/physics.d.ts +32 -77
  170. package/dist/tools/physics.js +175 -582
  171. package/dist/tools/property-dictionary.d.ts +13 -0
  172. package/dist/tools/property-dictionary.js +82 -0
  173. package/dist/tools/sequence.d.ts +85 -60
  174. package/dist/tools/sequence.js +208 -747
  175. package/dist/tools/tool-definition-utils.d.ts +59 -0
  176. package/dist/tools/tool-definition-utils.js +35 -0
  177. package/dist/tools/ui.d.ts +64 -34
  178. package/dist/tools/ui.js +134 -214
  179. package/dist/types/automation-responses.d.ts +115 -0
  180. package/dist/types/automation-responses.js +2 -0
  181. package/dist/types/env.d.ts +0 -3
  182. package/dist/types/env.js +0 -7
  183. package/dist/types/responses.d.ts +249 -0
  184. package/dist/types/responses.js +2 -0
  185. package/dist/types/tool-interfaces.d.ts +898 -0
  186. package/dist/types/tool-interfaces.js +2 -0
  187. package/dist/types/tool-types.d.ts +183 -19
  188. package/dist/types/tool-types.js +0 -4
  189. package/dist/unreal-bridge.d.ts +24 -131
  190. package/dist/unreal-bridge.js +364 -1506
  191. package/dist/utils/command-validator.d.ts +9 -0
  192. package/dist/utils/command-validator.js +68 -0
  193. package/dist/utils/elicitation.d.ts +1 -1
  194. package/dist/utils/elicitation.js +12 -15
  195. package/dist/utils/error-handler.d.ts +2 -51
  196. package/dist/utils/error-handler.js +11 -87
  197. package/dist/utils/ini-reader.d.ts +3 -0
  198. package/dist/utils/ini-reader.js +69 -0
  199. package/dist/utils/logger.js +9 -6
  200. package/dist/utils/normalize.d.ts +3 -0
  201. package/dist/utils/normalize.js +56 -0
  202. package/dist/utils/path-security.d.ts +2 -0
  203. package/dist/utils/path-security.js +24 -0
  204. package/dist/utils/response-factory.d.ts +7 -0
  205. package/dist/utils/response-factory.js +27 -0
  206. package/dist/utils/response-validator.d.ts +3 -24
  207. package/dist/utils/response-validator.js +130 -81
  208. package/dist/utils/result-helpers.d.ts +4 -5
  209. package/dist/utils/result-helpers.js +15 -16
  210. package/dist/utils/safe-json.js +5 -11
  211. package/dist/utils/unreal-command-queue.d.ts +24 -0
  212. package/dist/utils/unreal-command-queue.js +120 -0
  213. package/dist/utils/validation.d.ts +0 -40
  214. package/dist/utils/validation.js +1 -78
  215. package/dist/wasm/index.d.ts +70 -0
  216. package/dist/wasm/index.js +535 -0
  217. package/docs/GraphQL-API.md +888 -0
  218. package/docs/Migration-Guide-v0.5.0.md +684 -0
  219. package/docs/Roadmap.md +53 -0
  220. package/docs/WebAssembly-Integration.md +628 -0
  221. package/docs/editor-plugin-extension.md +370 -0
  222. package/docs/handler-mapping.md +242 -0
  223. package/docs/native-automation-progress.md +128 -0
  224. package/docs/testing-guide.md +423 -0
  225. package/mcp-config-example.json +6 -6
  226. package/package.json +67 -28
  227. package/plugins/McpAutomationBridge/Config/FilterPlugin.ini +8 -0
  228. package/plugins/McpAutomationBridge/McpAutomationBridge.uplugin +64 -0
  229. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/McpAutomationBridge.Build.cs +189 -0
  230. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.cpp +22 -0
  231. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.h +30 -0
  232. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeHelpers.h +1983 -0
  233. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeModule.cpp +72 -0
  234. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSettings.cpp +46 -0
  235. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSubsystem.cpp +581 -0
  236. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AnimationHandlers.cpp +2394 -0
  237. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetQueryHandlers.cpp +300 -0
  238. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetWorkflowHandlers.cpp +2807 -0
  239. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AudioHandlers.cpp +1087 -0
  240. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BehaviorTreeHandlers.cpp +488 -0
  241. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.cpp +643 -0
  242. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.h +31 -0
  243. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintGraphHandlers.cpp +1184 -0
  244. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers.cpp +5652 -0
  245. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers_List.cpp +152 -0
  246. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ControlHandlers.cpp +2614 -0
  247. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_DebugHandlers.cpp +42 -0
  248. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EditorFunctionHandlers.cpp +1237 -0
  249. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EffectHandlers.cpp +1701 -0
  250. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EnvironmentHandlers.cpp +2145 -0
  251. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_FoliageHandlers.cpp +954 -0
  252. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InputHandlers.cpp +209 -0
  253. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InsightsHandlers.cpp +41 -0
  254. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LandscapeHandlers.cpp +1164 -0
  255. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LevelHandlers.cpp +762 -0
  256. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LightingHandlers.cpp +634 -0
  257. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LogHandlers.cpp +136 -0
  258. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MaterialGraphHandlers.cpp +494 -0
  259. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraGraphHandlers.cpp +278 -0
  260. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraHandlers.cpp +625 -0
  261. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PerformanceHandlers.cpp +401 -0
  262. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PipelineHandlers.cpp +67 -0
  263. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ProcessRequest.cpp +735 -0
  264. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PropertyHandlers.cpp +2634 -0
  265. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_RenderHandlers.cpp +189 -0
  266. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.cpp +917 -0
  267. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.h +39 -0
  268. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequenceHandlers.cpp +2670 -0
  269. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequencerHandlers.cpp +519 -0
  270. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_TestHandlers.cpp +38 -0
  271. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_UiHandlers.cpp +668 -0
  272. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_WorldPartitionHandlers.cpp +346 -0
  273. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.cpp +1330 -0
  274. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.h +149 -0
  275. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpConnectionManager.cpp +783 -0
  276. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSettings.h +115 -0
  277. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSubsystem.h +796 -0
  278. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpConnectionManager.h +117 -0
  279. package/scripts/check-unreal-connection.mjs +19 -0
  280. package/scripts/clean-tmp.js +23 -0
  281. package/scripts/patch-wasm.js +26 -0
  282. package/scripts/run-all-tests.mjs +136 -0
  283. package/scripts/smoke-test.ts +94 -0
  284. package/scripts/sync-mcp-plugin.js +143 -0
  285. package/scripts/test-no-plugin-alternates.mjs +113 -0
  286. package/scripts/validate-server.js +46 -0
  287. package/scripts/verify-automation-bridge.js +200 -0
  288. package/server.json +58 -21
  289. package/src/automation/bridge.ts +558 -0
  290. package/src/automation/connection-manager.ts +130 -0
  291. package/src/automation/handshake.ts +99 -0
  292. package/src/automation/index.ts +2 -0
  293. package/src/automation/message-handler.ts +167 -0
  294. package/src/automation/request-tracker.ts +123 -0
  295. package/src/automation/types.ts +107 -0
  296. package/src/cli.ts +33 -6
  297. package/src/config.ts +73 -0
  298. package/src/constants.ts +19 -0
  299. package/src/graphql/loaders.ts +244 -0
  300. package/src/graphql/resolvers.ts +1008 -0
  301. package/src/graphql/schema.ts +452 -0
  302. package/src/graphql/server.ts +156 -0
  303. package/src/graphql/types.ts +10 -0
  304. package/src/handlers/resource-handlers.ts +186 -0
  305. package/src/index.ts +166 -664
  306. package/src/resources/actors.ts +58 -76
  307. package/src/resources/assets.ts +148 -134
  308. package/src/resources/levels.ts +28 -33
  309. package/src/server/resource-registry.ts +47 -0
  310. package/src/server/tool-registry.ts +354 -0
  311. package/src/server-setup.ts +114 -0
  312. package/src/services/health-monitor.ts +132 -0
  313. package/src/services/metrics-server.ts +142 -0
  314. package/src/tools/actors.ts +426 -323
  315. package/src/tools/animation.ts +672 -461
  316. package/src/tools/assets.ts +364 -289
  317. package/src/tools/audio.ts +323 -766
  318. package/src/tools/base-tool.ts +52 -0
  319. package/src/tools/behavior-tree.ts +45 -0
  320. package/src/tools/blueprint.ts +792 -970
  321. package/src/tools/consolidated-tool-definitions.ts +993 -515
  322. package/src/tools/consolidated-tool-handlers.ts +258 -1146
  323. package/src/tools/debug.ts +292 -187
  324. package/src/tools/dynamic-handler-registry.ts +33 -0
  325. package/src/tools/editor.ts +329 -253
  326. package/src/tools/engine.ts +14 -3
  327. package/src/tools/environment.ts +281 -0
  328. package/src/tools/foliage.ts +330 -392
  329. package/src/tools/handlers/actor-handlers.ts +265 -0
  330. package/src/tools/handlers/animation-handlers.ts +237 -0
  331. package/src/tools/handlers/argument-helper.ts +142 -0
  332. package/src/tools/handlers/asset-handlers.ts +532 -0
  333. package/src/tools/handlers/audio-handlers.ts +194 -0
  334. package/src/tools/handlers/blueprint-handlers.ts +380 -0
  335. package/src/tools/handlers/common-handlers.ts +87 -0
  336. package/src/tools/handlers/editor-handlers.ts +123 -0
  337. package/src/tools/handlers/effect-handlers.ts +220 -0
  338. package/src/tools/handlers/environment-handlers.ts +183 -0
  339. package/src/tools/handlers/graph-handlers.ts +116 -0
  340. package/src/tools/handlers/input-handlers.ts +28 -0
  341. package/src/tools/handlers/inspect-handlers.ts +450 -0
  342. package/src/tools/handlers/level-handlers.ts +252 -0
  343. package/src/tools/handlers/lighting-handlers.ts +147 -0
  344. package/src/tools/handlers/performance-handlers.ts +132 -0
  345. package/src/tools/handlers/pipeline-handlers.ts +127 -0
  346. package/src/tools/handlers/sequence-handlers.ts +415 -0
  347. package/src/tools/handlers/system-handlers.ts +564 -0
  348. package/src/tools/input.ts +101 -0
  349. package/src/tools/introspection.ts +493 -584
  350. package/src/tools/landscape.ts +418 -507
  351. package/src/tools/level.ts +786 -708
  352. package/src/tools/lighting.ts +588 -984
  353. package/src/tools/logs.ts +9 -57
  354. package/src/tools/materials.ts +237 -121
  355. package/src/tools/niagara.ts +335 -168
  356. package/src/tools/performance.ts +320 -169
  357. package/src/tools/physics.ts +274 -613
  358. package/src/tools/property-dictionary.ts +98 -0
  359. package/src/tools/sequence.ts +276 -820
  360. package/src/tools/tool-definition-utils.ts +35 -0
  361. package/src/tools/ui.ts +205 -283
  362. package/src/types/automation-responses.ts +119 -0
  363. package/src/types/env.ts +0 -10
  364. package/src/types/responses.ts +355 -0
  365. package/src/types/tool-interfaces.ts +250 -0
  366. package/src/types/tool-types.ts +243 -21
  367. package/src/unreal-bridge.ts +460 -1550
  368. package/src/utils/command-validator.ts +76 -0
  369. package/src/utils/elicitation.ts +10 -7
  370. package/src/utils/error-handler.ts +14 -90
  371. package/src/utils/ini-reader.ts +86 -0
  372. package/src/utils/logger.ts +8 -3
  373. package/src/utils/normalize.test.ts +162 -0
  374. package/src/utils/normalize.ts +60 -0
  375. package/src/utils/path-security.ts +43 -0
  376. package/src/utils/response-factory.ts +44 -0
  377. package/src/utils/response-validator.ts +176 -56
  378. package/src/utils/result-helpers.ts +21 -19
  379. package/src/utils/safe-json.test.ts +90 -0
  380. package/src/utils/safe-json.ts +14 -11
  381. package/src/utils/unreal-command-queue.ts +152 -0
  382. package/src/utils/validation.test.ts +184 -0
  383. package/src/utils/validation.ts +4 -1
  384. package/src/wasm/index.ts +838 -0
  385. package/test-server.mjs +100 -0
  386. package/tests/run-unreal-tool-tests.mjs +242 -14
  387. package/tests/test-animation.mjs +369 -0
  388. package/tests/test-asset-advanced.mjs +82 -0
  389. package/tests/test-asset-errors.mjs +35 -0
  390. package/tests/test-asset-graph.mjs +311 -0
  391. package/tests/test-audio.mjs +417 -0
  392. package/tests/test-automation-timeouts.mjs +98 -0
  393. package/tests/test-behavior-tree.mjs +444 -0
  394. package/tests/test-blueprint-graph.mjs +410 -0
  395. package/tests/test-blueprint.mjs +577 -0
  396. package/tests/test-client-mode.mjs +86 -0
  397. package/tests/test-console-command.mjs +56 -0
  398. package/tests/test-control-actor.mjs +425 -0
  399. package/tests/test-control-editor.mjs +112 -0
  400. package/tests/test-graphql.mjs +372 -0
  401. package/tests/test-input.mjs +349 -0
  402. package/tests/test-inspect.mjs +302 -0
  403. package/tests/test-landscape.mjs +316 -0
  404. package/tests/test-lighting.mjs +428 -0
  405. package/tests/test-manage-asset.mjs +438 -0
  406. package/tests/test-manage-level.mjs +89 -0
  407. package/tests/test-materials.mjs +356 -0
  408. package/tests/test-niagara.mjs +185 -0
  409. package/tests/test-no-inline-python.mjs +122 -0
  410. package/tests/test-performance.mjs +539 -0
  411. package/tests/test-plugin-handshake.mjs +82 -0
  412. package/tests/test-runner.mjs +933 -0
  413. package/tests/test-sequence.mjs +104 -0
  414. package/tests/test-system.mjs +96 -0
  415. package/tests/test-wasm.mjs +283 -0
  416. package/tests/test-world-partition.mjs +215 -0
  417. package/tsconfig.json +3 -3
  418. package/vitest.config.ts +35 -0
  419. package/wasm/Cargo.lock +363 -0
  420. package/wasm/Cargo.toml +42 -0
  421. package/wasm/LICENSE +21 -0
  422. package/wasm/README.md +253 -0
  423. package/wasm/src/dependency_resolver.rs +377 -0
  424. package/wasm/src/lib.rs +153 -0
  425. package/wasm/src/property_parser.rs +271 -0
  426. package/wasm/src/transform_math.rs +396 -0
  427. package/wasm/tests/integration.rs +109 -0
  428. package/.github/workflows/smithery-build.yml +0 -29
  429. package/dist/prompts/index.d.ts +0 -21
  430. package/dist/prompts/index.js +0 -217
  431. package/dist/tools/build_environment_advanced.d.ts +0 -65
  432. package/dist/tools/build_environment_advanced.js +0 -633
  433. package/dist/tools/rc.d.ts +0 -110
  434. package/dist/tools/rc.js +0 -437
  435. package/dist/tools/visual.d.ts +0 -40
  436. package/dist/tools/visual.js +0 -282
  437. package/dist/utils/http.d.ts +0 -6
  438. package/dist/utils/http.js +0 -151
  439. package/dist/utils/python-output.d.ts +0 -18
  440. package/dist/utils/python-output.js +0 -290
  441. package/dist/utils/python.d.ts +0 -2
  442. package/dist/utils/python.js +0 -4
  443. package/dist/utils/stdio-redirect.d.ts +0 -2
  444. package/dist/utils/stdio-redirect.js +0 -20
  445. package/docs/unreal-tool-test-cases.md +0 -574
  446. package/smithery.yaml +0 -29
  447. package/src/prompts/index.ts +0 -249
  448. package/src/tools/build_environment_advanced.ts +0 -732
  449. package/src/tools/rc.ts +0 -515
  450. package/src/tools/visual.ts +0 -281
  451. package/src/utils/http.ts +0 -187
  452. package/src/utils/python-output.ts +0 -351
  453. package/src/utils/python.ts +0 -3
  454. 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
+ };