unreal-engine-mcp-server 0.4.7 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (454) hide show
  1. package/.env.example +26 -0
  2. package/.env.production +38 -7
  3. package/.eslintrc.json +0 -54
  4. package/.eslintrc.override.json +8 -0
  5. package/.github/ISSUE_TEMPLATE/bug_report.yml +94 -0
  6. package/.github/ISSUE_TEMPLATE/config.yml +8 -0
  7. package/.github/ISSUE_TEMPLATE/feature_request.yml +56 -0
  8. package/.github/copilot-instructions.md +478 -45
  9. package/.github/dependabot.yml +19 -0
  10. package/.github/labeler.yml +24 -0
  11. package/.github/labels.yml +70 -0
  12. package/.github/pull_request_template.md +42 -0
  13. package/.github/release-drafter-config.yml +51 -0
  14. package/.github/workflows/auto-merge.yml +38 -0
  15. package/.github/workflows/ci.yml +38 -0
  16. package/.github/workflows/dependency-review.yml +17 -0
  17. package/.github/workflows/gemini-issue-triage.yml +172 -0
  18. package/.github/workflows/greetings.yml +27 -0
  19. package/.github/workflows/labeler.yml +17 -0
  20. package/.github/workflows/links.yml +80 -0
  21. package/.github/workflows/pr-size-labeler.yml +137 -0
  22. package/.github/workflows/publish-mcp.yml +13 -7
  23. package/.github/workflows/release-drafter.yml +23 -0
  24. package/.github/workflows/release.yml +112 -0
  25. package/.github/workflows/semantic-pull-request.yml +35 -0
  26. package/.github/workflows/smoke-test.yml +36 -0
  27. package/.github/workflows/stale.yml +28 -0
  28. package/CHANGELOG.md +338 -31
  29. package/CONTRIBUTING.md +140 -0
  30. package/GEMINI.md +115 -0
  31. package/Public/Plugin_setup_guide.mp4 +0 -0
  32. package/README.md +189 -128
  33. package/claude_desktop_config_example.json +7 -6
  34. package/dist/automation/bridge.d.ts +50 -0
  35. package/dist/automation/bridge.js +452 -0
  36. package/dist/automation/connection-manager.d.ts +23 -0
  37. package/dist/automation/connection-manager.js +107 -0
  38. package/dist/automation/handshake.d.ts +11 -0
  39. package/dist/automation/handshake.js +89 -0
  40. package/dist/automation/index.d.ts +3 -0
  41. package/dist/automation/index.js +3 -0
  42. package/dist/automation/message-handler.d.ts +12 -0
  43. package/dist/automation/message-handler.js +149 -0
  44. package/dist/automation/request-tracker.d.ts +25 -0
  45. package/dist/automation/request-tracker.js +98 -0
  46. package/dist/automation/types.d.ts +130 -0
  47. package/dist/automation/types.js +2 -0
  48. package/dist/cli.js +32 -5
  49. package/dist/config.d.ts +26 -0
  50. package/dist/config.js +59 -0
  51. package/dist/constants.d.ts +16 -0
  52. package/dist/constants.js +16 -0
  53. package/dist/graphql/loaders.d.ts +64 -0
  54. package/dist/graphql/loaders.js +117 -0
  55. package/dist/graphql/resolvers.d.ts +268 -0
  56. package/dist/graphql/resolvers.js +746 -0
  57. package/dist/graphql/schema.d.ts +5 -0
  58. package/dist/graphql/schema.js +437 -0
  59. package/dist/graphql/server.d.ts +26 -0
  60. package/dist/graphql/server.js +117 -0
  61. package/dist/graphql/types.d.ts +9 -0
  62. package/dist/graphql/types.js +2 -0
  63. package/dist/handlers/resource-handlers.d.ts +20 -0
  64. package/dist/handlers/resource-handlers.js +180 -0
  65. package/dist/index.d.ts +33 -18
  66. package/dist/index.js +130 -619
  67. package/dist/resources/actors.d.ts +17 -12
  68. package/dist/resources/actors.js +56 -76
  69. package/dist/resources/assets.d.ts +6 -14
  70. package/dist/resources/assets.js +115 -147
  71. package/dist/resources/levels.d.ts +13 -13
  72. package/dist/resources/levels.js +25 -34
  73. package/dist/server/resource-registry.d.ts +20 -0
  74. package/dist/server/resource-registry.js +37 -0
  75. package/dist/server/tool-registry.d.ts +23 -0
  76. package/dist/server/tool-registry.js +322 -0
  77. package/dist/server-setup.d.ts +20 -0
  78. package/dist/server-setup.js +71 -0
  79. package/dist/services/health-monitor.d.ts +34 -0
  80. package/dist/services/health-monitor.js +105 -0
  81. package/dist/services/metrics-server.d.ts +11 -0
  82. package/dist/services/metrics-server.js +105 -0
  83. package/dist/tools/actors.d.ts +163 -9
  84. package/dist/tools/actors.js +356 -311
  85. package/dist/tools/animation.d.ts +135 -4
  86. package/dist/tools/animation.js +510 -411
  87. package/dist/tools/assets.d.ts +75 -29
  88. package/dist/tools/assets.js +265 -284
  89. package/dist/tools/audio.d.ts +102 -42
  90. package/dist/tools/audio.js +272 -685
  91. package/dist/tools/base-tool.d.ts +17 -0
  92. package/dist/tools/base-tool.js +46 -0
  93. package/dist/tools/behavior-tree.d.ts +94 -0
  94. package/dist/tools/behavior-tree.js +39 -0
  95. package/dist/tools/blueprint.d.ts +208 -126
  96. package/dist/tools/blueprint.js +685 -832
  97. package/dist/tools/consolidated-tool-definitions.d.ts +5462 -1781
  98. package/dist/tools/consolidated-tool-definitions.js +829 -496
  99. package/dist/tools/consolidated-tool-handlers.d.ts +2 -1
  100. package/dist/tools/consolidated-tool-handlers.js +198 -1027
  101. package/dist/tools/debug.d.ts +143 -85
  102. package/dist/tools/debug.js +234 -180
  103. package/dist/tools/dynamic-handler-registry.d.ts +13 -0
  104. package/dist/tools/dynamic-handler-registry.js +23 -0
  105. package/dist/tools/editor.d.ts +30 -83
  106. package/dist/tools/editor.js +247 -244
  107. package/dist/tools/engine.d.ts +10 -4
  108. package/dist/tools/engine.js +13 -5
  109. package/dist/tools/environment.d.ts +30 -0
  110. package/dist/tools/environment.js +267 -0
  111. package/dist/tools/foliage.d.ts +65 -99
  112. package/dist/tools/foliage.js +221 -331
  113. package/dist/tools/handlers/actor-handlers.d.ts +3 -0
  114. package/dist/tools/handlers/actor-handlers.js +227 -0
  115. package/dist/tools/handlers/animation-handlers.d.ts +3 -0
  116. package/dist/tools/handlers/animation-handlers.js +185 -0
  117. package/dist/tools/handlers/argument-helper.d.ts +16 -0
  118. package/dist/tools/handlers/argument-helper.js +80 -0
  119. package/dist/tools/handlers/asset-handlers.d.ts +3 -0
  120. package/dist/tools/handlers/asset-handlers.js +496 -0
  121. package/dist/tools/handlers/audio-handlers.d.ts +3 -0
  122. package/dist/tools/handlers/audio-handlers.js +166 -0
  123. package/dist/tools/handlers/blueprint-handlers.d.ts +4 -0
  124. package/dist/tools/handlers/blueprint-handlers.js +358 -0
  125. package/dist/tools/handlers/common-handlers.d.ts +14 -0
  126. package/dist/tools/handlers/common-handlers.js +56 -0
  127. package/dist/tools/handlers/editor-handlers.d.ts +3 -0
  128. package/dist/tools/handlers/editor-handlers.js +119 -0
  129. package/dist/tools/handlers/effect-handlers.d.ts +3 -0
  130. package/dist/tools/handlers/effect-handlers.js +171 -0
  131. package/dist/tools/handlers/environment-handlers.d.ts +3 -0
  132. package/dist/tools/handlers/environment-handlers.js +170 -0
  133. package/dist/tools/handlers/graph-handlers.d.ts +3 -0
  134. package/dist/tools/handlers/graph-handlers.js +90 -0
  135. package/dist/tools/handlers/input-handlers.d.ts +3 -0
  136. package/dist/tools/handlers/input-handlers.js +21 -0
  137. package/dist/tools/handlers/inspect-handlers.d.ts +3 -0
  138. package/dist/tools/handlers/inspect-handlers.js +383 -0
  139. package/dist/tools/handlers/level-handlers.d.ts +3 -0
  140. package/dist/tools/handlers/level-handlers.js +237 -0
  141. package/dist/tools/handlers/lighting-handlers.d.ts +3 -0
  142. package/dist/tools/handlers/lighting-handlers.js +144 -0
  143. package/dist/tools/handlers/performance-handlers.d.ts +3 -0
  144. package/dist/tools/handlers/performance-handlers.js +130 -0
  145. package/dist/tools/handlers/pipeline-handlers.d.ts +3 -0
  146. package/dist/tools/handlers/pipeline-handlers.js +110 -0
  147. package/dist/tools/handlers/sequence-handlers.d.ts +3 -0
  148. package/dist/tools/handlers/sequence-handlers.js +376 -0
  149. package/dist/tools/handlers/system-handlers.d.ts +4 -0
  150. package/dist/tools/handlers/system-handlers.js +506 -0
  151. package/dist/tools/input.d.ts +19 -0
  152. package/dist/tools/input.js +89 -0
  153. package/dist/tools/introspection.d.ts +103 -40
  154. package/dist/tools/introspection.js +425 -568
  155. package/dist/tools/landscape.d.ts +54 -93
  156. package/dist/tools/landscape.js +284 -409
  157. package/dist/tools/level.d.ts +66 -27
  158. package/dist/tools/level.js +647 -675
  159. package/dist/tools/lighting.d.ts +77 -38
  160. package/dist/tools/lighting.js +445 -943
  161. package/dist/tools/logs.d.ts +3 -3
  162. package/dist/tools/logs.js +5 -57
  163. package/dist/tools/materials.d.ts +91 -24
  164. package/dist/tools/materials.js +194 -118
  165. package/dist/tools/niagara.d.ts +149 -39
  166. package/dist/tools/niagara.js +267 -182
  167. package/dist/tools/performance.d.ts +27 -13
  168. package/dist/tools/performance.js +203 -122
  169. package/dist/tools/physics.d.ts +32 -77
  170. package/dist/tools/physics.js +175 -582
  171. package/dist/tools/property-dictionary.d.ts +13 -0
  172. package/dist/tools/property-dictionary.js +82 -0
  173. package/dist/tools/sequence.d.ts +85 -60
  174. package/dist/tools/sequence.js +208 -747
  175. package/dist/tools/tool-definition-utils.d.ts +59 -0
  176. package/dist/tools/tool-definition-utils.js +35 -0
  177. package/dist/tools/ui.d.ts +64 -34
  178. package/dist/tools/ui.js +134 -214
  179. package/dist/types/automation-responses.d.ts +115 -0
  180. package/dist/types/automation-responses.js +2 -0
  181. package/dist/types/env.d.ts +0 -3
  182. package/dist/types/env.js +0 -7
  183. package/dist/types/responses.d.ts +249 -0
  184. package/dist/types/responses.js +2 -0
  185. package/dist/types/tool-interfaces.d.ts +898 -0
  186. package/dist/types/tool-interfaces.js +2 -0
  187. package/dist/types/tool-types.d.ts +183 -19
  188. package/dist/types/tool-types.js +0 -4
  189. package/dist/unreal-bridge.d.ts +24 -131
  190. package/dist/unreal-bridge.js +364 -1506
  191. package/dist/utils/command-validator.d.ts +9 -0
  192. package/dist/utils/command-validator.js +68 -0
  193. package/dist/utils/elicitation.d.ts +1 -1
  194. package/dist/utils/elicitation.js +12 -15
  195. package/dist/utils/error-handler.d.ts +2 -51
  196. package/dist/utils/error-handler.js +11 -87
  197. package/dist/utils/ini-reader.d.ts +3 -0
  198. package/dist/utils/ini-reader.js +69 -0
  199. package/dist/utils/logger.js +9 -6
  200. package/dist/utils/normalize.d.ts +3 -0
  201. package/dist/utils/normalize.js +56 -0
  202. package/dist/utils/path-security.d.ts +2 -0
  203. package/dist/utils/path-security.js +24 -0
  204. package/dist/utils/response-factory.d.ts +7 -0
  205. package/dist/utils/response-factory.js +27 -0
  206. package/dist/utils/response-validator.d.ts +3 -24
  207. package/dist/utils/response-validator.js +130 -81
  208. package/dist/utils/result-helpers.d.ts +4 -5
  209. package/dist/utils/result-helpers.js +15 -16
  210. package/dist/utils/safe-json.js +5 -11
  211. package/dist/utils/unreal-command-queue.d.ts +24 -0
  212. package/dist/utils/unreal-command-queue.js +120 -0
  213. package/dist/utils/validation.d.ts +0 -40
  214. package/dist/utils/validation.js +1 -78
  215. package/dist/wasm/index.d.ts +70 -0
  216. package/dist/wasm/index.js +535 -0
  217. package/docs/GraphQL-API.md +888 -0
  218. package/docs/Migration-Guide-v0.5.0.md +684 -0
  219. package/docs/Roadmap.md +53 -0
  220. package/docs/WebAssembly-Integration.md +628 -0
  221. package/docs/editor-plugin-extension.md +370 -0
  222. package/docs/handler-mapping.md +242 -0
  223. package/docs/native-automation-progress.md +128 -0
  224. package/docs/testing-guide.md +423 -0
  225. package/mcp-config-example.json +6 -6
  226. package/package.json +67 -28
  227. package/plugins/McpAutomationBridge/Config/FilterPlugin.ini +8 -0
  228. package/plugins/McpAutomationBridge/McpAutomationBridge.uplugin +64 -0
  229. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/McpAutomationBridge.Build.cs +189 -0
  230. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.cpp +22 -0
  231. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.h +30 -0
  232. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeHelpers.h +1983 -0
  233. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeModule.cpp +72 -0
  234. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSettings.cpp +46 -0
  235. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSubsystem.cpp +581 -0
  236. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AnimationHandlers.cpp +2394 -0
  237. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetQueryHandlers.cpp +300 -0
  238. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetWorkflowHandlers.cpp +2807 -0
  239. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AudioHandlers.cpp +1087 -0
  240. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BehaviorTreeHandlers.cpp +488 -0
  241. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.cpp +643 -0
  242. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.h +31 -0
  243. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintGraphHandlers.cpp +1184 -0
  244. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers.cpp +5652 -0
  245. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers_List.cpp +152 -0
  246. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ControlHandlers.cpp +2614 -0
  247. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_DebugHandlers.cpp +42 -0
  248. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EditorFunctionHandlers.cpp +1237 -0
  249. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EffectHandlers.cpp +1701 -0
  250. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EnvironmentHandlers.cpp +2145 -0
  251. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_FoliageHandlers.cpp +954 -0
  252. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InputHandlers.cpp +209 -0
  253. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InsightsHandlers.cpp +41 -0
  254. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LandscapeHandlers.cpp +1164 -0
  255. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LevelHandlers.cpp +762 -0
  256. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LightingHandlers.cpp +634 -0
  257. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LogHandlers.cpp +136 -0
  258. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MaterialGraphHandlers.cpp +494 -0
  259. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraGraphHandlers.cpp +278 -0
  260. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraHandlers.cpp +625 -0
  261. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PerformanceHandlers.cpp +401 -0
  262. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PipelineHandlers.cpp +67 -0
  263. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ProcessRequest.cpp +735 -0
  264. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PropertyHandlers.cpp +2634 -0
  265. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_RenderHandlers.cpp +189 -0
  266. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.cpp +917 -0
  267. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.h +39 -0
  268. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequenceHandlers.cpp +2670 -0
  269. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequencerHandlers.cpp +519 -0
  270. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_TestHandlers.cpp +38 -0
  271. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_UiHandlers.cpp +668 -0
  272. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_WorldPartitionHandlers.cpp +346 -0
  273. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.cpp +1330 -0
  274. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.h +149 -0
  275. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpConnectionManager.cpp +783 -0
  276. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSettings.h +115 -0
  277. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSubsystem.h +796 -0
  278. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpConnectionManager.h +117 -0
  279. package/scripts/check-unreal-connection.mjs +19 -0
  280. package/scripts/clean-tmp.js +23 -0
  281. package/scripts/patch-wasm.js +26 -0
  282. package/scripts/run-all-tests.mjs +136 -0
  283. package/scripts/smoke-test.ts +94 -0
  284. package/scripts/sync-mcp-plugin.js +143 -0
  285. package/scripts/test-no-plugin-alternates.mjs +113 -0
  286. package/scripts/validate-server.js +46 -0
  287. package/scripts/verify-automation-bridge.js +200 -0
  288. package/server.json +58 -21
  289. package/src/automation/bridge.ts +558 -0
  290. package/src/automation/connection-manager.ts +130 -0
  291. package/src/automation/handshake.ts +99 -0
  292. package/src/automation/index.ts +2 -0
  293. package/src/automation/message-handler.ts +167 -0
  294. package/src/automation/request-tracker.ts +123 -0
  295. package/src/automation/types.ts +107 -0
  296. package/src/cli.ts +33 -6
  297. package/src/config.ts +73 -0
  298. package/src/constants.ts +19 -0
  299. package/src/graphql/loaders.ts +244 -0
  300. package/src/graphql/resolvers.ts +1008 -0
  301. package/src/graphql/schema.ts +452 -0
  302. package/src/graphql/server.ts +156 -0
  303. package/src/graphql/types.ts +10 -0
  304. package/src/handlers/resource-handlers.ts +186 -0
  305. package/src/index.ts +166 -664
  306. package/src/resources/actors.ts +58 -76
  307. package/src/resources/assets.ts +148 -134
  308. package/src/resources/levels.ts +28 -33
  309. package/src/server/resource-registry.ts +47 -0
  310. package/src/server/tool-registry.ts +354 -0
  311. package/src/server-setup.ts +114 -0
  312. package/src/services/health-monitor.ts +132 -0
  313. package/src/services/metrics-server.ts +142 -0
  314. package/src/tools/actors.ts +426 -323
  315. package/src/tools/animation.ts +672 -461
  316. package/src/tools/assets.ts +364 -289
  317. package/src/tools/audio.ts +323 -766
  318. package/src/tools/base-tool.ts +52 -0
  319. package/src/tools/behavior-tree.ts +45 -0
  320. package/src/tools/blueprint.ts +792 -970
  321. package/src/tools/consolidated-tool-definitions.ts +993 -515
  322. package/src/tools/consolidated-tool-handlers.ts +258 -1146
  323. package/src/tools/debug.ts +292 -187
  324. package/src/tools/dynamic-handler-registry.ts +33 -0
  325. package/src/tools/editor.ts +329 -253
  326. package/src/tools/engine.ts +14 -3
  327. package/src/tools/environment.ts +281 -0
  328. package/src/tools/foliage.ts +330 -392
  329. package/src/tools/handlers/actor-handlers.ts +265 -0
  330. package/src/tools/handlers/animation-handlers.ts +237 -0
  331. package/src/tools/handlers/argument-helper.ts +142 -0
  332. package/src/tools/handlers/asset-handlers.ts +532 -0
  333. package/src/tools/handlers/audio-handlers.ts +194 -0
  334. package/src/tools/handlers/blueprint-handlers.ts +380 -0
  335. package/src/tools/handlers/common-handlers.ts +87 -0
  336. package/src/tools/handlers/editor-handlers.ts +123 -0
  337. package/src/tools/handlers/effect-handlers.ts +220 -0
  338. package/src/tools/handlers/environment-handlers.ts +183 -0
  339. package/src/tools/handlers/graph-handlers.ts +116 -0
  340. package/src/tools/handlers/input-handlers.ts +28 -0
  341. package/src/tools/handlers/inspect-handlers.ts +450 -0
  342. package/src/tools/handlers/level-handlers.ts +252 -0
  343. package/src/tools/handlers/lighting-handlers.ts +147 -0
  344. package/src/tools/handlers/performance-handlers.ts +132 -0
  345. package/src/tools/handlers/pipeline-handlers.ts +127 -0
  346. package/src/tools/handlers/sequence-handlers.ts +415 -0
  347. package/src/tools/handlers/system-handlers.ts +564 -0
  348. package/src/tools/input.ts +101 -0
  349. package/src/tools/introspection.ts +493 -584
  350. package/src/tools/landscape.ts +418 -507
  351. package/src/tools/level.ts +786 -708
  352. package/src/tools/lighting.ts +588 -984
  353. package/src/tools/logs.ts +9 -57
  354. package/src/tools/materials.ts +237 -121
  355. package/src/tools/niagara.ts +335 -168
  356. package/src/tools/performance.ts +320 -169
  357. package/src/tools/physics.ts +274 -613
  358. package/src/tools/property-dictionary.ts +98 -0
  359. package/src/tools/sequence.ts +276 -820
  360. package/src/tools/tool-definition-utils.ts +35 -0
  361. package/src/tools/ui.ts +205 -283
  362. package/src/types/automation-responses.ts +119 -0
  363. package/src/types/env.ts +0 -10
  364. package/src/types/responses.ts +355 -0
  365. package/src/types/tool-interfaces.ts +250 -0
  366. package/src/types/tool-types.ts +243 -21
  367. package/src/unreal-bridge.ts +460 -1550
  368. package/src/utils/command-validator.ts +76 -0
  369. package/src/utils/elicitation.ts +10 -7
  370. package/src/utils/error-handler.ts +14 -90
  371. package/src/utils/ini-reader.ts +86 -0
  372. package/src/utils/logger.ts +8 -3
  373. package/src/utils/normalize.test.ts +162 -0
  374. package/src/utils/normalize.ts +60 -0
  375. package/src/utils/path-security.ts +43 -0
  376. package/src/utils/response-factory.ts +44 -0
  377. package/src/utils/response-validator.ts +176 -56
  378. package/src/utils/result-helpers.ts +21 -19
  379. package/src/utils/safe-json.test.ts +90 -0
  380. package/src/utils/safe-json.ts +14 -11
  381. package/src/utils/unreal-command-queue.ts +152 -0
  382. package/src/utils/validation.test.ts +184 -0
  383. package/src/utils/validation.ts +4 -1
  384. package/src/wasm/index.ts +838 -0
  385. package/test-server.mjs +100 -0
  386. package/tests/run-unreal-tool-tests.mjs +242 -14
  387. package/tests/test-animation.mjs +369 -0
  388. package/tests/test-asset-advanced.mjs +82 -0
  389. package/tests/test-asset-errors.mjs +35 -0
  390. package/tests/test-asset-graph.mjs +311 -0
  391. package/tests/test-audio.mjs +417 -0
  392. package/tests/test-automation-timeouts.mjs +98 -0
  393. package/tests/test-behavior-tree.mjs +444 -0
  394. package/tests/test-blueprint-graph.mjs +410 -0
  395. package/tests/test-blueprint.mjs +577 -0
  396. package/tests/test-client-mode.mjs +86 -0
  397. package/tests/test-console-command.mjs +56 -0
  398. package/tests/test-control-actor.mjs +425 -0
  399. package/tests/test-control-editor.mjs +112 -0
  400. package/tests/test-graphql.mjs +372 -0
  401. package/tests/test-input.mjs +349 -0
  402. package/tests/test-inspect.mjs +302 -0
  403. package/tests/test-landscape.mjs +316 -0
  404. package/tests/test-lighting.mjs +428 -0
  405. package/tests/test-manage-asset.mjs +438 -0
  406. package/tests/test-manage-level.mjs +89 -0
  407. package/tests/test-materials.mjs +356 -0
  408. package/tests/test-niagara.mjs +185 -0
  409. package/tests/test-no-inline-python.mjs +122 -0
  410. package/tests/test-performance.mjs +539 -0
  411. package/tests/test-plugin-handshake.mjs +82 -0
  412. package/tests/test-runner.mjs +933 -0
  413. package/tests/test-sequence.mjs +104 -0
  414. package/tests/test-system.mjs +96 -0
  415. package/tests/test-wasm.mjs +283 -0
  416. package/tests/test-world-partition.mjs +215 -0
  417. package/tsconfig.json +3 -3
  418. package/vitest.config.ts +35 -0
  419. package/wasm/Cargo.lock +363 -0
  420. package/wasm/Cargo.toml +42 -0
  421. package/wasm/LICENSE +21 -0
  422. package/wasm/README.md +253 -0
  423. package/wasm/src/dependency_resolver.rs +377 -0
  424. package/wasm/src/lib.rs +153 -0
  425. package/wasm/src/property_parser.rs +271 -0
  426. package/wasm/src/transform_math.rs +396 -0
  427. package/wasm/tests/integration.rs +109 -0
  428. package/.github/workflows/smithery-build.yml +0 -29
  429. package/dist/prompts/index.d.ts +0 -21
  430. package/dist/prompts/index.js +0 -217
  431. package/dist/tools/build_environment_advanced.d.ts +0 -65
  432. package/dist/tools/build_environment_advanced.js +0 -633
  433. package/dist/tools/rc.d.ts +0 -110
  434. package/dist/tools/rc.js +0 -437
  435. package/dist/tools/visual.d.ts +0 -40
  436. package/dist/tools/visual.js +0 -282
  437. package/dist/utils/http.d.ts +0 -6
  438. package/dist/utils/http.js +0 -151
  439. package/dist/utils/python-output.d.ts +0 -18
  440. package/dist/utils/python-output.js +0 -290
  441. package/dist/utils/python.d.ts +0 -2
  442. package/dist/utils/python.js +0 -4
  443. package/dist/utils/stdio-redirect.d.ts +0 -2
  444. package/dist/utils/stdio-redirect.js +0 -20
  445. package/docs/unreal-tool-test-cases.md +0 -574
  446. package/smithery.yaml +0 -29
  447. package/src/prompts/index.ts +0 -249
  448. package/src/tools/build_environment_advanced.ts +0 -732
  449. package/src/tools/rc.ts +0 -515
  450. package/src/tools/visual.ts +0 -281
  451. package/src/utils/http.ts +0 -187
  452. package/src/utils/python-output.ts +0 -351
  453. package/src/utils/python.ts +0 -3
  454. package/src/utils/stdio-redirect.ts +0 -18
