ue-mcp 1.2.3 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (255) hide show
  1. package/README.md +4 -3
  2. package/dist/action-class.js +56 -3
  3. package/dist/action-class.js.map +1 -1
  4. package/dist/bridge-timeouts.d.ts +64 -0
  5. package/dist/bridge-timeouts.js +118 -0
  6. package/dist/bridge-timeouts.js.map +1 -0
  7. package/dist/bridge.js +10 -2
  8. package/dist/bridge.js.map +1 -1
  9. package/dist/editor-control.d.ts +72 -5
  10. package/dist/editor-control.js +164 -140
  11. package/dist/editor-control.js.map +1 -1
  12. package/dist/editor-target.d.ts +55 -1
  13. package/dist/editor-target.js +107 -10
  14. package/dist/editor-target.js.map +1 -1
  15. package/dist/engine-observer.d.ts +59 -5
  16. package/dist/engine-observer.js +249 -29
  17. package/dist/engine-observer.js.map +1 -1
  18. package/dist/engine-root.d.ts +91 -0
  19. package/dist/engine-root.js +448 -0
  20. package/dist/engine-root.js.map +1 -0
  21. package/dist/epic-enrich.js +16 -1
  22. package/dist/epic-enrich.js.map +1 -1
  23. package/dist/feedback-cli.js +4 -0
  24. package/dist/feedback-cli.js.map +1 -1
  25. package/dist/feedback-deferred.d.ts +9 -0
  26. package/dist/feedback-deferred.js +2 -0
  27. package/dist/feedback-deferred.js.map +1 -1
  28. package/dist/feedback-fallback.d.ts +99 -0
  29. package/dist/feedback-fallback.js +173 -0
  30. package/dist/feedback-fallback.js.map +1 -0
  31. package/dist/flow/write-methods.js +20 -0
  32. package/dist/flow/write-methods.js.map +1 -1
  33. package/dist/index.js +41 -2
  34. package/dist/index.js.map +1 -1
  35. package/dist/lean-context.js +8 -3
  36. package/dist/lean-context.js.map +1 -1
  37. package/dist/locking.js +11 -2
  38. package/dist/locking.js.map +1 -1
  39. package/dist/plugin/loader.d.ts +4 -0
  40. package/dist/plugin/loader.js +22 -8
  41. package/dist/plugin/loader.js.map +1 -1
  42. package/dist/plugin/manifest.d.ts +72 -52
  43. package/dist/plugin/manifest.js +118 -21
  44. package/dist/plugin/manifest.js.map +1 -1
  45. package/dist/plugin-cli.js +16 -3
  46. package/dist/plugin-cli.js.map +1 -1
  47. package/dist/project.d.ts +11 -0
  48. package/dist/project.js +23 -2
  49. package/dist/project.js.map +1 -1
  50. package/dist/python-gate.d.ts +95 -0
  51. package/dist/python-gate.js +175 -0
  52. package/dist/python-gate.js.map +1 -0
  53. package/dist/tool-counts.json +16 -16
  54. package/dist/tools/animation.js +288 -24
  55. package/dist/tools/animation.js.map +1 -1
  56. package/dist/tools/asset.js +79 -14
  57. package/dist/tools/asset.js.map +1 -1
  58. package/dist/tools/audio.js +59 -5
  59. package/dist/tools/audio.js.map +1 -1
  60. package/dist/tools/blueprint.js +29 -12
  61. package/dist/tools/blueprint.js.map +1 -1
  62. package/dist/tools/chooser.js +4 -1
  63. package/dist/tools/chooser.js.map +1 -1
  64. package/dist/tools/editor.js +81 -43
  65. package/dist/tools/editor.js.map +1 -1
  66. package/dist/tools/feedback.js +214 -48
  67. package/dist/tools/feedback.js.map +1 -1
  68. package/dist/tools/foliage.js +27 -1
  69. package/dist/tools/foliage.js.map +1 -1
  70. package/dist/tools/gameplay.js +38 -11
  71. package/dist/tools/gameplay.js.map +1 -1
  72. package/dist/tools/gas.js +17 -5
  73. package/dist/tools/gas.js.map +1 -1
  74. package/dist/tools/landscape.js +22 -7
  75. package/dist/tools/landscape.js.map +1 -1
  76. package/dist/tools/level.js +322 -51
  77. package/dist/tools/level.js.map +1 -1
  78. package/dist/tools/material.js +10 -4
  79. package/dist/tools/material.js.map +1 -1
  80. package/dist/tools/niagara.js +3 -2
  81. package/dist/tools/niagara.js.map +1 -1
  82. package/dist/tools/pcg.js +6 -5
  83. package/dist/tools/pcg.js.map +1 -1
  84. package/dist/tools/plugins.js +2 -0
  85. package/dist/tools/plugins.js.map +1 -1
  86. package/dist/tools/project.js +47 -20
  87. package/dist/tools/project.js.map +1 -1
  88. package/dist/types.d.ts +24 -0
  89. package/dist/types.js +46 -3
  90. package/dist/types.js.map +1 -1
  91. package/dist/ue-mcp.default.yml +493 -125
  92. package/package.json +2 -2
  93. package/plugin/ue_mcp_bridge/NATIVE_RUNTIME_VISIBILITY.md +53 -0
  94. package/plugin/ue_mcp_bridge/NATIVE_STATIC_MESH_USAGE_PROGRESS.md +115 -0
  95. package/plugin/ue_mcp_bridge/README_MassHandlers.md +97 -0
  96. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/BridgeServer.cpp +14 -0
  97. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/GameThreadExecutor.cpp +21 -4
  98. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/GameThreadExecutor.h +9 -0
  99. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/HandlerJsonProperty.h +395 -69
  100. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers.cpp +79 -3
  101. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers.h +25 -0
  102. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_ControlRig.cpp +3 -3
  103. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_ControlRigSequencer.cpp +3061 -0
  104. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_IKRetargeterAuthoring.cpp +961 -0
  105. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_IKRigAuthoring.cpp +900 -0
  106. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_Pose.cpp +769 -0
  107. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_Sequence.cpp +44 -3
  108. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_SkeletalLive.cpp +217 -11
  109. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_StateMachine.cpp +551 -24
  110. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_Validation.cpp +735 -0
  111. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers.cpp +352 -73
  112. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers.h +8 -0
  113. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkProperties.cpp +1 -1
  114. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkRead.cpp +503 -0
  115. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkRead.h +21 -0
  116. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkUpsert.cpp +2 -2
  117. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Enum.cpp +2 -2
  118. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Geometry.cpp +902 -0
  119. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Geometry.h +33 -0
  120. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Import.cpp +478 -134
  121. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Mesh.cpp +5 -5
  122. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_MeshBoolean.cpp +933 -0
  123. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_MeshBoolean.h +35 -0
  124. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Redirectors.cpp +478 -0
  125. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_RenderTarget.cpp +1 -1
  126. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Sockets.cpp +4 -4
  127. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Struct.cpp +2 -2
  128. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Subobject.cpp +319 -0
  129. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AudioHandlers_SoundCue.cpp +3 -3
  130. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers.cpp +482 -35
  131. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers.h +10 -0
  132. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Collision.cpp +502 -0
  133. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Collision.h +43 -0
  134. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Functions.cpp +89 -7
  135. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Graph.cpp +17 -51
  136. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Internal.h +107 -0
  137. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Properties.cpp +10 -10
  138. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Search.cpp +648 -0
  139. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DemoHandlers.cpp +5 -1
  140. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DiffHandlers.cpp +252 -1
  141. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DiffHandlers.h +10 -2
  142. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers.cpp +252 -41
  143. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers.h +4 -0
  144. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_Build.cpp +250 -27
  145. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_PIE.cpp +303 -125
  146. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_PIERuntime.cpp +73 -50
  147. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_RuntimeVisibility.cpp +757 -0
  148. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/FoliageHandlers.cpp +11 -2
  149. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/FoliageHandlers.h +3 -0
  150. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/FoliageHandlers_Batch.cpp +419 -0
  151. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers.cpp +39 -161
  152. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers.h +66 -1
  153. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers_BTAuthoring.cpp +1323 -0
  154. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers_BehaviorTree.cpp +1211 -0
  155. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers.cpp +2 -0
  156. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers.h +60 -0
  157. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers_Runtime.cpp +363 -12
  158. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/HandlerEditorState.h +84 -0
  159. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers.cpp +1288 -36
  160. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers.h +3 -0
  161. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers_Sculpt.cpp +41 -16
  162. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers.cpp +959 -213
  163. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers.h +66 -0
  164. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_BatchWrite.cpp +1571 -0
  165. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Convert.cpp +316 -0
  166. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Inspection.cpp +280 -0
  167. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_InstanceProjection.cpp +666 -0
  168. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_InstanceProjection_Internal.h +63 -0
  169. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Lights.cpp +43 -12
  170. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_MultiLevel.cpp +13 -47
  171. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_PostProcess.cpp +527 -0
  172. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Query.cpp +1169 -0
  173. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Refresh.cpp +723 -0
  174. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Save.cpp +325 -0
  175. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Spatial.cpp +360 -0
  176. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Trace.cpp +178 -58
  177. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Transient.cpp +337 -0
  178. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Volumes.cpp +25 -12
  179. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_WorldPartition.cpp +10 -9
  180. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_WorldPartitionSettings.cpp +686 -0
  181. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MassHandlers.cpp +467 -0
  182. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MassHandlers.h +22 -0
  183. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers.cpp +452 -175
  184. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers.h +5 -0
  185. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers_Build.cpp +597 -0
  186. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/NiagaraHandlers.cpp +17 -15
  187. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/PCGHandlers.cpp +38 -168
  188. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/PhysicsHandlers.cpp +39 -30
  189. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers.cpp +392 -78
  190. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers.h +2 -0
  191. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers_Internal.h +83 -0
  192. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SkeletalMeshHandlers.cpp +200 -0
  193. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SkeletalMeshHandlers.h +15 -0
  194. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SplineHandlers.cpp +19 -12
  195. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/StateTreeHandlers.cpp +31 -1
  196. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers.cpp +307 -69
  197. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers.h +84 -0
  198. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers_Extraction.cpp +8 -41
  199. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers_Properties.cpp +33 -52
  200. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/ActorSelectorTests.cpp +344 -0
  201. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AnimationControlRigTimelineTests.cpp +60 -0
  202. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AnimationPoseTests.cpp +115 -0
  203. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetBulkReadTests.cpp +177 -0
  204. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetDeleteGuardTests.cpp +276 -0
  205. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetPathResolutionTests.cpp +243 -0
  206. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetPropertyPersistTests.cpp +213 -0
  207. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetSubobjectTests.cpp +235 -0
  208. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AutomationRunnerTests.cpp +72 -0
  209. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BehaviorTreeAuthoringTests.cpp +526 -0
  210. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BehaviorTreeNodeTests.cpp +334 -0
  211. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BlueprintHandlerSurfaceTests.cpp +362 -0
  212. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BridgeProtocolTests.cpp +14 -0
  213. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/CollisionQueryTests.cpp +288 -0
  214. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/DataTableRowWriteTests.cpp +453 -0
  215. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/EditorFunctionCallTests.cpp +223 -0
  216. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/FoliageBatchTests.cpp +147 -0
  217. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/GameThreadGateTests.cpp +82 -0
  218. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/GasLiveAttributeTests.cpp +183 -0
  219. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LandscapeSampleTests.cpp +119 -0
  220. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelBatchWriteTests.cpp +176 -0
  221. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelComponentTreeInstanceCountTests.cpp +189 -0
  222. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelConvertBrushesTests.cpp +100 -0
  223. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelInstanceProjectionTests.cpp +265 -0
  224. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelPostProcessOverrideTests.cpp +258 -0
  225. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelQueryComponentsTests.cpp +195 -0
  226. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelRefreshTests.cpp +142 -0
  227. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelSaveReportTests.cpp +125 -0
  228. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelScriptHandlersTests.cpp +42 -0
  229. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelStaticMeshUsageTests.cpp +412 -0
  230. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelTraceWorldScopeTests.cpp +133 -0
  231. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelTransientActorTests.cpp +205 -0
  232. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelWorldPartitionSettingsTests.cpp +168 -0
  233. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MCPInstancedStructPathTestTypes.h +51 -0
  234. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MCPInstancedStructPathTests.cpp +379 -0
  235. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MaterialBuildTests.cpp +238 -0
  236. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MeshBooleanTests.cpp +170 -0
  237. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MeshGeometryTests.cpp +116 -0
  238. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/PackageSaveExtensionTests.cpp +161 -0
  239. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/PackageWriteGuardTests.cpp +253 -0
  240. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/RuntimeVisibilityTests.cpp +143 -0
  241. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/SequencerHandlerTests.cpp +660 -0
  242. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/SequencerScrubTests.cpp +50 -0
  243. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/SkeletonDiffTests.cpp +685 -0
  244. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/WidgetBlueprintResolveTests.cpp +145 -0
  245. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/UE_MCP_Bridge.cpp +80 -0
  246. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Public/HandlerFunctionCall.h +509 -0
  247. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Public/HandlerQuery.h +407 -0
  248. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Public/HandlerUtils.h +1008 -77
  249. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/UE_MCP_Bridge.Build.cs +87 -0
  250. package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/Private/MCPEngineStatus.cpp +152 -14
  251. package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/Private/Tests/MCPEngineStatusTests.cpp +65 -0
  252. package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/Public/MCPEngineStatus.h +32 -1
  253. package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/UE_MCP_BridgeStatus.Build.cs +2 -0
  254. package/skills/ue-mcp-animation/SKILL.md +154 -0
  255. package/skills/ue-mcp-blueprint/SKILL.md +71 -0
