unreal-engine-mcp-server 0.5.3 → 0.5.5

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 (480) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/dist/automation/bridge.d.ts +1 -0
  3. package/dist/automation/bridge.d.ts.map +1 -0
  4. package/dist/automation/bridge.js +62 -4
  5. package/dist/automation/bridge.js.map +1 -0
  6. package/dist/automation/connection-manager.d.ts.map +1 -0
  7. package/dist/automation/connection-manager.js.map +1 -0
  8. package/dist/automation/handshake.d.ts.map +1 -0
  9. package/dist/automation/handshake.js.map +1 -0
  10. package/dist/automation/index.d.ts.map +1 -0
  11. package/dist/automation/index.js.map +1 -0
  12. package/dist/automation/message-handler.d.ts.map +1 -0
  13. package/dist/automation/message-handler.js.map +1 -0
  14. package/dist/automation/request-tracker.d.ts.map +1 -0
  15. package/dist/automation/request-tracker.js.map +1 -0
  16. package/dist/automation/types.d.ts +1 -0
  17. package/dist/automation/types.d.ts.map +1 -0
  18. package/dist/automation/types.js.map +1 -0
  19. package/dist/cli.d.ts.map +1 -0
  20. package/dist/cli.js +4 -3
  21. package/dist/cli.js.map +1 -0
  22. package/dist/config/class-aliases.d.ts.map +1 -0
  23. package/dist/config/class-aliases.js.map +1 -0
  24. package/dist/config.d.ts.map +1 -0
  25. package/dist/config.js.map +1 -0
  26. package/dist/constants.d.ts +2 -0
  27. package/dist/constants.d.ts.map +1 -0
  28. package/dist/constants.js +2 -0
  29. package/dist/constants.js.map +1 -0
  30. package/dist/graphql/loaders.d.ts.map +1 -0
  31. package/dist/graphql/loaders.js.map +1 -0
  32. package/dist/graphql/resolvers.d.ts.map +1 -0
  33. package/dist/graphql/resolvers.js +29 -29
  34. package/dist/graphql/resolvers.js.map +1 -0
  35. package/dist/graphql/schema.d.ts.map +1 -0
  36. package/dist/graphql/schema.js.map +1 -0
  37. package/dist/graphql/server.d.ts +0 -1
  38. package/dist/graphql/server.d.ts.map +1 -0
  39. package/dist/graphql/server.js +15 -16
  40. package/dist/graphql/server.js.map +1 -0
  41. package/dist/graphql/types.d.ts.map +1 -0
  42. package/dist/graphql/types.js.map +1 -0
  43. package/dist/handlers/resource-handlers.d.ts.map +1 -0
  44. package/dist/handlers/resource-handlers.js.map +1 -0
  45. package/dist/index.d.ts +1 -0
  46. package/dist/index.d.ts.map +1 -0
  47. package/dist/index.js +64 -7
  48. package/dist/index.js.map +1 -0
  49. package/dist/resources/actors.d.ts.map +1 -0
  50. package/dist/resources/actors.js.map +1 -0
  51. package/dist/resources/assets.d.ts.map +1 -0
  52. package/dist/resources/assets.js +6 -4
  53. package/dist/resources/assets.js.map +1 -0
  54. package/dist/resources/levels.d.ts.map +1 -0
  55. package/dist/resources/levels.js.map +1 -0
  56. package/dist/server/resource-registry.d.ts.map +1 -0
  57. package/dist/server/resource-registry.js.map +1 -0
  58. package/dist/server/tool-registry.d.ts.map +1 -0
  59. package/dist/server/tool-registry.js.map +1 -0
  60. package/dist/server-setup.d.ts.map +1 -0
  61. package/dist/server-setup.js.map +1 -0
  62. package/dist/services/health-monitor.d.ts.map +1 -0
  63. package/dist/services/health-monitor.js.map +1 -0
  64. package/dist/services/metrics-server.d.ts.map +1 -0
  65. package/dist/services/metrics-server.js +3 -3
  66. package/dist/services/metrics-server.js.map +1 -0
  67. package/dist/tools/actors.d.ts.map +1 -0
  68. package/dist/tools/actors.js +3 -1
  69. package/dist/tools/actors.js.map +1 -0
  70. package/dist/tools/animation.d.ts.map +1 -0
  71. package/dist/tools/animation.js +2 -2
  72. package/dist/tools/animation.js.map +1 -0
  73. package/dist/tools/assets.d.ts.map +1 -0
  74. package/dist/tools/assets.js.map +1 -0
  75. package/dist/tools/audio.d.ts.map +1 -0
  76. package/dist/tools/audio.js.map +1 -0
  77. package/dist/tools/base-tool.d.ts.map +1 -0
  78. package/dist/tools/base-tool.js.map +1 -0
  79. package/dist/tools/behavior-tree.d.ts.map +1 -0
  80. package/dist/tools/behavior-tree.js.map +1 -0
  81. package/dist/tools/blueprint.d.ts.map +1 -0
  82. package/dist/tools/blueprint.js +4 -2
  83. package/dist/tools/blueprint.js.map +1 -0
  84. package/dist/tools/consolidated-tool-definitions.d.ts.map +1 -0
  85. package/dist/tools/consolidated-tool-definitions.js.map +1 -0
  86. package/dist/tools/consolidated-tool-handlers.d.ts.map +1 -0
  87. package/dist/tools/consolidated-tool-handlers.js.map +1 -0
  88. package/dist/tools/debug.d.ts.map +1 -0
  89. package/dist/tools/debug.js +3 -1
  90. package/dist/tools/debug.js.map +1 -0
  91. package/dist/tools/dynamic-handler-registry.d.ts.map +1 -0
  92. package/dist/tools/dynamic-handler-registry.js +3 -1
  93. package/dist/tools/dynamic-handler-registry.js.map +1 -0
  94. package/dist/tools/editor.d.ts.map +1 -0
  95. package/dist/tools/editor.js +1 -1
  96. package/dist/tools/editor.js.map +1 -0
  97. package/dist/tools/engine.d.ts.map +1 -0
  98. package/dist/tools/engine.js.map +1 -0
  99. package/dist/tools/environment.d.ts.map +1 -0
  100. package/dist/tools/environment.js +2 -2
  101. package/dist/tools/environment.js.map +1 -0
  102. package/dist/tools/foliage.d.ts.map +1 -0
  103. package/dist/tools/foliage.js.map +1 -0
  104. package/dist/tools/handlers/actor-handlers.d.ts +1 -1
  105. package/dist/tools/handlers/actor-handlers.d.ts.map +1 -0
  106. package/dist/tools/handlers/actor-handlers.js +6 -5
  107. package/dist/tools/handlers/actor-handlers.js.map +1 -0
  108. package/dist/tools/handlers/animation-handlers.d.ts.map +1 -0
  109. package/dist/tools/handlers/animation-handlers.js.map +1 -0
  110. package/dist/tools/handlers/argument-helper.d.ts.map +1 -0
  111. package/dist/tools/handlers/argument-helper.js.map +1 -0
  112. package/dist/tools/handlers/asset-handlers.d.ts.map +1 -0
  113. package/dist/tools/handlers/asset-handlers.js +5 -1
  114. package/dist/tools/handlers/asset-handlers.js.map +1 -0
  115. package/dist/tools/handlers/audio-handlers.d.ts.map +1 -0
  116. package/dist/tools/handlers/audio-handlers.js.map +1 -0
  117. package/dist/tools/handlers/blueprint-handlers.d.ts.map +1 -0
  118. package/dist/tools/handlers/blueprint-handlers.js +2 -1
  119. package/dist/tools/handlers/blueprint-handlers.js.map +1 -0
  120. package/dist/tools/handlers/common-handlers.d.ts.map +1 -0
  121. package/dist/tools/handlers/common-handlers.js.map +1 -0
  122. package/dist/tools/handlers/editor-handlers.d.ts.map +1 -0
  123. package/dist/tools/handlers/editor-handlers.js +12 -2
  124. package/dist/tools/handlers/editor-handlers.js.map +1 -0
  125. package/dist/tools/handlers/effect-handlers.d.ts.map +1 -0
  126. package/dist/tools/handlers/effect-handlers.js.map +1 -0
  127. package/dist/tools/handlers/environment-handlers.d.ts.map +1 -0
  128. package/dist/tools/handlers/environment-handlers.js.map +1 -0
  129. package/dist/tools/handlers/graph-handlers.d.ts.map +1 -0
  130. package/dist/tools/handlers/graph-handlers.js +61 -1
  131. package/dist/tools/handlers/graph-handlers.js.map +1 -0
  132. package/dist/tools/handlers/input-handlers.d.ts.map +1 -0
  133. package/dist/tools/handlers/input-handlers.js.map +1 -0
  134. package/dist/tools/handlers/inspect-handlers.d.ts.map +1 -0
  135. package/dist/tools/handlers/inspect-handlers.js.map +1 -0
  136. package/dist/tools/handlers/level-handlers.d.ts.map +1 -0
  137. package/dist/tools/handlers/level-handlers.js.map +1 -0
  138. package/dist/tools/handlers/lighting-handlers.d.ts.map +1 -0
  139. package/dist/tools/handlers/lighting-handlers.js +23 -1
  140. package/dist/tools/handlers/lighting-handlers.js.map +1 -0
  141. package/dist/tools/handlers/performance-handlers.d.ts.map +1 -0
  142. package/dist/tools/handlers/performance-handlers.js +15 -2
  143. package/dist/tools/handlers/performance-handlers.js.map +1 -0
  144. package/dist/tools/handlers/pipeline-handlers.d.ts.map +1 -0
  145. package/dist/tools/handlers/pipeline-handlers.js +61 -7
  146. package/dist/tools/handlers/pipeline-handlers.js.map +1 -0
  147. package/dist/tools/handlers/sequence-handlers.d.ts.map +1 -0
  148. package/dist/tools/handlers/sequence-handlers.js.map +1 -0
  149. package/dist/tools/handlers/system-handlers.d.ts.map +1 -0
  150. package/dist/tools/handlers/system-handlers.js +16 -1
  151. package/dist/tools/handlers/system-handlers.js.map +1 -0
  152. package/dist/tools/input.d.ts.map +1 -0
  153. package/dist/tools/input.js +3 -1
  154. package/dist/tools/input.js.map +1 -0
  155. package/dist/tools/introspection.d.ts.map +1 -0
  156. package/dist/tools/introspection.js.map +1 -0
  157. package/dist/tools/landscape.d.ts.map +1 -0
  158. package/dist/tools/landscape.js +3 -1
  159. package/dist/tools/landscape.js.map +1 -0
  160. package/dist/tools/level.d.ts.map +1 -0
  161. package/dist/tools/level.js.map +1 -0
  162. package/dist/tools/lighting.d.ts.map +1 -0
  163. package/dist/tools/lighting.js +3 -1
  164. package/dist/tools/lighting.js.map +1 -0
  165. package/dist/tools/logs.d.ts.map +1 -0
  166. package/dist/tools/logs.js.map +1 -0
  167. package/dist/tools/materials.d.ts.map +1 -0
  168. package/dist/tools/materials.js +3 -1
  169. package/dist/tools/materials.js.map +1 -0
  170. package/dist/tools/niagara.d.ts.map +1 -0
  171. package/dist/tools/niagara.js +7 -5
  172. package/dist/tools/niagara.js.map +1 -0
  173. package/dist/tools/performance.d.ts.map +1 -0
  174. package/dist/tools/performance.js.map +1 -0
  175. package/dist/tools/physics.d.ts.map +1 -0
  176. package/dist/tools/physics.js +9 -7
  177. package/dist/tools/physics.js.map +1 -0
  178. package/dist/tools/property-dictionary.d.ts.map +1 -0
  179. package/dist/tools/property-dictionary.js.map +1 -0
  180. package/dist/tools/sequence.d.ts.map +1 -0
  181. package/dist/tools/sequence.js +3 -1
  182. package/dist/tools/sequence.js.map +1 -0
  183. package/dist/tools/tool-definition-utils.d.ts.map +1 -0
  184. package/dist/tools/tool-definition-utils.js.map +1 -0
  185. package/dist/tools/ui.d.ts.map +1 -0
  186. package/dist/tools/ui.js +3 -1
  187. package/dist/tools/ui.js.map +1 -0
  188. package/dist/types/automation-responses.d.ts.map +1 -0
  189. package/dist/types/automation-responses.js.map +1 -0
  190. package/dist/types/env.d.ts.map +1 -0
  191. package/dist/types/env.js.map +1 -0
  192. package/dist/types/handler-types.d.ts.map +1 -0
  193. package/dist/types/handler-types.js.map +1 -0
  194. package/dist/types/tool-interfaces.d.ts.map +1 -0
  195. package/dist/types/tool-interfaces.js.map +1 -0
  196. package/dist/types/tool-types.d.ts.map +1 -0
  197. package/dist/types/tool-types.js.map +1 -0
  198. package/dist/unreal-bridge.d.ts +1 -0
  199. package/dist/unreal-bridge.d.ts.map +1 -0
  200. package/dist/unreal-bridge.js +8 -0
  201. package/dist/unreal-bridge.js.map +1 -0
  202. package/dist/utils/command-validator.d.ts.map +1 -0
  203. package/dist/utils/command-validator.js.map +1 -0
  204. package/dist/utils/elicitation.d.ts.map +1 -0
  205. package/dist/utils/elicitation.js.map +1 -0
  206. package/dist/utils/error-handler.d.ts.map +1 -0
  207. package/dist/utils/error-handler.js.map +1 -0
  208. package/dist/utils/ini-reader.d.ts.map +1 -0
  209. package/dist/utils/ini-reader.js.map +1 -0
  210. package/dist/utils/logger.d.ts.map +1 -0
  211. package/dist/utils/logger.js.map +1 -0
  212. package/dist/utils/normalize.d.ts.map +1 -0
  213. package/dist/utils/normalize.js.map +1 -0
  214. package/dist/utils/path-security.d.ts.map +1 -0
  215. package/dist/utils/path-security.js.map +1 -0
  216. package/dist/utils/response-factory.d.ts.map +1 -0
  217. package/dist/utils/response-factory.js +3 -1
  218. package/dist/utils/response-factory.js.map +1 -0
  219. package/dist/utils/response-validator.d.ts.map +1 -0
  220. package/dist/utils/response-validator.js.map +1 -0
  221. package/dist/utils/result-helpers.d.ts.map +1 -0
  222. package/dist/utils/result-helpers.js.map +1 -0
  223. package/dist/utils/safe-json.d.ts.map +1 -0
  224. package/dist/utils/safe-json.js.map +1 -0
  225. package/dist/utils/unreal-command-queue.d.ts.map +1 -0
  226. package/dist/utils/unreal-command-queue.js.map +1 -0
  227. package/dist/utils/validation.d.ts.map +1 -0
  228. package/dist/utils/validation.js.map +1 -0
  229. package/dist/wasm/index.d.ts.map +1 -0
  230. package/dist/wasm/index.js.map +1 -0
  231. package/package.json +12 -34
  232. package/server.json +2 -2
  233. package/.dockerignore +0 -57
  234. package/.env.example +0 -26
  235. package/.env.production +0 -61
  236. package/.eslintrc.json +0 -0
  237. package/.eslintrc.override.json +0 -8
  238. package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -94
  239. package/.github/ISSUE_TEMPLATE/config.yml +0 -8
  240. package/.github/ISSUE_TEMPLATE/feature_request.yml +0 -56
  241. package/.github/copilot-instructions.md +0 -478
  242. package/.github/dependabot.yml +0 -19
  243. package/.github/labeler.yml +0 -24
  244. package/.github/labels.yml +0 -70
  245. package/.github/pull_request_template.md +0 -42
  246. package/.github/release-drafter-config.yml +0 -51
  247. package/.github/workflows/auto-merge.yml +0 -38
  248. package/.github/workflows/ci.yml +0 -38
  249. package/.github/workflows/dependency-review.yml +0 -17
  250. package/.github/workflows/gemini-issue-triage.yml +0 -172
  251. package/.github/workflows/greetings.yml +0 -27
  252. package/.github/workflows/labeler.yml +0 -17
  253. package/.github/workflows/links.yml +0 -80
  254. package/.github/workflows/pr-size-labeler.yml +0 -137
  255. package/.github/workflows/publish-mcp.yml +0 -79
  256. package/.github/workflows/release-drafter.yml +0 -24
  257. package/.github/workflows/release.yml +0 -112
  258. package/.github/workflows/semantic-pull-request.yml +0 -35
  259. package/.github/workflows/smoke-test.yml +0 -36
  260. package/.github/workflows/stale.yml +0 -28
  261. package/CONTRIBUTING.md +0 -140
  262. package/Dockerfile +0 -37
  263. package/GEMINI.md +0 -115
  264. package/Public/Plugin_setup_guide.mp4 +0 -0
  265. package/Public/icon.png +0 -0
  266. package/claude_desktop_config_example.json +0 -15
  267. package/dist/types/responses.d.ts +0 -249
  268. package/dist/types/responses.js +0 -2
  269. package/docs/GraphQL-API.md +0 -888
  270. package/docs/Migration-Guide-v0.5.0.md +0 -684
  271. package/docs/Roadmap.md +0 -53
  272. package/docs/WebAssembly-Integration.md +0 -628
  273. package/docs/editor-plugin-extension.md +0 -370
  274. package/docs/handler-mapping.md +0 -249
  275. package/docs/native-automation-progress.md +0 -128
  276. package/docs/testing-guide.md +0 -423
  277. package/eslint.config.mjs +0 -68
  278. package/mcp-config-example.json +0 -14
  279. package/plugins/McpAutomationBridge/Config/FilterPlugin.ini +0 -8
  280. package/plugins/McpAutomationBridge/McpAutomationBridge.uplugin +0 -64
  281. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/McpAutomationBridge.Build.cs +0 -189
  282. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.cpp +0 -22
  283. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.h +0 -30
  284. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeHelpers.h +0 -1983
  285. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeModule.cpp +0 -72
  286. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSettings.cpp +0 -46
  287. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSubsystem.cpp +0 -846
  288. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AnimationHandlers.cpp +0 -2393
  289. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetQueryHandlers.cpp +0 -300
  290. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetWorkflowHandlers.cpp +0 -2807
  291. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AudioHandlers.cpp +0 -1087
  292. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BehaviorTreeHandlers.cpp +0 -488
  293. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.cpp +0 -643
  294. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.h +0 -31
  295. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintGraphHandlers.cpp +0 -1094
  296. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers.cpp +0 -5750
  297. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers_List.cpp +0 -152
  298. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ControlHandlers.cpp +0 -2614
  299. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_DebugHandlers.cpp +0 -42
  300. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EditorFunctionHandlers.cpp +0 -1237
  301. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EffectHandlers.cpp +0 -1725
  302. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EnvironmentHandlers.cpp +0 -2241
  303. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_FoliageHandlers.cpp +0 -954
  304. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InputHandlers.cpp +0 -209
  305. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InsightsHandlers.cpp +0 -41
  306. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LandscapeHandlers.cpp +0 -1164
  307. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LevelHandlers.cpp +0 -762
  308. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LightingHandlers.cpp +0 -663
  309. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LogHandlers.cpp +0 -136
  310. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MaterialGraphHandlers.cpp +0 -494
  311. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraGraphHandlers.cpp +0 -278
  312. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraHandlers.cpp +0 -625
  313. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PerformanceHandlers.cpp +0 -401
  314. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PipelineHandlers.cpp +0 -67
  315. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ProcessRequest.cpp +0 -472
  316. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PropertyHandlers.cpp +0 -2634
  317. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_RenderHandlers.cpp +0 -189
  318. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.cpp +0 -917
  319. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.h +0 -39
  320. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequenceHandlers.cpp +0 -2706
  321. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequencerHandlers.cpp +0 -519
  322. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_TestHandlers.cpp +0 -38
  323. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_UiHandlers.cpp +0 -668
  324. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_WorldPartitionHandlers.cpp +0 -346
  325. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.cpp +0 -1330
  326. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.h +0 -149
  327. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpConnectionManager.cpp +0 -782
  328. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSettings.h +0 -115
  329. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSubsystem.h +0 -796
  330. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpConnectionManager.h +0 -117
  331. package/scripts/check-unreal-connection.mjs +0 -19
  332. package/scripts/clean-tmp.js +0 -23
  333. package/scripts/patch-wasm.js +0 -26
  334. package/scripts/run-all-tests.mjs +0 -136
  335. package/scripts/smoke-test.ts +0 -94
  336. package/scripts/sync-mcp-plugin.js +0 -143
  337. package/scripts/test-no-plugin-alternates.mjs +0 -113
  338. package/scripts/validate-server.js +0 -46
  339. package/scripts/verify-automation-bridge.js +0 -200
  340. package/src/automation/bridge.ts +0 -560
  341. package/src/automation/connection-manager.ts +0 -148
  342. package/src/automation/handshake.ts +0 -99
  343. package/src/automation/index.ts +0 -2
  344. package/src/automation/message-handler.ts +0 -192
  345. package/src/automation/request-tracker.ts +0 -155
  346. package/src/automation/types.ts +0 -107
  347. package/src/cli.ts +0 -34
  348. package/src/config/class-aliases.ts +0 -65
  349. package/src/config.ts +0 -73
  350. package/src/constants.ts +0 -24
  351. package/src/graphql/loaders.ts +0 -244
  352. package/src/graphql/resolvers.ts +0 -1008
  353. package/src/graphql/schema.ts +0 -452
  354. package/src/graphql/server.ts +0 -156
  355. package/src/graphql/types.ts +0 -10
  356. package/src/handlers/resource-handlers.ts +0 -186
  357. package/src/index.ts +0 -243
  358. package/src/resources/actors.ts +0 -127
  359. package/src/resources/assets.ts +0 -286
  360. package/src/resources/levels.ts +0 -68
  361. package/src/server/resource-registry.ts +0 -47
  362. package/src/server/tool-registry.ts +0 -354
  363. package/src/server-setup.ts +0 -114
  364. package/src/services/health-monitor.ts +0 -132
  365. package/src/services/metrics-server.ts +0 -176
  366. package/src/tools/actors.ts +0 -564
  367. package/src/tools/animation.ts +0 -941
  368. package/src/tools/assets.ts +0 -394
  369. package/src/tools/audio.ts +0 -499
  370. package/src/tools/base-tool.ts +0 -52
  371. package/src/tools/behavior-tree.ts +0 -45
  372. package/src/tools/blueprint.ts +0 -940
  373. package/src/tools/consolidated-tool-definitions.ts +0 -1256
  374. package/src/tools/consolidated-tool-handlers.ts +0 -302
  375. package/src/tools/debug.ts +0 -622
  376. package/src/tools/dynamic-handler-registry.ts +0 -33
  377. package/src/tools/editor.ts +0 -435
  378. package/src/tools/engine.ts +0 -43
  379. package/src/tools/environment.ts +0 -281
  380. package/src/tools/foliage.ts +0 -596
  381. package/src/tools/handlers/actor-handlers.ts +0 -244
  382. package/src/tools/handlers/animation-handlers.ts +0 -237
  383. package/src/tools/handlers/argument-helper.ts +0 -142
  384. package/src/tools/handlers/asset-handlers.ts +0 -550
  385. package/src/tools/handlers/audio-handlers.ts +0 -194
  386. package/src/tools/handlers/blueprint-handlers.ts +0 -380
  387. package/src/tools/handlers/common-handlers.ts +0 -108
  388. package/src/tools/handlers/editor-handlers.ts +0 -124
  389. package/src/tools/handlers/effect-handlers.ts +0 -224
  390. package/src/tools/handlers/environment-handlers.ts +0 -183
  391. package/src/tools/handlers/graph-handlers.ts +0 -117
  392. package/src/tools/handlers/input-handlers.ts +0 -28
  393. package/src/tools/handlers/inspect-handlers.ts +0 -450
  394. package/src/tools/handlers/level-handlers.ts +0 -253
  395. package/src/tools/handlers/lighting-handlers.ts +0 -151
  396. package/src/tools/handlers/performance-handlers.ts +0 -132
  397. package/src/tools/handlers/pipeline-handlers.ts +0 -128
  398. package/src/tools/handlers/sequence-handlers.ts +0 -438
  399. package/src/tools/handlers/system-handlers.ts +0 -564
  400. package/src/tools/input.ts +0 -160
  401. package/src/tools/introspection.ts +0 -689
  402. package/src/tools/landscape.ts +0 -649
  403. package/src/tools/level.ts +0 -989
  404. package/src/tools/lighting.ts +0 -1052
  405. package/src/tools/logs.ts +0 -219
  406. package/src/tools/materials.ts +0 -295
  407. package/src/tools/niagara.ts +0 -485
  408. package/src/tools/performance.ts +0 -661
  409. package/src/tools/physics.ts +0 -679
  410. package/src/tools/property-dictionary.ts +0 -98
  411. package/src/tools/sequence.ts +0 -385
  412. package/src/tools/tool-definition-utils.ts +0 -35
  413. package/src/tools/ui.ts +0 -452
  414. package/src/types/automation-responses.ts +0 -119
  415. package/src/types/env.ts +0 -17
  416. package/src/types/handler-types.ts +0 -442
  417. package/src/types/responses.ts +0 -355
  418. package/src/types/tool-interfaces.ts +0 -250
  419. package/src/types/tool-types.ts +0 -575
  420. package/src/unreal-bridge.ts +0 -693
  421. package/src/utils/command-validator.ts +0 -139
  422. package/src/utils/elicitation.ts +0 -132
  423. package/src/utils/error-handler.ts +0 -287
  424. package/src/utils/ini-reader.ts +0 -86
  425. package/src/utils/logger.ts +0 -35
  426. package/src/utils/normalize.test.ts +0 -162
  427. package/src/utils/normalize.ts +0 -146
  428. package/src/utils/path-security.ts +0 -43
  429. package/src/utils/response-factory.ts +0 -44
  430. package/src/utils/response-validator.ts +0 -395
  431. package/src/utils/result-helpers.ts +0 -195
  432. package/src/utils/safe-json.test.ts +0 -90
  433. package/src/utils/safe-json.ts +0 -70
  434. package/src/utils/unreal-command-queue.ts +0 -166
  435. package/src/utils/validation.test.ts +0 -184
  436. package/src/utils/validation.ts +0 -312
  437. package/src/wasm/index.ts +0 -838
  438. package/test-server.mjs +0 -100
  439. package/tests/run-unreal-tool-tests.mjs +0 -948
  440. package/tests/test-animation.mjs +0 -369
  441. package/tests/test-asset-advanced.mjs +0 -82
  442. package/tests/test-asset-errors.mjs +0 -35
  443. package/tests/test-asset-graph.mjs +0 -311
  444. package/tests/test-audio.mjs +0 -417
  445. package/tests/test-automation-timeouts.mjs +0 -98
  446. package/tests/test-behavior-tree.mjs +0 -444
  447. package/tests/test-blueprint-graph.mjs +0 -410
  448. package/tests/test-blueprint.mjs +0 -577
  449. package/tests/test-client-mode.mjs +0 -86
  450. package/tests/test-console-command.mjs +0 -56
  451. package/tests/test-control-actor.mjs +0 -425
  452. package/tests/test-control-editor.mjs +0 -112
  453. package/tests/test-graphql.mjs +0 -372
  454. package/tests/test-input.mjs +0 -349
  455. package/tests/test-inspect.mjs +0 -302
  456. package/tests/test-landscape.mjs +0 -316
  457. package/tests/test-lighting.mjs +0 -428
  458. package/tests/test-manage-asset.mjs +0 -438
  459. package/tests/test-manage-level.mjs +0 -89
  460. package/tests/test-materials.mjs +0 -356
  461. package/tests/test-niagara.mjs +0 -185
  462. package/tests/test-no-inline-python.mjs +0 -122
  463. package/tests/test-performance.mjs +0 -539
  464. package/tests/test-plugin-handshake.mjs +0 -82
  465. package/tests/test-runner.mjs +0 -933
  466. package/tests/test-sequence.mjs +0 -104
  467. package/tests/test-system.mjs +0 -96
  468. package/tests/test-wasm.mjs +0 -283
  469. package/tests/test-world-partition.mjs +0 -215
  470. package/tsconfig.json +0 -56
  471. package/vitest.config.ts +0 -35
  472. package/wasm/Cargo.lock +0 -363
  473. package/wasm/Cargo.toml +0 -42
  474. package/wasm/LICENSE +0 -21
  475. package/wasm/README.md +0 -253
  476. package/wasm/src/dependency_resolver.rs +0 -377
  477. package/wasm/src/lib.rs +0 -153
  478. package/wasm/src/property_parser.rs +0 -271
  479. package/wasm/src/transform_math.rs +0 -396
  480. package/wasm/tests/integration.rs +0 -109
