unreal-engine-mcp-server 0.4.7 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (438) hide show
  1. package/.env.example +26 -0
  2. package/.env.production +38 -7
  3. package/.eslintrc.json +0 -54
  4. package/.eslintrc.override.json +8 -0
  5. package/.github/ISSUE_TEMPLATE/bug_report.yml +94 -0
  6. package/.github/ISSUE_TEMPLATE/config.yml +8 -0
  7. package/.github/ISSUE_TEMPLATE/feature_request.yml +56 -0
  8. package/.github/copilot-instructions.md +478 -45
  9. package/.github/dependabot.yml +19 -0
  10. package/.github/labeler.yml +24 -0
  11. package/.github/labels.yml +70 -0
  12. package/.github/pull_request_template.md +42 -0
  13. package/.github/release-drafter.yml +148 -0
  14. package/.github/workflows/auto-merge.yml +38 -0
  15. package/.github/workflows/ci.yml +38 -0
  16. package/.github/workflows/dependency-review.yml +17 -0
  17. package/.github/workflows/gemini-issue-triage.yml +172 -0
  18. package/.github/workflows/greetings.yml +23 -0
  19. package/.github/workflows/labeler.yml +16 -0
  20. package/.github/workflows/links.yml +80 -0
  21. package/.github/workflows/pr-size-labeler.yml +137 -0
  22. package/.github/workflows/publish-mcp.yml +12 -7
  23. package/.github/workflows/release-drafter.yml +23 -0
  24. package/.github/workflows/release.yml +112 -0
  25. package/.github/workflows/semantic-pull-request.yml +35 -0
  26. package/.github/workflows/smoke-test.yml +36 -0
  27. package/.github/workflows/stale.yml +28 -0
  28. package/CHANGELOG.md +267 -31
  29. package/CONTRIBUTING.md +140 -0
  30. package/README.md +166 -71
  31. package/claude_desktop_config_example.json +7 -6
  32. package/dist/automation/bridge.d.ts +50 -0
  33. package/dist/automation/bridge.js +452 -0
  34. package/dist/automation/connection-manager.d.ts +23 -0
  35. package/dist/automation/connection-manager.js +107 -0
  36. package/dist/automation/handshake.d.ts +11 -0
  37. package/dist/automation/handshake.js +89 -0
  38. package/dist/automation/index.d.ts +3 -0
  39. package/dist/automation/index.js +3 -0
  40. package/dist/automation/message-handler.d.ts +12 -0
  41. package/dist/automation/message-handler.js +149 -0
  42. package/dist/automation/request-tracker.d.ts +25 -0
  43. package/dist/automation/request-tracker.js +98 -0
  44. package/dist/automation/types.d.ts +130 -0
  45. package/dist/automation/types.js +2 -0
  46. package/dist/cli.js +32 -5
  47. package/dist/config.d.ts +27 -0
  48. package/dist/config.js +60 -0
  49. package/dist/constants.d.ts +12 -0
  50. package/dist/constants.js +12 -0
  51. package/dist/graphql/resolvers.d.ts +268 -0
  52. package/dist/graphql/resolvers.js +743 -0
  53. package/dist/graphql/schema.d.ts +5 -0
  54. package/dist/graphql/schema.js +437 -0
  55. package/dist/graphql/server.d.ts +26 -0
  56. package/dist/graphql/server.js +115 -0
  57. package/dist/graphql/types.d.ts +7 -0
  58. package/dist/graphql/types.js +2 -0
  59. package/dist/handlers/resource-handlers.d.ts +20 -0
  60. package/dist/handlers/resource-handlers.js +180 -0
  61. package/dist/index.d.ts +31 -18
  62. package/dist/index.js +119 -619
  63. package/dist/prompts/index.js +4 -4
  64. package/dist/resources/actors.d.ts +17 -12
  65. package/dist/resources/actors.js +56 -76
  66. package/dist/resources/assets.d.ts +6 -14
  67. package/dist/resources/assets.js +115 -147
  68. package/dist/resources/levels.d.ts +13 -13
  69. package/dist/resources/levels.js +25 -34
  70. package/dist/server/resource-registry.d.ts +20 -0
  71. package/dist/server/resource-registry.js +37 -0
  72. package/dist/server/tool-registry.d.ts +23 -0
  73. package/dist/server/tool-registry.js +322 -0
  74. package/dist/server-setup.d.ts +21 -0
  75. package/dist/server-setup.js +111 -0
  76. package/dist/services/health-monitor.d.ts +34 -0
  77. package/dist/services/health-monitor.js +105 -0
  78. package/dist/services/metrics-server.d.ts +11 -0
  79. package/dist/services/metrics-server.js +105 -0
  80. package/dist/tools/actors.d.ts +147 -9
  81. package/dist/tools/actors.js +350 -311
  82. package/dist/tools/animation.d.ts +135 -4
  83. package/dist/tools/animation.js +510 -411
  84. package/dist/tools/assets.d.ts +117 -19
  85. package/dist/tools/assets.js +259 -284
  86. package/dist/tools/audio.d.ts +102 -42
  87. package/dist/tools/audio.js +272 -685
  88. package/dist/tools/base-tool.d.ts +17 -0
  89. package/dist/tools/base-tool.js +46 -0
  90. package/dist/tools/behavior-tree.d.ts +94 -0
  91. package/dist/tools/behavior-tree.js +39 -0
  92. package/dist/tools/blueprint/helpers.d.ts +29 -0
  93. package/dist/tools/blueprint/helpers.js +182 -0
  94. package/dist/tools/blueprint.d.ts +228 -118
  95. package/dist/tools/blueprint.js +685 -832
  96. package/dist/tools/consolidated-tool-definitions.d.ts +5462 -1781
  97. package/dist/tools/consolidated-tool-definitions.js +829 -496
  98. package/dist/tools/consolidated-tool-handlers.d.ts +2 -1
  99. package/dist/tools/consolidated-tool-handlers.js +211 -1026
  100. package/dist/tools/debug.d.ts +143 -85
  101. package/dist/tools/debug.js +234 -180
  102. package/dist/tools/dynamic-handler-registry.d.ts +11 -0
  103. package/dist/tools/dynamic-handler-registry.js +101 -0
  104. package/dist/tools/editor.d.ts +139 -18
  105. package/dist/tools/editor.js +239 -244
  106. package/dist/tools/engine.d.ts +10 -4
  107. package/dist/tools/engine.js +13 -5
  108. package/dist/tools/environment.d.ts +36 -0
  109. package/dist/tools/environment.js +267 -0
  110. package/dist/tools/foliage.d.ts +105 -14
  111. package/dist/tools/foliage.js +219 -331
  112. package/dist/tools/handlers/actor-handlers.d.ts +3 -0
  113. package/dist/tools/handlers/actor-handlers.js +232 -0
  114. package/dist/tools/handlers/animation-handlers.d.ts +3 -0
  115. package/dist/tools/handlers/animation-handlers.js +185 -0
  116. package/dist/tools/handlers/argument-helper.d.ts +16 -0
  117. package/dist/tools/handlers/argument-helper.js +80 -0
  118. package/dist/tools/handlers/asset-handlers.d.ts +3 -0
  119. package/dist/tools/handlers/asset-handlers.js +496 -0
  120. package/dist/tools/handlers/audio-handlers.d.ts +3 -0
  121. package/dist/tools/handlers/audio-handlers.js +166 -0
  122. package/dist/tools/handlers/blueprint-handlers.d.ts +4 -0
  123. package/dist/tools/handlers/blueprint-handlers.js +358 -0
  124. package/dist/tools/handlers/common-handlers.d.ts +14 -0
  125. package/dist/tools/handlers/common-handlers.js +56 -0
  126. package/dist/tools/handlers/editor-handlers.d.ts +3 -0
  127. package/dist/tools/handlers/editor-handlers.js +119 -0
  128. package/dist/tools/handlers/effect-handlers.d.ts +3 -0
  129. package/dist/tools/handlers/effect-handlers.js +171 -0
  130. package/dist/tools/handlers/environment-handlers.d.ts +3 -0
  131. package/dist/tools/handlers/environment-handlers.js +170 -0
  132. package/dist/tools/handlers/graph-handlers.d.ts +3 -0
  133. package/dist/tools/handlers/graph-handlers.js +90 -0
  134. package/dist/tools/handlers/input-handlers.d.ts +3 -0
  135. package/dist/tools/handlers/input-handlers.js +21 -0
  136. package/dist/tools/handlers/inspect-handlers.d.ts +3 -0
  137. package/dist/tools/handlers/inspect-handlers.js +383 -0
  138. package/dist/tools/handlers/level-handlers.d.ts +3 -0
  139. package/dist/tools/handlers/level-handlers.js +237 -0
  140. package/dist/tools/handlers/lighting-handlers.d.ts +3 -0
  141. package/dist/tools/handlers/lighting-handlers.js +144 -0
  142. package/dist/tools/handlers/performance-handlers.d.ts +3 -0
  143. package/dist/tools/handlers/performance-handlers.js +130 -0
  144. package/dist/tools/handlers/pipeline-handlers.d.ts +3 -0
  145. package/dist/tools/handlers/pipeline-handlers.js +110 -0
  146. package/dist/tools/handlers/sequence-handlers.d.ts +3 -0
  147. package/dist/tools/handlers/sequence-handlers.js +376 -0
  148. package/dist/tools/handlers/system-handlers.d.ts +4 -0
  149. package/dist/tools/handlers/system-handlers.js +506 -0
  150. package/dist/tools/input.d.ts +19 -0
  151. package/dist/tools/input.js +89 -0
  152. package/dist/tools/introspection.d.ts +103 -40
  153. package/dist/tools/introspection.js +425 -568
  154. package/dist/tools/landscape.d.ts +97 -36
  155. package/dist/tools/landscape.js +280 -409
  156. package/dist/tools/level.d.ts +130 -10
  157. package/dist/tools/level.js +639 -675
  158. package/dist/tools/lighting.d.ts +77 -38
  159. package/dist/tools/lighting.js +441 -943
  160. package/dist/tools/logs.d.ts +3 -3
  161. package/dist/tools/logs.js +5 -57
  162. package/dist/tools/materials.d.ts +91 -24
  163. package/dist/tools/materials.js +190 -118
  164. package/dist/tools/niagara.d.ts +149 -39
  165. package/dist/tools/niagara.js +232 -182
  166. package/dist/tools/performance.d.ts +27 -12
  167. package/dist/tools/performance.js +204 -122
  168. package/dist/tools/physics.d.ts +32 -77
  169. package/dist/tools/physics.js +171 -582
  170. package/dist/tools/property-dictionary.d.ts +13 -0
  171. package/dist/tools/property-dictionary.js +82 -0
  172. package/dist/tools/sequence.d.ts +73 -48
  173. package/dist/tools/sequence.js +196 -748
  174. package/dist/tools/tool-definition-utils.d.ts +59 -0
  175. package/dist/tools/tool-definition-utils.js +35 -0
  176. package/dist/tools/ui.d.ts +66 -34
  177. package/dist/tools/ui.js +134 -214
  178. package/dist/types/env.d.ts +0 -3
  179. package/dist/types/env.js +0 -7
  180. package/dist/types/tool-interfaces.d.ts +898 -0
  181. package/dist/types/tool-interfaces.js +2 -0
  182. package/dist/types/tool-types.d.ts +183 -19
  183. package/dist/types/tool-types.js +0 -4
  184. package/dist/unreal-bridge.d.ts +24 -131
  185. package/dist/unreal-bridge.js +364 -1506
  186. package/dist/utils/command-validator.d.ts +9 -0
  187. package/dist/utils/command-validator.js +67 -0
  188. package/dist/utils/elicitation.d.ts +1 -1
  189. package/dist/utils/elicitation.js +12 -15
  190. package/dist/utils/error-handler.d.ts +2 -51
  191. package/dist/utils/error-handler.js +11 -87
  192. package/dist/utils/ini-reader.d.ts +3 -0
  193. package/dist/utils/ini-reader.js +69 -0
  194. package/dist/utils/logger.js +9 -6
  195. package/dist/utils/normalize.d.ts +3 -0
  196. package/dist/utils/normalize.js +56 -0
  197. package/dist/utils/response-factory.d.ts +7 -0
  198. package/dist/utils/response-factory.js +33 -0
  199. package/dist/utils/response-validator.d.ts +3 -24
  200. package/dist/utils/response-validator.js +130 -81
  201. package/dist/utils/result-helpers.d.ts +4 -5
  202. package/dist/utils/result-helpers.js +15 -16
  203. package/dist/utils/safe-json.js +5 -11
  204. package/dist/utils/unreal-command-queue.d.ts +24 -0
  205. package/dist/utils/unreal-command-queue.js +120 -0
  206. package/dist/utils/validation.d.ts +0 -40
  207. package/dist/utils/validation.js +1 -78
  208. package/dist/wasm/index.d.ts +70 -0
  209. package/dist/wasm/index.js +535 -0
  210. package/docs/GraphQL-API.md +888 -0
  211. package/docs/Migration-Guide-v0.5.0.md +692 -0
  212. package/docs/Roadmap.md +53 -0
  213. package/docs/WebAssembly-Integration.md +628 -0
  214. package/docs/editor-plugin-extension.md +370 -0
  215. package/docs/handler-mapping.md +242 -0
  216. package/docs/native-automation-progress.md +128 -0
  217. package/docs/testing-guide.md +423 -0
  218. package/mcp-config-example.json +6 -6
  219. package/package.json +60 -27
  220. package/plugins/McpAutomationBridge/Config/FilterPlugin.ini +8 -0
  221. package/plugins/McpAutomationBridge/McpAutomationBridge.uplugin +64 -0
  222. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/McpAutomationBridge.Build.cs +189 -0
  223. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.cpp +22 -0
  224. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.h +30 -0
  225. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeHelpers.h +1983 -0
  226. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeModule.cpp +72 -0
  227. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSettings.cpp +46 -0
  228. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSubsystem.cpp +581 -0
  229. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AnimationHandlers.cpp +2394 -0
  230. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetQueryHandlers.cpp +300 -0
  231. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetWorkflowHandlers.cpp +2807 -0
  232. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AudioHandlers.cpp +1087 -0
  233. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BehaviorTreeHandlers.cpp +488 -0
  234. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.cpp +643 -0
  235. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.h +31 -0
  236. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintGraphHandlers.cpp +1184 -0
  237. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers.cpp +5652 -0
  238. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers_List.cpp +152 -0
  239. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ControlHandlers.cpp +2614 -0
  240. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_DebugHandlers.cpp +42 -0
  241. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EditorFunctionHandlers.cpp +1237 -0
  242. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EffectHandlers.cpp +1701 -0
  243. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EnvironmentHandlers.cpp +2145 -0
  244. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_FoliageHandlers.cpp +954 -0
  245. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InputHandlers.cpp +209 -0
  246. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InsightsHandlers.cpp +41 -0
  247. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LandscapeHandlers.cpp +1164 -0
  248. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LevelHandlers.cpp +762 -0
  249. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LightingHandlers.cpp +634 -0
  250. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LogHandlers.cpp +136 -0
  251. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MaterialGraphHandlers.cpp +494 -0
  252. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraGraphHandlers.cpp +278 -0
  253. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraHandlers.cpp +625 -0
  254. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PerformanceHandlers.cpp +401 -0
  255. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PipelineHandlers.cpp +67 -0
  256. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ProcessRequest.cpp +735 -0
  257. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PropertyHandlers.cpp +2634 -0
  258. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_RenderHandlers.cpp +189 -0
  259. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.cpp +917 -0
  260. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.h +39 -0
  261. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequenceHandlers.cpp +2670 -0
  262. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequencerHandlers.cpp +519 -0
  263. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_TestHandlers.cpp +38 -0
  264. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_UiHandlers.cpp +668 -0
  265. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_WorldPartitionHandlers.cpp +346 -0
  266. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.cpp +1330 -0
  267. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.h +149 -0
  268. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpConnectionManager.cpp +783 -0
  269. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSettings.h +115 -0
  270. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSubsystem.h +796 -0
  271. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpConnectionManager.h +117 -0
  272. package/scripts/check-unreal-connection.mjs +19 -0
  273. package/scripts/clean-tmp.js +23 -0
  274. package/scripts/patch-wasm.js +26 -0
  275. package/scripts/run-all-tests.mjs +131 -0
  276. package/scripts/smoke-test.ts +94 -0
  277. package/scripts/sync-mcp-plugin.js +143 -0
  278. package/scripts/test-no-plugin-alternates.mjs +113 -0
  279. package/scripts/validate-server.js +46 -0
  280. package/scripts/verify-automation-bridge.js +200 -0
  281. package/server.json +57 -21
  282. package/src/automation/bridge.ts +558 -0
  283. package/src/automation/connection-manager.ts +130 -0
  284. package/src/automation/handshake.ts +99 -0
  285. package/src/automation/index.ts +2 -0
  286. package/src/automation/message-handler.ts +167 -0
  287. package/src/automation/request-tracker.ts +123 -0
  288. package/src/automation/types.ts +107 -0
  289. package/src/cli.ts +33 -6
  290. package/src/config.ts +73 -0
  291. package/src/constants.ts +12 -0
  292. package/src/graphql/resolvers.ts +1010 -0
  293. package/src/graphql/schema.ts +452 -0
  294. package/src/graphql/server.ts +154 -0
  295. package/src/graphql/types.ts +7 -0
  296. package/src/handlers/resource-handlers.ts +186 -0
  297. package/src/index.ts +152 -663
  298. package/src/prompts/index.ts +4 -4
  299. package/src/resources/actors.ts +58 -76
  300. package/src/resources/assets.ts +147 -134
  301. package/src/resources/levels.ts +28 -33
  302. package/src/server/resource-registry.ts +47 -0
  303. package/src/server/tool-registry.ts +354 -0
  304. package/src/server-setup.ts +148 -0
  305. package/src/services/health-monitor.ts +132 -0
  306. package/src/services/metrics-server.ts +142 -0
  307. package/src/tools/actors.ts +417 -322
  308. package/src/tools/animation.ts +671 -461
  309. package/src/tools/assets.ts +353 -289
  310. package/src/tools/audio.ts +323 -766
  311. package/src/tools/base-tool.ts +52 -0
  312. package/src/tools/behavior-tree.ts +45 -0
  313. package/src/tools/blueprint/helpers.ts +189 -0
  314. package/src/tools/blueprint.ts +787 -965
  315. package/src/tools/consolidated-tool-definitions.ts +993 -515
  316. package/src/tools/consolidated-tool-handlers.ts +272 -1139
  317. package/src/tools/debug.ts +292 -187
  318. package/src/tools/dynamic-handler-registry.ts +151 -0
  319. package/src/tools/editor.ts +309 -246
  320. package/src/tools/engine.ts +14 -3
  321. package/src/tools/environment.ts +287 -0
  322. package/src/tools/foliage.ts +314 -379
  323. package/src/tools/handlers/actor-handlers.ts +271 -0
  324. package/src/tools/handlers/animation-handlers.ts +237 -0
  325. package/src/tools/handlers/argument-helper.ts +142 -0
  326. package/src/tools/handlers/asset-handlers.ts +532 -0
  327. package/src/tools/handlers/audio-handlers.ts +194 -0
  328. package/src/tools/handlers/blueprint-handlers.ts +380 -0
  329. package/src/tools/handlers/common-handlers.ts +87 -0
  330. package/src/tools/handlers/editor-handlers.ts +123 -0
  331. package/src/tools/handlers/effect-handlers.ts +220 -0
  332. package/src/tools/handlers/environment-handlers.ts +183 -0
  333. package/src/tools/handlers/graph-handlers.ts +116 -0
  334. package/src/tools/handlers/input-handlers.ts +28 -0
  335. package/src/tools/handlers/inspect-handlers.ts +450 -0
  336. package/src/tools/handlers/level-handlers.ts +252 -0
  337. package/src/tools/handlers/lighting-handlers.ts +147 -0
  338. package/src/tools/handlers/performance-handlers.ts +132 -0
  339. package/src/tools/handlers/pipeline-handlers.ts +127 -0
  340. package/src/tools/handlers/sequence-handlers.ts +415 -0
  341. package/src/tools/handlers/system-handlers.ts +564 -0
  342. package/src/tools/input.ts +101 -0
  343. package/src/tools/introspection.ts +493 -584
  344. package/src/tools/landscape.ts +394 -489
  345. package/src/tools/level.ts +752 -694
  346. package/src/tools/lighting.ts +583 -984
  347. package/src/tools/logs.ts +9 -57
  348. package/src/tools/materials.ts +231 -121
  349. package/src/tools/niagara.ts +293 -168
  350. package/src/tools/performance.ts +320 -168
  351. package/src/tools/physics.ts +268 -613
  352. package/src/tools/property-dictionary.ts +98 -0
  353. package/src/tools/sequence.ts +255 -815
  354. package/src/tools/tool-definition-utils.ts +35 -0
  355. package/src/tools/ui.ts +207 -283
  356. package/src/types/env.ts +0 -10
  357. package/src/types/tool-interfaces.ts +250 -0
  358. package/src/types/tool-types.ts +243 -21
  359. package/src/unreal-bridge.ts +460 -1550
  360. package/src/utils/command-validator.ts +75 -0
  361. package/src/utils/elicitation.ts +10 -7
  362. package/src/utils/error-handler.ts +14 -90
  363. package/src/utils/ini-reader.ts +86 -0
  364. package/src/utils/logger.ts +8 -3
  365. package/src/utils/normalize.ts +60 -0
  366. package/src/utils/response-factory.ts +39 -0
  367. package/src/utils/response-validator.ts +176 -56
  368. package/src/utils/result-helpers.ts +21 -19
  369. package/src/utils/safe-json.ts +14 -11
  370. package/src/utils/unreal-command-queue.ts +152 -0
  371. package/src/utils/validation.ts +4 -1
  372. package/src/wasm/index.ts +838 -0
  373. package/test-server.mjs +100 -0
  374. package/tests/run-unreal-tool-tests.mjs +242 -14
  375. package/tests/test-animation.mjs +44 -0
  376. package/tests/test-asset-advanced.mjs +82 -0
  377. package/tests/test-asset-errors.mjs +35 -0
  378. package/tests/test-audio.mjs +219 -0
  379. package/tests/test-automation-timeouts.mjs +98 -0
  380. package/tests/test-behavior-tree.mjs +261 -0
  381. package/tests/test-blueprint-events.mjs +35 -0
  382. package/tests/test-blueprint-graph.mjs +79 -0
  383. package/tests/test-blueprint.mjs +577 -0
  384. package/tests/test-client-mode.mjs +86 -0
  385. package/tests/test-console-command.mjs +56 -0
  386. package/tests/test-control-actor.mjs +425 -0
  387. package/tests/test-control-editor.mjs +80 -0
  388. package/tests/test-extra-tools.mjs +38 -0
  389. package/tests/test-graphql.mjs +322 -0
  390. package/tests/test-inspect.mjs +72 -0
  391. package/tests/test-landscape.mjs +60 -0
  392. package/tests/test-manage-asset.mjs +438 -0
  393. package/tests/test-manage-level.mjs +70 -0
  394. package/tests/test-materials.mjs +356 -0
  395. package/tests/test-niagara.mjs +185 -0
  396. package/tests/test-no-inline-python.mjs +122 -0
  397. package/tests/test-plugin-handshake.mjs +82 -0
  398. package/tests/test-render.mjs +33 -0
  399. package/tests/test-runner.mjs +933 -0
  400. package/tests/test-search-assets.mjs +66 -0
  401. package/tests/test-sequence.mjs +68 -0
  402. package/tests/test-system.mjs +57 -0
  403. package/tests/test-wasm.mjs +193 -0
  404. package/tests/test-world-partition.mjs +215 -0
  405. package/tsconfig.json +3 -3
  406. package/wasm/Cargo.lock +363 -0
  407. package/wasm/Cargo.toml +42 -0
  408. package/wasm/LICENSE +21 -0
  409. package/wasm/README.md +253 -0
  410. package/wasm/src/dependency_resolver.rs +377 -0
  411. package/wasm/src/lib.rs +153 -0
  412. package/wasm/src/property_parser.rs +271 -0
  413. package/wasm/src/transform_math.rs +396 -0
  414. package/wasm/tests/integration.rs +109 -0
  415. package/.github/workflows/smithery-build.yml +0 -29
  416. package/dist/tools/build_environment_advanced.d.ts +0 -65
  417. package/dist/tools/build_environment_advanced.js +0 -633
  418. package/dist/tools/rc.d.ts +0 -110
  419. package/dist/tools/rc.js +0 -437
  420. package/dist/tools/visual.d.ts +0 -40
  421. package/dist/tools/visual.js +0 -282
  422. package/dist/utils/http.d.ts +0 -6
  423. package/dist/utils/http.js +0 -151
  424. package/dist/utils/python-output.d.ts +0 -18
  425. package/dist/utils/python-output.js +0 -290
  426. package/dist/utils/python.d.ts +0 -2
  427. package/dist/utils/python.js +0 -4
  428. package/dist/utils/stdio-redirect.d.ts +0 -2
  429. package/dist/utils/stdio-redirect.js +0 -20
  430. package/docs/unreal-tool-test-cases.md +0 -574
  431. package/smithery.yaml +0 -29
  432. package/src/tools/build_environment_advanced.ts +0 -732
  433. package/src/tools/rc.ts +0 -515
  434. package/src/tools/visual.ts +0 -281
  435. package/src/utils/http.ts +0 -187
  436. package/src/utils/python-output.ts +0 -351
  437. package/src/utils/python.ts +0 -3
  438. package/src/utils/stdio-redirect.ts +0 -18