@@ -1,319 +1,394 @@
1
- import { UnrealBridge } from '../unreal-bridge.js';
2
- import { promises as fs } from 'fs';
3
- import path from 'path';
4
- import { bestEffortInterpretedText, coerceNumber, coerceStringArray, interpretStandardResult } from '../utils/result-helpers.js';
1
+ import { BaseTool } from './base-tool.js';
2
+ import { IAssetTools, StandardActionResponse, SourceControlState } from '../types/tool-interfaces.js';
3
+ import { wasmIntegration } from '../wasm/index.js';
4
+ import { Logger } from '../utils/logger.js';
5
+ import { AssetResponse } from '../types/automation-responses.js';
6
+ import { sanitizePath } from '../utils/path-security.js';
7
+ import {
8
+ DEFAULT_ASSET_OP_TIMEOUT_MS,
9
+ EXTENDED_ASSET_OP_TIMEOUT_MS,
10
+ LONG_RUNNING_OP_TIMEOUT_MS
11
+ } from '../constants.js';
5
12
 
6
- export class AssetTools {
7
- constructor(private bridge: UnrealBridge) {}
13
+ const log = new Logger('AssetTools');
8
14
 
9
- async importAsset(sourcePath: string, destinationPath: string) {
10
- let createdTestFile = false;
11
- try {
12
- // Sanitize destination path (remove trailing slash) and normalize UE path
13
- let cleanDest = destinationPath.replace(/\/$/, '');
14
- // Map /Content -> /Game for UE asset destinations
15
- if (/^\/?content(\/|$)/i.test(cleanDest)) {
16
- cleanDest = '/Game' + cleanDest.replace(/^\/?content/i, '');
17
- }
18
-
19
- // Create test FBX file if it's a test file
20
- if (sourcePath.includes('test_model.fbx')) {
21
- // Create the file outside of Python, before import
22
- try {
23
- await this.createTestFBX(sourcePath);
24
- createdTestFile = true;
25
- } catch (_err) {
26
- // If we can't create the file, we'll handle it in Python
27
- }
28
- }
15
+ export class AssetTools extends BaseTool implements IAssetTools {
16
+ private normalizeAssetPath(path: string): string {
17
+ if (!path) return '';
18
+ let normalized = path.replace(/\\/g, '/').trim();
29
19
 
30
- // Use Python API to import asset with file creation fallback
31
- const pythonCode = `
32
- import unreal
33
- import os
34
- import json
35
-
36
- # Create test FBX if needed
37
- source_path = r'${sourcePath}'
38
- if 'test_model.fbx' in source_path and not os.path.exists(source_path):
39
- # Create directory if needed
40
- os.makedirs(os.path.dirname(source_path), exist_ok=True)
41
-
42
- # Create a valid FBX ASCII file
43
- fbx_content = """FBXHeaderExtension: {
44
- FBXHeaderVersion: 1003
45
- FBXVersion: 7400
46
- CreationTimeStamp: {
47
- Version: 1000
48
- }
49
- Creator: "MCP FBX Test Generator"
50
- }
51
- GlobalSettings: {
52
- Version: 1000
53
- Properties70: {
54
- P: "UpAxis", "int", "Integer", "",2
55
- P: "UpAxisSign", "int", "Integer", "",1
56
- P: "FrontAxis", "int", "Integer", "",1
57
- P: "FrontAxisSign", "int", "Integer", "",1
58
- P: "CoordAxis", "int", "Integer", "",0
59
- P: "CoordAxisSign", "int", "Integer", "",1
60
- P: "UnitScaleFactor", "double", "Number", "",1
20
+ // Handle typical prefixes if missing leading slash
21
+ if (!normalized.startsWith('/')) {
22
+ if (normalized.startsWith('Game/')) normalized = '/' + normalized;
23
+ else if (normalized.startsWith('Engine/')) normalized = '/' + normalized;
24
+ else if (normalized.startsWith('Script/')) normalized = '/' + normalized;
25
+ // Default to Game content if no known prefix
26
+ else normalized = '/Game/' + normalized;
61
27
  }
62
- }
63
- Definitions: {
64
- Version: 100
65
- Count: 2
66
- ObjectType: "Model" {
67
- Count: 1
68
- }
69
- ObjectType: "Geometry" {
70
- Count: 1
28
+
29
+ // Remove double slashes just in case
30
+ normalized = normalized.replace(/\/+/g, '/');
31
+
32
+ // Security check
33
+ return sanitizePath(normalized);
34
+ }
35
+
36
+ async importAsset(params: { sourcePath: string; destinationPath: string; overwrite?: boolean; save?: boolean }): Promise<StandardActionResponse> {
37
+ const res = await this.sendRequest<AssetResponse>('manage_asset', {
38
+ ...params,
39
+ subAction: 'import'
40
+ }, 'manage_asset', { timeoutMs: EXTENDED_ASSET_OP_TIMEOUT_MS });
41
+ if (res && res.success) {
42
+ return { ...res, asset: this.normalizeAssetPath(params.destinationPath), source: params.sourcePath };
71
43
  }
72
- }
73
- Objects: {
74
- Geometry: 1234567, "Geometry::Cube", "Mesh" {
75
- Vertices: *24 {
76
- a: -50,-50,50,50,-50,50,50,50,50,-50,50,50,-50,-50,-50,50,-50,-50,50,50,-50,-50,50,-50
77
- }
78
- PolygonVertexIndex: *36 {
79
- a: 0,1,2,-4,4,5,6,-8,0,4,7,-4,1,5,4,-1,2,6,5,-2,3,7,6,-3
80
- }
81
- GeometryVersion: 124
82
- LayerElementNormal: 0 {
83
- Version: 101
84
- Name: ""
85
- MappingInformationType: "ByPolygonVertex"
86
- ReferenceInformationType: "Direct"
87
- Normals: *108 {
88
- a: 0,0,1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,-1,0,0,-1,0,0,-1,0,0,-1,0,1,0,0,1,0,0,1,0,0,1,0,0,0,1,0,0,1,0,0,1,0,0,1,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0
89
- }
90
- }
91
- LayerElementUV: 0 {
92
- Version: 101
93
- Name: "UVMap"
94
- MappingInformationType: "ByPolygonVertex"
95
- ReferenceInformationType: "IndexToDirect"
96
- UV: *48 {
97
- a: 0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,1
98
- }
99
- UVIndex: *36 {
100
- a: 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35
101
- }
102
- }
103
- Layer: 0 {
104
- Version: 100
105
- LayerElement: {
106
- Type: "LayerElementNormal"
107
- TypedIndex: 0
108
- }
109
- LayerElement: {
110
- Type: "LayerElementUV"
111
- TypedIndex: 0
112
- }
113
- }
44
+ return res;
45
+ }
46
+
47
+ async duplicateAsset(params: { sourcePath: string; destinationPath: string; overwrite?: boolean }): Promise<StandardActionResponse> {
48
+ const sourcePath = this.normalizeAssetPath(params.sourcePath);
49
+ const destinationPath = this.normalizeAssetPath(params.destinationPath);
50
+
51
+ const res = await this.sendRequest<AssetResponse>('manage_asset', {
52
+ sourcePath,
53
+ destinationPath,
54
+ overwrite: params.overwrite ?? false,
55
+ subAction: 'duplicate'
56
+ }, 'manage_asset', { timeoutMs: DEFAULT_ASSET_OP_TIMEOUT_MS });
57
+ if (res && res.success) {
58
+ return { ...res, asset: destinationPath, source: sourcePath };
114
59
  }
115
- Model: 2345678, "Model::Cube", "Mesh" {
116
- Version: 232
117
- Properties70: {
118
- P: "InheritType", "enum", "", "",1
119
- P: "ScalingMax", "Vector3D", "Vector", "",0,0,0
120
- P: "DefaultAttributeIndex", "int", "Integer", "",0
121
- }
122
- Shading: Y
123
- Culling: "CullingOff"
60
+ return res;
61
+ }
62
+
63
+ async renameAsset(params: { sourcePath: string; destinationPath: string }): Promise<StandardActionResponse> {
64
+ const sourcePath = this.normalizeAssetPath(params.sourcePath);
65
+ const destinationPath = this.normalizeAssetPath(params.destinationPath);
66
+
67
+ const res = await this.sendRequest<AssetResponse>('manage_asset', {
68
+ sourcePath,
69
+ destinationPath,
70
+ subAction: 'rename'
71
+ }, 'manage_asset', { timeoutMs: DEFAULT_ASSET_OP_TIMEOUT_MS });
72
+ if (res && res.success) {
73
+ return { ...res, asset: destinationPath, oldName: sourcePath };
124
74
  }
125
- }
126
- Connections: {
127
- C: "OO",1234567,2345678
128
- }
129
- """
130
-
131
- with open(source_path, 'w') as f:
132
- f.write(fbx_content)
133
- print(f"Created test FBX file: {source_path}")
134
-
135
- # Set up the import task
136
- task = unreal.AssetImportTask()
137
- task.filename = r'${sourcePath}'
138
- task.destination_path = '${cleanDest}'
139
- task.automated = True
140
- task.save = True
141
- task.replace_existing = True
142
-
143
- # Configure FBX import options
144
- options = unreal.FbxImportUI()
145
- options.import_mesh = True
146
- options.import_as_skeletal = False
147
- options.mesh_type_to_import = unreal.FBXImportType.FBXIT_STATIC_MESH
148
- options.static_mesh_import_data.combine_meshes = True
149
- options.static_mesh_import_data.generate_lightmap_u_vs = False
150
- options.static_mesh_import_data.auto_generate_collision = False
151
- task.options = options
152
-
153
- # Use AssetTools to import
154
- asset_tools = unreal.AssetToolsHelpers.get_asset_tools()
155
- result = {'success': False, 'error': 'No assets imported', 'source': task.filename}
156
-
157
- try:
158
- asset_tools.import_asset_tasks([task])
159
- if task.imported_object_paths:
160
- result = {
161
- 'success': True,
162
- 'imported': len(task.imported_object_paths),
163
- 'paths': list(task.imported_object_paths)
164
- }
165
- except Exception as e:
166
- result = {'success': False, 'error': str(e), 'source': task.filename}
167
-
168
- print('RESULT:' + json.dumps(result))
169
- `.trim();
170
-
171
- const pyResp = await this.bridge.executePython(pythonCode);
172
-
173
- const interpreted = interpretStandardResult(pyResp, {
174
- successMessage: `Imported assets to ${cleanDest}`,
175
- failureMessage: 'Import failed'
176
- });
177
-
178
- if (interpreted.success) {
179
- const count = coerceNumber(interpreted.payload.imported) ?? 0;
180
- const paths = coerceStringArray(interpreted.payload.paths) ?? [];
181
- return {
182
- success: true,
183
- message: `Imported ${count} assets to ${cleanDest}`,
184
- imported: count,
185
- paths
186
- };
187
- }
75
+ return res;
76
+ }
188
77
 
189
- const errorMessage = `Import failed: ${interpreted.error ?? 'Unknown error'} (source: ${interpreted.payload.source ?? sourcePath})`;
190
- return {
191
- success: false,
192
- message: errorMessage,
193
- error: errorMessage,
194
- details: bestEffortInterpretedText(interpreted)
195
- };
196
- } catch (err) {
197
- return { success: false, error: `Failed to import asset: ${err}` };
198
- } finally {
199
- if (createdTestFile) {
200
- try {
201
- await fs.rm(sourcePath, { force: true });
202
- } catch (cleanupError) {
203
- // Swallow cleanup error but log for debug visibility
204
- console.warn(`Failed to clean up temporary FBX ${sourcePath}:`, cleanupError);
205
- }
206
- }
78
+ async moveAsset(params: { sourcePath: string; destinationPath: string }): Promise<StandardActionResponse> {
79
+ const sourcePath = this.normalizeAssetPath(params.sourcePath);
80
+ const destinationPath = this.normalizeAssetPath(params.destinationPath);
81
+
82
+ const res = await this.sendRequest<AssetResponse>('manage_asset', {
83
+ sourcePath,
84
+ destinationPath,
85
+ subAction: 'move'
86
+ }, 'manage_asset', { timeoutMs: DEFAULT_ASSET_OP_TIMEOUT_MS });
87
+ if (res && res.success) {
88
+ return { ...res, asset: destinationPath, from: sourcePath };
207
89
  }
90
+ return res;
208
91
  }
209
92
 
210
- async duplicateAsset(sourcePath: string, destinationPath: string) {
211
- try {
212
- const res = await this.bridge.call({
213
- objectPath: '/Script/EditorScriptingUtilities.Default__EditorAssetLibrary',
214
- functionName: 'DuplicateAsset',
215
- parameters: {
216
- SourceAssetPath: sourcePath,
217
- DestinationAssetPath: destinationPath
218
- }
219
- });
220
- return res?.Result ?? res;
221
- } catch (err) {
222
- return { error: `Failed to duplicate asset: ${err}` };
93
+ async findByTag(params: { tag: string; value?: string }): Promise<StandardActionResponse> {
94
+ // tag searches don't usually involve paths, but if they did we'd normalize.
95
+ // preserving existing logic for findByTag as it takes 'tag' and 'value'.
96
+ return this.sendRequest<AssetResponse>('asset_query', {
97
+ ...params,
98
+ subAction: 'find_by_tag'
99
+ }, 'asset_query', { timeoutMs: DEFAULT_ASSET_OP_TIMEOUT_MS });
100
+ }
101
+
102
+ async deleteAssets(params: { paths: string[]; fixupRedirectors?: boolean; timeoutMs?: number }): Promise<StandardActionResponse> {
103
+ const assetPaths = (Array.isArray(params.paths) ? params.paths : [])
104
+ .map(p => this.normalizeAssetPath(p));
105
+
106
+ return this.sendRequest<AssetResponse>('manage_asset', {
107
+ assetPaths,
108
+ fixupRedirectors: params.fixupRedirectors,
109
+ subAction: 'delete'
110
+ }, 'manage_asset', { timeoutMs: params.timeoutMs || EXTENDED_ASSET_OP_TIMEOUT_MS });
111
+ }
112
+
113
+ async searchAssets(params: { classNames?: string[]; packagePaths?: string[]; recursivePaths?: boolean; recursiveClasses?: boolean; limit?: number }): Promise<StandardActionResponse> {
114
+ // Normalize package paths if provided
115
+ const packagePaths = params.packagePaths
116
+ ? params.packagePaths.map(p => this.normalizeAssetPath(p))
117
+ : ['/Game'];
118
+
119
+ // Route via asset_query action with subAction 'search_assets'
120
+ const response = await this.sendRequest<AssetResponse>('asset_query', {
121
+ ...params,
122
+ packagePaths,
123
+ subAction: 'search_assets'
124
+ }, 'asset_query', { timeoutMs: DEFAULT_ASSET_OP_TIMEOUT_MS });
125
+
126
+ if (!response.success) {
127
+ const errorMsg = typeof response.error === 'string' ? response.error : JSON.stringify(response.error);
128
+ return { success: false, error: errorMsg || 'Failed to search assets' };
223
129
  }
130
+
131
+ const assetsRaw = response.assets || response.data || response.result;
132
+ const assets = Array.isArray(assetsRaw) ? assetsRaw : [];
133
+
134
+ return {
135
+ success: true,
136
+ message: `Found ${assets.length} assets`,
137
+ assets,
138
+ count: assets.length
139
+ };
224
140
  }
225
141
 
226
- async deleteAsset(assetPath: string) {
142
+ async saveAsset(assetPath: string): Promise<StandardActionResponse> {
143
+ const normalizedPath = this.normalizeAssetPath(assetPath);
227
144
  try {
228
- const res = await this.bridge.call({
229
- objectPath: '/Script/EditorScriptingUtilities.Default__EditorAssetLibrary',
230
- functionName: 'DeleteAsset',
231
- parameters: {
232
- AssetPathToDelete: assetPath
145
+ // Try Automation Bridge first
146
+ const bridge = this.getAutomationBridge();
147
+ if (bridge && typeof bridge.sendAutomationRequest === 'function') {
148
+ try {
149
+ const response: any = await bridge.sendAutomationRequest(
150
+ 'manage_asset',
151
+ { assetPath: normalizedPath, subAction: 'save_asset' }, // 'save_asset' isn't explicitly in HandleAssetAction but usually falls back or Editor handles it?
152
+ // Wait, HandleAssetAction does NOT have 'save'.
153
+ // But 'execute_editor_function' usually handles SAVE_ASSET.
154
+ // Let's check fallback. The original code tried 'save_asset' command which likely failed.
155
+ // Actually, keep safe fallback to 'executeEditorFunction'.
156
+ // But if we want to add save support, we should assume 'save_asset' command failed implies we need fallback.
157
+ // Let's stick to the existing fallback logic but maybe fix the command if known?
158
+ // Since 'save_asset' is not in Subsystem.cpp, it fails.
159
+ // Let's rely on executeEditorFunction below.
160
+ { timeoutMs: DEFAULT_ASSET_OP_TIMEOUT_MS }
161
+ );
162
+
163
+ if (response && response.success !== false) {
164
+ return {
165
+ success: true,
166
+ saved: response.saved ?? true,
167
+ message: response.message || 'Asset saved',
168
+ ...response
169
+ };
170
+ }
171
+ } catch (primaryError) {
172
+ // Log the primary method failure before trying fallback
173
+ // This helps debugging when both methods fail
174
+ log.debug('saveAsset primary method failed, trying fallback', primaryError);
175
+ // Fall through to executeEditorFunction fallback
233
176
  }
234
- });
235
- return res?.Result ?? res;
177
+ }
178
+
179
+ // Fallback to executeEditorFunction
180
+ const res = await this.bridge.executeEditorFunction('SAVE_ASSET', { path: normalizedPath });
181
+ if (res && typeof res === 'object' && (res.success === true || (res.result && res.result.success === true))) {
182
+ const saved = Boolean(res.saved ?? (res.result && res.result.saved));
183
+ return { success: true, saved, ...res, ...(res.result || {}) };
184
+ }
185
+
186
+ return { success: false, error: (res as any)?.error ?? 'Failed to save asset' };
236
187
  } catch (err) {
237
- return { error: `Failed to delete asset: ${err}` };
188
+ return { success: false, error: `Failed to save asset: ${err} ` };
238
189
  }
239
190
  }
240
191
 
241
- async saveAsset(assetPath: string) {
192
+ async createFolder(folderPath: string): Promise<StandardActionResponse> {
193
+ // Folders are paths too
194
+ const path = this.normalizeAssetPath(folderPath);
195
+ return this.sendRequest<AssetResponse>('manage_asset', {
196
+ path,
197
+ subAction: 'create_folder'
198
+ }, 'manage_asset', { timeoutMs: DEFAULT_ASSET_OP_TIMEOUT_MS });
199
+ }
200
+
201
+ async getDependencies(params: { assetPath: string; recursive?: boolean }): Promise<StandardActionResponse> {
202
+ // get_dependencies is typically an asset query or managed asset action?
203
+ // HandleAssetAction has 'get_dependencies' dispatch.
204
+ return this.sendRequest<AssetResponse>('manage_asset', {
205
+ ...params,
206
+ assetPath: this.normalizeAssetPath(params.assetPath),
207
+ subAction: 'get_dependencies'
208
+ }, 'manage_asset');
209
+ }
210
+
211
+ async getSourceControlState(params: { assetPath: string }): Promise<SourceControlState | StandardActionResponse> {
212
+ // Source control state usually via 'asset_query' or 'manage_asset'?
213
+ // It's not in HandleAssetAction explicitly, maybe 'asset_query' subAction?
214
+ // Let's check AssetQueryHandlers.cpp or AssetWorkflowHandlers.cpp dispatch.
215
+ // Assuming 'asset_query' supports it (original code used asset_query).
216
+ return this.sendRequest<AssetResponse>('asset_query', {
217
+ ...params,
218
+ assetPath: this.normalizeAssetPath(params.assetPath),
219
+ subAction: 'get_source_control_state'
220
+ }, 'asset_query');
221
+ }
222
+
223
+ async getMetadata(params: { assetPath: string }): Promise<StandardActionResponse> {
224
+ const response = await this.sendRequest<AssetResponse>('manage_asset', {
225
+ ...params,
226
+ assetPath: this.normalizeAssetPath(params.assetPath),
227
+ subAction: 'get_metadata'
228
+ }, 'manage_asset');
229
+
230
+ // BaseTool unwraps the result, so 'response' is likely the payload itself.
231
+ // However, if the result was null, 'response' might be the wrapper.
232
+ // We handle both cases to be robust.
233
+ const resultObj = (response.result || response) as Record<string, any>;
234
+ return {
235
+ success: true,
236
+ message: 'Metadata retrieved',
237
+ ...resultObj
238
+ };
239
+ }
240
+
241
+ async analyzeGraph(params: { assetPath: string; maxDepth?: number }): Promise<StandardActionResponse> {
242
+ const maxDepth = params.maxDepth ?? 3;
243
+ const assetPath = this.normalizeAssetPath(params.assetPath);
244
+
242
245
  try {
243
- const res = await this.bridge.call({
244
- objectPath: '/Script/EditorScriptingUtilities.Default__EditorAssetLibrary',
245
- functionName: 'SaveAsset',
246
- parameters: {
247
- AssetToSave: assetPath
246
+ // Offload the heavy graph traversal to C++
247
+ const response: any = await this.sendRequest<AssetResponse>('manage_asset', {
248
+ assetPath,
249
+ maxDepth,
250
+ subAction: 'get_asset_graph'
251
+ }, 'manage_asset', { timeoutMs: DEFAULT_ASSET_OP_TIMEOUT_MS });
252
+
253
+ if (!response.success || !response.graph) {
254
+ return { success: false, error: response.error || 'Failed to retrieve asset graph from engine' };
255
+ }
256
+
257
+ const graph: Record<string, string[]> = {};
258
+ // Convert the JSON object (Record<string, any[]>) to string[]
259
+ for (const [key, value] of Object.entries(response.graph)) {
260
+ if (Array.isArray(value)) {
261
+ graph[key] = value.map(v => String(v));
248
262
  }
249
- });
250
- return res?.Result ?? res;
251
- } catch (err) {
252
- return { error: `Failed to save asset: ${err}` };
263
+ }
264
+
265
+ // Use WASM for analysis on the constructed graph
266
+ const base = await wasmIntegration.resolveDependencies(
267
+ assetPath,
268
+ graph,
269
+ { maxDepth }
270
+ );
271
+
272
+ const depth = await wasmIntegration.calculateDependencyDepth(
273
+ assetPath,
274
+ graph,
275
+ { maxDepth }
276
+ );
277
+
278
+ const circularDependencies = await wasmIntegration.findCircularDependencies(
279
+ graph,
280
+ { maxDepth }
281
+ );
282
+
283
+ const topologicalOrder = await wasmIntegration.topologicalSort(graph);
284
+
285
+ const dependenciesList = Array.isArray((base as any).dependencies)
286
+ ? (base as any).dependencies as any[]
287
+ : [];
288
+
289
+ const totalDependencyCount =
290
+ (base as any).totalDependencyCount ??
291
+ (base as any).total_dependency_count ??
292
+ dependenciesList.length;
293
+
294
+ const analysis = {
295
+ asset: (base as any).asset ?? assetPath,
296
+ dependencies: dependenciesList,
297
+ totalDependencyCount,
298
+ requestedMaxDepth: maxDepth,
299
+ maxDepthUsed: depth,
300
+ circularDependencies,
301
+ topologicalOrder,
302
+ stats: {
303
+ nodeCount: dependenciesList.length,
304
+ leafCount: dependenciesList.filter((d: any) => !d.dependencies || d.dependencies.length === 0).length
305
+ }
306
+ };
307
+
308
+ return {
309
+ success: true,
310
+ message: 'graph analyzed',
311
+ analysis
312
+ };
313
+ } catch (e: any) {
314
+ return { success: false, error: `Analysis failed: ${e.message} ` };
253
315
  }
254
316
  }
255
317
 
256
- private async createTestFBX(filePath: string): Promise<void> {
257
- // Create a minimal valid FBX ASCII file for testing
258
- const fbxContent = `; FBX 7.5.0 project file
259
- FBXHeaderExtension: {
260
- FBXHeaderVersion: 1003
261
- FBXVersion: 7500
262
- CreationTimeStamp: {
263
- Version: 1000
264
- Year: 2024
265
- Month: 1
266
- Day: 1
267
- Hour: 0
268
- Minute: 0
269
- Second: 0
270
- Millisecond: 0
271
- }
272
- Creator: "MCP Test FBX Generator"
273
- }
274
- GlobalSettings: {
275
- Version: 1000
276
- }
277
- Definitions: {
278
- Version: 100
279
- Count: 2
280
- ObjectType: "Model" {
281
- Count: 1
282
- }
283
- ObjectType: "Geometry" {
284
- Count: 1
285
- }
286
- }
287
- Objects: {
288
- Geometry: 1234567, "Geometry::Cube", "Mesh" {
289
- Vertices: *24 {
290
- a: -50,-50,-50,50,-50,-50,50,50,-50,-50,50,-50,-50,-50,50,50,-50,50,50,50,50,-50,50,50
291
- }
292
- PolygonVertexIndex: *36 {
293
- a: 0,1,2,-4,4,7,6,-6,0,4,5,-2,1,5,6,-3,2,6,7,-4,4,0,3,-8
294
- }
295
- GeometryVersion: 124
318
+ async createThumbnail(params: { assetPath: string; width?: number; height?: number }): Promise<StandardActionResponse> {
319
+ return this.sendRequest<AssetResponse>('manage_asset', {
320
+ ...params,
321
+ assetPath: this.normalizeAssetPath(params.assetPath),
322
+ subAction: 'generate_thumbnail'
323
+ }, 'manage_asset', { timeoutMs: DEFAULT_ASSET_OP_TIMEOUT_MS });
324
+ }
325
+
326
+ async setTags(params: { assetPath: string; tags: string[] }): Promise<StandardActionResponse> {
327
+ return this.sendRequest<AssetResponse>('manage_asset', {
328
+ ...params,
329
+ assetPath: this.normalizeAssetPath(params.assetPath),
330
+ subAction: 'set_tags'
331
+ }, 'manage_asset', { timeoutMs: DEFAULT_ASSET_OP_TIMEOUT_MS });
332
+ }
333
+
334
+ async generateReport(params: { directory: string; reportType?: string; outputPath?: string }): Promise<StandardActionResponse> {
335
+ return this.sendRequest<AssetResponse>('manage_asset', {
336
+ ...params,
337
+ directory: this.normalizeAssetPath(params.directory),
338
+ subAction: 'generate_report'
339
+ }, 'manage_asset', { timeoutMs: LONG_RUNNING_OP_TIMEOUT_MS });
340
+ }
341
+
342
+ async validate(params: { assetPath: string }): Promise<StandardActionResponse> {
343
+ return this.sendRequest<AssetResponse>('manage_asset', {
344
+ ...params,
345
+ assetPath: this.normalizeAssetPath(params.assetPath),
346
+ subAction: 'validate'
347
+ }, 'manage_asset', { timeoutMs: LONG_RUNNING_OP_TIMEOUT_MS });
348
+ }
349
+
350
+ async generateLODs(params: { assetPath: string; lodCount: number }): Promise<StandardActionResponse> {
351
+ const assetPath = this.normalizeAssetPath(String(params.assetPath ?? '').trim());
352
+ const lodCountRaw = Number(params.lodCount);
353
+
354
+ if (!assetPath) {
355
+ return { success: false, error: 'assetPath is required' };
296
356
  }
297
- Model: 2345678, "Model::TestCube", "Mesh" {
298
- Version: 232
299
- Properties70: {
300
- P: "ScalingMax", "Vector3D", "Vector", "",0,0,0
301
- P: "DefaultAttributeIndex", "int", "Integer", "",0
302
- }
357
+ if (!Number.isFinite(lodCountRaw) || lodCountRaw <= 0) {
358
+ return { success: false, error: 'lodCount must be a positive number' };
303
359
  }
304
- }
305
- Connections: {
306
- C: "OO",1234567,2345678
307
- }
308
- `;
309
-
310
- // Ensure directory exists
311
- const dir = path.dirname(filePath);
360
+ const lodCount = Math.floor(lodCountRaw);
361
+
312
362
  try {
313
- await fs.mkdir(dir, { recursive: true });
314
- } catch {}
315
-
316
- // Write the FBX file
317
- await fs.writeFile(filePath, fbxContent, 'utf8');
363
+ const automation = this.getAutomationBridge();
364
+ const response: any = await automation.sendAutomationRequest('manage_asset', {
365
+ assetPaths: [assetPath],
366
+ numLODs: lodCount,
367
+ subAction: 'generate_lods'
368
+ }, { timeoutMs: EXTENDED_ASSET_OP_TIMEOUT_MS });
369
+
370
+ if (!response || response.success === false) {
371
+ return {
372
+ success: false,
373
+ error: response?.error || response?.message || 'Failed to generate LODs',
374
+ details: response?.result
375
+ };
376
+ }
377
+
378
+ const result = (response.result && typeof response.result === 'object') ? response.result as Record<string, unknown> : {};
379
+
380
+ return {
381
+ success: true,
382
+ message: response.message || 'LODs generated successfully',
383
+ assetPath: (result.assetPath as string) ?? assetPath,
384
+ lodCount: (result.lodCount as number) ?? lodCount,
385
+ ...result
386
+ };
387
+ } catch (error) {
388
+ return {
389
+ success: false,
390
+ error: `Failed to generate LODs: ${error instanceof Error ? error.message : String(error)} `
391
+ };
392
+ }
318
393
  }
319
394
  }