@@ -1,933 +0,0 @@
1
- import { Client } from '@modelcontextprotocol/sdk/client/index.js';
2
- import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
3
- import path from 'node:path';
4
- import fs from 'node:fs/promises';
5
- import { fileURLToPath } from 'node:url';
6
- import net from 'node:net';
7
-
8
- const __filename = fileURLToPath(import.meta.url);
9
- const __dirname = path.dirname(__filename);
10
- const repoRoot = path.resolve(__dirname, '..');
11
- const reportsDir = path.join(__dirname, 'reports');
12
-
13
- // Common failure keywords to check against
14
- const failureKeywords = ['failed', 'error', 'exception', 'invalid', 'not found', 'missing', 'timed out', 'timeout', 'unsupported', 'unknown'];
15
- const successKeywords = ['success', 'created', 'updated', 'deleted', 'completed', 'done', 'ok'];
16
-
17
- // Defaults for spawning the MCP server.
18
- let serverCommand = process.env.UNREAL_MCP_SERVER_CMD ?? 'node';
19
- let serverArgs = process.env.UNREAL_MCP_SERVER_ARGS ? process.env.UNREAL_MCP_SERVER_ARGS.split(',') : [path.join(repoRoot, 'dist', 'cli.js')];
20
- const serverCwd = process.env.UNREAL_MCP_SERVER_CWD ?? repoRoot;
21
- const serverEnv = Object.assign({}, process.env);
22
-
23
- function formatResultLine(testCase, status, detail, durationMs) {
24
- const durationText = typeof durationMs === 'number' ? ` (${durationMs.toFixed(1)} ms)` : '';
25
- return `[${status.toUpperCase()}] ${testCase.scenario}${durationText}${detail ? ` => ${detail}` : ''}`;
26
- }
27
-
28
- async function persistResults(toolName, results) {
29
- await fs.mkdir(reportsDir, { recursive: true });
30
- const timestamp = new Date().toISOString().replace(/[:]/g, '-');
31
- const resultsPath = path.join(reportsDir, `${toolName}-test-results-${timestamp}.json`);
32
- const serializable = results.map((result) => ({
33
- scenario: result.scenario,
34
- toolName: result.toolName,
35
- arguments: result.arguments,
36
- status: result.status,
37
- durationMs: result.durationMs,
38
- detail: result.detail
39
- }));
40
- await fs.writeFile(resultsPath, JSON.stringify({ generatedAt: new Date().toISOString(), toolName, results: serializable }, null, 2));
41
- return resultsPath;
42
- }
43
-
44
- function summarize(toolName, results, resultsPath) {
45
- const totals = results.reduce((acc, result) => { acc.total += 1; acc[result.status] = (acc[result.status] ?? 0) + 1; return acc; }, { total: 0, passed: 0, failed: 0, skipped: 0 });
46
- console.log('\n' + '='.repeat(60));
47
- console.log(`${toolName} Test Summary`);
48
- console.log('='.repeat(60));
49
- console.log(`Total cases: ${totals.total}`);
50
- console.log(`✅ Passed: ${totals.passed ?? 0}`);
51
- console.log(`❌ Failed: ${totals.failed ?? 0}`);
52
- console.log(`⏭️ Skipped: ${totals.skipped ?? 0}`);
53
- if (totals.passed && totals.total > 0) console.log(`Pass rate: ${((totals.passed / totals.total) * 100).toFixed(1)}%`);
54
- console.log(`Results saved to: ${resultsPath}`);
55
- console.log('='.repeat(60));
56
- }
57
-
58
- /**
59
- * Evaluates whether a test case passed based on expected outcome
60
- */
61
- function evaluateExpectation(testCase, response) {
62
- const expectation = testCase.expected;
63
-
64
- // Normalize expected into a comparable form. If expected is an object
65
- // (e.g. {condition: 'success|error', errorPattern: 'SC_DISABLED'}), then
66
- // we extract the condition string as the primary expectation string.
67
- const expectedCondition = (typeof expectation === 'object' && expectation !== null && expectation.condition)
68
- ? expectation.condition
69
- : (typeof expectation === 'string' ? expectation : String(expectation));
70
-
71
- const lowerExpected = expectedCondition.toLowerCase();
72
-
73
- // Determine failure/success intent from condition keywords
74
- const containsFailure = failureKeywords.some((word) => lowerExpected.includes(word));
75
- const containsSuccess = successKeywords.some((word) => lowerExpected.includes(word));
76
-
77
- const structuredSuccess = typeof response.structuredContent?.success === 'boolean'
78
- ? response.structuredContent.success
79
- : undefined;
80
- const actualSuccess = structuredSuccess ?? !response.isError;
81
-
82
- // Extract actual error/message from response
83
- let actualError = null;
84
- let actualMessage = null;
85
- if (response.structuredContent) {
86
- actualError = response.structuredContent.error;
87
- actualMessage = response.structuredContent.message;
88
- }
89
-
90
- // Also extract flattened plain-text content for matching when structured
91
- // fields are missing or when MCP errors (e.g. timeouts) are only reported
92
- // via the textual content array.
93
- let contentText = '';
94
- if (Array.isArray(response.content) && response.content.length > 0) {
95
- contentText = response.content
96
- .map((entry) => (entry && typeof entry.text === 'string' ? entry.text : ''))
97
- .filter((t) => t.length > 0)
98
- .join('\n');
99
- }
100
-
101
- // Helper to get effective actual strings for matching
102
- const messageStr = (actualMessage || '').toString().toLowerCase();
103
- const errorStr = (actualError || '').toString().toLowerCase();
104
- const contentStr = contentText.toString().toLowerCase();
105
- const combined = `${messageStr} ${errorStr} ${contentStr}`;
106
-
107
- // If expectation is an object with specific pattern constraints, apply them
108
- if (typeof expectation === 'object' && expectation !== null) {
109
- // If actual outcome was success, check successPattern
110
- if (actualSuccess && expectation.successPattern) {
111
- const pattern = expectation.successPattern.toLowerCase();
112
- if (combined.includes(pattern)) {
113
- return { passed: true, reason: `Success pattern matched: ${expectation.successPattern}` };
114
- }
115
- }
116
- // If actual outcome was error/failure, check errorPattern
117
- if (!actualSuccess && expectation.errorPattern) {
118
- const pattern = expectation.errorPattern.toLowerCase();
119
- if (combined.includes(pattern)) {
120
- return { passed: true, reason: `Error pattern matched: ${expectation.errorPattern}` };
121
- }
122
- }
123
- }
124
-
125
- // Handle multi-condition expectations using "or" or pipe separators
126
- // e.g., "success or LOAD_FAILED" or "success|no_instances|load_failed"
127
- if (lowerExpected.includes(' or ') || lowerExpected.includes('|')) {
128
- const separator = lowerExpected.includes(' or ') ? ' or ' : '|';
129
- const conditions = lowerExpected.split(separator).map((c) => c.trim()).filter(Boolean);
130
- for (const condition of conditions) {
131
- if (successKeywords.some((kw) => condition.includes(kw)) && actualSuccess === true) {
132
- return { passed: true, reason: JSON.stringify(response.structuredContent) };
133
- }
134
- if (condition === 'handled' && response.structuredContent && response.structuredContent.handled === true) {
135
- return { passed: true, reason: 'Handled gracefully' };
136
- }
137
-
138
- // Special-case timeout expectations so that MCP transport timeouts
139
- // (e.g. "Request timed out") satisfy conditions containing "timeout".
140
- if (condition === 'timeout' || condition.includes('timeout')) {
141
- if (combined.includes('timeout') || combined.includes('timed out')) {
142
- return { passed: true, reason: `Expected timeout condition met: ${condition}` };
143
- }
144
- }
145
-
146
- if (combined.includes(condition)) {
147
- return { passed: true, reason: `Expected condition met: ${condition}` };
148
- }
149
- }
150
- // If none of the OR/pipe conditions matched, it's a failure
151
- return { passed: false, reason: `None of the expected conditions matched: ${expectedCondition}` };
152
- }
153
-
154
- // Also flag common automation/plugin failure phrases
155
- const pluginFailureIndicators = ['does not match prefix', 'unknown', 'not implemented', 'unavailable', 'unsupported'];
156
- const hasPluginFailure = pluginFailureIndicators.some(term => combined.includes(term));
157
-
158
- if (!containsFailure && hasPluginFailure) {
159
- return {
160
- passed: false,
161
- reason: `Expected success but plugin reported failure: ${actualMessage || actualError}`
162
- };
163
- }
164
-
165
- // CRITICAL: Check if message says "failed" but success is true (FALSE POSITIVE)
166
- if (actualSuccess && (
167
- messageStr.includes('failed') ||
168
- messageStr.includes('python execution failed') ||
169
- errorStr.includes('failed')
170
- )) {
171
- return {
172
- passed: false,
173
- reason: `False positive: success=true but message indicates failure: ${actualMessage}`
174
- };
175
- }
176
-
177
- // CRITICAL FIX: UE_NOT_CONNECTED errors should ALWAYS fail tests unless explicitly expected
178
- if (actualError === 'UE_NOT_CONNECTED') {
179
- const explicitlyExpectsDisconnection = lowerExpected.includes('not connected') ||
180
- lowerExpected.includes('ue_not_connected') ||
181
- lowerExpected.includes('disconnected');
182
- if (!explicitlyExpectsDisconnection) {
183
- return {
184
- passed: false,
185
- reason: `Test requires Unreal Engine connection, but got: ${actualError} - ${actualMessage}`
186
- };
187
- }
188
- }
189
-
190
- // For tests that expect specific error types, validate the actual error matches
191
- const expectedFailure = containsFailure && !containsSuccess;
192
- if (expectedFailure && !actualSuccess) {
193
- // Test expects failure and got failure - but verify it's the RIGHT kind of failure
194
- const lowerReason = actualMessage?.toLowerCase() || actualError?.toLowerCase() || contentStr || '';
195
-
196
- // Check for specific error types (not just generic "error" keyword)
197
- const specificErrorTypes = ['not found', 'invalid', 'missing', 'already exists', 'does not exist', 'sc_disabled'];
198
- const expectedErrorType = specificErrorTypes.find(type => lowerExpected.includes(type));
199
- let errorTypeMatch = expectedErrorType ? lowerReason.includes(expectedErrorType) :
200
- failureKeywords.some(keyword => lowerExpected.includes(keyword) && lowerReason.includes(keyword));
201
-
202
- // Also check detail field if main error check failed (handles wrapped exceptions)
203
- if (!errorTypeMatch && response.detail && typeof response.detail === 'string') {
204
- const lowerDetail = response.detail.toLowerCase();
205
- if (expectedErrorType) {
206
- if (lowerDetail.includes(expectedErrorType)) errorTypeMatch = true;
207
- } else {
208
- // If no specific error type, just check if detail contains expected string
209
- if (lowerDetail.includes(lowerExpected)) errorTypeMatch = true;
210
- }
211
- }
212
-
213
- // If expected outcome specifies an error type, actual error should match it
214
- if (lowerExpected.includes('not found') || lowerExpected.includes('invalid') ||
215
- lowerExpected.includes('missing') || lowerExpected.includes('already exists') || lowerExpected.includes('sc_disabled')) {
216
- const passed = errorTypeMatch;
217
- let reason;
218
- if (response.isError) {
219
- reason = response.content?.map((entry) => ('text' in entry ? entry.text : JSON.stringify(entry))).join('\n');
220
- } else if (response.structuredContent) {
221
- reason = JSON.stringify(response.structuredContent);
222
- } else {
223
- reason = 'No structured response returned';
224
- }
225
- return { passed, reason };
226
- }
227
- }
228
-
229
- // Default evaluation logic
230
- const passed = expectedFailure ? !actualSuccess : !!actualSuccess;
231
- let reason;
232
- if (response.isError) {
233
- reason = response.content?.map((entry) => ('text' in entry ? entry.text : JSON.stringify(entry))).join('\n');
234
- } else if (response.structuredContent) {
235
- reason = JSON.stringify(response.structuredContent);
236
- } else if (response.content?.length) {
237
- reason = response.content.map((entry) => ('text' in entry ? entry.text : JSON.stringify(entry))).join('\n');
238
- } else {
239
- reason = 'No structured response returned';
240
- }
241
- return { passed, reason };
242
- }
243
-
244
- /**
245
- * Main test runner function
246
- */
247
- export async function runToolTests(toolName, testCases) {
248
- console.log(`Total test cases: ${testCases.length}`);
249
- console.log('='.repeat(60));
250
- console.log('');
251
-
252
- let transport;
253
- let client;
254
- const results = [];
255
- // callToolOnce is assigned after the MCP client is initialized. Declare here so
256
- // the test loop can call it regardless of block scoping rules.
257
- let callToolOnce;
258
-
259
- try {
260
- // Wait for the automation bridge ports to be available so the spawned MCP server
261
- // process can successfully connect to the editor plugin.
262
- const bridgeHost = process.env.MCP_AUTOMATION_WS_HOST ?? '127.0.0.1';
263
- const envPorts = process.env.MCP_AUTOMATION_WS_PORTS
264
- ? process.env.MCP_AUTOMATION_WS_PORTS.split(',').map((p) => parseInt(p.trim(), 10)).filter(Boolean)
265
- : [8090, 8091];
266
- const waitMs = 10000; // Hardcoded increased timeout
267
-
268
- console.log(`Waiting up to ${waitMs}ms for automation bridge on ${bridgeHost}:${envPorts.join(',')}`);
269
-
270
- async function waitForAnyPort(host, ports, timeoutMs = 10000) {
271
- const start = Date.now();
272
- while (Date.now() - start < timeoutMs) {
273
- for (const port of ports) {
274
- try {
275
- await new Promise((resolve, reject) => {
276
- const sock = new net.Socket();
277
- let settled = false;
278
- sock.setTimeout(1000);
279
- sock.once('connect', () => { settled = true; sock.destroy(); resolve(true); });
280
- sock.once('timeout', () => { if (!settled) { settled = true; sock.destroy(); reject(new Error('timeout')); } });
281
- sock.once('error', () => { if (!settled) { settled = true; sock.destroy(); reject(new Error('error')); } });
282
- sock.connect(port, host);
283
- });
284
- console.log(`✅ Automation bridge appears to be listening on ${host}:${port}`);
285
- return port;
286
- } catch {
287
- // ignore and try next port
288
- }
289
- }
290
- // Yield to the event loop once instead of sleeping.
291
- await new Promise((r) => setImmediate(r));
292
- }
293
- throw new Error(`Timed out waiting for automation bridge on ports: ${ports.join(',')}`);
294
- }
295
-
296
- try {
297
- await waitForAnyPort(bridgeHost, envPorts, waitMs);
298
- } catch (err) {
299
- console.warn('Automation bridge did not become available before tests started:', err.message);
300
- }
301
-
302
- // Decide whether to run the built server (dist/cli.js) or to run the
303
- // TypeScript source directly. Prefer the built dist when it is up-to-date
304
- // with the src tree. Fall back to running src with ts-node when dist is
305
- // missing or older than the src modification time to avoid running stale code.
306
- const distPath = path.join(repoRoot, 'dist', 'cli.js');
307
- const srcDir = path.join(repoRoot, 'src');
308
-
309
- async function getLatestMtime(dir) {
310
- let latest = 0;
311
- try {
312
- const entries = await fs.readdir(dir, { withFileTypes: true });
313
- for (const e of entries) {
314
- const full = path.join(dir, e.name);
315
- if (e.isDirectory()) {
316
- const child = await getLatestMtime(full);
317
- if (child > latest) latest = child;
318
- } else {
319
- try {
320
- const st = await fs.stat(full);
321
- const m = st.mtimeMs || 0;
322
- if (m > latest) latest = m;
323
- } catch (_) { }
324
- }
325
- }
326
- } catch (_) {
327
- // ignore
328
- }
329
- return latest;
330
- }
331
-
332
- // Choose how to launch the server. Prefer using the built `dist/` executable so
333
- // Node resolves ESM imports cleanly. If `dist/` is missing, attempt an automatic
334
- // `npm run build` so users that run live tests don't hit ts-node resolution errors.
335
- let useDist = false;
336
- let distExists = false;
337
- try {
338
- await fs.access(distPath);
339
- distExists = true;
340
- } catch (e) {
341
- distExists = false;
342
- }
343
-
344
- if (process.env.UNREAL_MCP_FORCE_DIST === '1') {
345
- useDist = true;
346
- console.log('Forcing use of dist build via UNREAL_MCP_FORCE_DIST=1');
347
- } else if (distExists) {
348
- try {
349
- const distStat = await fs.stat(distPath);
350
- const srcLatest = await getLatestMtime(srcDir);
351
- const srcIsNewer = srcLatest > (distStat.mtimeMs || 0);
352
- const autoBuildEnabled = process.env.UNREAL_MCP_AUTO_BUILD === '1';
353
- const autoBuildDisabled = process.env.UNREAL_MCP_NO_AUTO_BUILD === '1';
354
- if (srcIsNewer) {
355
- if (!autoBuildEnabled && !autoBuildDisabled) {
356
- console.log('Detected newer source files than dist; attempting automatic build to refresh dist/ (set UNREAL_MCP_NO_AUTO_BUILD=1 to disable)');
357
- }
358
- if (autoBuildEnabled || !autoBuildDisabled) {
359
- const { spawn } = await import('node:child_process');
360
- try {
361
- await new Promise((resolve, reject) => {
362
- const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
363
- const ps = process.platform === 'win32'
364
- ? spawn(`${npmCmd} run build`, { cwd: repoRoot, stdio: 'inherit', shell: true })
365
- : spawn(npmCmd, ['run', 'build'], { cwd: repoRoot, stdio: 'inherit' });
366
- ps.on('close', (code) => (code === 0 ? resolve() : reject(new Error(`Build failed with code ${code}`))));
367
- ps.on('error', (err) => reject(err));
368
- });
369
- console.log('Build succeeded — using dist/ for live tests');
370
- useDist = true;
371
- } catch (buildErr) {
372
- console.warn('Automatic build failed or could not stat files — falling back to TypeScript source for live tests:', String(buildErr));
373
- useDist = false;
374
- }
375
- } else {
376
- console.log('Detected newer source files than dist but automatic build is disabled.');
377
- console.log('Set UNREAL_MCP_AUTO_BUILD=1 to enable automatic builds, or run `npm run build` manually.');
378
- useDist = false;
379
- }
380
- } else {
381
- useDist = true;
382
- console.log('Using built dist for live tests');
383
- }
384
- } catch (buildErr) {
385
- console.warn('Automatic build failed or could not stat files — falling back to TypeScript source for live tests:', String(buildErr));
386
- useDist = false;
387
- console.log('Preferring TypeScript source for tests to pick up local changes (set UNREAL_MCP_FORCE_DIST=1 to force dist)');
388
- }
389
- } else {
390
- console.log('dist not found — attempting to run `npm run build` to produce dist/ for live tests');
391
- try {
392
- const { spawn } = await import('node:child_process');
393
- await new Promise((resolve, reject) => {
394
- const ps = spawn(process.platform === 'win32' ? 'npm.cmd' : 'npm', ['run', 'build'], { cwd: repoRoot, stdio: 'inherit' });
395
- ps.on('close', (code) => (code === 0 ? resolve() : reject(new Error(`Build failed with code ${code}`))));
396
- ps.on('error', (err) => reject(err));
397
- });
398
- useDist = true;
399
- console.log('Build succeeded — using dist/ for live tests');
400
- } catch (buildErr) {
401
- console.warn('Automatic build failed — falling back to running TypeScript source with ts-node-esm:', String(buildErr));
402
- useDist = false;
403
- }
404
- }
405
-
406
- if (!useDist) {
407
- serverCommand = process.env.UNREAL_MCP_SERVER_CMD ?? 'npx';
408
- serverArgs = ['ts-node-esm', path.join(repoRoot, 'src', 'cli.ts')];
409
- } else {
410
- serverCommand = process.env.UNREAL_MCP_SERVER_CMD ?? serverCommand;
411
- serverArgs = process.env.UNREAL_MCP_SERVER_ARGS?.split(',') ?? serverArgs;
412
- }
413
-
414
- transport = new StdioClientTransport({
415
- command: serverCommand,
416
- args: serverArgs,
417
- cwd: serverCwd,
418
- stderr: 'inherit',
419
- env: serverEnv
420
- });
421
-
422
- client = new Client({
423
- name: 'unreal-mcp-test-runner',
424
- version: '1.0.0'
425
- });
426
-
427
- await client.connect(transport);
428
- await client.listTools({});
429
- console.log('✅ Connected to Unreal MCP Server\n');
430
-
431
- // Single-attempt call helper (no retries). This forwards a timeoutMs
432
- // argument to the server so server-side automation calls use the same
433
- // timeout the test harness expects.
434
- callToolOnce = async function (callOptions, baseTimeoutMs) {
435
- const envDefault = Number(process.env.UNREAL_MCP_TEST_CALL_TIMEOUT_MS ?? '60000') || 60000;
436
- const perCall = Number(callOptions?.arguments?.timeoutMs) || undefined;
437
- const base = typeof baseTimeoutMs === 'number' && baseTimeoutMs > 0 ? baseTimeoutMs : (perCall || envDefault);
438
- const timeoutMs = base;
439
- try {
440
- console.log(`[CALL] ${callOptions.name} (timeout ${timeoutMs}ms)`);
441
- const outgoing = Object.assign({}, callOptions, { arguments: { ...(callOptions.arguments || {}), timeoutMs } });
442
- // Prefer instructing the MCP client to use a matching timeout if
443
- // the client library supports per-call options; fall back to the
444
- // plain call if not supported.
445
- let callPromise;
446
- try {
447
- // Correct parameter order: (params, resultSchema?, options)
448
- callPromise = client.callTool(outgoing, undefined, { timeout: timeoutMs });
449
- } catch (err) {
450
- // Fall back to calling the older signature where options might be second param
451
- try {
452
- callPromise = client.callTool(outgoing, { timeout: timeoutMs });
453
- } catch (inner) {
454
- try {
455
- callPromise = client.callTool(outgoing);
456
- } catch (inner2) {
457
- throw inner2 || inner || err;
458
- }
459
- }
460
- }
461
-
462
- let timeoutId;
463
- const timeoutPromise = new Promise((_, rej) => {
464
- timeoutId = setTimeout(() => rej(new Error(`Local test runner timeout after ${timeoutMs}ms`)), timeoutMs);
465
- if (timeoutId && typeof timeoutId.unref === 'function') {
466
- timeoutId.unref();
467
- }
468
- });
469
- try {
470
- const timed = Promise.race([
471
- callPromise,
472
- timeoutPromise
473
- ]);
474
- return await timed;
475
- } finally {
476
- if (timeoutId) {
477
- clearTimeout(timeoutId);
478
- }
479
- }
480
- } catch (e) {
481
- const msg = String(e?.message || e || '');
482
- if (msg.includes('Unknown blueprint action')) {
483
- return { structuredContent: { success: false, error: msg } };
484
- }
485
- throw e;
486
- }
487
- };
488
-
489
- // Run each test case
490
- for (let i = 0; i < testCases.length; i++) {
491
- const testCase = testCases[i];
492
- const testCaseTimeoutMs = Number(process.env.UNREAL_MCP_TEST_CASE_TIMEOUT_MS ?? testCase.arguments?.timeoutMs ?? '180000');
493
- const startTime = performance.now();
494
-
495
- try {
496
- // Log test start to Unreal Engine console
497
- const cleanScenario = (testCase.scenario || 'Unknown Test').replace(/"/g, "'");
498
- await callToolOnce({
499
- name: 'system_control',
500
- arguments: { action: 'console_command', command: `Log "---- STARTING TEST: ${cleanScenario} ----"` }
501
- }, 5000).catch(() => { });
502
- } catch (e) { /* ignore */ }
503
-
504
- try {
505
- const response = await callToolOnce({ name: testCase.toolName, arguments: testCase.arguments }, testCaseTimeoutMs);
506
-
507
- const endTime = performance.now();
508
- const durationMs = endTime - startTime;
509
-
510
- let structuredContent = response.structuredContent ?? null;
511
- if (structuredContent === null && response.content?.length) {
512
- for (const entry of response.content) {
513
- if (entry?.type !== 'text' || typeof entry.text !== 'string') continue;
514
- try { structuredContent = JSON.parse(entry.text); break; } catch { }
515
- }
516
- }
517
- const normalizedResponse = { ...response, structuredContent };
518
- const { passed, reason } = evaluateExpectation(testCase, normalizedResponse);
519
-
520
- if (!passed) {
521
- console.log(`[FAILED] ${testCase.scenario} (${durationMs.toFixed(1)} ms) => ${reason}`);
522
- if (normalizedResponse) {
523
- console.log(`[DEBUG] Full response for ${testCase.scenario}:`, JSON.stringify(normalizedResponse, null, 2));
524
- }
525
- results.push({
526
- scenario: testCase.scenario,
527
- toolName: testCase.toolName,
528
- arguments: testCase.arguments,
529
- status: 'failed',
530
- durationMs,
531
- detail: reason,
532
- response: normalizedResponse
533
- });
534
- } else {
535
- console.log(`[PASSED] ${testCase.scenario} (${durationMs.toFixed(1)} ms)`);
536
- results.push({
537
- scenario: testCase.scenario,
538
- toolName: testCase.toolName,
539
- arguments: testCase.arguments,
540
- status: 'passed',
541
- durationMs,
542
- detail: reason
543
- });
544
- }
545
-
546
- } catch (error) {
547
- const endTime = performance.now();
548
- const durationMs = endTime - startTime;
549
- const errorMessage = String(error?.message || error || '');
550
- const lowerExpected = (testCase.expected || '').toString().toLowerCase();
551
- const lowerError = errorMessage.toLowerCase();
552
-
553
- // If the test explicitly expects a timeout (e.g. "timeout|error"), then
554
- // an MCP/client timeout should be treated as the expected outcome rather
555
- // than as a hard harness failure. Accept both "timeout" and "timed out"
556
- // phrasing from different MCP client implementations.
557
- if (lowerExpected.includes('timeout') && (lowerError.includes('timeout') || lowerError.includes('timed out'))) {
558
- console.log(`[PASSED] ${testCase.scenario} (${durationMs.toFixed(1)} ms)`);
559
- results.push({
560
- scenario: testCase.scenario,
561
- toolName: testCase.toolName,
562
- arguments: testCase.arguments,
563
- status: 'passed',
564
- durationMs,
565
- detail: errorMessage
566
- });
567
- continue;
568
- }
569
-
570
- console.log(`[FAILED] ${testCase.scenario} (${durationMs.toFixed(1)} ms) => Error: ${errorMessage}`);
571
- results.push({
572
- scenario: testCase.scenario,
573
- toolName: testCase.toolName,
574
- arguments: testCase.arguments,
575
- status: 'failed',
576
- durationMs,
577
- detail: errorMessage
578
- });
579
- }
580
- }
581
-
582
- const resultsPath = await persistResults(toolName, results);
583
- summarize(toolName, results, resultsPath);
584
-
585
- const hasFailures = results.some((result) => result.status === 'failed');
586
- process.exitCode = hasFailures ? 1 : 0;
587
-
588
- } catch (error) {
589
- console.error('Test runner failed:', error);
590
- process.exit(1);
591
- } finally {
592
- if (client) {
593
- try {
594
- await client.close();
595
- } catch {
596
- // ignore
597
- }
598
- }
599
- if (transport) {
600
- try {
601
- await transport.close();
602
- } catch {
603
- // ignore
604
- }
605
- }
606
- }
607
- }
608
-
609
- export class TestRunner {
610
- constructor(suiteName) {
611
- this.suiteName = suiteName || 'Test Suite';
612
- this.steps = [];
613
- }
614
-
615
- addStep(name, fn) {
616
- this.steps.push({ name, fn });
617
- }
618
-
619
- async run() {
620
- if (this.steps.length === 0) {
621
- console.warn(`No steps registered for ${this.suiteName}`);
622
- return;
623
- }
624
-
625
- console.log('\n' + '='.repeat(60));
626
- console.log(`${this.suiteName}`);
627
- console.log('='.repeat(60));
628
- console.log(`Total steps: ${this.steps.length}`);
629
- console.log('');
630
-
631
- let transport;
632
- let client;
633
- const results = [];
634
-
635
- try {
636
- const bridgeHost = process.env.MCP_AUTOMATION_WS_HOST ?? '127.0.0.1';
637
- const envPorts = process.env.MCP_AUTOMATION_WS_PORTS
638
- ? process.env.MCP_AUTOMATION_WS_PORTS.split(',').map((p) => parseInt(p.trim(), 10)).filter(Boolean)
639
- : [8090, 8091];
640
- const waitMs = parseInt(process.env.UNREAL_MCP_WAIT_PORT_MS ?? '5000', 10);
641
-
642
- async function waitForAnyPort(host, ports, timeoutMs = 10000) {
643
- const start = Date.now();
644
- while (Date.now() - start < timeoutMs) {
645
- for (const port of ports) {
646
- try {
647
- await new Promise((resolve, reject) => {
648
- const sock = new net.Socket();
649
- let settled = false;
650
- sock.setTimeout(1000);
651
- sock.once('connect', () => { settled = true; sock.destroy(); resolve(true); });
652
- sock.once('timeout', () => { if (!settled) { settled = true; sock.destroy(); reject(new Error('timeout')); } });
653
- sock.once('error', () => { if (!settled) { settled = true; sock.destroy(); reject(new Error('error')); } });
654
- sock.connect(port, host);
655
- });
656
- console.log(`✅ Automation bridge appears to be listening on ${host}:${port}`);
657
- return port;
658
- } catch {
659
- }
660
- }
661
- await new Promise((r) => setImmediate(r));
662
- }
663
- throw new Error(`Timed out waiting for automation bridge on ports: ${ports.join(',')}`);
664
- }
665
-
666
- try {
667
- await waitForAnyPort(bridgeHost, envPorts, waitMs);
668
- } catch (err) {
669
- console.warn('Automation bridge did not become available before tests started:', err.message);
670
- }
671
-
672
- const distPath = path.join(repoRoot, 'dist', 'cli.js');
673
- const srcDir = path.join(repoRoot, 'src');
674
-
675
- async function getLatestMtime(dir) {
676
- let latest = 0;
677
- try {
678
- const entries = await fs.readdir(dir, { withFileTypes: true });
679
- for (const e of entries) {
680
- const full = path.join(dir, e.name);
681
- if (e.isDirectory()) {
682
- const child = await getLatestMtime(full);
683
- if (child > latest) latest = child;
684
- } else {
685
- try {
686
- const st = await fs.stat(full);
687
- const m = st.mtimeMs || 0;
688
- if (m > latest) latest = m;
689
- } catch (_) { }
690
- }
691
- }
692
- } catch (_) {
693
- }
694
- return latest;
695
- }
696
-
697
- let useDist = false;
698
- let distExists = false;
699
- try {
700
- await fs.access(distPath);
701
- distExists = true;
702
- } catch (e) {
703
- distExists = false;
704
- }
705
-
706
- if (process.env.UNREAL_MCP_FORCE_DIST === '1') {
707
- useDist = true;
708
- console.log('Forcing use of dist build via UNREAL_MCP_FORCE_DIST=1');
709
- } else if (distExists) {
710
- try {
711
- const distStat = await fs.stat(distPath);
712
- const srcLatest = await getLatestMtime(srcDir);
713
- const srcIsNewer = srcLatest > (distStat.mtimeMs || 0);
714
- const autoBuildEnabled = process.env.UNREAL_MCP_AUTO_BUILD === '1';
715
- const autoBuildDisabled = process.env.UNREAL_MCP_NO_AUTO_BUILD === '1';
716
- if (srcIsNewer) {
717
- if (!autoBuildEnabled && !autoBuildDisabled) {
718
- console.log('Detected newer source files than dist; attempting automatic build to refresh dist/ (set UNREAL_MCP_NO_AUTO_BUILD=1 to disable)');
719
- }
720
- if (autoBuildEnabled || !autoBuildDisabled) {
721
- const { spawn } = await import('node:child_process');
722
- try {
723
- await new Promise((resolve, reject) => {
724
- const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
725
- const ps = spawn(npmCmd, ['run', 'build'], { cwd: repoRoot, stdio: 'inherit', shell: process.platform === 'win32' });
726
- ps.on('close', (code) => (code === 0 ? resolve() : reject(new Error(`Build failed with code ${code}`))));
727
- ps.on('error', (err) => reject(err));
728
- });
729
- console.log('Build succeeded — using dist/ for live tests');
730
- useDist = true;
731
- } catch (buildErr) {
732
- console.warn('Automatic build failed or could not stat files — falling back to TypeScript source for live tests:', String(buildErr));
733
- useDist = false;
734
- }
735
- } else {
736
- console.log('Detected newer source files than dist but automatic build is disabled.');
737
- console.log('Set UNREAL_MCP_AUTO_BUILD=1 to enable automatic builds, or run `npm run build` manually.');
738
- useDist = false;
739
- }
740
- } else {
741
- useDist = true;
742
- console.log('Using built dist for live tests');
743
- }
744
- } catch (buildErr) {
745
- console.warn('Automatic build failed or could not stat files — falling back to TypeScript source for live tests:', String(buildErr));
746
- useDist = false;
747
- console.log('Preferring TypeScript source for tests to pick up local changes (set UNREAL_MCP_FORCE_DIST=1 to force dist)');
748
- }
749
- } else {
750
- console.log('dist not found — attempting to run `npm run build` to produce dist/ for live tests');
751
- try {
752
- const { spawn } = await import('node:child_process');
753
- await new Promise((resolve, reject) => {
754
- const ps = spawn(process.platform === 'win32' ? 'npm.cmd' : 'npm', ['run', 'build'], { cwd: repoRoot, stdio: 'inherit' });
755
- ps.on('close', (code) => (code === 0 ? resolve() : reject(new Error(`Build failed with code ${code}`))));
756
- ps.on('error', (err) => reject(err));
757
- });
758
- useDist = true;
759
- console.log('Build succeeded — using dist/ for live tests');
760
- } catch (buildErr) {
761
- console.warn('Automatic build failed — falling back to running TypeScript source with ts-node-esm:', String(buildErr));
762
- useDist = false;
763
- }
764
- }
765
-
766
- if (!useDist) {
767
- serverCommand = process.env.UNREAL_MCP_SERVER_CMD ?? 'npx';
768
- serverArgs = ['ts-node-esm', path.join(repoRoot, 'src', 'cli.ts')];
769
- } else {
770
- serverCommand = process.env.UNREAL_MCP_SERVER_CMD ?? serverCommand;
771
- serverArgs = process.env.UNREAL_MCP_SERVER_ARGS?.split(',') ?? serverArgs;
772
- }
773
-
774
- transport = new StdioClientTransport({
775
- command: serverCommand,
776
- args: serverArgs,
777
- cwd: serverCwd,
778
- stderr: 'inherit',
779
- env: serverEnv
780
- });
781
-
782
- client = new Client({
783
- name: 'unreal-mcp-step-runner',
784
- version: '1.0.0'
785
- });
786
-
787
- await client.connect(transport);
788
- await client.listTools({});
789
- console.log('✅ Connected to Unreal MCP Server\n');
790
-
791
- const callToolOnce = async function (callOptions, baseTimeoutMs) {
792
- const envDefault = Number(process.env.UNREAL_MCP_TEST_CALL_TIMEOUT_MS ?? '60000') || 60000;
793
- const perCall = Number(callOptions?.arguments?.timeoutMs) || undefined;
794
- const base = typeof baseTimeoutMs === 'number' && baseTimeoutMs > 0 ? baseTimeoutMs : (perCall || envDefault);
795
- const timeoutMs = base;
796
- try {
797
- console.log(`[CALL] ${callOptions.name} (timeout ${timeoutMs}ms)`);
798
- const outgoing = Object.assign({}, callOptions, { arguments: { ...(callOptions.arguments || {}), timeoutMs } });
799
- let callPromise;
800
- try {
801
- callPromise = client.callTool(outgoing, undefined, { timeout: timeoutMs });
802
- } catch (err) {
803
- try {
804
- callPromise = client.callTool(outgoing, { timeout: timeoutMs });
805
- } catch (inner) {
806
- try {
807
- callPromise = client.callTool(outgoing);
808
- } catch (inner2) {
809
- throw inner2 || inner || err;
810
- }
811
- }
812
- }
813
-
814
- let timeoutId;
815
- const timeoutPromise = new Promise((_, rej) => {
816
- timeoutId = setTimeout(() => rej(new Error(`Local test runner timeout after ${timeoutMs}ms`)), timeoutMs);
817
- if (timeoutId && typeof timeoutId.unref === 'function') {
818
- timeoutId.unref();
819
- }
820
- });
821
- try {
822
- const timed = Promise.race([
823
- callPromise,
824
- timeoutPromise
825
- ]);
826
- return await timed;
827
- } finally {
828
- if (timeoutId) {
829
- clearTimeout(timeoutId);
830
- }
831
- }
832
- } catch (e) {
833
- const msg = String(e?.message || e || '');
834
- if (msg.includes('Unknown blueprint action')) {
835
- return { structuredContent: { success: false, error: msg } };
836
- }
837
- throw e;
838
- }
839
- };
840
-
841
- const tools = {
842
- async executeTool(toolName, args, options = {}) {
843
- const timeoutMs = typeof options.timeoutMs === 'number' ? options.timeoutMs : undefined;
844
- const response = await callToolOnce({ name: toolName, arguments: args }, timeoutMs);
845
- let structuredContent = response.structuredContent ?? null;
846
- if (structuredContent === null && response.content?.length) {
847
- for (const entry of response.content) {
848
- if (entry?.type !== 'text' || typeof entry.text !== 'string') continue;
849
- try {
850
- structuredContent = JSON.parse(entry.text);
851
- break;
852
- } catch {
853
- }
854
- }
855
- }
856
-
857
- if (structuredContent && typeof structuredContent === 'object') {
858
- return structuredContent;
859
- }
860
-
861
- return {
862
- success: !response.isError,
863
- message: undefined,
864
- error: undefined
865
- };
866
- }
867
- };
868
-
869
- for (const step of this.steps) {
870
- const startTime = performance.now();
871
-
872
- try {
873
- // Log step start to Unreal Engine console
874
- const cleanName = (step.name || 'Unknown Step').replace(/"/g, "'");
875
- await callToolOnce({
876
- name: 'system_control',
877
- arguments: { action: 'console_command', command: `Log "---- STARTING STEP: ${cleanName} ----"` }
878
- }, 5000).catch(() => { });
879
- } catch (e) { /* ignore */ }
880
-
881
- try {
882
- const ok = await step.fn(tools);
883
- const durationMs = performance.now() - startTime;
884
- const status = ok ? 'passed' : 'failed';
885
- console.log(formatResultLine({ scenario: step.name }, status, ok ? '' : 'Step returned false', durationMs));
886
- results.push({
887
- scenario: step.name,
888
- toolName: null,
889
- arguments: null,
890
- status,
891
- durationMs,
892
- detail: ok ? undefined : 'Step returned false'
893
- });
894
- } catch (err) {
895
- const durationMs = performance.now() - startTime;
896
- const detail = err?.message || String(err);
897
- console.log(formatResultLine({ scenario: step.name }, 'failed', detail, durationMs));
898
- results.push({
899
- scenario: step.name,
900
- toolName: null,
901
- arguments: null,
902
- status: 'failed',
903
- durationMs,
904
- detail
905
- });
906
- }
907
- }
908
-
909
- const resultsPath = await persistResults(this.suiteName, results);
910
- summarize(this.suiteName, results, resultsPath);
911
-
912
- const hasFailures = results.some((result) => result.status === 'failed');
913
- process.exitCode = hasFailures ? 1 : 0;
914
- } catch (error) {
915
- console.error('Step-based test runner failed:', error);
916
- process.exit(1);
917
- } finally {
918
- if (client) {
919
- try {
920
- await client.close();
921
- } catch {
922
- }
923
- }
924
- if (transport) {
925
- try {
926
- await transport.close();
927
- } catch {
928
- }
929
- }
930
- }
931
- }
932
- }
933
-