unreal-engine-mcp-server 0.4.7 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (438) hide show
  1. package/.env.example +26 -0
  2. package/.env.production +38 -7
  3. package/.eslintrc.json +0 -54
  4. package/.eslintrc.override.json +8 -0
  5. package/.github/ISSUE_TEMPLATE/bug_report.yml +94 -0
  6. package/.github/ISSUE_TEMPLATE/config.yml +8 -0
  7. package/.github/ISSUE_TEMPLATE/feature_request.yml +56 -0
  8. package/.github/copilot-instructions.md +478 -45
  9. package/.github/dependabot.yml +19 -0
  10. package/.github/labeler.yml +24 -0
  11. package/.github/labels.yml +70 -0
  12. package/.github/pull_request_template.md +42 -0
  13. package/.github/release-drafter.yml +148 -0
  14. package/.github/workflows/auto-merge.yml +38 -0
  15. package/.github/workflows/ci.yml +38 -0
  16. package/.github/workflows/dependency-review.yml +17 -0
  17. package/.github/workflows/gemini-issue-triage.yml +172 -0
  18. package/.github/workflows/greetings.yml +23 -0
  19. package/.github/workflows/labeler.yml +16 -0
  20. package/.github/workflows/links.yml +80 -0
  21. package/.github/workflows/pr-size-labeler.yml +137 -0
  22. package/.github/workflows/publish-mcp.yml +12 -7
  23. package/.github/workflows/release-drafter.yml +23 -0
  24. package/.github/workflows/release.yml +112 -0
  25. package/.github/workflows/semantic-pull-request.yml +35 -0
  26. package/.github/workflows/smoke-test.yml +36 -0
  27. package/.github/workflows/stale.yml +28 -0
  28. package/CHANGELOG.md +267 -31
  29. package/CONTRIBUTING.md +140 -0
  30. package/README.md +166 -71
  31. package/claude_desktop_config_example.json +7 -6
  32. package/dist/automation/bridge.d.ts +50 -0
  33. package/dist/automation/bridge.js +452 -0
  34. package/dist/automation/connection-manager.d.ts +23 -0
  35. package/dist/automation/connection-manager.js +107 -0
  36. package/dist/automation/handshake.d.ts +11 -0
  37. package/dist/automation/handshake.js +89 -0
  38. package/dist/automation/index.d.ts +3 -0
  39. package/dist/automation/index.js +3 -0
  40. package/dist/automation/message-handler.d.ts +12 -0
  41. package/dist/automation/message-handler.js +149 -0
  42. package/dist/automation/request-tracker.d.ts +25 -0
  43. package/dist/automation/request-tracker.js +98 -0
  44. package/dist/automation/types.d.ts +130 -0
  45. package/dist/automation/types.js +2 -0
  46. package/dist/cli.js +32 -5
  47. package/dist/config.d.ts +27 -0
  48. package/dist/config.js +60 -0
  49. package/dist/constants.d.ts +12 -0
  50. package/dist/constants.js +12 -0
  51. package/dist/graphql/resolvers.d.ts +268 -0
  52. package/dist/graphql/resolvers.js +743 -0
  53. package/dist/graphql/schema.d.ts +5 -0
  54. package/dist/graphql/schema.js +437 -0
  55. package/dist/graphql/server.d.ts +26 -0
  56. package/dist/graphql/server.js +115 -0
  57. package/dist/graphql/types.d.ts +7 -0
  58. package/dist/graphql/types.js +2 -0
  59. package/dist/handlers/resource-handlers.d.ts +20 -0
  60. package/dist/handlers/resource-handlers.js +180 -0
  61. package/dist/index.d.ts +31 -18
  62. package/dist/index.js +119 -619
  63. package/dist/prompts/index.js +4 -4
  64. package/dist/resources/actors.d.ts +17 -12
  65. package/dist/resources/actors.js +56 -76
  66. package/dist/resources/assets.d.ts +6 -14
  67. package/dist/resources/assets.js +115 -147
  68. package/dist/resources/levels.d.ts +13 -13
  69. package/dist/resources/levels.js +25 -34
  70. package/dist/server/resource-registry.d.ts +20 -0
  71. package/dist/server/resource-registry.js +37 -0
  72. package/dist/server/tool-registry.d.ts +23 -0
  73. package/dist/server/tool-registry.js +322 -0
  74. package/dist/server-setup.d.ts +21 -0
  75. package/dist/server-setup.js +111 -0
  76. package/dist/services/health-monitor.d.ts +34 -0
  77. package/dist/services/health-monitor.js +105 -0
  78. package/dist/services/metrics-server.d.ts +11 -0
  79. package/dist/services/metrics-server.js +105 -0
  80. package/dist/tools/actors.d.ts +147 -9
  81. package/dist/tools/actors.js +350 -311
  82. package/dist/tools/animation.d.ts +135 -4
  83. package/dist/tools/animation.js +510 -411
  84. package/dist/tools/assets.d.ts +117 -19
  85. package/dist/tools/assets.js +259 -284
  86. package/dist/tools/audio.d.ts +102 -42
  87. package/dist/tools/audio.js +272 -685
  88. package/dist/tools/base-tool.d.ts +17 -0
  89. package/dist/tools/base-tool.js +46 -0
  90. package/dist/tools/behavior-tree.d.ts +94 -0
  91. package/dist/tools/behavior-tree.js +39 -0
  92. package/dist/tools/blueprint/helpers.d.ts +29 -0
  93. package/dist/tools/blueprint/helpers.js +182 -0
  94. package/dist/tools/blueprint.d.ts +228 -118
  95. package/dist/tools/blueprint.js +685 -832
  96. package/dist/tools/consolidated-tool-definitions.d.ts +5462 -1781
  97. package/dist/tools/consolidated-tool-definitions.js +829 -496
  98. package/dist/tools/consolidated-tool-handlers.d.ts +2 -1
  99. package/dist/tools/consolidated-tool-handlers.js +211 -1026
  100. package/dist/tools/debug.d.ts +143 -85
  101. package/dist/tools/debug.js +234 -180
  102. package/dist/tools/dynamic-handler-registry.d.ts +11 -0
  103. package/dist/tools/dynamic-handler-registry.js +101 -0
  104. package/dist/tools/editor.d.ts +139 -18
  105. package/dist/tools/editor.js +239 -244
  106. package/dist/tools/engine.d.ts +10 -4
  107. package/dist/tools/engine.js +13 -5
  108. package/dist/tools/environment.d.ts +36 -0
  109. package/dist/tools/environment.js +267 -0
  110. package/dist/tools/foliage.d.ts +105 -14
  111. package/dist/tools/foliage.js +219 -331
  112. package/dist/tools/handlers/actor-handlers.d.ts +3 -0
  113. package/dist/tools/handlers/actor-handlers.js +232 -0
  114. package/dist/tools/handlers/animation-handlers.d.ts +3 -0
  115. package/dist/tools/handlers/animation-handlers.js +185 -0
  116. package/dist/tools/handlers/argument-helper.d.ts +16 -0
  117. package/dist/tools/handlers/argument-helper.js +80 -0
  118. package/dist/tools/handlers/asset-handlers.d.ts +3 -0
  119. package/dist/tools/handlers/asset-handlers.js +496 -0
  120. package/dist/tools/handlers/audio-handlers.d.ts +3 -0
  121. package/dist/tools/handlers/audio-handlers.js +166 -0
  122. package/dist/tools/handlers/blueprint-handlers.d.ts +4 -0
  123. package/dist/tools/handlers/blueprint-handlers.js +358 -0
  124. package/dist/tools/handlers/common-handlers.d.ts +14 -0
  125. package/dist/tools/handlers/common-handlers.js +56 -0
  126. package/dist/tools/handlers/editor-handlers.d.ts +3 -0
  127. package/dist/tools/handlers/editor-handlers.js +119 -0
  128. package/dist/tools/handlers/effect-handlers.d.ts +3 -0
  129. package/dist/tools/handlers/effect-handlers.js +171 -0
  130. package/dist/tools/handlers/environment-handlers.d.ts +3 -0
  131. package/dist/tools/handlers/environment-handlers.js +170 -0
  132. package/dist/tools/handlers/graph-handlers.d.ts +3 -0
  133. package/dist/tools/handlers/graph-handlers.js +90 -0
  134. package/dist/tools/handlers/input-handlers.d.ts +3 -0
  135. package/dist/tools/handlers/input-handlers.js +21 -0
  136. package/dist/tools/handlers/inspect-handlers.d.ts +3 -0
  137. package/dist/tools/handlers/inspect-handlers.js +383 -0
  138. package/dist/tools/handlers/level-handlers.d.ts +3 -0
  139. package/dist/tools/handlers/level-handlers.js +237 -0
  140. package/dist/tools/handlers/lighting-handlers.d.ts +3 -0
  141. package/dist/tools/handlers/lighting-handlers.js +144 -0
  142. package/dist/tools/handlers/performance-handlers.d.ts +3 -0
  143. package/dist/tools/handlers/performance-handlers.js +130 -0
  144. package/dist/tools/handlers/pipeline-handlers.d.ts +3 -0
  145. package/dist/tools/handlers/pipeline-handlers.js +110 -0
  146. package/dist/tools/handlers/sequence-handlers.d.ts +3 -0
  147. package/dist/tools/handlers/sequence-handlers.js +376 -0
  148. package/dist/tools/handlers/system-handlers.d.ts +4 -0
  149. package/dist/tools/handlers/system-handlers.js +506 -0
  150. package/dist/tools/input.d.ts +19 -0
  151. package/dist/tools/input.js +89 -0
  152. package/dist/tools/introspection.d.ts +103 -40
  153. package/dist/tools/introspection.js +425 -568
  154. package/dist/tools/landscape.d.ts +97 -36
  155. package/dist/tools/landscape.js +280 -409
  156. package/dist/tools/level.d.ts +130 -10
  157. package/dist/tools/level.js +639 -675
  158. package/dist/tools/lighting.d.ts +77 -38
  159. package/dist/tools/lighting.js +441 -943
  160. package/dist/tools/logs.d.ts +3 -3
  161. package/dist/tools/logs.js +5 -57
  162. package/dist/tools/materials.d.ts +91 -24
  163. package/dist/tools/materials.js +190 -118
  164. package/dist/tools/niagara.d.ts +149 -39
  165. package/dist/tools/niagara.js +232 -182
  166. package/dist/tools/performance.d.ts +27 -12
  167. package/dist/tools/performance.js +204 -122
  168. package/dist/tools/physics.d.ts +32 -77
  169. package/dist/tools/physics.js +171 -582
  170. package/dist/tools/property-dictionary.d.ts +13 -0
  171. package/dist/tools/property-dictionary.js +82 -0
  172. package/dist/tools/sequence.d.ts +73 -48
  173. package/dist/tools/sequence.js +196 -748
  174. package/dist/tools/tool-definition-utils.d.ts +59 -0
  175. package/dist/tools/tool-definition-utils.js +35 -0
  176. package/dist/tools/ui.d.ts +66 -34
  177. package/dist/tools/ui.js +134 -214
  178. package/dist/types/env.d.ts +0 -3
  179. package/dist/types/env.js +0 -7
  180. package/dist/types/tool-interfaces.d.ts +898 -0
  181. package/dist/types/tool-interfaces.js +2 -0
  182. package/dist/types/tool-types.d.ts +183 -19
  183. package/dist/types/tool-types.js +0 -4
  184. package/dist/unreal-bridge.d.ts +24 -131
  185. package/dist/unreal-bridge.js +364 -1506
  186. package/dist/utils/command-validator.d.ts +9 -0
  187. package/dist/utils/command-validator.js +67 -0
  188. package/dist/utils/elicitation.d.ts +1 -1
  189. package/dist/utils/elicitation.js +12 -15
  190. package/dist/utils/error-handler.d.ts +2 -51
  191. package/dist/utils/error-handler.js +11 -87
  192. package/dist/utils/ini-reader.d.ts +3 -0
  193. package/dist/utils/ini-reader.js +69 -0
  194. package/dist/utils/logger.js +9 -6
  195. package/dist/utils/normalize.d.ts +3 -0
  196. package/dist/utils/normalize.js +56 -0
  197. package/dist/utils/response-factory.d.ts +7 -0
  198. package/dist/utils/response-factory.js +33 -0
  199. package/dist/utils/response-validator.d.ts +3 -24
  200. package/dist/utils/response-validator.js +130 -81
  201. package/dist/utils/result-helpers.d.ts +4 -5
  202. package/dist/utils/result-helpers.js +15 -16
  203. package/dist/utils/safe-json.js +5 -11
  204. package/dist/utils/unreal-command-queue.d.ts +24 -0
  205. package/dist/utils/unreal-command-queue.js +120 -0
  206. package/dist/utils/validation.d.ts +0 -40
  207. package/dist/utils/validation.js +1 -78
  208. package/dist/wasm/index.d.ts +70 -0
  209. package/dist/wasm/index.js +535 -0
  210. package/docs/GraphQL-API.md +888 -0
  211. package/docs/Migration-Guide-v0.5.0.md +692 -0
  212. package/docs/Roadmap.md +53 -0
  213. package/docs/WebAssembly-Integration.md +628 -0
  214. package/docs/editor-plugin-extension.md +370 -0
  215. package/docs/handler-mapping.md +242 -0
  216. package/docs/native-automation-progress.md +128 -0
  217. package/docs/testing-guide.md +423 -0
  218. package/mcp-config-example.json +6 -6
  219. package/package.json +60 -27
  220. package/plugins/McpAutomationBridge/Config/FilterPlugin.ini +8 -0
  221. package/plugins/McpAutomationBridge/McpAutomationBridge.uplugin +64 -0
  222. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/McpAutomationBridge.Build.cs +189 -0
  223. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.cpp +22 -0
  224. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.h +30 -0
  225. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeHelpers.h +1983 -0
  226. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeModule.cpp +72 -0
  227. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSettings.cpp +46 -0
  228. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSubsystem.cpp +581 -0
  229. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AnimationHandlers.cpp +2394 -0
  230. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetQueryHandlers.cpp +300 -0
  231. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetWorkflowHandlers.cpp +2807 -0
  232. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AudioHandlers.cpp +1087 -0
  233. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BehaviorTreeHandlers.cpp +488 -0
  234. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.cpp +643 -0
  235. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.h +31 -0
  236. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintGraphHandlers.cpp +1184 -0
  237. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers.cpp +5652 -0
  238. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers_List.cpp +152 -0
  239. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ControlHandlers.cpp +2614 -0
  240. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_DebugHandlers.cpp +42 -0
  241. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EditorFunctionHandlers.cpp +1237 -0
  242. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EffectHandlers.cpp +1701 -0
  243. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EnvironmentHandlers.cpp +2145 -0
  244. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_FoliageHandlers.cpp +954 -0
  245. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InputHandlers.cpp +209 -0
  246. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InsightsHandlers.cpp +41 -0
  247. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LandscapeHandlers.cpp +1164 -0
  248. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LevelHandlers.cpp +762 -0
  249. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LightingHandlers.cpp +634 -0
  250. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LogHandlers.cpp +136 -0
  251. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MaterialGraphHandlers.cpp +494 -0
  252. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraGraphHandlers.cpp +278 -0
  253. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraHandlers.cpp +625 -0
  254. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PerformanceHandlers.cpp +401 -0
  255. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PipelineHandlers.cpp +67 -0
  256. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ProcessRequest.cpp +735 -0
  257. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PropertyHandlers.cpp +2634 -0
  258. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_RenderHandlers.cpp +189 -0
  259. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.cpp +917 -0
  260. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.h +39 -0
  261. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequenceHandlers.cpp +2670 -0
  262. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequencerHandlers.cpp +519 -0
  263. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_TestHandlers.cpp +38 -0
  264. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_UiHandlers.cpp +668 -0
  265. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_WorldPartitionHandlers.cpp +346 -0
  266. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.cpp +1330 -0
  267. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.h +149 -0
  268. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpConnectionManager.cpp +783 -0
  269. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSettings.h +115 -0
  270. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSubsystem.h +796 -0
  271. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpConnectionManager.h +117 -0
  272. package/scripts/check-unreal-connection.mjs +19 -0
  273. package/scripts/clean-tmp.js +23 -0
  274. package/scripts/patch-wasm.js +26 -0
  275. package/scripts/run-all-tests.mjs +131 -0
  276. package/scripts/smoke-test.ts +94 -0
  277. package/scripts/sync-mcp-plugin.js +143 -0
  278. package/scripts/test-no-plugin-alternates.mjs +113 -0
  279. package/scripts/validate-server.js +46 -0
  280. package/scripts/verify-automation-bridge.js +200 -0
  281. package/server.json +57 -21
  282. package/src/automation/bridge.ts +558 -0
  283. package/src/automation/connection-manager.ts +130 -0
  284. package/src/automation/handshake.ts +99 -0
  285. package/src/automation/index.ts +2 -0
  286. package/src/automation/message-handler.ts +167 -0
  287. package/src/automation/request-tracker.ts +123 -0
  288. package/src/automation/types.ts +107 -0
  289. package/src/cli.ts +33 -6
  290. package/src/config.ts +73 -0
  291. package/src/constants.ts +12 -0
  292. package/src/graphql/resolvers.ts +1010 -0
  293. package/src/graphql/schema.ts +452 -0
  294. package/src/graphql/server.ts +154 -0
  295. package/src/graphql/types.ts +7 -0
  296. package/src/handlers/resource-handlers.ts +186 -0
  297. package/src/index.ts +152 -663
  298. package/src/prompts/index.ts +4 -4
  299. package/src/resources/actors.ts +58 -76
  300. package/src/resources/assets.ts +147 -134
  301. package/src/resources/levels.ts +28 -33
  302. package/src/server/resource-registry.ts +47 -0
  303. package/src/server/tool-registry.ts +354 -0
  304. package/src/server-setup.ts +148 -0
  305. package/src/services/health-monitor.ts +132 -0
  306. package/src/services/metrics-server.ts +142 -0
  307. package/src/tools/actors.ts +417 -322
  308. package/src/tools/animation.ts +671 -461
  309. package/src/tools/assets.ts +353 -289
  310. package/src/tools/audio.ts +323 -766
  311. package/src/tools/base-tool.ts +52 -0
  312. package/src/tools/behavior-tree.ts +45 -0
  313. package/src/tools/blueprint/helpers.ts +189 -0
  314. package/src/tools/blueprint.ts +787 -965
  315. package/src/tools/consolidated-tool-definitions.ts +993 -515
  316. package/src/tools/consolidated-tool-handlers.ts +272 -1139
  317. package/src/tools/debug.ts +292 -187
  318. package/src/tools/dynamic-handler-registry.ts +151 -0
  319. package/src/tools/editor.ts +309 -246
  320. package/src/tools/engine.ts +14 -3
  321. package/src/tools/environment.ts +287 -0
  322. package/src/tools/foliage.ts +314 -379
  323. package/src/tools/handlers/actor-handlers.ts +271 -0
  324. package/src/tools/handlers/animation-handlers.ts +237 -0
  325. package/src/tools/handlers/argument-helper.ts +142 -0
  326. package/src/tools/handlers/asset-handlers.ts +532 -0
  327. package/src/tools/handlers/audio-handlers.ts +194 -0
  328. package/src/tools/handlers/blueprint-handlers.ts +380 -0
  329. package/src/tools/handlers/common-handlers.ts +87 -0
  330. package/src/tools/handlers/editor-handlers.ts +123 -0
  331. package/src/tools/handlers/effect-handlers.ts +220 -0
  332. package/src/tools/handlers/environment-handlers.ts +183 -0
  333. package/src/tools/handlers/graph-handlers.ts +116 -0
  334. package/src/tools/handlers/input-handlers.ts +28 -0
  335. package/src/tools/handlers/inspect-handlers.ts +450 -0
  336. package/src/tools/handlers/level-handlers.ts +252 -0
  337. package/src/tools/handlers/lighting-handlers.ts +147 -0
  338. package/src/tools/handlers/performance-handlers.ts +132 -0
  339. package/src/tools/handlers/pipeline-handlers.ts +127 -0
  340. package/src/tools/handlers/sequence-handlers.ts +415 -0
  341. package/src/tools/handlers/system-handlers.ts +564 -0
  342. package/src/tools/input.ts +101 -0
  343. package/src/tools/introspection.ts +493 -584
  344. package/src/tools/landscape.ts +394 -489
  345. package/src/tools/level.ts +752 -694
  346. package/src/tools/lighting.ts +583 -984
  347. package/src/tools/logs.ts +9 -57
  348. package/src/tools/materials.ts +231 -121
  349. package/src/tools/niagara.ts +293 -168
  350. package/src/tools/performance.ts +320 -168
  351. package/src/tools/physics.ts +268 -613
  352. package/src/tools/property-dictionary.ts +98 -0
  353. package/src/tools/sequence.ts +255 -815
  354. package/src/tools/tool-definition-utils.ts +35 -0
  355. package/src/tools/ui.ts +207 -283
  356. package/src/types/env.ts +0 -10
  357. package/src/types/tool-interfaces.ts +250 -0
  358. package/src/types/tool-types.ts +243 -21
  359. package/src/unreal-bridge.ts +460 -1550
  360. package/src/utils/command-validator.ts +75 -0
  361. package/src/utils/elicitation.ts +10 -7
  362. package/src/utils/error-handler.ts +14 -90
  363. package/src/utils/ini-reader.ts +86 -0
  364. package/src/utils/logger.ts +8 -3
  365. package/src/utils/normalize.ts +60 -0
  366. package/src/utils/response-factory.ts +39 -0
  367. package/src/utils/response-validator.ts +176 -56
  368. package/src/utils/result-helpers.ts +21 -19
  369. package/src/utils/safe-json.ts +14 -11
  370. package/src/utils/unreal-command-queue.ts +152 -0
  371. package/src/utils/validation.ts +4 -1
  372. package/src/wasm/index.ts +838 -0
  373. package/test-server.mjs +100 -0
  374. package/tests/run-unreal-tool-tests.mjs +242 -14
  375. package/tests/test-animation.mjs +44 -0
  376. package/tests/test-asset-advanced.mjs +82 -0
  377. package/tests/test-asset-errors.mjs +35 -0
  378. package/tests/test-audio.mjs +219 -0
  379. package/tests/test-automation-timeouts.mjs +98 -0
  380. package/tests/test-behavior-tree.mjs +261 -0
  381. package/tests/test-blueprint-events.mjs +35 -0
  382. package/tests/test-blueprint-graph.mjs +79 -0
  383. package/tests/test-blueprint.mjs +577 -0
  384. package/tests/test-client-mode.mjs +86 -0
  385. package/tests/test-console-command.mjs +56 -0
  386. package/tests/test-control-actor.mjs +425 -0
  387. package/tests/test-control-editor.mjs +80 -0
  388. package/tests/test-extra-tools.mjs +38 -0
  389. package/tests/test-graphql.mjs +322 -0
  390. package/tests/test-inspect.mjs +72 -0
  391. package/tests/test-landscape.mjs +60 -0
  392. package/tests/test-manage-asset.mjs +438 -0
  393. package/tests/test-manage-level.mjs +70 -0
  394. package/tests/test-materials.mjs +356 -0
  395. package/tests/test-niagara.mjs +185 -0
  396. package/tests/test-no-inline-python.mjs +122 -0
  397. package/tests/test-plugin-handshake.mjs +82 -0
  398. package/tests/test-render.mjs +33 -0
  399. package/tests/test-runner.mjs +933 -0
  400. package/tests/test-search-assets.mjs +66 -0
  401. package/tests/test-sequence.mjs +68 -0
  402. package/tests/test-system.mjs +57 -0
  403. package/tests/test-wasm.mjs +193 -0
  404. package/tests/test-world-partition.mjs +215 -0
  405. package/tsconfig.json +3 -3
  406. package/wasm/Cargo.lock +363 -0
  407. package/wasm/Cargo.toml +42 -0
  408. package/wasm/LICENSE +21 -0
  409. package/wasm/README.md +253 -0
  410. package/wasm/src/dependency_resolver.rs +377 -0
  411. package/wasm/src/lib.rs +153 -0
  412. package/wasm/src/property_parser.rs +271 -0
  413. package/wasm/src/transform_math.rs +396 -0
  414. package/wasm/tests/integration.rs +109 -0
  415. package/.github/workflows/smithery-build.yml +0 -29
  416. package/dist/tools/build_environment_advanced.d.ts +0 -65
  417. package/dist/tools/build_environment_advanced.js +0 -633
  418. package/dist/tools/rc.d.ts +0 -110
  419. package/dist/tools/rc.js +0 -437
  420. package/dist/tools/visual.d.ts +0 -40
  421. package/dist/tools/visual.js +0 -282
  422. package/dist/utils/http.d.ts +0 -6
  423. package/dist/utils/http.js +0 -151
  424. package/dist/utils/python-output.d.ts +0 -18
  425. package/dist/utils/python-output.js +0 -290
  426. package/dist/utils/python.d.ts +0 -2
  427. package/dist/utils/python.js +0 -4
  428. package/dist/utils/stdio-redirect.d.ts +0 -2
  429. package/dist/utils/stdio-redirect.js +0 -20
  430. package/docs/unreal-tool-test-cases.md +0 -574
  431. package/smithery.yaml +0 -29
  432. package/src/tools/build_environment_advanced.ts +0 -732
  433. package/src/tools/rc.ts +0 -515
  434. package/src/tools/visual.ts +0 -281
  435. package/src/utils/http.ts +0 -187
  436. package/src/utils/python-output.ts +0 -351
  437. package/src/utils/python.ts +0 -3
  438. package/src/utils/stdio-redirect.ts +0 -18
