ue-mcp 1.2.4 → 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 (243) 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/lean-context.js +8 -3
  34. package/dist/lean-context.js.map +1 -1
  35. package/dist/locking.js +11 -2
  36. package/dist/locking.js.map +1 -1
  37. package/dist/project.d.ts +11 -0
  38. package/dist/project.js +23 -2
  39. package/dist/project.js.map +1 -1
  40. package/dist/python-gate.d.ts +95 -0
  41. package/dist/python-gate.js +175 -0
  42. package/dist/python-gate.js.map +1 -0
  43. package/dist/tool-counts.json +16 -16
  44. package/dist/tools/animation.js +288 -24
  45. package/dist/tools/animation.js.map +1 -1
  46. package/dist/tools/asset.js +79 -14
  47. package/dist/tools/asset.js.map +1 -1
  48. package/dist/tools/audio.js +59 -5
  49. package/dist/tools/audio.js.map +1 -1
  50. package/dist/tools/blueprint.js +29 -12
  51. package/dist/tools/blueprint.js.map +1 -1
  52. package/dist/tools/chooser.js +4 -1
  53. package/dist/tools/chooser.js.map +1 -1
  54. package/dist/tools/editor.js +81 -43
  55. package/dist/tools/editor.js.map +1 -1
  56. package/dist/tools/feedback.js +214 -48
  57. package/dist/tools/feedback.js.map +1 -1
  58. package/dist/tools/foliage.js +27 -1
  59. package/dist/tools/foliage.js.map +1 -1
  60. package/dist/tools/gameplay.js +38 -11
  61. package/dist/tools/gameplay.js.map +1 -1
  62. package/dist/tools/gas.js +17 -5
  63. package/dist/tools/gas.js.map +1 -1
  64. package/dist/tools/landscape.js +22 -7
  65. package/dist/tools/landscape.js.map +1 -1
  66. package/dist/tools/level.js +322 -51
  67. package/dist/tools/level.js.map +1 -1
  68. package/dist/tools/material.js +10 -4
  69. package/dist/tools/material.js.map +1 -1
  70. package/dist/tools/niagara.js +3 -2
  71. package/dist/tools/niagara.js.map +1 -1
  72. package/dist/tools/pcg.js +6 -5
  73. package/dist/tools/pcg.js.map +1 -1
  74. package/dist/tools/project.js +47 -20
  75. package/dist/tools/project.js.map +1 -1
  76. package/dist/types.d.ts +21 -0
  77. package/dist/types.js +46 -3
  78. package/dist/types.js.map +1 -1
  79. package/dist/ue-mcp.default.yml +493 -125
  80. package/package.json +2 -2
  81. package/plugin/ue_mcp_bridge/NATIVE_RUNTIME_VISIBILITY.md +53 -0
  82. package/plugin/ue_mcp_bridge/NATIVE_STATIC_MESH_USAGE_PROGRESS.md +115 -0
  83. package/plugin/ue_mcp_bridge/README_MassHandlers.md +97 -0
  84. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/BridgeServer.cpp +14 -0
  85. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/GameThreadExecutor.cpp +21 -4
  86. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/GameThreadExecutor.h +9 -0
  87. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/HandlerJsonProperty.h +395 -69
  88. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers.cpp +79 -3
  89. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers.h +25 -0
  90. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_ControlRig.cpp +3 -3
  91. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_ControlRigSequencer.cpp +3061 -0
  92. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_IKRetargeterAuthoring.cpp +961 -0
  93. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_IKRigAuthoring.cpp +900 -0
  94. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_Pose.cpp +769 -0
  95. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_Sequence.cpp +44 -3
  96. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_SkeletalLive.cpp +217 -11
  97. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_StateMachine.cpp +551 -24
  98. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_Validation.cpp +735 -0
  99. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers.cpp +352 -73
  100. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers.h +8 -0
  101. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkProperties.cpp +1 -1
  102. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkRead.cpp +503 -0
  103. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkRead.h +21 -0
  104. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkUpsert.cpp +2 -2
  105. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Enum.cpp +2 -2
  106. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Geometry.cpp +902 -0
  107. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Geometry.h +33 -0
  108. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Import.cpp +478 -134
  109. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Mesh.cpp +5 -5
  110. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_MeshBoolean.cpp +933 -0
  111. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_MeshBoolean.h +35 -0
  112. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Redirectors.cpp +478 -0
  113. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_RenderTarget.cpp +1 -1
  114. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Sockets.cpp +4 -4
  115. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Struct.cpp +2 -2
  116. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Subobject.cpp +319 -0
  117. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AudioHandlers_SoundCue.cpp +3 -3
  118. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers.cpp +482 -35
  119. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers.h +10 -0
  120. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Collision.cpp +502 -0
  121. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Collision.h +43 -0
  122. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Functions.cpp +89 -7
  123. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Graph.cpp +17 -51
  124. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Internal.h +107 -0
  125. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Properties.cpp +10 -10
  126. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Search.cpp +648 -0
  127. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DemoHandlers.cpp +5 -1
  128. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DiffHandlers.cpp +252 -1
  129. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DiffHandlers.h +10 -2
  130. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers.cpp +252 -41
  131. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers.h +4 -0
  132. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_Build.cpp +250 -27
  133. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_PIE.cpp +303 -125
  134. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_PIERuntime.cpp +73 -50
  135. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_RuntimeVisibility.cpp +757 -0
  136. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/FoliageHandlers.cpp +11 -2
  137. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/FoliageHandlers.h +3 -0
  138. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/FoliageHandlers_Batch.cpp +419 -0
  139. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers.cpp +39 -161
  140. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers.h +66 -1
  141. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers_BTAuthoring.cpp +1323 -0
  142. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers_BehaviorTree.cpp +1211 -0
  143. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers.cpp +2 -0
  144. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers.h +60 -0
  145. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers_Runtime.cpp +363 -12
  146. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/HandlerEditorState.h +84 -0
  147. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers.cpp +1288 -36
  148. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers.h +3 -0
  149. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers_Sculpt.cpp +41 -16
  150. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers.cpp +959 -213
  151. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers.h +66 -0
  152. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_BatchWrite.cpp +1571 -0
  153. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Convert.cpp +316 -0
  154. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Inspection.cpp +280 -0
  155. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_InstanceProjection.cpp +666 -0
  156. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_InstanceProjection_Internal.h +63 -0
  157. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Lights.cpp +43 -12
  158. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_MultiLevel.cpp +13 -47
  159. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_PostProcess.cpp +527 -0
  160. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Query.cpp +1169 -0
  161. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Refresh.cpp +723 -0
  162. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Save.cpp +325 -0
  163. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Spatial.cpp +360 -0
  164. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Trace.cpp +178 -58
  165. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Transient.cpp +337 -0
  166. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Volumes.cpp +25 -12
  167. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_WorldPartition.cpp +10 -9
  168. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_WorldPartitionSettings.cpp +686 -0
  169. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MassHandlers.cpp +467 -0
  170. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MassHandlers.h +22 -0
  171. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers.cpp +452 -175
  172. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers.h +5 -0
  173. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers_Build.cpp +597 -0
  174. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/NiagaraHandlers.cpp +17 -15
  175. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/PCGHandlers.cpp +38 -168
  176. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/PhysicsHandlers.cpp +39 -30
  177. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers.cpp +392 -78
  178. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers.h +2 -0
  179. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers_Internal.h +83 -0
  180. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SkeletalMeshHandlers.cpp +200 -0
  181. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SkeletalMeshHandlers.h +15 -0
  182. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SplineHandlers.cpp +19 -12
  183. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/StateTreeHandlers.cpp +31 -1
  184. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers.cpp +307 -69
  185. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers.h +84 -0
  186. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers_Extraction.cpp +8 -41
  187. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers_Properties.cpp +33 -52
  188. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/ActorSelectorTests.cpp +344 -0
  189. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AnimationControlRigTimelineTests.cpp +60 -0
  190. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AnimationPoseTests.cpp +115 -0
  191. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetBulkReadTests.cpp +177 -0
  192. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetDeleteGuardTests.cpp +276 -0
  193. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetPathResolutionTests.cpp +243 -0
  194. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetPropertyPersistTests.cpp +213 -0
  195. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetSubobjectTests.cpp +235 -0
  196. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AutomationRunnerTests.cpp +72 -0
  197. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BehaviorTreeAuthoringTests.cpp +526 -0
  198. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BehaviorTreeNodeTests.cpp +334 -0
  199. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BlueprintHandlerSurfaceTests.cpp +362 -0
  200. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BridgeProtocolTests.cpp +14 -0
  201. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/CollisionQueryTests.cpp +288 -0
  202. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/DataTableRowWriteTests.cpp +453 -0
  203. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/EditorFunctionCallTests.cpp +223 -0
  204. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/FoliageBatchTests.cpp +147 -0
  205. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/GameThreadGateTests.cpp +82 -0
  206. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/GasLiveAttributeTests.cpp +183 -0
  207. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LandscapeSampleTests.cpp +119 -0
  208. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelBatchWriteTests.cpp +176 -0
  209. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelComponentTreeInstanceCountTests.cpp +189 -0
  210. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelConvertBrushesTests.cpp +100 -0
  211. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelInstanceProjectionTests.cpp +265 -0
  212. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelPostProcessOverrideTests.cpp +258 -0
  213. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelQueryComponentsTests.cpp +195 -0
  214. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelRefreshTests.cpp +142 -0
  215. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelSaveReportTests.cpp +125 -0
  216. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelScriptHandlersTests.cpp +42 -0
  217. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelStaticMeshUsageTests.cpp +412 -0
  218. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelTraceWorldScopeTests.cpp +133 -0
  219. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelTransientActorTests.cpp +205 -0
  220. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelWorldPartitionSettingsTests.cpp +168 -0
  221. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MCPInstancedStructPathTestTypes.h +51 -0
  222. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MCPInstancedStructPathTests.cpp +379 -0
  223. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MaterialBuildTests.cpp +238 -0
  224. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MeshBooleanTests.cpp +170 -0
  225. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MeshGeometryTests.cpp +116 -0
  226. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/PackageSaveExtensionTests.cpp +161 -0
  227. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/PackageWriteGuardTests.cpp +253 -0
  228. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/RuntimeVisibilityTests.cpp +143 -0
  229. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/SequencerHandlerTests.cpp +660 -0
  230. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/SequencerScrubTests.cpp +50 -0
  231. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/SkeletonDiffTests.cpp +685 -0
  232. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/WidgetBlueprintResolveTests.cpp +145 -0
  233. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/UE_MCP_Bridge.cpp +80 -0
  234. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Public/HandlerFunctionCall.h +509 -0
  235. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Public/HandlerQuery.h +407 -0
  236. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Public/HandlerUtils.h +1008 -77
  237. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/UE_MCP_Bridge.Build.cs +87 -0
  238. package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/Private/MCPEngineStatus.cpp +152 -14
  239. package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/Private/Tests/MCPEngineStatusTests.cpp +65 -0
  240. package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/Public/MCPEngineStatus.h +32 -1
  241. package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/UE_MCP_BridgeStatus.Build.cs +2 -0
  242. package/skills/ue-mcp-animation/SKILL.md +154 -0
  243. package/skills/ue-mcp-blueprint/SKILL.md +71 -0
