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
@@ -0,0 +1,100 @@
1
+ import { spawn } from 'child_process';
2
+ import { exec } from 'child_process';
3
+
4
+ const server = spawn('node', ['dist/cli.js'], {
5
+ stdio: ['pipe', 'pipe', 'inherit'],
6
+ cwd: process.cwd()
7
+ });
8
+
9
+ let initialized = false;
10
+ let gotResponse = false;
11
+
12
+ server.stdout.on('data', (data) => {
13
+ const text = data.toString();
14
+ console.log('SERVER OUTPUT:', text);
15
+
16
+ try {
17
+ const parsed = JSON.parse(text.trim());
18
+ if (parsed.id === 1 && parsed.result && !initialized) {
19
+ initialized = true;
20
+ console.log('Server initialized successfully');
21
+
22
+ // Send initialized message
23
+ const initializedMsg = JSON.stringify({
24
+ jsonrpc: '2.0',
25
+ method: 'initialized',
26
+ params: {}
27
+ }) + '\n';
28
+ console.log('Sending initialized message...');
29
+ server.stdin.write(initializedMsg);
30
+ }
31
+
32
+ if (parsed.id === 2 && parsed.result) {
33
+ gotResponse = true;
34
+ console.log('Got health response, checking port...');
35
+
36
+ exec('netstat -ano | findstr :8090', (err, stdout) => {
37
+ if (stdout && stdout.trim()) {
38
+ console.log('✅ Port 8090 is listening!');
39
+ console.log('Port details:', stdout.trim());
40
+ } else {
41
+ console.log('❌ Port 8090 is not listening');
42
+ }
43
+ server.kill();
44
+ process.exit(0);
45
+ });
46
+ }
47
+ } catch (e) {
48
+ // Not JSON, ignore
49
+ }
50
+ });
51
+
52
+ server.on('exit', (code) => {
53
+ console.log('Server exited with code:', code);
54
+ if (!gotResponse) {
55
+ process.exit(1);
56
+ }
57
+ });
58
+
59
+ server.on('error', (err) => {
60
+ console.error('Failed to start server:', err);
61
+ process.exit(1);
62
+ });
63
+
64
+ // Send initialize message
65
+ const initMsg = JSON.stringify({
66
+ jsonrpc: '2.0',
67
+ id: 1,
68
+ method: 'initialize',
69
+ params: {
70
+ protocolVersion: '2024-11-05',
71
+ capabilities: {},
72
+ clientInfo: { name: 'test', version: '1.0.0' }
73
+ }
74
+ }) + '\n';
75
+
76
+ console.log('Sending initialize message...');
77
+ server.stdin.write(initMsg);
78
+
79
+ // Send health check after a delay
80
+ setTimeout(() => {
81
+ if (initialized && !gotResponse) {
82
+ console.log('Sending health check...');
83
+ const healthMsg = JSON.stringify({
84
+ jsonrpc: '2.0',
85
+ id: 2,
86
+ method: 'resources/read',
87
+ params: { uri: 'ue://health' }
88
+ }) + '\n';
89
+ server.stdin.write(healthMsg);
90
+ }
91
+ }, 1000);
92
+
93
+ // Timeout
94
+ setTimeout(() => {
95
+ if (!gotResponse) {
96
+ console.log('Timeout: Server did not respond');
97
+ server.kill();
98
+ process.exit(1);
99
+ }
100
+ }, 5000);
@@ -37,13 +37,15 @@ const defaultDocPath = path.resolve(repoRoot, 'docs', 'unreal-tool-test-cases.md
37
37
  const docPath = path.resolve(repoRoot, process.env.UNREAL_MCP_TEST_DOC ?? defaultDocPath);
38
38
  const reportsDir = path.resolve(repoRoot, 'tests', 'reports');
39
39
  const resultsPath = path.join(reportsDir, `unreal-tool-test-results-${new Date().toISOString().replace(/[:]/g, '-')}.json`);
40
- const defaultFbxDir = normalizeWindowsPath(process.env.UNREAL_MCP_FBX_DIR ?? 'C:\\Users\\micro\\Downloads\\Compressed\\fbx');
40
+ const defaultFbxDir = normalizeWindowsPath(process.env.UNREAL_MCP_FBX_DIR ?? path.join(repoRoot, 'tests', 'assets', 'fbx'));
41
41
  const defaultFbxFile = normalizeWindowsPath(process.env.UNREAL_MCP_FBX_FILE ?? path.join(defaultFbxDir, 'test_model.fbx'));
42
42
 
43
43
  const cliOptions = parseCliOptions(process.argv.slice(2));
44
44
  const serverCommand = process.env.UNREAL_MCP_SERVER_CMD ?? 'node';
45
45
  const serverArgs = parseArgsList(process.env.UNREAL_MCP_SERVER_ARGS) ?? [path.join(repoRoot, 'dist', 'cli.js')];
46
46
  const serverCwd = process.env.UNREAL_MCP_SERVER_CWD ?? repoRoot;
47
+ const stressTestMode = process.env.STRESS_TEST_MODE === '1';
48
+ const benchmarkMode = process.env.BENCHMARK_MODE === '1';
47
49
 
48
50
  async function main() {
49
51
  await ensureFbxDirectory();
@@ -70,6 +72,8 @@ async function main() {
70
72
 
71
73
  let transport; let client;
72
74
  const runResults = [];
75
+ let automationBridgeStatus = { connected: false, summary: null };
76
+ let automationBridgeTestsEnabled = process.env.UNREAL_MCP_AUTOMATION_BRIDGE === '1';
73
77
 
74
78
  if (!cliOptions.dryRun) {
75
79
  try {
@@ -87,6 +91,25 @@ async function main() {
87
91
 
88
92
  await client.connect(transport);
89
93
  await client.listTools({});
94
+
95
+ try {
96
+ const bridgeResource = await client.readResource({ uri: 'ue://automation-bridge' });
97
+ const text = bridgeResource.contents?.[0]?.text;
98
+ if (text) {
99
+ const parsed = JSON.parse(text);
100
+ if (parsed && typeof parsed === 'object') {
101
+ const summary = parsed.summary ?? parsed;
102
+ const connected = Boolean(summary?.connected);
103
+ automationBridgeStatus = { connected, summary };
104
+ if (connected) {
105
+ automationBridgeTestsEnabled = true;
106
+ }
107
+ }
108
+ }
109
+ } catch (err) {
110
+ // Resource may not exist on older servers; treat as unavailable without failing run
111
+ console.warn('[warn] Unable to query ue://automation-bridge resource:', err?.message ?? String(err));
112
+ }
90
113
  } catch (err) {
91
114
  console.error('Failed to start or initialize MCP server:', err);
92
115
  if (transport) {
@@ -98,6 +121,22 @@ async function main() {
98
121
  }
99
122
 
100
123
  for (const testCase of filteredCases) {
124
+ let skipReason = testCase.skipReason;
125
+
126
+ if (!skipReason && testCase.groupName === 'Automation Bridge') {
127
+ if (!automationBridgeTestsEnabled) {
128
+ skipReason = 'Automation bridge tests disabled (set UNREAL_MCP_AUTOMATION_BRIDGE=1 or connect the plugin).';
129
+ } else {
130
+ const requestedTransport = typeof testCase.arguments?.transport === 'string'
131
+ ? testCase.arguments.transport.trim().toLowerCase()
132
+ : '';
133
+ const wantsBridge = ['automation_bridge', 'automation', 'bridge'].includes(requestedTransport);
134
+ if (wantsBridge && !automationBridgeStatus.connected) {
135
+ skipReason = 'Automation bridge transport requested but plugin is not connected.';
136
+ }
137
+ }
138
+ }
139
+
101
140
  if (testCase.skipReason) {
102
141
  runResults.push({
103
142
  ...testCase,
@@ -108,6 +147,16 @@ async function main() {
108
147
  continue;
109
148
  }
110
149
 
150
+ if (skipReason) {
151
+ runResults.push({
152
+ ...testCase,
153
+ status: 'skipped',
154
+ detail: skipReason
155
+ });
156
+ console.log(formatResultLine(testCase, 'skipped', skipReason));
157
+ continue;
158
+ }
159
+
111
160
  if (cliOptions.dryRun) {
112
161
  runResults.push({
113
162
  ...testCase,
@@ -162,6 +211,11 @@ async function main() {
162
211
  await persistResults(runResults);
163
212
  summarize(runResults);
164
213
 
214
+ // Performance statistics if benchmarking
215
+ if (benchmarkMode) {
216
+ generateBenchmarkReport(runResults);
217
+ }
218
+
165
219
  if (runResults.some((result) => result.status === 'failed')) {
166
220
  process.exitCode = 1;
167
221
  }
@@ -581,12 +635,6 @@ function enrichTestCase(rawCase) {
581
635
  case 'System Control Tools': {
582
636
  if (!payloadValue) return { ...base, skipReason: 'No JSON payload provided' };
583
637
  if (!payloadValue.action) return { ...base, skipReason: 'Missing action in payload' };
584
- if (payloadValue.action === 'engine_quit' || payloadValue.action === 'engine_start') {
585
- return {
586
- ...base,
587
- skipReason: 'Skipping engine process management during automated run'
588
- };
589
- }
590
638
  return {
591
639
  ...base,
592
640
  toolName: 'system_control',
@@ -604,14 +652,109 @@ function enrichTestCase(rawCase) {
604
652
  arguments: payloadValue
605
653
  };
606
654
  }
607
- case 'Remote Control Preset Tools': {
655
+ case 'Asset Boundary Tests': {
608
656
  if (!payloadValue) return { ...base, skipReason: 'No JSON payload provided' };
609
- if (!payloadValue.action) return { ...base, skipReason: 'Missing action in payload' };
610
- return {
611
- ...base,
612
- toolName: 'manage_rc',
613
- arguments: payloadValue
614
- };
657
+ return { ...base, toolName: 'manage_asset', arguments: payloadValue };
658
+ }
659
+ case 'Actor Boundary Tests': {
660
+ if (!payloadValue) return { ...base, skipReason: 'No JSON payload provided' };
661
+ const args = { ...payloadValue };
662
+ if (Array.isArray(args.location) && args.location.length === 3) {
663
+ args.location = { x: args.location[0], y: args.location[1], z: args.location[2] };
664
+ }
665
+ if (Array.isArray(args.rotation) && args.rotation.length === 3) {
666
+ args.rotation = { pitch: args.rotation[0], yaw: args.rotation[1], roll: args.rotation[2] };
667
+ }
668
+ if (Array.isArray(args.scale) && args.scale.length === 3) {
669
+ args.scale = { x: args.scale[0], y: args.scale[1], z: args.scale[2] };
670
+ }
671
+ if (Array.isArray(args.force) && args.force.length === 3) {
672
+ args.force = { x: args.force[0], y: args.force[1], z: args.force[2] };
673
+ }
674
+ return { ...base, toolName: 'control_actor', arguments: args };
675
+ }
676
+ case 'Editor Boundary Tests':
677
+ case 'Editor Control Boundary Tests': {
678
+ if (!payloadValue) return { ...base, skipReason: 'No JSON payload provided' };
679
+ return { ...base, toolName: 'control_editor', arguments: payloadValue };
680
+ }
681
+ case 'Level Boundary Tests':
682
+ case 'Level Management Boundary Tests': {
683
+ if (!payloadValue) return { ...base, skipReason: 'No JSON payload provided' };
684
+ return { ...base, toolName: 'manage_level', arguments: payloadValue };
685
+ }
686
+ case 'Animation Boundary Tests': {
687
+ if (!payloadValue) return { ...base, skipReason: 'No JSON payload provided' };
688
+ return { ...base, toolName: 'animation_physics', arguments: payloadValue };
689
+ }
690
+ case 'Blueprint Boundary Tests':
691
+ case 'Blueprint Control Boundary Tests': {
692
+ if (!payloadValue) return { ...base, skipReason: 'No JSON payload provided' };
693
+ return { ...base, toolName: 'manage_blueprint', arguments: payloadValue };
694
+ }
695
+ case 'Effects Boundary Tests':
696
+ case 'Effects Control Boundary Tests': {
697
+ if (!payloadValue) return { ...base, skipReason: 'No JSON payload provided' };
698
+ const args = { ...payloadValue };
699
+ if (Array.isArray(args.location) && args.location.length === 3) {
700
+ args.location = { x: args.location[0], y: args.location[1], z: args.location[2] };
701
+ }
702
+ if (Array.isArray(args.start) && args.start.length === 3) {
703
+ args.start = { x: args.start[0], y: args.start[1], z: args.start[2] };
704
+ }
705
+ if (Array.isArray(args.end) && args.end.length === 3) {
706
+ args.end = { x: args.end[0], y: args.end[1], z: args.end[2] };
707
+ }
708
+ return { ...base, toolName: 'create_effect', arguments: args };
709
+ }
710
+ case 'Environment Boundary Tests':
711
+ case 'Environment Building Boundary Tests': {
712
+ if (!payloadValue) return { ...base, skipReason: 'No JSON payload provided' };
713
+ const args = { ...payloadValue };
714
+ if (Array.isArray(args.location) && args.location.length === 3) {
715
+ args.location = { x: args.location[0], y: args.location[1], z: args.location[2] };
716
+ }
717
+ if (args.bounds) {
718
+ const bounds = { ...args.bounds };
719
+ if (Array.isArray(bounds.location) && bounds.location.length === 3) {
720
+ bounds.location = { x: bounds.location[0], y: bounds.location[1], z: bounds.location[2] };
721
+ }
722
+ if (Array.isArray(bounds.size) && bounds.size.length === 3) {
723
+ bounds.size = { x: bounds.size[0], y: bounds.size[1], z: bounds.size[2] };
724
+ }
725
+ args.bounds = bounds;
726
+ }
727
+ return { ...base, toolName: 'build_environment', arguments: args };
728
+ }
729
+ case 'System Boundary Tests':
730
+ case 'System Control Boundary Tests': {
731
+ if (!payloadValue) return { ...base, skipReason: 'No JSON payload provided' };
732
+ return { ...base, toolName: 'system_control', arguments: payloadValue };
733
+ }
734
+ case 'Sequence Boundary Tests':
735
+ case 'Sequence Control Boundary Tests': {
736
+ if (!payloadValue) return { ...base, skipReason: 'No JSON payload provided' };
737
+ return { ...base, toolName: 'manage_sequence', arguments: payloadValue };
738
+ }
739
+ case 'Remote Control Boundary Tests':
740
+ case 'Remote Control Preset Boundary Tests': {
741
+ // No consolidated remote control tool mapping; treated as unsupported group
742
+ return { ...base, skipReason: `Unknown tool group '${rawCase.groupName}'` };
743
+ }
744
+ case 'Python Execution Boundary Tests': {
745
+ // No consolidated Python execution tool; treated as unsupported group
746
+ return { ...base, skipReason: `Unknown tool group '${rawCase.groupName}'` };
747
+ }
748
+ case 'Inspection Boundary Tests': {
749
+ if (!payloadValue) return { ...base, skipReason: 'No JSON payload provided' };
750
+ return { ...base, toolName: 'inspect', arguments: payloadValue };
751
+ }
752
+ case 'Cross-Tool Integration Tests':
753
+ case 'Stress Test Scenarios':
754
+ case 'Error Recovery Tests': {
755
+ if (!payloadValue) return { ...base, skipReason: 'No JSON payload provided' };
756
+ // These require multi-step execution or special handling
757
+ return { ...base, skipReason: 'Multi-step test scenario - requires custom test implementation' };
615
758
  }
616
759
  default:
617
760
  return { ...base, skipReason: `Unknown tool group '${rawCase.groupName}'` };
@@ -628,7 +771,51 @@ function evaluateExpectation(testCase, response) {
628
771
  : undefined;
629
772
  const actualSuccess = structuredSuccess ?? !response.isError;
630
773
 
774
+ // Extract actual error/message from response
775
+ let actualError = null;
776
+ let actualMessage = null;
777
+ if (response.structuredContent) {
778
+ actualError = response.structuredContent.error;
779
+ actualMessage = response.structuredContent.message;
780
+ }
781
+
782
+ // CRITICAL FIX: UE_NOT_CONNECTED errors should ALWAYS fail tests unless explicitly expected
783
+ if (actualError === 'UE_NOT_CONNECTED') {
784
+ const explicitlyExpectsDisconnection = lowerExpected.includes('not connected') ||
785
+ lowerExpected.includes('ue_not_connected') ||
786
+ lowerExpected.includes('disconnected');
787
+ if (!explicitlyExpectsDisconnection) {
788
+ return {
789
+ passed: false,
790
+ reason: `Test requires Unreal Engine connection, but got: ${actualError} - ${actualMessage}`
791
+ };
792
+ }
793
+ }
794
+
795
+ // For tests that expect specific error types, validate the actual error matches
631
796
  const expectedFailure = containsFailure && !containsSuccess;
797
+ if (expectedFailure && !actualSuccess) {
798
+ // Test expects failure and got failure - but verify it's the RIGHT kind of failure
799
+ const lowerReason = actualMessage?.toLowerCase() || actualError?.toLowerCase() || '';
800
+ const errorTypeMatch = failureKeywords.some(keyword => lowerExpected.includes(keyword) && lowerReason.includes(keyword));
801
+
802
+ // If expected outcome specifies an error type, actual error should match it
803
+ if (lowerExpected.includes('not found') || lowerExpected.includes('invalid') ||
804
+ lowerExpected.includes('missing') || lowerExpected.includes('already exists')) {
805
+ const passed = errorTypeMatch;
806
+ let reason;
807
+ if (response.isError) {
808
+ reason = response.content?.map((entry) => ('text' in entry ? entry.text : JSON.stringify(entry))).join('\n');
809
+ } else if (response.structuredContent) {
810
+ reason = JSON.stringify(response.structuredContent);
811
+ } else {
812
+ reason = 'No structured response returned';
813
+ }
814
+ return { passed, reason };
815
+ }
816
+ }
817
+
818
+ // Default evaluation logic
632
819
  const passed = expectedFailure ? !actualSuccess : !!actualSuccess;
633
820
  let reason;
634
821
  if (response.isError) {
@@ -685,6 +872,47 @@ function summarize(results) {
685
872
  console.log(`Results written to: ${resultsPath}`);
686
873
  }
687
874
 
875
+ function generateBenchmarkReport(results) {
876
+ const passedResults = results.filter(r => r.status === 'passed' && r.durationMs);
877
+
878
+ if (passedResults.length === 0) {
879
+ console.log('\nNo performance data available for benchmarking.');
880
+ return;
881
+ }
882
+
883
+ const durations = passedResults.map(r => r.durationMs).sort((a, b) => a - b);
884
+ const sum = durations.reduce((a, b) => a + b, 0);
885
+ const avg = sum / durations.length;
886
+ const median = durations[Math.floor(durations.length / 2)];
887
+ const min = durations[0];
888
+ const max = durations[durations.length - 1];
889
+ const p95 = durations[Math.floor(durations.length * 0.95)];
890
+ const p99 = durations[Math.floor(durations.length * 0.99)];
891
+
892
+ console.log('\nPerformance Benchmark');
893
+ console.log('====================');
894
+ console.log(`Total operations: ${passedResults.length}`);
895
+ console.log(`Average: ${avg.toFixed(2)} ms`);
896
+ console.log(`Median: ${median.toFixed(2)} ms`);
897
+ console.log(`Min: ${min.toFixed(2)} ms`);
898
+ console.log(`Max: ${max.toFixed(2)} ms`);
899
+ console.log(`95th percentile: ${p95.toFixed(2)} ms`);
900
+ console.log(`99th percentile: ${p99.toFixed(2)} ms`);
901
+
902
+ // Group by tool
903
+ const byTool = {};
904
+ passedResults.forEach(r => {
905
+ if (!byTool[r.toolName]) byTool[r.toolName] = [];
906
+ byTool[r.toolName].push(r.durationMs);
907
+ });
908
+
909
+ console.log('\nBy Tool:');
910
+ Object.entries(byTool).forEach(([tool, times]) => {
911
+ const toolAvg = times.reduce((a, b) => a + b, 0) / times.length;
912
+ console.log(` ${tool}: ${toolAvg.toFixed(2)} ms avg (${times.length} ops)`);
913
+ });
914
+ }
915
+
688
916
  function normalizeWindowsPath(value) {
689
917
  if (typeof value !== 'string') return value;
690
918
  return value.replace(/\\+/g, '\\').replace(/\/+/g, '\\');
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Condensed Animation & Physics Test Suite (15 cases) — safe operations.
4
+ * Tool: animation_physics
5
+ */
6
+
7
+ import { runToolTests } from './test-runner.mjs';
8
+
9
+ const testCases = [
10
+ { scenario: 'Create simple animation blueprint (best-effort)', toolName: 'animation_physics', arguments: { action: 'create_animation_bp', name: 'ABP_TC', skeletonPath: '/Engine/Tutorial/SubEditors/TutorialAssets/Character/TutorialTPP_Skeleton', savePath: '/Game/Animations' }, expected: 'success or not found' },
11
+ { scenario: 'Play montage (no-op safe)', toolName: 'animation_physics', arguments: { action: 'play_montage', actorName: 'SkeletalCharacter', montagePath: '/Game/Animations/AM_Test', playRate: 1.0 }, expected: 'success or not found' },
12
+ { scenario: 'Setup ragdoll (best-effort)', toolName: 'animation_physics', arguments: { action: 'setup_ragdoll', skeletonPath: '/Engine/Tutorial/SubEditors/TutorialAssets/Character/TutorialTPP_Skeleton', actorName: 'RagdollCharacter', physicsAssetName: 'PA_Mannequin', blendWeight: 1.0 }, expected: 'success or not found' },
13
+ { scenario: 'Create blend space (safe)', toolName: 'animation_physics', arguments: { action: 'create_blend_space', name: 'TC_Locomotion', skeletonPath: '/Engine/Tutorial/SubEditors/TutorialAssets/Character/TutorialTPP_Skeleton', horizontalAxis: { name: 'Speed', minValue: 0, maxValue: 100 }, verticalAxis: { name: 'Direction', minValue: -180, maxValue: 180 }, animations: [] }, expected: 'success - blend space created' },
14
+ { scenario: 'Create state machine skeleton', toolName: 'animation_physics', arguments: { action: 'create_state_machine', machineName: 'TC_States', states: [{ name: 'Idle' }, { name: 'Run' }], transitions: [] }, expected: 'success' },
15
+ { scenario: 'Create simple IK setup', toolName: 'animation_physics', arguments: { action: 'setup_ik', actorName: 'Character', chain: { rootBone: 'pelvis', endBone: 'foot_l' }, effector: { targetActor: 'TargetActor' } }, expected: 'success' },
16
+ { scenario: 'Create procedural animation', toolName: 'animation_physics', arguments: { action: 'create_procedural_anim', systemName: 'TC_Procedural', baseAnimation: '/Game/Animations/BasePose', settings: {} }, expected: 'success' },
17
+ { scenario: 'Create animation blueprint minimal', toolName: 'animation_physics', arguments: { action: 'create_animation_bp', blueprintName: 'TC_AnimBP', parentClass: 'AnimInstance', targetSkeleton: '/Engine/Tutorial/SubEditors/TutorialAssets/Character/TutorialTPP_Skeleton', stateMachines: [] }, expected: 'success or not found' },
18
+ // { scenario: 'Activate ragdoll trigger (no-op)', toolName: 'animation_physics', arguments: { action: 'activate_ragdoll', actorName: 'Character', blendTime: 0.2 }, expected: 'success' },
19
+ { scenario: 'Create blend tree', toolName: 'animation_physics', arguments: { action: 'create_blend_tree', treeName: 'TC_UpperBody', basePose: '/Game/Animations/BasePose', additiveAnimations: [] }, expected: 'success' },
20
+ { scenario: 'Create simple animation asset', toolName: 'animation_physics', arguments: { action: 'create_animation_asset', name: 'ANIM_TC', skeletonPath: '/Engine/Tutorial/SubEditors/TutorialAssets/Character/TutorialTPP_Skeleton', savePath: '/Game/Animations', assetType: 'sequence' }, expected: 'success or not found' },
21
+
22
+ // PRE-CLEANUP: Ensure target doesn't exist to avoid "DuplicateAsset failed"
23
+ { scenario: 'Pre-cleanup Retargeted asset', toolName: 'manage_asset', arguments: { action: 'delete_asset', assetPath: '/Game/Animations/Retargeted/ANIM_TC_Retargeted' }, expected: 'success|not_found|error' },
24
+
25
+ { scenario: 'Retargeting sequence (best-effort)', toolName: 'animation_physics', arguments: { action: 'setup_retargeting', sourceSkeleton: '/Engine/Tutorial/SubEditors/TutorialAssets/Character/TutorialTPP_Skeleton', targetSkeleton: '/Engine/Tutorial/SubEditors/TutorialAssets/Character/TutorialTPP_Skeleton', assets: ['/Game/Animations/ANIM_TC'], savePath: '/Game/Animations/Retargeted', overwrite: true }, expected: 'success or not connected or not found' },
26
+ { scenario: 'Setup physics simulation (best-effort)', toolName: 'animation_physics', arguments: { action: 'setup_physics_simulation', skeletonPath: '/Engine/Tutorial/SubEditors/TutorialAssets/Character/TutorialTPP_Skeleton', savePath: '/Game/Physics', physicsAssetName: 'PA_TC', assignToMesh: false }, expected: 'success or not found' },
27
+ { scenario: 'Play and stop a short montage (best-effort)', toolName: 'animation_physics', arguments: { action: 'play_montage', actorName: 'SkeletalCharacter', montagePath: '/Game/Animations/AM_Test', playRate: 1.0 }, expected: 'success or not found' },
28
+
29
+ { scenario: 'Anim Setup - Create Montage', toolName: 'animation_physics', arguments: { action: 'create_animation_asset', name: 'AM_Setup', skeletonPath: '/Engine/Tutorial/SubEditors/TutorialAssets/Character/TutorialTPP_Skeleton', savePath: '/Game/Animations', assetType: 'montage' }, expected: 'success or not found' },
30
+ { scenario: 'Anim Setup - Add Notify', toolName: 'animation_physics', arguments: { action: 'add_notify', assetPath: '/Game/Animations/AM_Setup', notifyName: 'AnimNotify_PlaySound', time: 0.5 }, expected: 'success or not found or AUTOMATION_BRIDGE_UNAVAILABLE' },
31
+
32
+ // Edge cases
33
+ { scenario: 'Error: Invalid skeleton path', toolName: 'animation_physics', arguments: { action: 'create_animation_asset', assetName: 'InvalidAnim', skeletonPath: '/Game/Invalid/Skeleton' }, expected: 'error|asset_not_found' },
34
+ { scenario: 'Edge: Play rate 0 (pause)', toolName: 'animation_physics', arguments: { action: 'play_montage', actorName: 'SkeletalCharacter', montagePath: '/Game/Animations/AM_Setup', playRate: 0 }, expected: 'success|no_op' },
35
+ { scenario: 'Border: Negative RPM vehicle', toolName: 'animation_physics', arguments: { action: 'configure_vehicle', vehicleName: 'TestVehicle', maxRPM: -1000 }, expected: 'success|clamped' },
36
+ { scenario: 'Error: Empty wheels array', toolName: 'animation_physics', arguments: { action: 'configure_vehicle', vehicleName: 'TestVehicle', wheels: [] }, expected: 'success|default_wheels' },
37
+ { scenario: 'Edge: No plugin deps', toolName: 'animation_physics', arguments: { action: 'configure_vehicle', vehicleName: 'TestVehicle', plugins: ['InvalidPlugin'] }, expected: 'error|plugin_missing|MISSING_ENGINE_PLUGINS' },
38
+
39
+ // FINAL CLEANUP
40
+ { scenario: 'Cleanup all animation artifacts', toolName: 'animation_physics', arguments: { action: 'cleanup', artifacts: ['ABP_TC', 'TC_AnimBP', 'ANIM_TC', 'AM_Setup', 'TC_Locomotion', 'PA_TC', 'TC_States', 'TC_UpperBody'] }, expected: 'success or no matching' },
41
+ { scenario: 'Cleanup retargeted asset', toolName: 'manage_asset', arguments: { action: 'delete_asset', assetPath: '/Game/Animations/Retargeted/ANIM_TC_Retargeted' }, expected: 'success|not_found' }
42
+ ];
43
+
44
+ await runToolTests('Animation & Physics', testCases);
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Advanced Asset Tools Test Suite
4
+ * Tool: manage_asset
5
+ *
6
+ * This file tests newer/advanced capabilities of the manage_asset tool:
7
+ * - create_thumbnail
8
+ * - analyze_graph
9
+ * - get_source_control_state
10
+ */
11
+
12
+ import { runToolTests } from './test-runner.mjs';
13
+
14
+ const testCases = [
15
+ {
16
+ scenario: "Setup: Create test folder",
17
+ toolName: "manage_asset",
18
+ arguments: {
19
+ action: "create_folder",
20
+ path: "/Game/Tests/AdvancedAssets"
21
+ },
22
+ expected: {
23
+ condition: "success|error",
24
+ successPattern: "created",
25
+ errorPattern: "CREATE_FAILED"
26
+ }
27
+ },
28
+ {
29
+ scenario: "Setup: Create base material",
30
+ toolName: "manage_asset",
31
+ arguments: {
32
+ action: "create_material",
33
+ name: "M_Base",
34
+ path: "/Game/Tests/AdvancedAssets",
35
+ materialType: "Master"
36
+ },
37
+ expected: "success"
38
+ },
39
+ {
40
+ scenario: "Create Thumbnail",
41
+ toolName: "manage_asset",
42
+ arguments: {
43
+ action: "create_thumbnail",
44
+ assetPath: "/Game/Tests/AdvancedAssets/M_Base"
45
+ },
46
+ expected: "success - thumbnail created"
47
+ },
48
+ {
49
+ scenario: "Analyze Dependency Graph",
50
+ toolName: "manage_asset",
51
+ arguments: {
52
+ action: "analyze_graph",
53
+ assetPath: "/Game/Tests/AdvancedAssets/M_Base",
54
+ maxDepth: 1
55
+ },
56
+ expected: "success - graph analyzed"
57
+ },
58
+ {
59
+ scenario: "Get Source Control State",
60
+ toolName: "manage_asset",
61
+ arguments: {
62
+ action: "get_source_control_state",
63
+ assetPath: "/Game/Tests/AdvancedAssets/M_Base"
64
+ },
65
+ expected: {
66
+ condition: "success|error",
67
+ successPattern: "state retrieved",
68
+ errorPattern: "SC_DISABLED"
69
+ }
70
+ },
71
+ {
72
+ scenario: "Cleanup: Delete test folder",
73
+ toolName: "manage_asset",
74
+ arguments: {
75
+ action: "delete_assets",
76
+ assetPaths: ["/Game/Tests/AdvancedAssets"]
77
+ },
78
+ expected: "success"
79
+ }
80
+ ];
81
+
82
+ await runToolTests('Advanced Asset Tools', testCases);
@@ -0,0 +1,35 @@
1
+ import { UnrealAutomationClient } from '../src/unreal-client.js';
2
+ import { runUnrealTests, assert } from './run-unreal-tool-tests.mjs';
3
+
4
+ runUnrealTests('asset_error_messages', [
5
+ {
6
+ name: 'Import Non-Existent File',
7
+ action: async (client) => {
8
+ try {
9
+ await client.sendRequest('import', {
10
+ sourcePath: 'C:/Non/Existent/File.fbx',
11
+ destinationPath: '/Game/Tests/ImportFail'
12
+ });
13
+ throw new Error('Should have failed');
14
+ } catch (error) {
15
+ assert(error.code === 'SOURCE_NOT_FOUND', `Unexpected error code: ${error.code}`);
16
+ assert(error.message.includes('Source file not found'), 'Unexpected error message');
17
+ }
18
+ }
19
+ },
20
+ {
21
+ name: 'Rename Non-Existent Asset',
22
+ action: async (client) => {
23
+ try {
24
+ await client.sendRequest('rename', {
25
+ sourcePath: '/Game/NonExistentAsset',
26
+ destinationPath: '/Game/NewLocation'
27
+ });
28
+ throw new Error('Should have failed');
29
+ } catch (error) {
30
+ assert(error.code === 'ASSET_NOT_FOUND', `Unexpected error code: ${error.code}`);
31
+ assert(error.message.includes('Source asset not found'), 'Unexpected error message');
32
+ }
33
+ }
34
+ }
35
+ ]);