package/src/index.ts CHANGED
@@ -1,156 +1,120 @@
1
- import 'dotenv/config';
1
+
2
2
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
3
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
4
  import { Logger } from './utils/logger.js';
5
5
  import { UnrealBridge } from './unreal-bridge.js';
6
- import { AssetResources } from './resources/assets.js';
7
- import { ActorResources } from './resources/actors.js';
8
- import { LevelResources } from './resources/levels.js';
9
- import { ActorTools } from './tools/actors.js';
10
- import { AssetTools } from './tools/assets.js';
11
- import { EditorTools } from './tools/editor.js';
12
- import { MaterialTools } from './tools/materials.js';
13
- import { AnimationTools } from './tools/animation.js';
14
- import { PhysicsTools } from './tools/physics.js';
15
- import { NiagaraTools } from './tools/niagara.js';
16
- import { BlueprintTools } from './tools/blueprint.js';
17
- import { LevelTools } from './tools/level.js';
18
- import { LightingTools } from './tools/lighting.js';
19
- import { LandscapeTools } from './tools/landscape.js';
20
- import { BuildEnvironmentAdvanced } from './tools/build_environment_advanced.js';
21
- import { FoliageTools } from './tools/foliage.js';
22
- import { DebugVisualizationTools } from './tools/debug.js';
23
- import { PerformanceTools } from './tools/performance.js';
24
- import { AudioTools } from './tools/audio.js';
25
- import { UITools } from './tools/ui.js';
26
- import { RcTools } from './tools/rc.js';
27
- import { SequenceTools } from './tools/sequence.js';
28
- import { IntrospectionTools } from './tools/introspection.js';
29
- import { VisualTools } from './tools/visual.js';
30
- import { EngineTools } from './tools/engine.js';
31
- import { LogTools } from './tools/logs.js';
32
- import { consolidatedToolDefinitions } from './tools/consolidated-tool-definitions.js';
33
- import { handleConsolidatedToolCall } from './tools/consolidated-tool-handlers.js';
34
- import { prompts } from './prompts/index.js';
35
- import {
36
- CallToolRequestSchema,
37
- ListToolsRequestSchema,
38
- ListResourcesRequestSchema,
39
- ReadResourceRequestSchema,
40
- ListPromptsRequestSchema,
41
- GetPromptRequestSchema
42
- } from '@modelcontextprotocol/sdk/types.js';
6
+ import { AutomationBridge } from './automation/index.js';
7
+ import { createRequire } from 'node:module';
43
8
  import { responseValidator } from './utils/response-validator.js';
