unreal-engine-mcp-server 0.4.7 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (454) hide show
  1. package/.env.example +26 -0
  2. package/.env.production +38 -7
  3. package/.eslintrc.json +0 -54
  4. package/.eslintrc.override.json +8 -0
  5. package/.github/ISSUE_TEMPLATE/bug_report.yml +94 -0
  6. package/.github/ISSUE_TEMPLATE/config.yml +8 -0
  7. package/.github/ISSUE_TEMPLATE/feature_request.yml +56 -0
  8. package/.github/copilot-instructions.md +478 -45
  9. package/.github/dependabot.yml +19 -0
  10. package/.github/labeler.yml +24 -0
  11. package/.github/labels.yml +70 -0
  12. package/.github/pull_request_template.md +42 -0
  13. package/.github/release-drafter-config.yml +51 -0
  14. package/.github/workflows/auto-merge.yml +38 -0
  15. package/.github/workflows/ci.yml +38 -0
  16. package/.github/workflows/dependency-review.yml +17 -0
  17. package/.github/workflows/gemini-issue-triage.yml +172 -0
  18. package/.github/workflows/greetings.yml +27 -0
  19. package/.github/workflows/labeler.yml +17 -0
  20. package/.github/workflows/links.yml +80 -0
  21. package/.github/workflows/pr-size-labeler.yml +137 -0
  22. package/.github/workflows/publish-mcp.yml +13 -7
  23. package/.github/workflows/release-drafter.yml +23 -0
  24. package/.github/workflows/release.yml +112 -0
  25. package/.github/workflows/semantic-pull-request.yml +35 -0
  26. package/.github/workflows/smoke-test.yml +36 -0
  27. package/.github/workflows/stale.yml +28 -0
  28. package/CHANGELOG.md +338 -31
  29. package/CONTRIBUTING.md +140 -0
  30. package/GEMINI.md +115 -0
  31. package/Public/Plugin_setup_guide.mp4 +0 -0
  32. package/README.md +189 -128
  33. package/claude_desktop_config_example.json +7 -6
  34. package/dist/automation/bridge.d.ts +50 -0
  35. package/dist/automation/bridge.js +452 -0
  36. package/dist/automation/connection-manager.d.ts +23 -0
  37. package/dist/automation/connection-manager.js +107 -0
  38. package/dist/automation/handshake.d.ts +11 -0
  39. package/dist/automation/handshake.js +89 -0
  40. package/dist/automation/index.d.ts +3 -0
  41. package/dist/automation/index.js +3 -0
  42. package/dist/automation/message-handler.d.ts +12 -0
  43. package/dist/automation/message-handler.js +149 -0
  44. package/dist/automation/request-tracker.d.ts +25 -0
  45. package/dist/automation/request-tracker.js +98 -0
  46. package/dist/automation/types.d.ts +130 -0
  47. package/dist/automation/types.js +2 -0
  48. package/dist/cli.js +32 -5
  49. package/dist/config.d.ts +26 -0
  50. package/dist/config.js +59 -0
  51. package/dist/constants.d.ts +16 -0
  52. package/dist/constants.js +16 -0
  53. package/dist/graphql/loaders.d.ts +64 -0
  54. package/dist/graphql/loaders.js +117 -0
  55. package/dist/graphql/resolvers.d.ts +268 -0
  56. package/dist/graphql/resolvers.js +746 -0
  57. package/dist/graphql/schema.d.ts +5 -0
  58. package/dist/graphql/schema.js +437 -0
  59. package/dist/graphql/server.d.ts +26 -0
  60. package/dist/graphql/server.js +117 -0
  61. package/dist/graphql/types.d.ts +9 -0
  62. package/dist/graphql/types.js +2 -0
  63. package/dist/handlers/resource-handlers.d.ts +20 -0
  64. package/dist/handlers/resource-handlers.js +180 -0
  65. package/dist/index.d.ts +33 -18
  66. package/dist/index.js +130 -619
  67. package/dist/resources/actors.d.ts +17 -12
  68. package/dist/resources/actors.js +56 -76
  69. package/dist/resources/assets.d.ts +6 -14
  70. package/dist/resources/assets.js +115 -147
  71. package/dist/resources/levels.d.ts +13 -13
  72. package/dist/resources/levels.js +25 -34
  73. package/dist/server/resource-registry.d.ts +20 -0
  74. package/dist/server/resource-registry.js +37 -0
  75. package/dist/server/tool-registry.d.ts +23 -0
  76. package/dist/server/tool-registry.js +322 -0
  77. package/dist/server-setup.d.ts +20 -0
  78. package/dist/server-setup.js +71 -0
  79. package/dist/services/health-monitor.d.ts +34 -0
  80. package/dist/services/health-monitor.js +105 -0
  81. package/dist/services/metrics-server.d.ts +11 -0
  82. package/dist/services/metrics-server.js +105 -0
  83. package/dist/tools/actors.d.ts +163 -9
  84. package/dist/tools/actors.js +356 -311
  85. package/dist/tools/animation.d.ts +135 -4
  86. package/dist/tools/animation.js +510 -411
  87. package/dist/tools/assets.d.ts +75 -29
  88. package/dist/tools/assets.js +265 -284
  89. package/dist/tools/audio.d.ts +102 -42
  90. package/dist/tools/audio.js +272 -685
  91. package/dist/tools/base-tool.d.ts +17 -0
  92. package/dist/tools/base-tool.js +46 -0
  93. package/dist/tools/behavior-tree.d.ts +94 -0
  94. package/dist/tools/behavior-tree.js +39 -0
  95. package/dist/tools/blueprint.d.ts +208 -126
  96. package/dist/tools/blueprint.js +685 -832
  97. package/dist/tools/consolidated-tool-definitions.d.ts +5462 -1781
  98. package/dist/tools/consolidated-tool-definitions.js +829 -496
  99. package/dist/tools/consolidated-tool-handlers.d.ts +2 -1
  100. package/dist/tools/consolidated-tool-handlers.js +198 -1027
  101. package/dist/tools/debug.d.ts +143 -85
  102. package/dist/tools/debug.js +234 -180
  103. package/dist/tools/dynamic-handler-registry.d.ts +13 -0
  104. package/dist/tools/dynamic-handler-registry.js +23 -0
  105. package/dist/tools/editor.d.ts +30 -83
  106. package/dist/tools/editor.js +247 -244
  107. package/dist/tools/engine.d.ts +10 -4
  108. package/dist/tools/engine.js +13 -5
  109. package/dist/tools/environment.d.ts +30 -0
  110. package/dist/tools/environment.js +267 -0
  111. package/dist/tools/foliage.d.ts +65 -99
  112. package/dist/tools/foliage.js +221 -331
  113. package/dist/tools/handlers/actor-handlers.d.ts +3 -0
  114. package/dist/tools/handlers/actor-handlers.js +227 -0
  115. package/dist/tools/handlers/animation-handlers.d.ts +3 -0
  116. package/dist/tools/handlers/animation-handlers.js +185 -0
  117. package/dist/tools/handlers/argument-helper.d.ts +16 -0
  118. package/dist/tools/handlers/argument-helper.js +80 -0
  119. package/dist/tools/handlers/asset-handlers.d.ts +3 -0
  120. package/dist/tools/handlers/asset-handlers.js +496 -0
  121. package/dist/tools/handlers/audio-handlers.d.ts +3 -0
  122. package/dist/tools/handlers/audio-handlers.js +166 -0
  123. package/dist/tools/handlers/blueprint-handlers.d.ts +4 -0
  124. package/dist/tools/handlers/blueprint-handlers.js +358 -0
  125. package/dist/tools/handlers/common-handlers.d.ts +14 -0
  126. package/dist/tools/handlers/common-handlers.js +56 -0
  127. package/dist/tools/handlers/editor-handlers.d.ts +3 -0
  128. package/dist/tools/handlers/editor-handlers.js +119 -0
  129. package/dist/tools/handlers/effect-handlers.d.ts +3 -0
  130. package/dist/tools/handlers/effect-handlers.js +171 -0
  131. package/dist/tools/handlers/environment-handlers.d.ts +3 -0
  132. package/dist/tools/handlers/environment-handlers.js +170 -0
  133. package/dist/tools/handlers/graph-handlers.d.ts +3 -0
  134. package/dist/tools/handlers/graph-handlers.js +90 -0
  135. package/dist/tools/handlers/input-handlers.d.ts +3 -0
  136. package/dist/tools/handlers/input-handlers.js +21 -0
  137. package/dist/tools/handlers/inspect-handlers.d.ts +3 -0
  138. package/dist/tools/handlers/inspect-handlers.js +383 -0
  139. package/dist/tools/handlers/level-handlers.d.ts +3 -0
  140. package/dist/tools/handlers/level-handlers.js +237 -0
  141. package/dist/tools/handlers/lighting-handlers.d.ts +3 -0
  142. package/dist/tools/handlers/lighting-handlers.js +144 -0
  143. package/dist/tools/handlers/performance-handlers.d.ts +3 -0
  144. package/dist/tools/handlers/performance-handlers.js +130 -0
  145. package/dist/tools/handlers/pipeline-handlers.d.ts +3 -0
  146. package/dist/tools/handlers/pipeline-handlers.js +110 -0
  147. package/dist/tools/handlers/sequence-handlers.d.ts +3 -0
  148. package/dist/tools/handlers/sequence-handlers.js +376 -0
  149. package/dist/tools/handlers/system-handlers.d.ts +4 -0
  150. package/dist/tools/handlers/system-handlers.js +506 -0
  151. package/dist/tools/input.d.ts +19 -0
  152. package/dist/tools/input.js +89 -0
  153. package/dist/tools/introspection.d.ts +103 -40
  154. package/dist/tools/introspection.js +425 -568
  155. package/dist/tools/landscape.d.ts +54 -93
  156. package/dist/tools/landscape.js +284 -409
  157. package/dist/tools/level.d.ts +66 -27
  158. package/dist/tools/level.js +647 -675
  159. package/dist/tools/lighting.d.ts +77 -38
  160. package/dist/tools/lighting.js +445 -943
  161. package/dist/tools/logs.d.ts +3 -3
  162. package/dist/tools/logs.js +5 -57
  163. package/dist/tools/materials.d.ts +91 -24
  164. package/dist/tools/materials.js +194 -118
  165. package/dist/tools/niagara.d.ts +149 -39
  166. package/dist/tools/niagara.js +267 -182
  167. package/dist/tools/performance.d.ts +27 -13
  168. package/dist/tools/performance.js +203 -122
  169. package/dist/tools/physics.d.ts +32 -77
  170. package/dist/tools/physics.js +175 -582
  171. package/dist/tools/property-dictionary.d.ts +13 -0
  172. package/dist/tools/property-dictionary.js +82 -0
  173. package/dist/tools/sequence.d.ts +85 -60
  174. package/dist/tools/sequence.js +208 -747
  175. package/dist/tools/tool-definition-utils.d.ts +59 -0
  176. package/dist/tools/tool-definition-utils.js +35 -0
  177. package/dist/tools/ui.d.ts +64 -34
  178. package/dist/tools/ui.js +134 -214
  179. package/dist/types/automation-responses.d.ts +115 -0
  180. package/dist/types/automation-responses.js +2 -0
  181. package/dist/types/env.d.ts +0 -3
  182. package/dist/types/env.js +0 -7
  183. package/dist/types/responses.d.ts +249 -0
  184. package/dist/types/responses.js +2 -0
  185. package/dist/types/tool-interfaces.d.ts +898 -0
  186. package/dist/types/tool-interfaces.js +2 -0
  187. package/dist/types/tool-types.d.ts +183 -19
  188. package/dist/types/tool-types.js +0 -4
  189. package/dist/unreal-bridge.d.ts +24 -131
  190. package/dist/unreal-bridge.js +364 -1506
  191. package/dist/utils/command-validator.d.ts +9 -0
  192. package/dist/utils/command-validator.js +68 -0
  193. package/dist/utils/elicitation.d.ts +1 -1
  194. package/dist/utils/elicitation.js +12 -15
  195. package/dist/utils/error-handler.d.ts +2 -51
  196. package/dist/utils/error-handler.js +11 -87
  197. package/dist/utils/ini-reader.d.ts +3 -0
  198. package/dist/utils/ini-reader.js +69 -0
  199. package/dist/utils/logger.js +9 -6
  200. package/dist/utils/normalize.d.ts +3 -0
  201. package/dist/utils/normalize.js +56 -0
  202. package/dist/utils/path-security.d.ts +2 -0
  203. package/dist/utils/path-security.js +24 -0
  204. package/dist/utils/response-factory.d.ts +7 -0
  205. package/dist/utils/response-factory.js +27 -0
  206. package/dist/utils/response-validator.d.ts +3 -24
  207. package/dist/utils/response-validator.js +130 -81
  208. package/dist/utils/result-helpers.d.ts +4 -5
  209. package/dist/utils/result-helpers.js +15 -16
  210. package/dist/utils/safe-json.js +5 -11
  211. package/dist/utils/unreal-command-queue.d.ts +24 -0
  212. package/dist/utils/unreal-command-queue.js +120 -0
  213. package/dist/utils/validation.d.ts +0 -40
  214. package/dist/utils/validation.js +1 -78
  215. package/dist/wasm/index.d.ts +70 -0
  216. package/dist/wasm/index.js +535 -0
  217. package/docs/GraphQL-API.md +888 -0
  218. package/docs/Migration-Guide-v0.5.0.md +684 -0
  219. package/docs/Roadmap.md +53 -0
  220. package/docs/WebAssembly-Integration.md +628 -0
  221. package/docs/editor-plugin-extension.md +370 -0
  222. package/docs/handler-mapping.md +242 -0
  223. package/docs/native-automation-progress.md +128 -0
  224. package/docs/testing-guide.md +423 -0
  225. package/mcp-config-example.json +6 -6
  226. package/package.json +67 -28
  227. package/plugins/McpAutomationBridge/Config/FilterPlugin.ini +8 -0
  228. package/plugins/McpAutomationBridge/McpAutomationBridge.uplugin +64 -0
  229. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/McpAutomationBridge.Build.cs +189 -0
  230. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.cpp +22 -0
  231. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.h +30 -0
  232. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeHelpers.h +1983 -0
  233. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeModule.cpp +72 -0
  234. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSettings.cpp +46 -0
  235. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSubsystem.cpp +581 -0
  236. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AnimationHandlers.cpp +2394 -0
  237. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetQueryHandlers.cpp +300 -0
  238. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetWorkflowHandlers.cpp +2807 -0
  239. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AudioHandlers.cpp +1087 -0
  240. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BehaviorTreeHandlers.cpp +488 -0
  241. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.cpp +643 -0
  242. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.h +31 -0
  243. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintGraphHandlers.cpp +1184 -0
  244. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers.cpp +5652 -0
  245. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers_List.cpp +152 -0
  246. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ControlHandlers.cpp +2614 -0
  247. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_DebugHandlers.cpp +42 -0
  248. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EditorFunctionHandlers.cpp +1237 -0
  249. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EffectHandlers.cpp +1701 -0
  250. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EnvironmentHandlers.cpp +2145 -0
  251. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_FoliageHandlers.cpp +954 -0
  252. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InputHandlers.cpp +209 -0
  253. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InsightsHandlers.cpp +41 -0
  254. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LandscapeHandlers.cpp +1164 -0
  255. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LevelHandlers.cpp +762 -0
  256. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LightingHandlers.cpp +634 -0
  257. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LogHandlers.cpp +136 -0
  258. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MaterialGraphHandlers.cpp +494 -0
  259. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraGraphHandlers.cpp +278 -0
  260. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraHandlers.cpp +625 -0
  261. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PerformanceHandlers.cpp +401 -0
  262. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PipelineHandlers.cpp +67 -0
  263. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ProcessRequest.cpp +735 -0
  264. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PropertyHandlers.cpp +2634 -0
  265. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_RenderHandlers.cpp +189 -0
  266. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.cpp +917 -0
  267. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.h +39 -0
  268. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequenceHandlers.cpp +2670 -0
  269. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequencerHandlers.cpp +519 -0
  270. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_TestHandlers.cpp +38 -0
  271. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_UiHandlers.cpp +668 -0
  272. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_WorldPartitionHandlers.cpp +346 -0
  273. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.cpp +1330 -0
  274. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.h +149 -0
  275. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpConnectionManager.cpp +783 -0
  276. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSettings.h +115 -0
  277. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSubsystem.h +796 -0
  278. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpConnectionManager.h +117 -0
  279. package/scripts/check-unreal-connection.mjs +19 -0
  280. package/scripts/clean-tmp.js +23 -0
  281. package/scripts/patch-wasm.js +26 -0
  282. package/scripts/run-all-tests.mjs +136 -0
  283. package/scripts/smoke-test.ts +94 -0
  284. package/scripts/sync-mcp-plugin.js +143 -0
  285. package/scripts/test-no-plugin-alternates.mjs +113 -0
  286. package/scripts/validate-server.js +46 -0
  287. package/scripts/verify-automation-bridge.js +200 -0
  288. package/server.json +58 -21
  289. package/src/automation/bridge.ts +558 -0
  290. package/src/automation/connection-manager.ts +130 -0
  291. package/src/automation/handshake.ts +99 -0
  292. package/src/automation/index.ts +2 -0
  293. package/src/automation/message-handler.ts +167 -0
  294. package/src/automation/request-tracker.ts +123 -0
  295. package/src/automation/types.ts +107 -0
  296. package/src/cli.ts +33 -6
  297. package/src/config.ts +73 -0
  298. package/src/constants.ts +19 -0
  299. package/src/graphql/loaders.ts +244 -0
  300. package/src/graphql/resolvers.ts +1008 -0
  301. package/src/graphql/schema.ts +452 -0
  302. package/src/graphql/server.ts +156 -0
  303. package/src/graphql/types.ts +10 -0
  304. package/src/handlers/resource-handlers.ts +186 -0
  305. package/src/index.ts +166 -664
  306. package/src/resources/actors.ts +58 -76
  307. package/src/resources/assets.ts +148 -134
  308. package/src/resources/levels.ts +28 -33
  309. package/src/server/resource-registry.ts +47 -0
  310. package/src/server/tool-registry.ts +354 -0
  311. package/src/server-setup.ts +114 -0
  312. package/src/services/health-monitor.ts +132 -0
  313. package/src/services/metrics-server.ts +142 -0
  314. package/src/tools/actors.ts +426 -323
  315. package/src/tools/animation.ts +672 -461
  316. package/src/tools/assets.ts +364 -289
  317. package/src/tools/audio.ts +323 -766
  318. package/src/tools/base-tool.ts +52 -0
  319. package/src/tools/behavior-tree.ts +45 -0
  320. package/src/tools/blueprint.ts +792 -970
  321. package/src/tools/consolidated-tool-definitions.ts +993 -515
  322. package/src/tools/consolidated-tool-handlers.ts +258 -1146
  323. package/src/tools/debug.ts +292 -187
  324. package/src/tools/dynamic-handler-registry.ts +33 -0
  325. package/src/tools/editor.ts +329 -253
  326. package/src/tools/engine.ts +14 -3
  327. package/src/tools/environment.ts +281 -0
  328. package/src/tools/foliage.ts +330 -392
  329. package/src/tools/handlers/actor-handlers.ts +265 -0
  330. package/src/tools/handlers/animation-handlers.ts +237 -0
  331. package/src/tools/handlers/argument-helper.ts +142 -0
  332. package/src/tools/handlers/asset-handlers.ts +532 -0
  333. package/src/tools/handlers/audio-handlers.ts +194 -0
  334. package/src/tools/handlers/blueprint-handlers.ts +380 -0
  335. package/src/tools/handlers/common-handlers.ts +87 -0
  336. package/src/tools/handlers/editor-handlers.ts +123 -0
  337. package/src/tools/handlers/effect-handlers.ts +220 -0
  338. package/src/tools/handlers/environment-handlers.ts +183 -0
  339. package/src/tools/handlers/graph-handlers.ts +116 -0
  340. package/src/tools/handlers/input-handlers.ts +28 -0
  341. package/src/tools/handlers/inspect-handlers.ts +450 -0
  342. package/src/tools/handlers/level-handlers.ts +252 -0
  343. package/src/tools/handlers/lighting-handlers.ts +147 -0
  344. package/src/tools/handlers/performance-handlers.ts +132 -0
  345. package/src/tools/handlers/pipeline-handlers.ts +127 -0
  346. package/src/tools/handlers/sequence-handlers.ts +415 -0
  347. package/src/tools/handlers/system-handlers.ts +564 -0
  348. package/src/tools/input.ts +101 -0
  349. package/src/tools/introspection.ts +493 -584
  350. package/src/tools/landscape.ts +418 -507
  351. package/src/tools/level.ts +786 -708
  352. package/src/tools/lighting.ts +588 -984
  353. package/src/tools/logs.ts +9 -57
  354. package/src/tools/materials.ts +237 -121
  355. package/src/tools/niagara.ts +335 -168
  356. package/src/tools/performance.ts +320 -169
  357. package/src/tools/physics.ts +274 -613
  358. package/src/tools/property-dictionary.ts +98 -0
  359. package/src/tools/sequence.ts +276 -820
  360. package/src/tools/tool-definition-utils.ts +35 -0
  361. package/src/tools/ui.ts +205 -283
  362. package/src/types/automation-responses.ts +119 -0
  363. package/src/types/env.ts +0 -10
  364. package/src/types/responses.ts +355 -0
  365. package/src/types/tool-interfaces.ts +250 -0
  366. package/src/types/tool-types.ts +243 -21
  367. package/src/unreal-bridge.ts +460 -1550
  368. package/src/utils/command-validator.ts +76 -0
  369. package/src/utils/elicitation.ts +10 -7
  370. package/src/utils/error-handler.ts +14 -90
  371. package/src/utils/ini-reader.ts +86 -0
  372. package/src/utils/logger.ts +8 -3
  373. package/src/utils/normalize.test.ts +162 -0
  374. package/src/utils/normalize.ts +60 -0
  375. package/src/utils/path-security.ts +43 -0
  376. package/src/utils/response-factory.ts +44 -0
  377. package/src/utils/response-validator.ts +176 -56
  378. package/src/utils/result-helpers.ts +21 -19
  379. package/src/utils/safe-json.test.ts +90 -0
  380. package/src/utils/safe-json.ts +14 -11
  381. package/src/utils/unreal-command-queue.ts +152 -0
  382. package/src/utils/validation.test.ts +184 -0
  383. package/src/utils/validation.ts +4 -1
  384. package/src/wasm/index.ts +838 -0
  385. package/test-server.mjs +100 -0
  386. package/tests/run-unreal-tool-tests.mjs +242 -14
  387. package/tests/test-animation.mjs +369 -0
  388. package/tests/test-asset-advanced.mjs +82 -0
  389. package/tests/test-asset-errors.mjs +35 -0
  390. package/tests/test-asset-graph.mjs +311 -0
  391. package/tests/test-audio.mjs +417 -0
  392. package/tests/test-automation-timeouts.mjs +98 -0
  393. package/tests/test-behavior-tree.mjs +444 -0
  394. package/tests/test-blueprint-graph.mjs +410 -0
  395. package/tests/test-blueprint.mjs +577 -0
  396. package/tests/test-client-mode.mjs +86 -0
  397. package/tests/test-console-command.mjs +56 -0
  398. package/tests/test-control-actor.mjs +425 -0
  399. package/tests/test-control-editor.mjs +112 -0
  400. package/tests/test-graphql.mjs +372 -0
  401. package/tests/test-input.mjs +349 -0
  402. package/tests/test-inspect.mjs +302 -0
  403. package/tests/test-landscape.mjs +316 -0
  404. package/tests/test-lighting.mjs +428 -0
  405. package/tests/test-manage-asset.mjs +438 -0
  406. package/tests/test-manage-level.mjs +89 -0
  407. package/tests/test-materials.mjs +356 -0
  408. package/tests/test-niagara.mjs +185 -0
  409. package/tests/test-no-inline-python.mjs +122 -0
  410. package/tests/test-performance.mjs +539 -0
  411. package/tests/test-plugin-handshake.mjs +82 -0
  412. package/tests/test-runner.mjs +933 -0
  413. package/tests/test-sequence.mjs +104 -0
  414. package/tests/test-system.mjs +96 -0
  415. package/tests/test-wasm.mjs +283 -0
  416. package/tests/test-world-partition.mjs +215 -0
  417. package/tsconfig.json +3 -3
  418. package/vitest.config.ts +35 -0
  419. package/wasm/Cargo.lock +363 -0
  420. package/wasm/Cargo.toml +42 -0
  421. package/wasm/LICENSE +21 -0
  422. package/wasm/README.md +253 -0
  423. package/wasm/src/dependency_resolver.rs +377 -0
  424. package/wasm/src/lib.rs +153 -0
  425. package/wasm/src/property_parser.rs +271 -0
  426. package/wasm/src/transform_math.rs +396 -0
  427. package/wasm/tests/integration.rs +109 -0
  428. package/.github/workflows/smithery-build.yml +0 -29
  429. package/dist/prompts/index.d.ts +0 -21
  430. package/dist/prompts/index.js +0 -217
  431. package/dist/tools/build_environment_advanced.d.ts +0 -65
  432. package/dist/tools/build_environment_advanced.js +0 -633
  433. package/dist/tools/rc.d.ts +0 -110
  434. package/dist/tools/rc.js +0 -437
  435. package/dist/tools/visual.d.ts +0 -40
  436. package/dist/tools/visual.js +0 -282
  437. package/dist/utils/http.d.ts +0 -6
  438. package/dist/utils/http.js +0 -151
  439. package/dist/utils/python-output.d.ts +0 -18
  440. package/dist/utils/python-output.js +0 -290
  441. package/dist/utils/python.d.ts +0 -2
  442. package/dist/utils/python.js +0 -4
  443. package/dist/utils/stdio-redirect.d.ts +0 -2
  444. package/dist/utils/stdio-redirect.js +0 -20
  445. package/docs/unreal-tool-test-cases.md +0 -574
  446. package/smithery.yaml +0 -29
  447. package/src/prompts/index.ts +0 -249
  448. package/src/tools/build_environment_advanced.ts +0 -732
  449. package/src/tools/rc.ts +0 -515
  450. package/src/tools/visual.ts +0 -281
  451. package/src/utils/http.ts +0 -187
  452. package/src/utils/python-output.ts +0 -351
  453. package/src/utils/python.ts +0 -3
  454. package/src/utils/stdio-redirect.ts +0 -18
@@ -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, '\\');