@@ -16,20 +16,20 @@ export interface PromptTemplate {
16
16
  }>;
17
17
  }
18
18
 
19
- function clampChoice(value: unknown, choices: string[], fallback: string): string {
19
+ function clampChoice(value: unknown, choices: string[], defaultChoice: string): string {
20
20
  if (typeof value === 'string') {
21
21
  const normalized = value.toLowerCase();
22
22
  if (choices.includes(normalized)) {
23
23
  return normalized;
24
24
  }
25
25
  }
26
- return fallback;
26
+ return defaultChoice;
27
27
  }
28
28
 
29
- function coerceNumber(value: unknown, fallback: number, min?: number, max?: number): number {
29
+ function coerceNumber(value: unknown, defaultValue: number, min?: number, max?: number): number {
30
30
  const num = typeof value === 'number' ? value : Number(value);
31
31
  if (!Number.isFinite(num)) {
32
- return fallback;
32
+ return defaultValue;
33
33
  }
34
34
  if (min !== undefined && num < min) {
35
35
  return min;
@@ -1,5 +1,5 @@
1
1
  import { UnrealBridge } from '../unreal-bridge.js';
2
- import { bestEffortInterpretedText, coerceNumber, coerceString, interpretStandardResult } from '../utils/result-helpers.js';
2
+ import { coerceNumber, coerceString } from '../utils/result-helpers.js';
3
3
 
4
4
  interface CacheEntry {
5
5
  data: any;
@@ -9,8 +9,11 @@ interface CacheEntry {
9
9
  export class ActorResources {
10
10
  private cache = new Map<string, CacheEntry>();
11
11
  private readonly CACHE_TTL_MS = 5000; // 5 seconds cache for actors (they change more frequently)
12
-
13
- constructor(private bridge: UnrealBridge) {}
12
+ private automationBridgeAvailable = false;
13
+
14
+ constructor(private bridge: UnrealBridge, private automationBridge?: any) {
15
+ this.automationBridgeAvailable = Boolean(automationBridge && typeof automationBridge.sendAutomationRequest === 'function');
16
+ }
14
17
 
15
18
  private getFromCache(key: string): any | null {
16
19
  const entry = this.cache.get(key);
@@ -32,114 +35,93 @@ export class ActorResources {
32
35
  return cached;
33
36
  }
34
37
 
35
- // Use Python to get actors via EditorActorSubsystem
36
38
  try {
37
- const pythonCode = `
38
- import unreal, json
39
- actor_subsystem = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
40
- actors = actor_subsystem.get_all_level_actors() if actor_subsystem else []
41
- actor_list = []
42
- for actor in actors:
43
- try:
44
- if actor:
45
- actor_list.append({
46
- 'name': actor.get_name(),
47
- 'class': actor.get_class().get_name(),
48
- 'path': actor.get_path_name()
49
- })
50
- except Exception:
51
- pass
52
- print('RESULT:' + json.dumps({'success': True, 'count': len(actor_list), 'actors': actor_list}))
53
- `.trim();
54
-
55
- const response = await this.bridge.executePython(pythonCode);
56
- const interpreted = interpretStandardResult(response, {
57
- successMessage: 'Retrieved actor list',
58
- failureMessage: 'Failed to retrieve actor list'
59
- });
39
+ if (!this.automationBridgeAvailable) {
40
+ return { success: false, error: 'Automation bridge is not available. Please ensure Unreal Engine is running with the MCP Automation Bridge plugin.' };
41
+ }
60
42
 
61
- if (interpreted.success && Array.isArray(interpreted.payload.actors)) {
62
- const actors = interpreted.payload.actors as any[];
63
- const count = coerceNumber(interpreted.payload.count) ?? actors.length;
64
- const payload = {
65
- success: true as const,
66
- count,
67
- actors
68
- };
43
+ const resp: any = await this.automationBridge.sendAutomationRequest('control_actor', { action: 'list' });
44
+ if (resp && resp.success !== false && Array.isArray((resp.result || resp).actors)) {
45
+ const actors = (resp.result || resp).actors as any[];
46
+ const count = coerceNumber((resp.result || resp).count) ?? actors.length;
47
+ const payload = { success: true as const, count, actors };
69
48
  this.setCache('listActors', payload);
70
49
  return payload;
71
50
  }
72
51
 
73
- return {
74
- success: false,
75
- error: coerceString(interpreted.payload.error) ?? interpreted.error ?? 'Failed to parse actors list',
76
- note: bestEffortInterpretedText(interpreted)
77
- };
52
+ return { success: false, error: 'Failed to retrieve actor list from automation bridge' };
78
53
  } catch (err) {
79
54
  return { success: false, error: `Failed to list actors: ${err}` };
80
55
  }
81
56
  }
82
57
 
83
58
  async getActorByName(actorName: string) {
84
- // GetActorOfClass expects a class, not a name. Use Python to find by name
85
59
  try {
86
- const pythonCode = `
87
- import unreal
88
- import json
89
-
90
- actor_subsystem = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
91
- actors = actor_subsystem.get_all_level_actors() if actor_subsystem else []
92
-
93
- found = None
94
- for actor in actors:
95
- if actor and actor.get_name() == ${JSON.stringify(actorName)}:
96
- found = {
97
- 'success': True,
98
- 'name': actor.get_name(),
99
- 'path': actor.get_path_name(),
100
- 'class': actor.get_class().get_name()
101
- }
102
- break
103
-
104
- if not found:
105
- found = {'success': False, 'error': f"Actor not found: {actorName}"}
106
-
107
- print('RESULT:' + json.dumps(found))
108
- `.trim();
60
+ if (!this.automationBridgeAvailable) {
61
+ return { success: false, error: 'Automation bridge is not available' };
62
+ }
109
63
 
110
- const response = await this.bridge.executePython(pythonCode);
111
- const interpreted = interpretStandardResult(response, {
112
- successMessage: `Actor resolved: ${actorName}`,
113
- failureMessage: `Actor not found: ${actorName}`
64
+ const resp: any = await this.automationBridge.sendAutomationRequest('control_actor', {
65
+ action: 'find_by_name',
66
+ name: actorName
114
67
  });
115
68
 
116
- if (interpreted.success) {
69
+ if (resp && resp.success !== false && resp.result) {
117
70
  return {
118
71
  success: true as const,
119
- name: coerceString(interpreted.payload.name) ?? actorName,
120
- path: coerceString(interpreted.payload.path),
121
- class: coerceString(interpreted.payload.class)
72
+ name: coerceString(resp.result.name) ?? actorName,
73
+ path: coerceString(resp.result.path),
74
+ class: coerceString(resp.result.class)
122
75
  };
123
76
  }
124
77
 
125
78
  return {
126
79
  success: false as const,
127
- error: coerceString(interpreted.payload.error) ?? interpreted.error ?? `Actor not found: ${actorName}`
80
+ error: `Actor not found: ${actorName}`
128
81
  };
129
82
  } catch (err) {
130
- return { error: `Failed to get actor: ${err}` };
83
+ return { success: false, error: `Failed to get actor: ${err}` };
131
84
  }
132
85
  }
133
86
 
134
87
  async getActorTransform(actorPath: string) {
135
88
  try {
136
- const res = await this.bridge.httpCall('/remote/object/property', 'GET', {
89
+ return await this.bridge.getObjectProperty({
137
90
  objectPath: actorPath,
138
91
  propertyName: 'ActorTransform'
139
92
  });
140
- return res;
141
93
  } catch (err) {
142
94
  return { error: `Failed to get transform: ${err}` };
143
95
  }
144
96
  }
97
+
98
+ async listActorComponents(actorPath: string) {
99
+ try {
100
+ if (!this.automationBridgeAvailable) {
101
+ return { success: false, error: 'Automation bridge is not available' };
102
+ }
103
+
104
+ const resp: any = await this.automationBridge.sendAutomationRequest('control_actor', {
105
+ action: 'list_components',
106
+ actor_path: actorPath
107
+ });
108
+
109
+ if (resp && resp.success !== false && Array.isArray(resp.result?.components)) {
110
+ return {
111
+ success: true as const,
112
+ components: resp.result.components
113
+ };
114
+ }
115
+
116
+ return {
117
+ success: false as const,
118
+ error: `Failed to resolve components for ${actorPath}`
119
+ };
120
+ } catch (err) {
121
+ return {
122
+ success: false as const,
123
+ error: `Component lookup failed: ${err}`
124
+ };
125
+ }
126
+ }
145
127
  }
@@ -1,14 +1,13 @@
1
- import { UnrealBridge } from '../unreal-bridge.js';
2
- import { coerceBoolean, coerceString, interpretStandardResult } from '../utils/result-helpers.js';
3
-
4
- export class AssetResources {
5
- constructor(private bridge: UnrealBridge) {}
1
+ import { BaseTool } from '../tools/base-tool.js';
2
+ import { IAssetResources } from '../types/tool-interfaces.js';
3
+ import { coerceString } from '../utils/result-helpers.js';
6
4
 
5
+ export class AssetResources extends BaseTool implements IAssetResources {
7
6
  // Simple in-memory cache for asset listing
8
7
  private cache = new Map<string, { timestamp: number; data: any }>();
9
8
  private get ttlMs(): number { return Number(process.env.ASSET_LIST_TTL_MS || 10000); }
10
- private makeKey(dir: string, recursive: boolean, page?: number) {
11
- return page !== undefined ? `${dir}::${recursive ? 1 : 0}::${page}` : `${dir}::${recursive ? 1 : 0}`;
9
+ private makeKey(dir: string, recursive: boolean, page?: number) {
10
+ return page !== undefined ? `${dir}::${recursive ? 1 : 0}::${page}` : `${dir}::${recursive ? 1 : 0}`;
12
11
  }
13
12
 
14
13
  // Normalize UE content paths:
@@ -32,11 +31,48 @@ export class AssetResources {
32
31
  }
33
32
  }
34
33
 
34
+ clearCache(dir?: string) {
35
+ if (!dir) {
36
+ this.cache.clear();
37
+ return;
38
+ }
39
+
40
+ const normalized = this.normalizeDir(dir);
41
+ for (const key of Array.from(this.cache.keys())) {
42
+ if (key.startsWith(`${normalized}::`)) {
43
+ this.cache.delete(key);
44
+ }
45
+ }
46
+ }
47
+
48
+ invalidateAssetPaths(paths: string[]) {
49
+ if (!Array.isArray(paths) || paths.length === 0) {
50
+ return;
51
+ }
52
+
53
+ const dirs = new Set<string>();
54
+ for (const rawPath of paths) {
55
+ if (typeof rawPath !== 'string' || rawPath.trim().length === 0) {
56
+ continue;
57
+ }
58
+ const normalized = this.normalizeDir(rawPath);
59
+ dirs.add(normalized);
60
+ const parent = this.parentDirectory(normalized);
61
+ if (parent) {
62
+ dirs.add(parent);
63
+ }
64
+ }
65
+
66
+ for (const dir of dirs) {
67
+ this.clearCache(dir);
68
+ }
69
+ }
70
+
35
71
  async list(dir = '/Game', _recursive = false, limit = 50) {
36
72
  // ALWAYS use non-recursive listing to show only immediate children
37
73
  // This prevents timeouts and makes navigation clearer
38
74
  _recursive = false; // Force non-recursive
39
-
75
+
40
76
  // Normalize directory first
41
77
  dir = this.normalizeDir(dir);
42
78
 
@@ -46,22 +82,24 @@ export class AssetResources {
46
82
  const entry = this.cache.get(key);
47
83
  const now = Date.now();
48
84
  if (entry && (now - entry.timestamp) < this.ttlMs) {
49
- return entry.data;
85
+ return { success: true, ...entry.data };
50
86
  }
51
- } catch {}
52
-
87
+ } catch { }
88
+
53
89
  // Check if bridge is connected
54
90
  if (!this.bridge.isConnected) {
55
91
  return {
92
+ success: false,
56
93
  assets: [],
57
- warning: 'Unreal Engine is not connected. Please ensure Unreal Engine is running with Remote Control enabled.',
94
+ warning: 'Unreal Engine is not connected. Please ensure Unreal Engine is running with the MCP server enabled.',
58
95
  connectionStatus: 'disconnected'
59
96
  };
60
97
  }
61
-
98
+
62
99
  // Always use directory-only listing (immediate children)
63
- return this.listDirectoryOnly(dir, false, limit);
64
- // End of list method - all logic is now in listDirectoryOnly
100
+ const listed = await this.listDirectoryOnly(dir, false, limit);
101
+ // Ensure a success flag is present so downstream evaluators don't assume success implicitly
102
+ return { ...listed, success: listed && (listed as any).success === false ? false : true };
65
103
  }
66
104
 
67
105
  /**
@@ -74,7 +112,7 @@ export class AssetResources {
74
112
  // Ensure pageSize doesn't exceed safe limit
75
113
  const safePageSize = Math.min(pageSize, 50);
76
114
  const offset = page * safePageSize;
77
-
115
+
78
116
  // Normalize directory and check cache for this specific page
79
117
  dir = this.normalizeDir(dir);
80
118
  const cacheKey = this.makeKey(dir, recursive, page);
@@ -82,7 +120,7 @@ export class AssetResources {
82
120
  if (cached && (Date.now() - cached.timestamp) < this.ttlMs) {
83
121
  return cached.data;
84
122
  }
85
-
123
+
86
124
  if (!this.bridge.isConnected) {
87
125
  return {
88
126
  assets: [],
@@ -92,17 +130,17 @@ export class AssetResources {
92
130
  connectionStatus: 'disconnected'
93
131
  };
94
132
  }
95
-
133
+
96
134
  try {
97
135
  // Use search API with pagination
98
136
  // Use the same directory listing approach but with pagination
99
137
  const allAssets = await this.listDirectoryOnly(dir, false, 1000);
100
-
138
+
101
139
  // Paginate the results
102
140
  const start = offset;
103
141
  const end = offset + safePageSize;
104
142
  const pagedAssets = allAssets.assets ? allAssets.assets.slice(start, end) : [];
105
-
143
+
106
144
  const result = {
107
145
  assets: pagedAssets,
108
146
  page,
@@ -112,13 +150,13 @@ export class AssetResources {
112
150
  hasMore: end < (allAssets.assets ? allAssets.assets.length : 0),
113
151
  method: 'directory_listing_paged'
114
152
  };
115
-
153
+
116
154
  this.cache.set(cacheKey, { timestamp: Date.now(), data: result });
117
155
  return result;
118
156
  } catch (err: any) {
119
157
  console.warn(`Asset listing page ${page} failed:`, err.message);
120
158
  }
121
-
159
+
122
160
  return {
123
161
  assets: [],
124
162
  page,
@@ -134,109 +172,80 @@ export class AssetResources {
134
172
  private async listDirectoryOnly(dir: string, _recursive: boolean, limit: number) {
135
173
  // Always return only immediate children to avoid timeout and improve navigation
136
174
  try {
137
- const py = `
138
- import unreal
139
- import json
140
-
141
- _dir = r"${this.normalizeDir(dir)}"
142
-
143
- try:
144
- ar = unreal.AssetRegistryHelpers.get_asset_registry()
145
- # Immediate subfolders
146
- sub_paths = ar.get_sub_paths(_dir, False)
147
- folders_list = []
148
- for p in sub_paths:
149
- try:
150
- name = p.split('/')[-1]
151
- folders_list.append({'n': name, 'p': p})
152
- except Exception:
153
- pass
154
-
155
- # Immediate assets at this path
156
- assets_data = ar.get_assets_by_path(_dir, False)
157
- assets = []
158
- for a in assets_data[:${limit}]:
159
- try:
160
- assets.append({
161
- 'n': str(a.asset_name),
162
- 'p': str(a.object_path),
163
- 'c': str(a.asset_class)
164
- })
165
- except Exception:
166
- pass
167
-
168
- print("RESULT:" + json.dumps({
169
- 'success': True,
170
- 'path': _dir,
171
- 'folders': len(folders_list),
172
- 'files': len(assets),
173
- 'folders_list': folders_list,
174
- 'assets': assets
175
- }))
176
- except Exception as e:
177
- print("RESULT:" + json.dumps({'success': False, 'error': str(e), 'path': _dir}))
178
- `.trim();
179
-
180
- const resp = await this.bridge.executePython(py);
181
- const interpreted = interpretStandardResult(resp, {
182
- successMessage: 'Directory contents retrieved',
183
- failureMessage: 'Failed to list directory contents'
184
- });
185
-
186
- if (interpreted.success) {
187
- const payload = interpreted.payload as Record<string, unknown>;
188
-
189
- const foldersArr = Array.isArray(payload.folders_list)
190
- ? payload.folders_list.map((f: any) => ({
191
- Name: coerceString(f?.n) ?? '',
192
- Path: coerceString(f?.p) ?? '',
175
+ // Use the native C++ plugin's list action instead of Python
176
+ try {
177
+ const normalizedDir = this.normalizeDir(dir);
178
+ const response = await this.sendAutomationRequest(
179
+ 'list',
180
+ { directory: normalizedDir, limit, recursive: false },
181
+ { timeoutMs: 30000 }
182
+ );
183
+
184
+ if (response.success !== false && response.result) {
185
+ const payload = response.result;
186
+
187
+ const foldersArr = Array.isArray(payload.folders_list)
188
+ ? payload.folders_list.map((f: any) => ({
189
+ Name: coerceString(f?.n ?? f?.Name ?? f?.name) ?? '',
190
+ Path: coerceString(f?.p ?? f?.Path ?? f?.path) ?? '',
193
191
  Class: 'Folder',
194
192
  isFolder: true
195
193
  }))
196
- : [];
197
-
198
- const assetsArr = Array.isArray(payload.assets)
199
- ? payload.assets.map((a: any) => ({
200
- Name: coerceString(a?.n) ?? '',
201
- Path: coerceString(a?.p) ?? '',
202
- Class: coerceString(a?.c) ?? 'Asset',
203
- isFolder: false
194
+ : [];
195
+
196
+ const assetsArr = Array.isArray(payload.assets)
197
+ ? payload.assets.map((a: any) => ({
198
+ Name: coerceString(a?.n ?? a?.Name ?? a?.name) ?? '',
199
+ Path: coerceString(a?.p ?? a?.Path ?? a?.path) ?? '',
200
+ Class: coerceString(a?.c ?? a?.Class ?? a?.class) ?? 'Object'
204
201
  }))
205
- : [];
206
-
207
- const total = foldersArr.length + assetsArr.length;
208
- const summary = {
209
- total,
210
- folders: foldersArr.length,
211
- assets: assetsArr.length
212
- };
213
-
214
- const resolvedPath = coerceString(payload.path) ?? this.normalizeDir(dir);
215
-
216
- return {
217
- success: true,
218
- path: resolvedPath,
219
- summary,
220
- foldersList: foldersArr,
221
- assets: assetsArr,
222
- count: total,
223
- note: `Immediate children of ${resolvedPath}: ${foldersArr.length} folder(s), ${assetsArr.length} asset(s)`,
224
- method: 'asset_registry_listing'
225
- };
226
- }
202
+ : [];
203
+
204
+ const result = {
205
+ success: true,
206
+ assets: [...foldersArr, ...assetsArr],
207
+ count: foldersArr.length + assetsArr.length,
208
+ folders: foldersArr.length,
209
+ files: assetsArr.length,
210
+ path: normalizedDir,
211
+ recursive: false,
212
+ method: 'automation_bridge',
213
+ cached: false
214
+ };
215
+
216
+ const key = this.makeKey(dir, false);
217
+ this.cache.set(key, { timestamp: Date.now(), data: result });
218
+ return result;
219
+ }
220
+ } catch { }
221
+
222
+ // No fallback available
227
223
  } catch (err: any) {
228
- console.warn('Engine asset listing failed:', err.message);
224
+ const errorMessage = err?.message ? String(err.message) : 'Asset registry request failed';
225
+ console.warn('Engine asset listing failed:', errorMessage);
226
+ return {
227
+ success: false,
228
+ path: this.normalizeDir(dir),
229
+ summary: { total: 0, folders: 0, assets: 0 },
230
+ foldersList: [],
231
+ assets: [],
232
+ error: errorMessage,
233
+ warning: 'AssetRegistry query failed. Ensure the MCP Automation Bridge is connected.',
234
+ transport: 'automation_bridge',
235
+ method: 'asset_registry_alternate'
236
+ };
229
237
  }
230
-
231
- // Fallback: return empty with explanation
238
+
232
239
  return {
233
- success: true,
240
+ success: false,
234
241
  path: this.normalizeDir(dir),
235
242
  summary: { total: 0, folders: 0, assets: 0 },
236
243
  foldersList: [],
237
244
  assets: [],
238
- warning: 'No items at this path or failed to query AssetRegistry.',
239
- method: 'asset_registry_fallback'
245
+ error: 'Asset registry returned no payload.',
246
+ warning: 'No items returned from AssetRegistry request.',
247
+ transport: 'automation_bridge',
248
+ method: 'asset_registry_empty'
240
249
  };
241
250
  }
242
251
 
@@ -246,27 +255,31 @@ except Exception as e:
246
255
  return false;
247
256
  }
248
257
 
249
- // Normalize asset path (support users passing /Content/...)
250
- const ap = this.normalizeDir(assetPath);
251
- const py = `
252
- import unreal
253
- apath = r"${ap}"
254
- try:
255
- exists = unreal.EditorAssetLibrary.does_asset_exist(apath)
256
- print("RESULT:{'success': True, 'exists': %s}" % ('True' if exists else 'False'))
257
- except Exception as e:
258
- print("RESULT:{'success': False, 'error': '" + str(e) + "'}")
259
- `.trim();
260
- const resp = await this.bridge.executePython(py);
261
- const interpreted = interpretStandardResult(resp, {
262
- successMessage: 'Asset existence verified',
263
- failureMessage: 'Failed to verify asset existence'
264
- });
265
-
266
- if (interpreted.success) {
267
- return coerceBoolean(interpreted.payload.exists, false) ?? false;
258
+ try {
259
+ const normalizedPath = this.normalizeDir(assetPath);
260
+ const response = await this.sendAutomationRequest(
261
+ 'asset_exists',
262
+ { asset_path: normalizedPath }
263
+ );
264
+
265
+ return response?.success !== false && response?.result?.exists === true;
266
+ } catch {
267
+ return false;
268
+ }
269
+ }
270
+
271
+ private parentDirectory(path: string): string | null {
272
+ if (!path || typeof path !== 'string') {
273
+ return null;
274
+ }
275
+
276
+ const normalized = this.normalizeDir(path);
277
+ const lastSlash = normalized.lastIndexOf('/');
278
+ if (lastSlash <= 0) {
279
+ return normalized === '/' ? '/' : null;
268
280
  }
269
281
 
270
- return false;
282
+ const parent = normalized.substring(0, lastSlash);
283
+ return parent.length > 0 ? parent : '/';
271
284
  }
272
285
  }