44
9
  import { z } from 'zod';
45
- import { routeStdoutLogsToStderr } from './utils/stdio-redirect.js';
46
- import { createElicitationHelper } from './utils/elicitation.js';
47
- import { cleanObject } from './utils/safe-json.js';
48
- import { ErrorHandler } from './utils/error-handler.js';
10
+ import { initializeWASM } from './wasm/index.js';
11
+ import { consolidatedToolDefinitions } from './tools/consolidated-tool-definitions.js';
12
+ import { HealthMonitor } from './services/health-monitor.js';
13
+ import { ServerSetup } from './server-setup.js';
14
+ import { startMetricsServer } from './services/metrics-server.js';
15
+ import { config } from './config.js';
49
16
 
50
- const log = new Logger('UE-MCP');
17
+ const require = createRequire(import.meta.url);
18
+ const packageInfo: { name?: string; version?: string } = (() => {
19
+ try {
20
+ return require('../package.json');
21
+ } catch (error) {
22
+ const log = new Logger('UE-MCP');
23
+ log.debug('Unable to read package.json for server metadata', error);
24
+ return {};
25
+ }
26
+ })();
27
+ const DEFAULT_SERVER_NAME = typeof packageInfo.name === 'string' && packageInfo.name.trim().length > 0
28
+ ? packageInfo.name
29
+ : 'unreal-engine-mcp';
30
+ const DEFAULT_SERVER_VERSION = typeof packageInfo.version === 'string' && packageInfo.version.trim().length > 0
31
+ ? packageInfo.version
32
+ : '0.0.0';
33
+
34
+ function routeStdoutLogsToStderr(): void {
35
+ if (!config.MCP_ROUTE_STDOUT_LOGS) {
36
+ return;
37
+ }
51
38
 
52
- // Ensure stdout remains JSON-only for MCP by routing logs to stderr unless opted out.
53
- routeStdoutLogsToStderr();
39
+ const writeToStderr = (...args: unknown[]): void => {
40
+ const line = args
41
+ .map((a) => (typeof a === 'string' ? a : JSON.stringify(a)))
42
+ .join(' ');
43
+ process.stderr.write(`${line}\n`);
44
+ };
54
45
 
55
- // Performance metrics
56
- interface PerformanceMetrics {
57
- totalRequests: number;
58
- successfulRequests: number;
59
- failedRequests: number;
60
- averageResponseTime: number;
61
- responseTimes: number[];
62
- connectionStatus: 'connected' | 'disconnected' | 'error';
63
- lastHealthCheck: Date;
64
- uptime: number;
65
- recentErrors: Array<{ time: string; scope: string; type: string; message: string; retriable: boolean }>;
46
+ console.log = (...args: unknown[]): void => { writeToStderr(...args); };
47
+ console.info = (...args: unknown[]): void => { writeToStderr(...args); };
48
+ if (typeof console.debug === 'function') {
49
+ console.debug = (...args: unknown[]): void => { writeToStderr(...args); };
50
+ }
66
51
  }
67
52
 
68
- const metrics: PerformanceMetrics = {
69
- totalRequests: 0,
70
- successfulRequests: 0,
71
- failedRequests: 0,
72
- averageResponseTime: 0,
73
- responseTimes: [],
74
- connectionStatus: 'disconnected',
75
- lastHealthCheck: new Date(),
76
- uptime: Date.now(),
77
- recentErrors: []
78
- };
53
+ const log = new Logger('UE-MCP');
79
54
 
80
- // Health check timer and last success tracking (stop pings after inactivity)
81
- let healthCheckTimer: NodeJS.Timeout | undefined;
82
- let lastHealthSuccessAt = 0;
55
+ // Ensure stdout remains JSON-only for MCP by routing logs to stderr unless opted out.
56
+ routeStdoutLogsToStderr();
83
57
 
84
58
  // Configuration
85
59
  const CONFIG = {
86
- // Tooling: use consolidated tools only (13 tools)
87
- // Connection retry settings
88
60
  MAX_RETRY_ATTEMPTS: 3,
89
61
  RETRY_DELAY_MS: 2000,
90
- // Server info
91
- SERVER_NAME: 'unreal-engine-mcp',
92
- SERVER_VERSION: '0.4.7',
93
- // Monitoring
94
- HEALTH_CHECK_INTERVAL_MS: 30000 // 30 seconds
62
+ SERVER_NAME: DEFAULT_SERVER_NAME,
63
+ SERVER_VERSION: DEFAULT_SERVER_VERSION,
64
+ AUTOMATION_HEARTBEAT_MS: 15000,
65
+ HEALTH_CHECK_INTERVAL_MS: 30000
95
66
  };
96
67
 
97
- // Helper function to track performance
98
- function trackPerformance(startTime: number, success: boolean) {
99
- const responseTime = Date.now() - startTime;
100
- metrics.totalRequests++;
101
- if (success) {
102
- metrics.successfulRequests++;
103
- } else {
104
- metrics.failedRequests++;
105
- }
106
-
107
- // Keep last 100 response times for average calculation
108
- metrics.responseTimes.push(responseTime);
109
- if (metrics.responseTimes.length > 100) {
110
- metrics.responseTimes.shift();
111
- }
112
-
113
- // Calculate average
114
- metrics.averageResponseTime = metrics.responseTimes.reduce((a, b) => a + b, 0) / metrics.responseTimes.length;
115
- }
116
-
117
- // Health check function
118
- async function performHealthCheck(bridge: UnrealBridge): Promise<boolean> {
119
- // If not connected, do not attempt any ping (stay quiet)
120
- if (!bridge.isConnected) {
121
- return false;
122
- }
123
- try {
124
- // Use a safe echo command that doesn't affect any settings
125
- await bridge.executeConsoleCommand('echo MCP Server Health Check');
126
- metrics.connectionStatus = 'connected';
127
- metrics.lastHealthCheck = new Date();
128
- lastHealthSuccessAt = Date.now();
129
- return true;
130
- } catch (err1) {
131
- // Fallback: minimal Python ping (if Python plugin is enabled)
132
- try {
133
- await bridge.executePython("import sys; sys.stdout.write('OK')");
134
- metrics.connectionStatus = 'connected';
135
- metrics.lastHealthCheck = new Date();
136
- lastHealthSuccessAt = Date.now();
137
- return true;
138
- } catch (err2) {
139
- metrics.connectionStatus = 'error';
140
- metrics.lastHealthCheck = new Date();
141
- // Avoid noisy warnings when engine may be shutting down; log at debug
142
- log.debug('Health check failed (console and python):', err1, err2);
143
- return false;
144
- }
145
- }
146
- }
147
-
148
68
  export function createServer() {
149
69
  const bridge = new UnrealBridge();
150
- // Disable auto-reconnect loops; connect only on-demand
151
- bridge.setAutoReconnectEnabled(false);
152
-
153
- // Initialize response validation with schemas
70
+ const healthMonitor = new HealthMonitor(log);
71
+
72
+ const automationBridge = new AutomationBridge({
73
+ serverName: CONFIG.SERVER_NAME,
74
+ serverVersion: CONFIG.SERVER_VERSION,
75
+ heartbeatIntervalMs: CONFIG.AUTOMATION_HEARTBEAT_MS,
76
+ clientMode: config.MCP_AUTOMATION_CLIENT_MODE
77
+ });
78
+ bridge.setAutomationBridge(automationBridge);
79
+
80
+
81
+ automationBridge.on('connected', ({ metadata, port, protocol }) => {
82
+ log.info(
83
+ `Automation bridge connected (port=${port}, protocol=${protocol ?? 'none'})`,
84
+ metadata
85
+ );
86
+ });
87
+
88
+ automationBridge.on('disconnected', ({ code, reason, port, protocol }) => {
89
+ log.info(
90
+ `Automation bridge disconnected (code=${code}, reason=${reason || 'n/a'}, port=${port}, protocol=${protocol ?? 'none'})`
91
+ );
92
+ });
93
+
94
+ automationBridge.on('handshakeFailed', ({ reason, port }) => {
95
+ log.warn(`Automation bridge handshake failed (port=${port}): ${reason}`);
96
+ });
97
+
98
+ automationBridge.on('message', (message) => {
99
+ log.debug('Automation bridge inbound message', message);
100
+ });
101
+
102
+ automationBridge.on('error', (error) => {
103
+ log.error('Automation bridge error', error);
104
+ });
105
+
106
+ // Optionally expose Prometheus-style metrics via /metrics
107
+ startMetricsServer({ healthMonitor, automationBridge, logger: log });
108
+
109
+ // Initialize WebAssembly module for high-performance operations (5-8x faster)
110
+ log.debug('Initializing WebAssembly integration...');
111
+ initializeWASM().then(() => {
112
+ log.info('✅ WebAssembly integration initialized (JSON parsing and math operations)');
113
+ }).catch((error) => {
114
+ log.warn('⚠️ WebAssembly initialization failed, using TypeScript fallbacks:', error);
115
+ });
116
+
117
+ // Initialize response validation with schemas
154
118
  log.debug('Initializing response validation...');
155
119
  const toolDefs = consolidatedToolDefinitions;
156
120
  toolDefs.forEach((tool: any) => {
@@ -158,87 +122,10 @@ export function createServer() {
158
122
  responseValidator.registerSchema(tool.name, tool.outputSchema);
159
123
  }
160
124
  });
161
- // Summary at debug level to avoid repeated noisy blocks in some shells
162
125
  log.debug(`Registered ${responseValidator.getStats().totalSchemas} output schemas for validation`);
163
126
 
164
- // Do NOT connect to Unreal at startup; connect on demand
165
127
  log.debug('Server starting without connecting to Unreal Engine');
166
- metrics.connectionStatus = 'disconnected';
167
-
168
- // Health checks manager (only active when connected)
169
- const startHealthChecks = () => {
170
- if (healthCheckTimer) return;
171
- lastHealthSuccessAt = Date.now();
172
- healthCheckTimer = setInterval(async () => {
173
- // Only attempt health pings while connected; stay silent otherwise
174
- if (!bridge.isConnected) {
175
- // Optionally pause fully after 5 minutes of no success
176
- const FIVE_MIN_MS = 5 * 60 * 1000;
177
- if (!lastHealthSuccessAt || Date.now() - lastHealthSuccessAt > FIVE_MIN_MS) {
178
- if (healthCheckTimer) {
179
- clearInterval(healthCheckTimer);
180
- healthCheckTimer = undefined;
181
- }
182
- log.info('Health checks paused after 5 minutes without a successful response');
183
- }
184
- return;
185
- }
186
-
187
- await performHealthCheck(bridge);
188
- // Stop sending echoes if we haven't had a successful response in > 5 minutes
189
- const FIVE_MIN_MS = 5 * 60 * 1000;
190
- if (!lastHealthSuccessAt || Date.now() - lastHealthSuccessAt > FIVE_MIN_MS) {
191
- if (healthCheckTimer) {
192
- clearInterval(healthCheckTimer);
193
- healthCheckTimer = undefined;
194
- log.info('Health checks paused after 5 minutes without a successful response');
195
- }
196
- }
197
- }, CONFIG.HEALTH_CHECK_INTERVAL_MS);
198
- };
199
-
200
- // On-demand connection helper
201
- const ensureConnectedOnDemand = async (): Promise<boolean> => {
202
- if (bridge.isConnected) return true;
203
- const ok = await bridge.tryConnect(3, 5000, 1000);
204
- if (ok) {
205
- metrics.connectionStatus = 'connected';
206
- startHealthChecks();
207
- } else {
208
- metrics.connectionStatus = 'disconnected';
209
- }
210
- return ok;
211
- };
212
-
213
- // Resources
214
- const assetResources = new AssetResources(bridge);
215
- const actorResources = new ActorResources(bridge);
216
- const levelResources = new LevelResources(bridge);
217
-
218
- // Tools
219
- const actorTools = new ActorTools(bridge);
220
- const assetTools = new AssetTools(bridge);
221
- const editorTools = new EditorTools(bridge);
222
- const materialTools = new MaterialTools(bridge);
223
- const animationTools = new AnimationTools(bridge);
224
- const physicsTools = new PhysicsTools(bridge);
225
- const niagaraTools = new NiagaraTools(bridge);
226
- const blueprintTools = new BlueprintTools(bridge);
227
- const levelTools = new LevelTools(bridge);
228
- const lightingTools = new LightingTools(bridge);
229
- const landscapeTools = new LandscapeTools(bridge);
230
- const foliageTools = new FoliageTools(bridge);
231
- const buildEnvAdvanced = new BuildEnvironmentAdvanced(bridge);
232
- const debugTools = new DebugVisualizationTools(bridge);
233
- const performanceTools = new PerformanceTools(bridge);
234
- const audioTools = new AudioTools(bridge);
235
- const uiTools = new UITools(bridge);
236
- const rcTools = new RcTools(bridge);
237
- const sequenceTools = new SequenceTools(bridge);
238
- const introspectionTools = new IntrospectionTools(bridge);
239
- const visualTools = new VisualTools(bridge);
240
- const engineTools = new EngineTools(bridge);
241
- const logTools = new LogTools(bridge);
128
+ healthMonitor.metrics.connectionStatus = 'disconnected';
242
129
 
243
130
  const server = new Server(
244
131
  {
@@ -247,472 +134,34 @@ export function createServer() {
247
134
  },
248
135
  {
249
136
  capabilities: {
250
- resources: {},
251
137
  tools: {},
252
- prompts: {
253
- listChanged: false
254
- },
255
- logging: {}
138
+ resources: {},
139
+ prompts: {}
256
140
  }
257
141
  }
258
142
  );
259
143
 
260
- // Optional elicitation helper – used only if client supports it.
261
- const elicitation = createElicitationHelper(server as any, log);
262
- const defaultElicitationTimeoutMs = elicitation.getDefaultTimeoutMs();
263
-
264
- const createNotConnectedResponse = (toolName: string) => {
265
- const payload = {
266
- success: false,
267
- error: 'UE_NOT_CONNECTED',
268
- message: 'Unreal Engine is not connected (after 3 attempts). Please open UE and try again.',
269
- retriable: false,
270
- scope: `tool-call/${toolName}`
271
- } as const;
272
-
273
- return responseValidator.wrapResponse(toolName, {
274
- ...payload,
275
- content: [
276
- {
277
- type: 'text',
278
- text: JSON.stringify(payload, null, 2)
279
- }
280
- ]
281
- });
282
- };
283
-
284
- // Handle resource listing
285
- server.setRequestHandler(ListResourcesRequestSchema, async () => {
286
- return {
287
- resources: [
288
- {
289
- uri: 'ue://assets',
290
- name: 'Project Assets',
291
- description: 'List all assets in the project',
292
- mimeType: 'application/json'
293
- },
294
- {
295
- uri: 'ue://actors',
296
- name: 'Level Actors',
297
- description: 'List all actors in the current level',
298
- mimeType: 'application/json'
299
- },
300
- {
301
- uri: 'ue://level',
302
- name: 'Current Level',
303
- description: 'Information about the current level',
304
- mimeType: 'application/json'
305
- },
306
- {
307
- uri: 'ue://exposed',
308
- name: 'Remote Control Exposed',
309
- description: 'List all exposed properties via Remote Control',
310
- mimeType: 'application/json'
311
- },
312
- {
313
- uri: 'ue://health',
314
- name: 'Health Status',
315
- description: 'Server health and performance metrics',
316
- mimeType: 'application/json'
317
- },
318
- {
319
- uri: 'ue://version',
320
- name: 'Engine Version',
321
- description: 'Unreal Engine version and compatibility info',
322
- mimeType: 'application/json'
323
- }
324
- ]
325
- };
326
- });
144
+ // Setup server using helper class
145
+ const serverSetup = new ServerSetup(server, bridge, automationBridge, log, healthMonitor);
146
+ serverSetup.setup(); // Register tools, resources, and prompts
327
147
 
328
- // Handle resource reading
329
- server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
330
- const uri = request.params.uri;
331
-
332
- if (uri === 'ue://assets') {
333
- const ok = await ensureConnectedOnDemand();
334
- if (!ok) {
335
- return { contents: [{ uri, mimeType: 'text/plain', text: 'Unreal Engine not connected (after 3 attempts).' }] };
336
- }
337
- const list = await assetResources.list('/Game', true);
338
- return {
339
- contents: [{
340
- uri,
341
- mimeType: 'application/json',
342
- text: JSON.stringify(list, null, 2)
343
- }]
344
- };
345
- }
346
-
347
- if (uri === 'ue://actors') {
348
- const ok = await ensureConnectedOnDemand();
349
- if (!ok) {
350
- return { contents: [{ uri, mimeType: 'text/plain', text: 'Unreal Engine not connected (after 3 attempts).' }] };
351
- }
352
- const list = await actorResources.listActors();
353
- return {
354
- contents: [{
355
- uri,
356
- mimeType: 'application/json',
357
- text: JSON.stringify(list, null, 2)
358
- }]
359
- };
360
- }
361
-
362
- if (uri === 'ue://level') {
363
- const ok = await ensureConnectedOnDemand();
364
- if (!ok) {
365
- return { contents: [{ uri, mimeType: 'text/plain', text: 'Unreal Engine not connected (after 3 attempts).' }] };
366
- }
367
- const level = await levelResources.getCurrentLevel();
368
- return {
369
- contents: [{
370
- uri,
371
- mimeType: 'application/json',
372
- text: JSON.stringify(level, null, 2)
373
- }]
374
- };
375
- }
376
-
377
- if (uri === 'ue://exposed') {
378
- const ok = await ensureConnectedOnDemand();
379
- if (!ok) {
380
- return { contents: [{ uri, mimeType: 'text/plain', text: 'Unreal Engine not connected (after 3 attempts).' }] };
381
- }
382
- try {
383
- const exposed = await bridge.getExposed();
384
- return {
385
- contents: [{
386
- uri,
387
- mimeType: 'application/json',
388
- text: JSON.stringify(exposed, null, 2)
389
- }]
390
- };
391
- } catch {
392
- return {
393
- contents: [{
394
- uri,
395
- mimeType: 'text/plain',
396
- text: 'Failed to get exposed properties. Ensure Remote Control is configured.'
397
- }]
398
- };
399
- }
400
- }
401
-
402
- if (uri === 'ue://health') {
403
- const uptimeMs = Date.now() - metrics.uptime;
404
- // Query engine version and feature flags only when connected
405
- let versionInfo: any = {};
406
- let featureFlags: any = {};
407
- if (bridge.isConnected) {
408
- try { versionInfo = await bridge.getEngineVersion(); } catch {}
409
- try { featureFlags = await bridge.getFeatureFlags(); } catch {}
410
- }
411
- const health = {
412
- status: metrics.connectionStatus,
413
- uptime: Math.floor(uptimeMs / 1000),
414
- performance: {
415
- totalRequests: metrics.totalRequests,
416
- successfulRequests: metrics.successfulRequests,
417
- failedRequests: metrics.failedRequests,
418
- successRate: metrics.totalRequests > 0 ?
419
- (metrics.successfulRequests / metrics.totalRequests * 100).toFixed(2) + '%' : 'N/A',
420
- averageResponseTime: Math.round(metrics.averageResponseTime) + 'ms'
421
- },
422
- lastHealthCheck: metrics.lastHealthCheck.toISOString(),
423
- unrealConnection: {
424
- status: bridge.isConnected ? 'connected' : 'disconnected',
425
- host: process.env.UE_HOST || 'localhost',
426
- httpPort: process.env.UE_RC_HTTP_PORT || 30010,
427
- wsPort: process.env.UE_RC_WS_PORT || 30020,
428
- engineVersion: versionInfo,
429
- features: {
430
- pythonEnabled: featureFlags.pythonEnabled === true,
431
- subsystems: featureFlags.subsystems || {},
432
- rcHttpReachable: bridge.isConnected
433
- }
434
- },
435
- recentErrors: metrics.recentErrors.slice(-5)
436
- };
437
-
438
- return {
439
- contents: [{
440
- uri,
441
- mimeType: 'application/json',
442
- text: JSON.stringify(health, null, 2)
443
- }]
444
- };
445
- }
446
-
447
- if (uri === 'ue://version') {
448
- const ok = await ensureConnectedOnDemand();
449
- if (!ok) {
450
- return { contents: [{ uri, mimeType: 'text/plain', text: 'Unreal Engine not connected (after 3 attempts).' }] };
451
- }
452
- const info = await bridge.getEngineVersion();
453
- return {
454
- contents: [{
455
- uri,
456
- mimeType: 'application/json',
457
- text: JSON.stringify(info, null, 2)
458
- }]
459
- };
460
- }
461
-
462
- throw new Error(`Unknown resource: ${uri}`);
463
- });
464
-
465
- // Handle tool listing - consolidated tools only
466
- server.setRequestHandler(ListToolsRequestSchema, async () => {
467
- log.info('Serving consolidated tools');
468
- return {
469
- tools: consolidatedToolDefinitions
470
- };
471
- });
472
-
473
- // Handle tool calls - consolidated tools only (13)
474
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
475
- const { name } = request.params;
476
- let args: any = request.params.arguments || {};
477
- const startTime = Date.now();
478
-
479
- let requiresEngine = true;
480
- try {
481
- const n = String(name);
482
- if (n === 'system_control') {
483
- const action = String((args || {}).action || '').trim();
484
- if (action === 'read_log') {
485
- requiresEngine = false;
486
- }
487
- }
488
- } catch {}
489
- if (requiresEngine) {
490
- const connected = await ensureConnectedOnDemand();
491
- if (!connected) {
492
- trackPerformance(startTime, false);
493
- return createNotConnectedResponse(name);
494
- }
495
- }
496
-
497
- // Create tools object for handler
498
- const tools = {
499
- actorTools,
500
- assetTools,
501
- materialTools,
502
- editorTools,
503
- animationTools,
504
- physicsTools,
505
- niagaraTools,
506
- blueprintTools,
507
- levelTools,
508
- lightingTools,
509
- landscapeTools,
510
- foliageTools,
511
- buildEnvAdvanced,
512
- debugTools,
513
- performanceTools,
514
- audioTools,
515
- uiTools,
516
- rcTools,
517
- sequenceTools,
518
- introspectionTools,
519
- visualTools,
520
- engineTools,
521
- logTools,
522
- // Elicitation (client-optional)
523
- elicit: elicitation.elicit,
524
- supportsElicitation: elicitation.supports,
525
- elicitationTimeoutMs: defaultElicitationTimeoutMs,
526
- // Resources for listing and info
527
- assetResources,
528
- actorResources,
529
- levelResources,
530
- bridge
531
- };
532
-
533
- // Execute consolidated tool handler
534
- try {
535
- log.debug(`Executing tool: ${name}`);
536
-
537
- // Opportunistic generic elicitation for missing primitive required fields
538
- try {
539
- const toolDef: any = (consolidatedToolDefinitions as any[]).find(t => t.name === name);
540
- const inputSchema: any = toolDef?.inputSchema;
541
- const elicitFn: any = (tools as any).elicit;
542
- if (inputSchema && typeof elicitFn === 'function') {
543
- const props = inputSchema.properties || {};
544
- const required: string[] = Array.isArray(inputSchema.required) ? inputSchema.required : [];
545
- const missing = required.filter((k: string) => {
546
- const v = (args as any)[k];
547
- if (v === undefined || v === null) return true;
548
- if (typeof v === 'string' && v.trim() === '') return true;
549
- return false;
550
- });
551
-
552
- // Build a flat primitive-only schema subset per MCP Elicitation rules
553
- const primitiveProps: any = {};
554
- for (const k of missing) {
555
- const p = props[k];
556
- if (!p || typeof p !== 'object') continue;
557
- const t = (p.type || '').toString();
558
- const isEnum = Array.isArray(p.enum);
559
- if (t === 'string' || t === 'number' || t === 'integer' || t === 'boolean' || isEnum) {
560
- primitiveProps[k] = {
561
- type: t || (isEnum ? 'string' : undefined),
562
- title: p.title,
563
- description: p.description,
564
- enum: p.enum,
565
- enumNames: p.enumNames,
566
- minimum: p.minimum,
567
- maximum: p.maximum,
568
- minLength: p.minLength,
569
- maxLength: p.maxLength,
570
- pattern: p.pattern,
571
- format: p.format,
572
- default: p.default
573
- };
574
- }
575
- }
576
-
577
- if (Object.keys(primitiveProps).length > 0) {
578
- const elicitOptions: any = { fallback: async () => ({ ok: false, error: 'missing-params' }) };
579
- if (typeof (tools as any).elicitationTimeoutMs === 'number' && Number.isFinite((tools as any).elicitationTimeoutMs)) {
580
- elicitOptions.timeoutMs = (tools as any).elicitationTimeoutMs;
581
- }
582
- const elicitRes = await elicitFn(
583
- `Provide missing parameters for ${name}`,
584
- { type: 'object', properties: primitiveProps, required: Object.keys(primitiveProps) },
585
- elicitOptions
586
- );
587
- if (elicitRes && elicitRes.ok && elicitRes.value) {
588
- args = { ...args, ...elicitRes.value };
589
- }
590
- }
591
- }
592
- } catch (e) {
593
- log.debug('Generic elicitation prefill skipped', { err: (e as any)?.message || String(e) });
594
- }
595
-
596
- let result = await handleConsolidatedToolCall(name, args, tools);
597
-
598
- log.debug(`Tool ${name} returned result`);
599
-
600
- // Clean the result to remove circular references
601
- result = cleanObject(result);
602
-
603
- // Validate and enhance response
604
- result = responseValidator.wrapResponse(name, result);
605
-
606
- trackPerformance(startTime, true);
607
-
608
- log.info(`Tool ${name} completed successfully in ${Date.now() - startTime}ms`);
609
-
610
- // Log that we're returning the response
611
- const responsePreview = JSON.stringify(result).substring(0, 100);
612
- log.debug(`Returning response to MCP client: ${responsePreview}...`);
613
-
614
- return result;
615
- } catch (error) {
616
- trackPerformance(startTime, false);
617
-
618
- // Use consistent error handling
619
- const errorResponse = ErrorHandler.createErrorResponse(error, name, { ...args, scope: `tool-call/${name}` });
620
- log.error(`Tool execution failed: ${name}`, errorResponse);
621
- // Record error for health diagnostics
622
- try {
623
- metrics.recentErrors.push({
624
- time: new Date().toISOString(),
625
- scope: (errorResponse as any).scope || `tool-call/${name}`,
626
- type: (errorResponse as any)._debug?.errorType || 'UNKNOWN',
627
- message: (errorResponse as any).error || (errorResponse as any).message || 'Unknown error',
628
- retriable: Boolean((errorResponse as any).retriable)
629
- });
630
- if (metrics.recentErrors.length > 20) metrics.recentErrors.splice(0, metrics.recentErrors.length - 20);
631
- } catch {}
632
-
633
- const sanitizedError = cleanObject(errorResponse);
634
- let errorText = '';
635
- try {
636
- errorText = JSON.stringify(sanitizedError, null, 2);
637
- } catch {
638
- errorText = sanitizedError.message || errorResponse.message || `Failed to execute ${name}`;
639
- }
640
-
641
- const wrappedError = {
642
- ...sanitizedError,
643
- isError: true,
644
- content: [{
645
- type: 'text',
646
- text: errorText
647
- }]
648
- };
649
-
650
- return responseValidator.wrapResponse(name, wrappedError);
651
- }
652
- });
653
-
654
- // Handle prompt listing
655
- server.setRequestHandler(ListPromptsRequestSchema, async () => {
656
- return {
657
- prompts: prompts.map(p => ({
658
- name: p.name,
659
- description: p.description,
660
- arguments: Object.entries(p.arguments || {}).map(([name, schema]) => {
661
- const meta: Record<string, unknown> = {};
662
- if (schema.type) meta.type = schema.type;
663
- if (schema.enum) meta.enum = schema.enum;
664
- if (schema.default !== undefined) meta.default = schema.default;
665
- return {
666
- name,
667
- description: schema.description,
668
- required: schema.required ?? false,
669
- ...(Object.keys(meta).length ? { _meta: meta } : {})
670
- };
671
- })
672
- }))
673
- };
674
- });
675
-
676
- // Handle prompt retrieval
677
- server.setRequestHandler(GetPromptRequestSchema, async (request) => {
678
- const prompt = prompts.find(p => p.name === request.params.name);
679
- if (!prompt) {
680
- throw new Error(`Unknown prompt: ${request.params.name}`);
681
- }
682
-
683
- const args = (request.params.arguments || {}) as Record<string, unknown>;
684
- const messages = prompt.build(args);
685
- return {
686
- description: prompt.description,
687
- messages
688
- };
689
- });
690
-
691
- return { server, bridge };
148
+ return { server, bridge, automationBridge };
692
149
  }