@@ -0,0 +1,35 @@
1
+ #pragma once
2
+
3
+ #include "CoreMinimal.h"
4
+ #include "Dom/JsonValue.h"
5
+ #include "Dom/JsonObject.h"
6
+
7
+ /**
8
+ * StaticMesh boolean CSG: union, subtract, intersect (#916).
9
+ *
10
+ * A StaticMesh repair needed subtract/union/intersect and no native action
11
+ * exposed one, so the only route was execute_python introspecting the
12
+ * unreal.GeometryScript_* modules at runtime, converting to a DynamicMesh,
13
+ * running the boolean, validating the result and writing it back by hand. That
14
+ * is a lot of moving parts to get right in a script, and every step of it is
15
+ * something the bridge should own.
16
+ *
17
+ * A separate class and translation unit rather than another FAssetHandlers
18
+ * partition: AssetHandlers.cpp is already 3700 lines and this registers itself.
19
+ * The action still reads as asset(mesh_boolean) on the wire.
20
+ *
21
+ * GeometryScripting is an engine PLUGIN, so its classes are reached through
22
+ * reflection at runtime rather than linked in Build.cs, the way the Water
23
+ * handlers reach their optional modules. A project with the plugin disabled
24
+ * gets a clear "GeometryScripting plugin not available" answer naming what to
25
+ * enable, instead of the module failing to link for everyone.
26
+ */
27
+ class FAssetMeshBooleanHandlers
28
+ {
29
+ public:
30
+ static void RegisterHandlers(class FMCPHandlerRegistry& Registry);
31
+
32
+ private:
33
+ /** Boolean two StaticMeshes into a third (or, opted into, back into one). */
34
+ static TSharedPtr<FJsonValue> MeshBoolean(const TSharedPtr<FJsonObject>& Params);
35
+ };
@@ -0,0 +1,478 @@
1
+ // asset(fixup_redirectors) (#908).
2
+ //
3
+ // bulk_rename moved 29 referenced assets and left 19 ObjectRedirector packages
4
+ // at the old paths, still pointed at by unloaded maps and assets. Nothing
5
+ // native could load and resave exactly those referencers, re-query the
6
+ // registry and delete only the redirectors that came out clean: UE 5.8's
7
+ // Python AssetTools does not expose fixup_referencers, and the blunt
8
+ // instrument, ResavePackages -FixupRedirects -ProjectOnly, started rewriting
9
+ // unrelated old packages.
10
+ //
11
+ // The shape of this action follows from that. It preflights the exact
12
+ // redirectors, their destinations and their hard and soft referencers, reports
13
+ // the exact packages it would touch, rewrites only those, re-queries the
14
+ // registry, and deletes a redirector only when nothing references it any more.
15
+ // A whole content root is refused unless the caller asks for it by name, so
16
+ // "fix these 19" cannot turn into "resave the project" by accident.
17
+
18
+ #include "AssetHandlers.h"
19
+ #include "HandlerRegistry.h"
20
+ #include "HandlerUtils.h"
21
+
22
+ #include "AssetRegistry/ARFilter.h"
23
+ #include "AssetRegistry/AssetRegistryModule.h"
24
+ #include "AssetRegistry/IAssetRegistry.h"
25
+ #include "AssetToolsModule.h"
26
+ #include "Dom/JsonObject.h"
27
+ #include "Dom/JsonValue.h"
28
+ #include "FileHelpers.h"
29
+ #include "IAssetTools.h"
30
+ #include "Misc/PackageName.h"
31
+ #include "Misc/Paths.h"
32
+ #include "ObjectTools.h"
33
+ #include "UObject/ObjectRedirector.h"
34
+ #include "UObject/Package.h"
35
+ #include "UObject/TopLevelAssetPath.h"
36
+ #include "UObject/UObjectGlobals.h"
37
+
38
+ namespace
39
+ {
40
+ const FTopLevelAssetPath MCPRedirectorClassPath(TEXT("/Script/CoreUObject"), TEXT("ObjectRedirector"));
41
+
42
+ /** One redirector and everything the preflight learned about it. */
43
+ struct FMCPRedirectorPlan
44
+ {
45
+ FString PackageName;
46
+ FString ObjectPath;
47
+ FString DestinationPath;
48
+ TArray<FString> HardReferencers;
49
+ TArray<FString> SoftReferencers;
50
+ FString PreflightError;
51
+
52
+ TArray<FString> AllReferencers() const
53
+ {
54
+ TArray<FString> All = HardReferencers;
55
+ for (const FString& Soft : SoftReferencers) All.AddUnique(Soft);
56
+ return All;
57
+ }
58
+ };
59
+
60
+ /** The content roots. Naming one of these as a path is a project-wide
61
+ * operation, which this action refuses without an explicit opt-in. */
62
+ bool MCPIsWholeContentRoot(const FString& Path)
63
+ {
64
+ FString Trimmed = Path;
65
+ Trimmed.TrimStartAndEndInline();
66
+ while (Trimmed.EndsWith(TEXT("/")) && Trimmed.Len() > 1)
67
+ {
68
+ Trimmed.LeftChopInline(1);
69
+ }
70
+ if (Trimmed.IsEmpty() || Trimmed == TEXT("/")) return true;
71
+ // "/Game" and "/MyPlugin" have exactly one slash: they name a mount
72
+ // point rather than a folder inside one.
73
+ int32 SlashCount = 0;
74
+ for (TCHAR Char : Trimmed)
75
+ {
76
+ if (Char == TEXT('/')) ++SlashCount;
77
+ }
78
+ return SlashCount <= 1;
79
+ }
80
+
81
+ TArray<TSharedPtr<FJsonValue>> MCPRedirectorStringsToJson(const TArray<FString>& Values)
82
+ {
83
+ TArray<TSharedPtr<FJsonValue>> Out;
84
+ Out.Reserve(Values.Num());
85
+ for (const FString& Value : Values) Out.Add(MakeShared<FJsonValueString>(Value));
86
+ return Out;
87
+ }
88
+
89
+ /** Package referencers of one package, split by whether the reference is
90
+ * hard. A soft referencer is the case the manual workaround kept missing:
91
+ * it does not force the package to load, so it never got rewritten. */
92
+ void MCPGatherReferencers(
93
+ IAssetRegistry& Registry,
94
+ const FName PackageName,
95
+ TArray<FString>& OutHard,
96
+ TArray<FString>& OutSoft)
97
+ {
98
+ TArray<FName> Hard;
99
+ Registry.GetReferencers(PackageName, Hard,
100
+ UE::AssetRegistry::EDependencyCategory::Package,
101
+ UE::AssetRegistry::EDependencyQuery::Hard);
102
+ TArray<FName> Soft;
103
+ Registry.GetReferencers(PackageName, Soft,
104
+ UE::AssetRegistry::EDependencyCategory::Package,
105
+ UE::AssetRegistry::EDependencyQuery::Soft);
106
+
107
+ for (const FName& Referencer : Hard)
108
+ {
109
+ if (Referencer != PackageName) OutHard.AddUnique(Referencer.ToString());
110
+ }
111
+ for (const FName& Referencer : Soft)
112
+ {
113
+ if (Referencer != PackageName) OutSoft.AddUnique(Referencer.ToString());
114
+ }
115
+ }
116
+ }
117
+
118
+ TSharedPtr<FJsonValue> FAssetHandlers::FixupRedirectors(const TSharedPtr<FJsonObject>& Params)
119
+ {
120
+ MCP_CHECK_GAME_THREAD();
121
+
122
+ const TArray<TSharedPtr<FJsonValue>>* PathsField = nullptr;
123
+ if (!Params->TryGetArrayField(TEXT("paths"), PathsField) || !PathsField)
124
+ {
125
+ return MCPError(TEXT("Missing 'paths' array: the redirector packages, or the folders holding them, to fix up."));
126
+ }
127
+ const TArray<FString> RequestedPaths = JsonArrayToStringList(PathsField);
128
+ if (RequestedPaths.Num() == 0)
129
+ {
130
+ return MCPError(TEXT("'paths' is empty. Name the redirector packages, or the folders holding them."));
131
+ }
132
+
133
+ const bool bDryRun = OptionalBool(Params, TEXT("dryRun"), false);
134
+ const bool bSave = OptionalBool(Params, TEXT("save"), true);
135
+ const bool bAllowProjectWide = OptionalBool(Params, TEXT("allowProjectWide"), false);
136
+
137
+ FAssetRegistryModule& RegistryModule =
138
+ FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
139
+ IAssetRegistry& Registry = RegistryModule.Get();
140
+
141
+ // ── Resolve the requested paths to an exact set of redirectors ──────────
142
+ TArray<FAssetData> Redirectors;
143
+ TArray<TSharedPtr<FJsonValue>> UnresolvedPaths;
144
+ for (const FString& RequestedPath : RequestedPaths)
145
+ {
146
+ if (MCPIsWholeContentRoot(RequestedPath) && !bAllowProjectWide)
147
+ {
148
+ return MCPError(FString::Printf(
149
+ TEXT("'%s' names a whole content root, which would resave the project. ")
150
+ TEXT("Name the redirector packages or the folders holding them, or pass allowProjectWide=true to mean it."),
151
+ *RequestedPath));
152
+ }
153
+ if (MCPIsProtectedAssetPath(RequestedPath))
154
+ {
155
+ return MCPError(FString::Printf(
156
+ TEXT("'%s' is on a protected mount (/Engine/, /Script/, /Memory/, /Temp/), which the bridge never rewrites or deletes."),
157
+ *RequestedPath));
158
+ }
159
+
160
+ const FMCPAssetPathForms Forms = MCPAssetPathForms(RequestedPath);
161
+
162
+ // A folder: every redirector under it, recursively.
163
+ FARFilter FolderFilter;
164
+ FolderFilter.PackagePaths.Add(FName(*Forms.PackagePath));
165
+ FolderFilter.bRecursivePaths = true;
166
+ FolderFilter.ClassPaths.Add(MCPRedirectorClassPath);
167
+ TArray<FAssetData> InFolder;
168
+ Registry.GetAssets(FolderFilter, InFolder);
169
+
170
+ // A package: the redirector living in it.
171
+ TArray<FAssetData> InPackage;
172
+ Registry.GetAssetsByPackageName(FName(*Forms.PackagePath), InPackage);
173
+
174
+ int32 FoundHere = 0;
175
+ for (const FAssetData& Candidate : InFolder)
176
+ {
177
+ if (!Candidate.IsRedirector()) continue;
178
+ Redirectors.AddUnique(Candidate);
179
+ ++FoundHere;
180
+ }
181
+ for (const FAssetData& Candidate : InPackage)
182
+ {
183
+ if (!Candidate.IsRedirector()) continue;
184
+ Redirectors.AddUnique(Candidate);
185
+ ++FoundHere;
186
+ }
187
+ if (FoundHere == 0)
188
+ {
189
+ // Not an error: a path with nothing to fix is the state this action
190
+ // is trying to reach. Reported so a typo is still visible.
191
+ TSharedPtr<FJsonObject> Entry = MakeShared<FJsonObject>();
192
+ Entry->SetStringField(TEXT("path"), RequestedPath);
193
+ Entry->SetStringField(TEXT("reason"), TEXT("no_redirector_found"));
194
+ UnresolvedPaths.Add(MakeShared<FJsonValueObject>(Entry));
195
+ }
196
+ }
197
+
198
+ // ── Preflight: destinations, referencers, and the packages to be written ─
199
+ TArray<FMCPRedirectorPlan> Plans;
200
+ TArray<UObjectRedirector*> RedirectorObjects;
201
+ TArray<FString> PackagesToLoad;
202
+ for (const FAssetData& Data : Redirectors)
203
+ {
204
+ FMCPRedirectorPlan Plan;
205
+ Plan.PackageName = Data.PackageName.ToString();
206
+ Plan.ObjectPath = Data.GetSoftObjectPath().ToString();
207
+
208
+ if (MCPIsProtectedAssetPath(Plan.PackageName))
209
+ {
210
+ Plan.PreflightError = TEXT("on a protected mount");
211
+ Plans.Add(MoveTemp(Plan));
212
+ continue;
213
+ }
214
+
215
+ UObjectRedirector* Redirector = Cast<UObjectRedirector>(MCPLoadAssetObject(Plan.ObjectPath));
216
+ if (!Redirector)
217
+ {
218
+ Plan.PreflightError = TEXT("the registry lists a redirector here but it would not load");
219
+ Plans.Add(MoveTemp(Plan));
220
+ continue;
221
+ }
222
+ Plan.DestinationPath = Redirector->DestinationObject
223
+ ? Redirector->DestinationObject->GetPathName()
224
+ : FString();
225
+ if (Plan.DestinationPath.IsEmpty())
226
+ {
227
+ // A redirector with no destination cannot be followed, so rewriting
228
+ // its referencers would drop the reference rather than repoint it.
229
+ Plan.PreflightError = TEXT("the redirector has no destination object, so its referencers cannot be repointed");
230
+ Plans.Add(MoveTemp(Plan));
231
+ continue;
232
+ }
233
+
234
+ MCPGatherReferencers(Registry, Data.PackageName, Plan.HardReferencers, Plan.SoftReferencers);
235
+ for (const FString& Referencer : Plan.AllReferencers())
236
+ {
237
+ PackagesToLoad.AddUnique(Referencer);
238
+ }
239
+
240
+ RedirectorObjects.Add(Redirector);
241
+ Plans.Add(MoveTemp(Plan));
242
+ }
243
+
244
+ // A referencer that is itself one of the redirectors being fixed is not a
245
+ // package to rewrite; it is one to delete.
246
+ for (const FMCPRedirectorPlan& Plan : Plans)
247
+ {
248
+ PackagesToLoad.Remove(Plan.PackageName);
249
+ }
250
+ PackagesToLoad.Sort();
251
+
252
+ TArray<TSharedPtr<FJsonValue>> PlanJson;
253
+ for (const FMCPRedirectorPlan& Plan : Plans)
254
+ {
255
+ TSharedPtr<FJsonObject> Entry = MakeShared<FJsonObject>();
256
+ Entry->SetStringField(TEXT("packageName"), Plan.PackageName);
257
+ Entry->SetStringField(TEXT("objectPath"), Plan.ObjectPath);
258
+ Entry->SetStringField(TEXT("destinationPath"), Plan.DestinationPath);
259
+ Entry->SetArrayField(TEXT("hardReferencers"), MCPRedirectorStringsToJson(Plan.HardReferencers));
260
+ Entry->SetArrayField(TEXT("softReferencers"), MCPRedirectorStringsToJson(Plan.SoftReferencers));
261
+ Entry->SetNumberField(TEXT("referencerCount"), Plan.AllReferencers().Num());
262
+ if (!Plan.PreflightError.IsEmpty())
263
+ {
264
+ Entry->SetStringField(TEXT("status"), TEXT("skipped"));
265
+ Entry->SetStringField(TEXT("error"), Plan.PreflightError);
266
+ }
267
+ PlanJson.Add(MakeShared<FJsonValueObject>(Entry));
268
+ }
269
+
270
+ auto Result = MCPSuccess();
271
+ Result->SetBoolField(TEXT("dryRun"), bDryRun);
272
+ Result->SetBoolField(TEXT("allowProjectWide"), bAllowProjectWide);
273
+ Result->SetNumberField(TEXT("redirectorCount"), Plans.Num());
274
+ Result->SetArrayField(TEXT("redirectors"), PlanJson);
275
+ Result->SetArrayField(TEXT("packagesToLoad"), MCPRedirectorStringsToJson(PackagesToLoad));
276
+ Result->SetArrayField(TEXT("packagesToSave"), MCPRedirectorStringsToJson(PackagesToLoad));
277
+ if (UnresolvedPaths.Num() > 0)
278
+ {
279
+ Result->SetArrayField(TEXT("unresolvedPaths"), UnresolvedPaths);
280
+ }
281
+
282
+ if (bDryRun || RedirectorObjects.Num() == 0)
283
+ {
284
+ Result->SetNumberField(TEXT("rewrittenPackageCount"), 0);
285
+ Result->SetNumberField(TEXT("deletedRedirectorCount"), 0);
286
+ if (RedirectorObjects.Num() == 0 && !bDryRun)
287
+ {
288
+ Result->SetStringField(TEXT("note"),
289
+ TEXT("No redirector needed fixing at the paths given, so nothing was loaded, saved or deleted."));
290
+ }
291
+ return MCPResult(Result);
292
+ }
293
+
294
+ // ── Rewrite exactly those packages ─────────────────────────────────────
295
+ // The referencers are loaded first because a soft reference in an unloaded
296
+ // package is invisible to the rewriter, which is how 19 stubs survived the
297
+ // original rename.
298
+ TArray<UPackage*> LoadedReferencers;
299
+ TArray<TSharedPtr<FJsonValue>> PackageStatuses;
300
+ for (const FString& PackageName : PackagesToLoad)
301
+ {
302
+ TSharedPtr<FJsonObject> Entry = MakeShared<FJsonObject>();
303
+ Entry->SetStringField(TEXT("packageName"), PackageName);
304
+
305
+ UPackage* Package = FindPackage(nullptr, *PackageName);
306
+ if (!Package)
307
+ {
308
+ Package = LoadPackage(nullptr, *PackageName, LOAD_None);
309
+ Entry->SetBoolField(TEXT("loadedByThisCall"), Package != nullptr);
310
+ }
311
+ else
312
+ {
313
+ Entry->SetBoolField(TEXT("loadedByThisCall"), false);
314
+ }
315
+
316
+ if (!Package)
317
+ {
318
+ Entry->SetStringField(TEXT("status"), TEXT("load_failed"));
319
+ PackageStatuses.Add(MakeShared<FJsonValueObject>(Entry));
320
+ continue;
321
+ }
322
+ LoadedReferencers.Add(Package);
323
+ Entry->SetStringField(TEXT("status"), TEXT("loaded"));
324
+ PackageStatuses.Add(MakeShared<FJsonValueObject>(Entry));
325
+ }
326
+
327
+ IAssetTools& AssetTools = FModuleManager::LoadModuleChecked<FAssetToolsModule>(TEXT("AssetTools")).Get();
328
+ // LeaveFixedUpRedirectors on purpose: the deletion below happens after the
329
+ // registry has been re-queried, so a redirector something still points at
330
+ // is never removed.
331
+ AssetTools.FixupReferencers(
332
+ RedirectorObjects,
333
+ /*bCheckoutDialogPrompt=*/false,
334
+ ERedirectFixupMode::LeaveFixedUpRedirectors);
335
+
336
+ const bool bFixupStillRunning = AssetTools.IsFixupReferencersInProgress();
337
+ Result->SetBoolField(TEXT("fixupInProgress"), bFixupStillRunning);
338
+
339
+ // ── Save exactly those packages ────────────────────────────────────────
340
+ // save=false skips this explicit pass only. The editor's own fix-up writes
341
+ // what it can on its own, so this is not a way to preview the change:
342
+ // dryRun is, and it returns before anything is loaded.
343
+ int32 SavedCount = 0;
344
+ if (!bSave)
345
+ {
346
+ Result->SetStringField(TEXT("saveNote"),
347
+ TEXT("save=false skipped the explicit save pass. The editor's referencer fix-up writes what it can regardless, ")
348
+ TEXT("so use dryRun=true to preview instead. Redirectors are still only deleted when the registry says nothing references them."));
349
+ }
350
+ if (bSave && !bFixupStillRunning)
351
+ {
352
+ TArray<UPackage*> ToSave;
353
+ for (UPackage* Package : LoadedReferencers)
354
+ {
355
+ if (Package && Package->IsDirty()) ToSave.Add(Package);
356
+ }
357
+ if (ToSave.Num() > 0)
358
+ {
359
+ UEditorLoadingAndSavingUtils::SavePackages(ToSave, /*bOnlyDirty=*/true);
360
+ }
361
+ TArray<FString> ModifiedFiles;
362
+ for (const TSharedPtr<FJsonValue>& Value : PackageStatuses)
363
+ {
364
+ const TSharedPtr<FJsonObject> Entry = Value->AsObject();
365
+ if (!Entry.IsValid()) continue;
366
+ FString PackageName;
367
+ Entry->TryGetStringField(TEXT("packageName"), PackageName);
368
+ UPackage* Package = FindPackage(nullptr, *PackageName);
369
+ if (!Package) continue;
370
+ const bool bStillDirty = Package->IsDirty();
371
+ Entry->SetBoolField(TEXT("stillDirty"), bStillDirty);
372
+ Entry->SetStringField(TEXT("status"), bStillDirty ? TEXT("save_failed") : TEXT("saved"));
373
+ if (!bStillDirty)
374
+ {
375
+ ++SavedCount;
376
+ FString FileName;
377
+ if (FPackageName::DoesPackageExist(PackageName, &FileName))
378
+ {
379
+ ModifiedFiles.Add(FileName);
380
+ }
381
+ }
382
+ }
383
+ // Re-query: the registry's referencer graph is what decides which
384
+ // redirectors are safe to delete, and it is stale until the rewritten
385
+ // packages have been rescanned.
386
+ if (ModifiedFiles.Num() > 0)
387
+ {
388
+ Registry.ScanModifiedAssetFiles(ModifiedFiles);
389
+ }
390
+ }
391
+ Result->SetNumberField(TEXT("rewrittenPackageCount"), SavedCount);
392
+ Result->SetArrayField(TEXT("packages"), PackageStatuses);
393
+
394
+ // ── Delete only the redirectors nothing points at any more ─────────────
395
+ TArray<UObject*> ToDelete;
396
+ TArray<TSharedPtr<FJsonValue>> RedirectorStatuses;
397
+ for (const FMCPRedirectorPlan& Plan : Plans)
398
+ {
399
+ TSharedPtr<FJsonObject> Entry = MakeShared<FJsonObject>();
400
+ Entry->SetStringField(TEXT("packageName"), Plan.PackageName);
401
+ Entry->SetStringField(TEXT("objectPath"), Plan.ObjectPath);
402
+ Entry->SetStringField(TEXT("destinationPath"), Plan.DestinationPath);
403
+ Entry->SetArrayField(TEXT("hardReferencers"), MCPRedirectorStringsToJson(Plan.HardReferencers));
404
+ Entry->SetArrayField(TEXT("softReferencers"), MCPRedirectorStringsToJson(Plan.SoftReferencers));
405
+
406
+ if (!Plan.PreflightError.IsEmpty())
407
+ {
408
+ Entry->SetStringField(TEXT("status"), TEXT("skipped"));
409
+ Entry->SetStringField(TEXT("error"), Plan.PreflightError);
410
+ RedirectorStatuses.Add(MakeShared<FJsonValueObject>(Entry));
411
+ continue;
412
+ }
413
+
414
+ TArray<FString> RemainingHard;
415
+ TArray<FString> RemainingSoft;
416
+ MCPGatherReferencers(Registry, FName(*Plan.PackageName), RemainingHard, RemainingSoft);
417
+ const int32 Remaining = RemainingHard.Num() + RemainingSoft.Num();
418
+ Entry->SetNumberField(TEXT("referencersBefore"), Plan.AllReferencers().Num());
419
+ Entry->SetNumberField(TEXT("referencersAfter"), Remaining);
420
+ Entry->SetArrayField(TEXT("remainingHardReferencers"), MCPRedirectorStringsToJson(RemainingHard));
421
+ Entry->SetArrayField(TEXT("remainingSoftReferencers"), MCPRedirectorStringsToJson(RemainingSoft));
422
+
423
+ if (Remaining > 0 || bFixupStillRunning)
424
+ {
425
+ Entry->SetStringField(TEXT("status"), TEXT("kept"));
426
+ Entry->SetStringField(TEXT("reason"), bFixupStillRunning
427
+ ? TEXT("the editor's referencer fix-up is still running, so nothing was deleted this call")
428
+ : TEXT("something still references it; rerun once those packages are saved"));
429
+ RedirectorStatuses.Add(MakeShared<FJsonValueObject>(Entry));
430
+ continue;
431
+ }
432
+
433
+ if (UObject* Redirector = MCPLoadAssetObject(Plan.ObjectPath))
434
+ {
435
+ ToDelete.Add(Redirector);
436
+ Entry->SetStringField(TEXT("status"), TEXT("deleting"));
437
+ }
438
+ else
439
+ {
440
+ // Already gone, which is the state this action is trying to reach.
441
+ Entry->SetStringField(TEXT("status"), TEXT("deleted"));
442
+ }
443
+ RedirectorStatuses.Add(MakeShared<FJsonValueObject>(Entry));
444
+ }
445
+
446
+ int32 DeletedCount = 0;
447
+ if (ToDelete.Num() > 0)
448
+ {
449
+ DeletedCount = ObjectTools::DeleteObjects(
450
+ ToDelete, /*bShowConfirmation=*/false, ObjectTools::EAllowCancelDuringDelete::CancelNotAllowed);
451
+ }
452
+
453
+ // The deletion is reported from what is on disk afterwards, not from the
454
+ // return count, because a package the editor declined to remove is exactly
455
+ // the case that must not read as cleaned up.
456
+ int32 ConfirmedDeleted = 0;
457
+ for (const TSharedPtr<FJsonValue>& Value : RedirectorStatuses)
458
+ {
459
+ const TSharedPtr<FJsonObject> Entry = Value->AsObject();
460
+ if (!Entry.IsValid()) continue;
461
+ FString Status;
462
+ Entry->TryGetStringField(TEXT("status"), Status);
463
+ if (Status != TEXT("deleting") && Status != TEXT("deleted")) continue;
464
+
465
+ FString PackageName;
466
+ Entry->TryGetStringField(TEXT("packageName"), PackageName);
467
+ const bool bStillOnDisk = FPackageName::DoesPackageExist(PackageName);
468
+ Entry->SetStringField(TEXT("status"), bStillOnDisk ? TEXT("delete_failed") : TEXT("deleted"));
469
+ if (!bStillOnDisk) ++ConfirmedDeleted;
470
+ }
471
+
472
+ Result->SetNumberField(TEXT("deleteAttemptedCount"), ToDelete.Num());
473
+ Result->SetNumberField(TEXT("deleteReportedCount"), DeletedCount);
474
+ Result->SetNumberField(TEXT("deletedRedirectorCount"), ConfirmedDeleted);
475
+ Result->SetArrayField(TEXT("redirectors"), RedirectorStatuses);
476
+ MCPSetUpdated(Result);
477
+ return MCPResult(Result);
478
+ }
@@ -199,7 +199,7 @@ TSharedPtr<FJsonValue> FAssetHandlers::CreateRenderTarget2D(const TSharedPtr<FJs
199
199
  // Probe first so the idempotent hit can report the settings of the asset
200
200
  // that is already on disk instead of the lean "existed" record.
201
201
  const FString ObjectPath = PackageName + TEXT(".") + Name;
202
- if (UObject* ExistingObject = LoadObject<UObject>(nullptr, *ObjectPath))
202
+ if (UObject* ExistingObject = MCPLoadAssetObject(ObjectPath))
203
203
  {
204
204
  if (OnConflict == TEXT("error"))
205
205
  {
@@ -49,7 +49,7 @@ TSharedPtr<FJsonValue> FAssetHandlers::AddSocket(const TSharedPtr<FJsonObject>&
49
49
  RelScale.Z = (*ScaleObj)->GetNumberField(TEXT("z"));
50
50
  }
51
51
 
52
- UObject* Asset = LoadObject<UObject>(nullptr, *AssetPath);
52
+ UObject* Asset = MCPLoadAssetObject(AssetPath);
53
53
  if (!Asset)
54
54
  {
55
55
  return MCPError(FString::Printf(TEXT("Could not load asset '%s'"), *AssetPath));
@@ -306,7 +306,7 @@ TSharedPtr<FJsonValue> FAssetHandlers::RemoveSocket(const TSharedPtr<FJsonObject
306
306
  FString SocketName;
307
307
  if (auto Err = RequireString(Params, TEXT("socketName"), SocketName)) return Err;
308
308
 
309
- UObject* Asset = LoadObject<UObject>(nullptr, *AssetPath);
309
+ UObject* Asset = MCPLoadAssetObject(AssetPath);
310
310
  if (!Asset)
311
311
  {
312
312
  return MCPError(FString::Printf(TEXT("Could not load asset '%s'"), *AssetPath));
@@ -405,7 +405,7 @@ TSharedPtr<FJsonValue> FAssetHandlers::ListSockets(const TSharedPtr<FJsonObject>
405
405
  FString AssetPath;
406
406
  if (auto Err = RequireString(Params, TEXT("assetPath"), AssetPath)) return Err;
407
407
 
408
- UObject* Asset = LoadObject<UObject>(nullptr, *AssetPath);
408
+ UObject* Asset = MCPLoadAssetObject(AssetPath);
409
409
  if (!Asset)
410
410
  {
411
411
  return MCPError(FString::Printf(TEXT("Could not load asset '%s'"), *AssetPath));
@@ -545,7 +545,7 @@ TSharedPtr<FJsonValue> FAssetHandlers::SetSocketTransform(const TSharedPtr<FJson
545
545
  FString SocketName;
546
546
  if (auto Err = RequireString(Params, TEXT("socketName"), SocketName)) return Err;
547
547
 
548
- UObject* Asset = LoadObject<UObject>(nullptr, *AssetPath);
548
+ UObject* Asset = MCPLoadAssetObject(AssetPath);
549
549
  if (!Asset)
550
550
  {
551
551
  return MCPError(FString::Printf(TEXT("Could not load asset '%s'"), *AssetPath));
@@ -168,7 +168,7 @@ TSharedPtr<FJsonValue> FAssetHandlers::ListStructFields(const TSharedPtr<FJsonOb
168
168
  FString AssetPath;
169
169
  if (auto Err = RequireStringAlt(Params, TEXT("assetPath"), TEXT("path"), AssetPath)) return Err;
170
170
 
171
- UUserDefinedStruct* Struct = Cast<UUserDefinedStruct>(UEditorAssetLibrary::LoadAsset(AssetPath));
171
+ UUserDefinedStruct* Struct = LoadAssetByPath<UUserDefinedStruct>(AssetPath);
172
172
  if (!Struct) return MCPError(FString::Printf(TEXT("UserDefinedStruct not found (native structs are not editable): %s"), *AssetPath));
173
173
 
174
174
  TSharedPtr<FJsonObject> Res = MCPSuccess();
@@ -193,7 +193,7 @@ TSharedPtr<FJsonValue> FAssetHandlers::EditUserDefinedStruct(const TSharedPtr<FJ
193
193
  FString Op;
194
194
  if (auto Err = RequireString(Params, TEXT("op"), Op)) return Err;
195
195
 
196
- UUserDefinedStruct* Struct = Cast<UUserDefinedStruct>(UEditorAssetLibrary::LoadAsset(AssetPath));
196
+ UUserDefinedStruct* Struct = LoadAssetByPath<UUserDefinedStruct>(AssetPath);
197
197
  if (!Struct) return MCPError(FString::Printf(TEXT("UserDefinedStruct not found (native structs are not editable): %s"), *AssetPath));
198
198
 
199
199
  TSharedPtr<FJsonObject> Res = MCPSuccess();