@@ -0,0 +1,177 @@
1
+ // Coverage for asset(bulk_read_properties) that is safe to run anywhere.
2
+ //
3
+ // run_automation_tests dispatches every EditorContext/EngineFilter test in the
4
+ // process against whatever project the bridge is attached to. This action
5
+ // never writes, so the assertions can run it for real; they are written about
6
+ // the contract (registration, argument rejection, the absent-versus-null
7
+ // distinction, the caps) rather than about any particular project's content.
8
+
9
+ #if WITH_DEV_AUTOMATION_TESTS
10
+
11
+ #include "HandlerRegistry.h"
12
+ #include "Handlers/AssetHandlers_BulkRead.h"
13
+ #include "Misc/AutomationTest.h"
14
+
15
+ namespace
16
+ {
17
+ TSharedPtr<FJsonObject> MCPBulkReadTestRun(FMCPHandlerRegistry& Registry, const TSharedPtr<FJsonObject>& Request)
18
+ {
19
+ const TSharedPtr<FJsonValue> Response =
20
+ Registry.ExecuteHandler(TEXT("bulk_read_asset_properties"), Request);
21
+ if (!Response.IsValid() || Response->Type != EJson::Object)
22
+ {
23
+ return nullptr;
24
+ }
25
+ return Response->AsObject();
26
+ }
27
+ }
28
+
29
+ IMPLEMENT_SIMPLE_AUTOMATION_TEST(
30
+ FAssetBulkReadContractTest,
31
+ "UE.MCP.Asset.BulkRead.Contract",
32
+ EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
33
+
34
+ bool FAssetBulkReadContractTest::RunTest(const FString& Parameters)
35
+ {
36
+ FMCPHandlerRegistry Registry;
37
+ FAssetBulkReadHandlers::RegisterHandlers(Registry);
38
+
39
+ TestTrue(TEXT("bulk_read_asset_properties is registered"),
40
+ Registry.HasHandler(TEXT("bulk_read_asset_properties")));
41
+ TestTrue(
42
+ TEXT("it carries its own timeout, because loading hundreds of assets outlives the default"),
43
+ Registry.GetHandlerTimeout(TEXT("bulk_read_asset_properties")) > 0.0f);
44
+
45
+ // No propertyNames means there is nothing to read, and returning an empty
46
+ // success would look like "no assets have these properties".
47
+ {
48
+ const TSharedPtr<FJsonObject> Response = MCPBulkReadTestRun(Registry, MakeShared<FJsonObject>());
49
+ TestTrue(TEXT("a request with no properties returns an object"), Response.IsValid());
50
+ if (Response.IsValid())
51
+ {
52
+ TestFalse(TEXT("a request with no properties fails"), Response->GetBoolField(TEXT("success")));
53
+ TestTrue(TEXT("the error names propertyNames"),
54
+ Response->GetStringField(TEXT("error")).Contains(TEXT("propertyNames")));
55
+ }
56
+ }
57
+
58
+ // Properties but no selection is the other half of the same mistake.
59
+ {
60
+ TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
61
+ Request->SetArrayField(TEXT("propertyNames"), { MakeShared<FJsonValueString>(TEXT("bCanEverTick")) });
62
+ const TSharedPtr<FJsonObject> Response = MCPBulkReadTestRun(Registry, Request);
63
+ if (Response.IsValid())
64
+ {
65
+ TestFalse(TEXT("a request with no selection fails"), Response->GetBoolField(TEXT("success")));
66
+ TestTrue(TEXT("the error offers both selection shapes"),
67
+ Response->GetStringField(TEXT("error")).Contains(TEXT("assetPaths")) &&
68
+ Response->GetStringField(TEXT("error")).Contains(TEXT("directory")));
69
+ }
70
+ }
71
+
72
+ // An unresolvable class filter is reported as such rather than silently
73
+ // matching nothing, which would read as "the directory is empty".
74
+ {
75
+ TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
76
+ Request->SetArrayField(TEXT("propertyNames"), { MakeShared<FJsonValueString>(TEXT("bCanEverTick")) });
77
+ Request->SetStringField(TEXT("directory"), TEXT("/Game"));
78
+ Request->SetArrayField(TEXT("classNames"), { MakeShared<FJsonValueString>(TEXT("NotARealUnrealClass")) });
79
+ const TSharedPtr<FJsonObject> Response = MCPBulkReadTestRun(Registry, Request);
80
+ if (Response.IsValid())
81
+ {
82
+ TestFalse(TEXT("an unresolvable class fails"), Response->GetBoolField(TEXT("success")));
83
+ TestEqual(TEXT("and says why"),
84
+ Response->GetStringField(TEXT("reason")), FString(TEXT("class_not_found")));
85
+ }
86
+ }
87
+
88
+ // suspectOnly is an AND, so pairing it with whereMode 'any' would change
89
+ // what the other predicates mean.
90
+ {
91
+ TSharedPtr<FJsonObject> Predicate = MakeShared<FJsonObject>();
92
+ Predicate->SetStringField(TEXT("field"), TEXT("className"));
93
+ Predicate->SetStringField(TEXT("op"), TEXT("contains"));
94
+ Predicate->SetStringField(TEXT("value"), TEXT("Sound"));
95
+
96
+ TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
97
+ Request->SetArrayField(TEXT("propertyNames"), { MakeShared<FJsonValueString>(TEXT("bCanEverTick")) });
98
+ Request->SetStringField(TEXT("directory"), TEXT("/Game"));
99
+ Request->SetBoolField(TEXT("suspectOnly"), true);
100
+ Request->SetStringField(TEXT("whereMode"), TEXT("any"));
101
+ Request->SetArrayField(TEXT("where"), { MakeShared<FJsonValueObject>(Predicate) });
102
+ const TSharedPtr<FJsonObject> Response = MCPBulkReadTestRun(Registry, Request);
103
+ if (Response.IsValid())
104
+ {
105
+ TestFalse(TEXT("suspectOnly with whereMode any fails"), Response->GetBoolField(TEXT("success")));
106
+ }
107
+ }
108
+
109
+ // An unknown operator is named, with the valid ones listed. This comes
110
+ // from the shared query header, so this assertion is also what stops the
111
+ // level and asset vocabularies from drifting apart unnoticed.
112
+ {
113
+ TSharedPtr<FJsonObject> Predicate = MakeShared<FJsonObject>();
114
+ Predicate->SetStringField(TEXT("field"), TEXT("className"));
115
+ Predicate->SetStringField(TEXT("op"), TEXT("soundsLike"));
116
+
117
+ TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
118
+ Request->SetArrayField(TEXT("propertyNames"), { MakeShared<FJsonValueString>(TEXT("bCanEverTick")) });
119
+ Request->SetStringField(TEXT("directory"), TEXT("/Game"));
120
+ Request->SetArrayField(TEXT("where"), { MakeShared<FJsonValueObject>(Predicate) });
121
+ const TSharedPtr<FJsonObject> Response = MCPBulkReadTestRun(Registry, Request);
122
+ if (Response.IsValid())
123
+ {
124
+ TestFalse(TEXT("an unknown operator fails"), Response->GetBoolField(TEXT("success")));
125
+ TestTrue(TEXT("and lists the valid operators"),
126
+ Response->GetStringField(TEXT("error")).Contains(TEXT("notContains")));
127
+ }
128
+ }
129
+
130
+ return true;
131
+ }
132
+
133
+ IMPLEMENT_SIMPLE_AUTOMATION_TEST(
134
+ FAssetBulkReadAbsentVersusNullTest,
135
+ "UE.MCP.Asset.BulkRead.AbsentIsNotNull",
136
+ EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
137
+
138
+ bool FAssetBulkReadAbsentVersusNullTest::RunTest(const FString& Parameters)
139
+ {
140
+ FMCPHandlerRegistry Registry;
141
+ FAssetBulkReadHandlers::RegisterHandlers(Registry);
142
+
143
+ // A property no class has, read off an engine asset that certainly exists.
144
+ // "this class has no such property" and "this property is unset" are
145
+ // different findings; collapsing them is what makes an audit unactionable.
146
+ TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
147
+ Request->SetArrayField(TEXT("assetPaths"), {
148
+ MakeShared<FJsonValueString>(TEXT("/Engine/BasicShapes/Cube.Cube")) });
149
+ Request->SetArrayField(TEXT("propertyNames"), {
150
+ MakeShared<FJsonValueString>(TEXT("ThisPropertyDoesNotExistAnywhere")) });
151
+
152
+ const TSharedPtr<FJsonObject> Response = MCPBulkReadTestRun(Registry, Request);
153
+ if (!Response.IsValid() || !Response->GetBoolField(TEXT("success")))
154
+ {
155
+ // The engine content pack is not guaranteed to be mounted in every
156
+ // context this test runs in, and this test may not create an asset of
157
+ // its own to stand in for it.
158
+ return true;
159
+ }
160
+
161
+ TestFalse(TEXT("reading an asset dirties nothing"), Response->GetBoolField(TEXT("dirtiedPackages")));
162
+
163
+ const TArray<TSharedPtr<FJsonValue>>& Rows = Response->GetArrayField(TEXT("rows"));
164
+ if (Rows.Num() == 1 && Rows[0].IsValid() && Rows[0]->Type == EJson::Object)
165
+ {
166
+ const TSharedPtr<FJsonObject> Row = Rows[0]->AsObject();
167
+ TestEqual(TEXT("the unknown property is reported as missing, not as null"),
168
+ Row->GetArrayField(TEXT("missingProperties")).Num(), 1);
169
+ TestTrue(TEXT("and the row is flagged suspect"), Row->GetBoolField(TEXT("suspect")));
170
+ TestEqual(TEXT("props carries no invented entry for it"),
171
+ Row->GetObjectField(TEXT("props"))->Values.Num(), 0);
172
+ }
173
+
174
+ return true;
175
+ }
176
+
177
+ #endif // WITH_DEV_AUTOMATION_TESTS
@@ -0,0 +1,276 @@
1
+ // Regression coverage for #976.
2
+ //
3
+ // asset(delete) and asset(delete_batch) both called
4
+ // UEditorAssetLibrary::DeleteAsset, whose own header says "This is a Force
5
+ // Delete. It doesn't check if the asset has references in other Levels or by
6
+ // Actors." They called it whatever the caller passed for `force`, so a
7
+ // referenced asset was destroyed and every package pointing at it was rewritten
8
+ // to point at nothing, while `force=false` read like a guard that did not
9
+ // exist.
10
+ //
11
+ // The fix asks the Asset Registry for referencers before the delete and refuses
12
+ // when it finds any, taking the force path only when the caller asked for it.
13
+ // What follows asserts both halves, plus that the protected-mount guardrail
14
+ // still stands in front of either path.
15
+ //
16
+ // Everything happens under a private mount point in the system temp area, for
17
+ // the same reason the property-persistence tests do it: run_automation_tests
18
+ // dispatches every EditorContext test against whatever project the bridge
19
+ // happens to be attached to, and a delete test must never be able to reach it.
20
+
21
+ #if WITH_DEV_AUTOMATION_TESTS
22
+
23
+ #include "HandlerRegistry.h"
24
+ #include "HandlerUtils.h"
25
+ #include "Handlers/AssetHandlers.h"
26
+
27
+ #include "AssetRegistry/AssetRegistryModule.h"
28
+ #include "AssetRegistry/IAssetRegistry.h"
29
+ #include "Dom/JsonObject.h"
30
+ #include "Dom/JsonValue.h"
31
+ #include "Engine/DataTable.h"
32
+ #include "HAL/FileManager.h"
33
+ #include "HAL/PlatformProcess.h"
34
+ #include "Misc/AutomationTest.h"
35
+ #include "Misc/Guid.h"
36
+ #include "Misc/PackageName.h"
37
+ #include "Misc/Paths.h"
38
+ #include "UObject/ObjectRedirector.h"
39
+ #include "UObject/Package.h"
40
+ #include "UObject/PerPlatformProperties.h"
41
+
42
+ namespace
43
+ {
44
+ const TCHAR* const MCPDeleteGuardTestRoot = TEXT("/UEMCPAssetDeleteGuardTest/");
45
+
46
+ /** Mount a private content root for the duration of the test and take it
47
+ * back down again, so no delete here can reach a user's content. */
48
+ struct FScopedDeleteGuardMount
49
+ {
50
+ FString RootPath;
51
+ FString ContentPath;
52
+
53
+ FScopedDeleteGuardMount()
54
+ : RootPath(MCPDeleteGuardTestRoot)
55
+ , ContentPath(FPaths::Combine(
56
+ FPaths::ConvertRelativePathToFull(FString(FPlatformProcess::UserTempDir())),
57
+ FString(TEXT("UEMCPAssetDeleteGuardTest")),
58
+ FGuid::NewGuid().ToString(EGuidFormats::Digits)))
59
+ {
60
+ IFileManager::Get().MakeDirectory(*ContentPath, /*Tree=*/true);
61
+ FPackageName::RegisterMountPoint(RootPath, ContentPath);
62
+ }
63
+
64
+ ~FScopedDeleteGuardMount()
65
+ {
66
+ FPackageName::UnRegisterMountPoint(RootPath, ContentPath);
67
+ IFileManager::Get().DeleteDirectory(*ContentPath, /*RequireExists=*/false, /*Tree=*/true);
68
+ }
69
+
70
+ FScopedDeleteGuardMount(const FScopedDeleteGuardMount&) = delete;
71
+ FScopedDeleteGuardMount& operator=(const FScopedDeleteGuardMount&) = delete;
72
+ };
73
+
74
+ TSharedPtr<FJsonObject> GuardResponseObject(const TSharedPtr<FJsonValue>& Response)
75
+ {
76
+ return (Response.IsValid() && Response->Type == EJson::Object)
77
+ ? Response->AsObject()
78
+ : TSharedPtr<FJsonObject>();
79
+ }
80
+
81
+ bool GuardResponseBool(const TSharedPtr<FJsonValue>& Response, const TCHAR* Field, bool bDefault = false)
82
+ {
83
+ const TSharedPtr<FJsonObject> Obj = GuardResponseObject(Response);
84
+ if (!Obj.IsValid()) return bDefault;
85
+ bool bValue = bDefault;
86
+ Obj->TryGetBoolField(Field, bValue);
87
+ return bValue;
88
+ }
89
+
90
+ FString GuardResponseString(const TSharedPtr<FJsonValue>& Response, const TCHAR* Field)
91
+ {
92
+ const TSharedPtr<FJsonObject> Obj = GuardResponseObject(Response);
93
+ if (!Obj.IsValid()) return FString();
94
+ FString Value;
95
+ Obj->TryGetStringField(Field, Value);
96
+ return Value;
97
+ }
98
+
99
+ /** Create a DataTable asset in its own package and write it to disk.
100
+ * Returns null when either step failed. */
101
+ UDataTable* MakeSavedProbeTable(const FString& PackageName)
102
+ {
103
+ UPackage* Package = CreatePackage(*PackageName);
104
+ if (!Package) return nullptr;
105
+
106
+ const FString AssetName = FPackageName::GetShortName(PackageName);
107
+ UDataTable* Table = NewObject<UDataTable>(
108
+ Package, FName(*AssetName), RF_Public | RF_Standalone);
109
+ if (!Table)
110
+ {
111
+ Package->SetDirtyFlag(false);
112
+ return nullptr;
113
+ }
114
+ Table->RowStruct = FPerPlatformInt::StaticStruct();
115
+
116
+ if (!SaveAssetPackage(Table))
117
+ {
118
+ Package->SetDirtyFlag(false);
119
+ return nullptr;
120
+ }
121
+ return Table;
122
+ }
123
+ }
124
+
125
+ IMPLEMENT_SIMPLE_AUTOMATION_TEST(
126
+ FMCPDeleteAssetRefusesReferencedAssetTest,
127
+ "UE.MCP.Asset.Delete.ForceFalseRefusesAReferencedAsset",
128
+ EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
129
+
130
+ bool FMCPDeleteAssetRefusesReferencedAssetTest::RunTest(const FString& Parameters)
131
+ {
132
+ const FScopedDeleteGuardMount Mount;
133
+
134
+ FMCPHandlerRegistry Registry;
135
+ FAssetHandlers::RegisterHandlers(Registry);
136
+ if (!TestTrue(TEXT("delete_asset is registered"), Registry.HasHandler(TEXT("delete_asset")))) return false;
137
+
138
+ const FString TargetPackage = FString(MCPDeleteGuardTestRoot) + TEXT("DT_DeleteTarget");
139
+ const FString ReferencerPackage = FString(MCPDeleteGuardTestRoot) + TEXT("R_DeleteReferencer");
140
+
141
+ UDataTable* Target = MakeSavedProbeTable(TargetPackage);
142
+ if (!TestNotNull(TEXT("the target asset was created and saved"), Target)) return false;
143
+ const FGCRootScope KeepTargetAlive(Target);
144
+
145
+ // A redirector is the cheapest asset that holds a hard reference to another
146
+ // package's object, which is exactly the edge the guard has to see.
147
+ UPackage* RefPackage = CreatePackage(*ReferencerPackage);
148
+ if (!TestNotNull(TEXT("the referencer package was created"), RefPackage)) return false;
149
+ UObjectRedirector* Referencer = NewObject<UObjectRedirector>(
150
+ RefPackage, FName(TEXT("R_DeleteReferencer")), RF_Public | RF_Standalone);
151
+ if (!TestNotNull(TEXT("the referencer asset was created"), Referencer))
152
+ {
153
+ RefPackage->SetDirtyFlag(false);
154
+ return false;
155
+ }
156
+ Referencer->DestinationObject = Target;
157
+ const FGCRootScope KeepReferencerAlive(Referencer);
158
+ if (!TestTrue(TEXT("the referencer package reached disk"), SaveAssetPackage(Referencer)))
159
+ {
160
+ RefPackage->SetDirtyFlag(false);
161
+ return false;
162
+ }
163
+
164
+ // The registry is the guard's only source, so the reference has to be
165
+ // indexed before the refusal can be asserted. A rescan of the private mount
166
+ // is what makes that happen inside one test run.
167
+ FAssetRegistryModule& ARM = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
168
+ TArray<FString> ScanRoots;
169
+ ScanRoots.Add(FString(MCPDeleteGuardTestRoot).LeftChop(1));
170
+ ARM.Get().ScanPathsSynchronous(ScanRoots, /*bForceRescan=*/true);
171
+
172
+ TArray<FName> Referencers;
173
+ ARM.Get().GetReferencers(FName(*TargetPackage), Referencers);
174
+ const bool bIndexed = Referencers.Contains(FName(*ReferencerPackage));
175
+
176
+ if (bIndexed)
177
+ {
178
+ TSharedPtr<FJsonObject> Params = MakeShared<FJsonObject>();
179
+ Params->SetStringField(TEXT("assetPath"), TargetPackage);
180
+
181
+ const TSharedPtr<FJsonValue> Response = Registry.ExecuteHandler(TEXT("delete_asset"), Params);
182
+
183
+ // The whole point of #976: the default is a refusal, not a delete.
184
+ TestFalse(TEXT("a referenced asset is refused with force=false"),
185
+ GuardResponseBool(Response, TEXT("success"), true));
186
+ TestEqual(TEXT("the refusal names why"),
187
+ GuardResponseString(Response, TEXT("reason")), FString(TEXT("has_referencers")));
188
+ TestFalse(TEXT("the refusal reports deleted=false"),
189
+ GuardResponseBool(Response, TEXT("deleted"), true));
190
+ TestTrue(TEXT("the refusal names the referencing package"),
191
+ GuardResponseString(Response, TEXT("error")).Contains(ReferencerPackage));
192
+ TestTrue(TEXT("the asset the caller asked to delete is still there"),
193
+ IsValid(Target));
194
+ }
195
+ else
196
+ {
197
+ // Nothing here can assert a guard whose only input is empty. Say so
198
+ // rather than reporting a pass that tested nothing.
199
+ AddInfo(TEXT("The Asset Registry did not index the private test mount's dependency graph in this ")
200
+ TEXT("environment, so the referencer refusal could not be exercised. The unreferenced-delete ")
201
+ TEXT("assertion below still ran."));
202
+ }
203
+
204
+ // An asset nothing points at still deletes on the default path: the guard
205
+ // must not turn every delete into a refusal.
206
+ {
207
+ const FString LonePackage = FString(MCPDeleteGuardTestRoot) + TEXT("DT_DeleteLone");
208
+ UDataTable* Lone = MakeSavedProbeTable(LonePackage);
209
+ if (TestNotNull(TEXT("the unreferenced asset was created and saved"), Lone))
210
+ {
211
+ TSharedPtr<FJsonObject> Params = MakeShared<FJsonObject>();
212
+ Params->SetStringField(TEXT("assetPath"), LonePackage);
213
+
214
+ const TSharedPtr<FJsonValue> Response = Registry.ExecuteHandler(TEXT("delete_asset"), Params);
215
+ TestTrue(FString::Printf(TEXT("an unreferenced asset deletes with force=false (%s)"),
216
+ *GuardResponseString(Response, TEXT("error"))),
217
+ GuardResponseBool(Response, TEXT("success")));
218
+ TestFalse(TEXT("an unreferenced delete is not reported as forced"),
219
+ GuardResponseBool(Response, TEXT("forced"), true));
220
+ }
221
+ }
222
+
223
+ if (UPackage* TargetPkg = Target->GetOutermost()) TargetPkg->SetDirtyFlag(false);
224
+ RefPackage->SetDirtyFlag(false);
225
+ return true;
226
+ }
227
+
228
+ IMPLEMENT_SIMPLE_AUTOMATION_TEST(
229
+ FMCPDeleteAssetProtectedMountTest,
230
+ "UE.MCP.Asset.Delete.ProtectedMountIsRefusedOnBothPaths",
231
+ EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
232
+
233
+ bool FMCPDeleteAssetProtectedMountTest::RunTest(const FString& Parameters)
234
+ {
235
+ FMCPHandlerRegistry Registry;
236
+ FAssetHandlers::RegisterHandlers(Registry);
237
+ if (!TestTrue(TEXT("delete_asset is registered"), Registry.HasHandler(TEXT("delete_asset")))) return false;
238
+ if (!TestTrue(TEXT("delete_asset_batch is registered"), Registry.HasHandler(TEXT("delete_asset_batch")))) return false;
239
+
240
+ // force=true is the path that used to be the only path, so the guardrail
241
+ // has to hold with it set, not just on the default.
242
+ {
243
+ TSharedPtr<FJsonObject> Params = MakeShared<FJsonObject>();
244
+ Params->SetStringField(TEXT("assetPath"), TEXT("/Engine/EngineMaterials/DefaultMaterial"));
245
+ Params->SetBoolField(TEXT("force"), true);
246
+
247
+ const TSharedPtr<FJsonValue> Response = Registry.ExecuteHandler(TEXT("delete_asset"), Params);
248
+ TestFalse(TEXT("engine content is refused even with force=true"),
249
+ GuardResponseBool(Response, TEXT("success"), true));
250
+ }
251
+
252
+ {
253
+ TSharedPtr<FJsonObject> Params = MakeShared<FJsonObject>();
254
+ TArray<TSharedPtr<FJsonValue>> Paths;
255
+ Paths.Add(MakeShared<FJsonValueString>(TEXT("/Engine/EngineMaterials/DefaultMaterial")));
256
+ Params->SetArrayField(TEXT("assetPaths"), Paths);
257
+ Params->SetBoolField(TEXT("force"), true);
258
+
259
+ const TSharedPtr<FJsonValue> Response = Registry.ExecuteHandler(TEXT("delete_asset_batch"), Params);
260
+ const TSharedPtr<FJsonObject> Obj = GuardResponseObject(Response);
261
+ if (TestTrue(TEXT("the batch answered with an object"), Obj.IsValid()))
262
+ {
263
+ double ProtectedCount = 0.0;
264
+ Obj->TryGetNumberField(TEXT("protected"), ProtectedCount);
265
+ TestEqual(TEXT("the engine path is counted as protected, not deleted"), ProtectedCount, 1.0);
266
+
267
+ double DeletedCount = 1.0;
268
+ Obj->TryGetNumberField(TEXT("deleted"), DeletedCount);
269
+ TestEqual(TEXT("nothing on a protected mount was deleted"), DeletedCount, 0.0);
270
+ }
271
+ }
272
+
273
+ return true;
274
+ }
275
+
276
+ #endif