693
150
 
694
- // Export configuration schema for Smithery session UI and validation
151
+ // Export configuration schema for session UI and runtime validation
695
152
  export const configSchema = z.object({
696
- ueHost: z.string().optional().default('127.0.0.1').describe('Unreal Engine host (e.g. 127.0.0.1)'),
697
- ueHttpPort: z.number().int().optional().default(30010).describe('Remote Control HTTP port'),
698
- ueWsPort: z.number().int().optional().default(30020).describe('Remote Control WebSocket port'),
699
153
  logLevel: z.enum(['debug', 'info', 'warn', 'error']).optional().default('info').describe('Runtime log level'),
700
154
  projectPath: z.string().optional().default('C:/Users/YourName/Documents/Unreal Projects/YourProject').describe('Absolute path to your Unreal .uproject file')
701
155
  });
702
156
 
703
- // Default export expected by Smithery TypeScript runtime. Accepts an optional config object
704
- // and injects values into the environment before creating the server.
157
+ // Default export for runtime configuration support.
705
158
  export default function createServerDefault({ config }: { config?: any } = {}) {
706
159
  try {
707
160
  if (config) {
708
- if (typeof config.ueHost === 'string' && config.ueHost.trim()) process.env.UE_HOST = config.ueHost;
709
- if (config.ueHttpPort !== undefined) process.env.UE_RC_HTTP_PORT = String(config.ueHttpPort);
710
- if (config.ueWsPort !== undefined) process.env.UE_RC_WS_PORT = String(config.ueWsPort);
711
- if (typeof config.logLevel === 'string') process.env.LOG_LEVEL = config.logLevel;
712
- if (typeof config.projectPath === 'string' && config.projectPath.trim()) process.env.UE_PROJECT_PATH = config.projectPath;
161
+ if (typeof config.logLevel === 'string') process.env.LOG_LEVEL = config.logLevel;
162
+ if (typeof config.projectPath === 'string' && config.projectPath.trim()) process.env.UE_PROJECT_PATH = config.projectPath;
713
163
  }
714
164
  } catch (e) {
715
- // Non-fatal: log and continue (console to avoid circular logger dependencies at top-level)
716
165
  console.debug('[createServerDefault] Failed to apply config to environment:', (e as any)?.message || e);
717
166
  }
718
167
 
@@ -721,19 +170,59 @@ export default function createServerDefault({ config }: { config?: any } = {}) {
721
170
  }
722
171
 
723
172
  export async function startStdioServer() {
724
- const { server } = createServer();
173
+ const { server, automationBridge } = createServer();
725
174
  const transport = new StdioServerTransport();
726
-
727
- // Add debugging for transport messages
175
+ let shuttingDown = false;
176
+
177
+ const handleShutdown = async (signal?: NodeJS.Signals) => {
178
+ if (shuttingDown) {
179
+ return;
180
+ }
181
+ shuttingDown = true;
182
+ const reason = signal ? ` due to ${signal}` : '';
183
+ log.info(`Shutting down MCP server${reason}`);
184
+ try {
185
+ automationBridge.stop();
186
+ } catch (error) {
187
+ log.warn('Failed to stop automation bridge cleanly', error);
188
+ }
189
+
190
+ try {
191
+ if (typeof (server as any).close === 'function') {
192
+ await (server as any).close();
193
+ }
194
+ } catch (error) {
195
+ log.warn('Failed to close MCP server transport cleanly', error);
196
+ }
197
+
198
+ if (signal) {
199
+ process.exit(0);
200
+ }
201
+ };
202
+
203
+ ['SIGINT', 'SIGTERM'].forEach((signal) => {
204
+ process.once(signal as NodeJS.Signals, () => {
205
+ void handleShutdown(signal as NodeJS.Signals);
206
+ });
207
+ });
208
+
209
+ process.once('beforeExit', () => {
210
+ automationBridge.stop();
211
+ });
212
+
213
+ process.once('exit', () => {
214
+ automationBridge.stop();
215
+ });
216
+
728
217
  const originalWrite = process.stdout.write;
729
- process.stdout.write = function(...args: any[]) {
218
+ process.stdout.write = function (...args: any[]) {
730
219
  const message = args[0];
731
220
  if (typeof message === 'string' && message.includes('jsonrpc')) {
732
221
  log.debug(`Sending to client: ${message.substring(0, 200)}...`);
733
222
  }
734
223
  return originalWrite.apply(process.stdout, args as any);
735
224
  } as any;
736
-
225
+
737
226
  await server.connect(transport);
738
227
  log.info('Unreal Engine MCP Server started on stdio');
739
228
  }