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
package/dist/index.js CHANGED
@@ -1,128 +1,97 @@
1
- import 'dotenv/config';
2
1
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
2
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
3
  import { Logger } from './utils/logger.js';
5
4
  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 { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema } from '@modelcontextprotocol/sdk/types.js';
5
+ import { AutomationBridge } from './automation/index.js';
6
+ import { createRequire } from 'node:module';
36
7
  import { responseValidator } from './utils/response-validator.js';
37
8
  import { z } from 'zod';
38
- import { routeStdoutLogsToStderr } from './utils/stdio-redirect.js';
39
- import { createElicitationHelper } from './utils/elicitation.js';
40
- import { cleanObject } from './utils/safe-json.js';
41
- import { ErrorHandler } from './utils/error-handler.js';
9
+ import { initializeWASM } from './wasm/index.js';
10
+ import { consolidatedToolDefinitions } from './tools/consolidated-tool-definitions.js';
11
+ import { HealthMonitor } from './services/health-monitor.js';
12
+ import { ServerSetup } from './server-setup.js';
13
+ import { startMetricsServer } from './services/metrics-server.js';
14
+ import { config } from './config.js';
15
+ import { GraphQLServer } from './graphql/server.js';
16
+ const require = createRequire(import.meta.url);
17
+ const packageInfo = (() => {
18
+ try {
19
+ return require('../package.json');
20
+ }
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
+ function routeStdoutLogsToStderr() {
34
+ if (!config.MCP_ROUTE_STDOUT_LOGS) {
35
+ return;
36
+ }
37
+ const writeToStderr = (...args) => {
38
+ const line = args
39
+ .map((a) => (typeof a === 'string' ? a : JSON.stringify(a)))
40
+ .join(' ');
41
+ process.stderr.write(`${line}\n`);
42
+ };
43
+ console.log = (...args) => { writeToStderr(...args); };
44
+ console.info = (...args) => { writeToStderr(...args); };
45
+ if (typeof console.debug === 'function') {
46
+ console.debug = (...args) => { writeToStderr(...args); };
47
+ }
48
+ }
42
49
  const log = new Logger('UE-MCP');
43
- // Ensure stdout remains JSON-only for MCP by routing logs to stderr unless opted out.
44
50
  routeStdoutLogsToStderr();
45
- const metrics = {
46
- totalRequests: 0,
47
- successfulRequests: 0,
48
- failedRequests: 0,
49
- averageResponseTime: 0,
50
- responseTimes: [],
51
- connectionStatus: 'disconnected',
52
- lastHealthCheck: new Date(),
53
- uptime: Date.now(),
54
- recentErrors: []
55
- };
56
- // Health check timer and last success tracking (stop pings after inactivity)
57
- let healthCheckTimer;
58
- let lastHealthSuccessAt = 0;
59
- // Configuration
60
51
  const CONFIG = {
61
- // Tooling: use consolidated tools only (13 tools)
62
- // Connection retry settings
63
52
  MAX_RETRY_ATTEMPTS: 3,
64
53
  RETRY_DELAY_MS: 2000,
65
- // Server info
66
- SERVER_NAME: 'unreal-engine-mcp',
67
- SERVER_VERSION: '0.4.7',
68
- // Monitoring
69
- HEALTH_CHECK_INTERVAL_MS: 30000 // 30 seconds
54
+ SERVER_NAME: DEFAULT_SERVER_NAME,
55
+ SERVER_VERSION: DEFAULT_SERVER_VERSION,
56
+ AUTOMATION_HEARTBEAT_MS: 15000,
57
+ HEALTH_CHECK_INTERVAL_MS: 30000
70
58
  };
71
- // Helper function to track performance
72
- function trackPerformance(startTime, success) {
73
- const responseTime = Date.now() - startTime;
74
- metrics.totalRequests++;
75
- if (success) {
76
- metrics.successfulRequests++;
77
- }
78
- else {
79
- metrics.failedRequests++;
80
- }
81
- // Keep last 100 response times for average calculation
82
- metrics.responseTimes.push(responseTime);
83
- if (metrics.responseTimes.length > 100) {
84
- metrics.responseTimes.shift();
85
- }
86
- // Calculate average
87
- metrics.averageResponseTime = metrics.responseTimes.reduce((a, b) => a + b, 0) / metrics.responseTimes.length;
88
- }
89
- // Health check function
90
- async function performHealthCheck(bridge) {
91
- // If not connected, do not attempt any ping (stay quiet)
92
- if (!bridge.isConnected) {
93
- return false;
94
- }
95
- try {
96
- // Use a safe echo command that doesn't affect any settings
97
- await bridge.executeConsoleCommand('echo MCP Server Health Check');
98
- metrics.connectionStatus = 'connected';
99
- metrics.lastHealthCheck = new Date();
100
- lastHealthSuccessAt = Date.now();
101
- return true;
102
- }
103
- catch (err1) {
104
- // Fallback: minimal Python ping (if Python plugin is enabled)
105
- try {
106
- await bridge.executePython("import sys; sys.stdout.write('OK')");
107
- metrics.connectionStatus = 'connected';
108
- metrics.lastHealthCheck = new Date();
109
- lastHealthSuccessAt = Date.now();
110
- return true;
111
- }
112
- catch (err2) {
113
- metrics.connectionStatus = 'error';
114
- metrics.lastHealthCheck = new Date();
115
- // Avoid noisy warnings when engine may be shutting down; log at debug
116
- log.debug('Health check failed (console and python):', err1, err2);
117
- return false;
118
- }
119
- }
120
- }
121
59
  export function createServer() {
122
60
  const bridge = new UnrealBridge();
123
- // Disable auto-reconnect loops; connect only on-demand
124
- bridge.setAutoReconnectEnabled(false);
125
- // Initialize response validation with schemas
61
+ const healthMonitor = new HealthMonitor(log);
62
+ const automationBridge = new AutomationBridge({
63
+ serverName: CONFIG.SERVER_NAME,
64
+ serverVersion: CONFIG.SERVER_VERSION,
65
+ heartbeatIntervalMs: CONFIG.AUTOMATION_HEARTBEAT_MS,
66
+ clientMode: config.MCP_AUTOMATION_CLIENT_MODE
67
+ });
68
+ bridge.setAutomationBridge(automationBridge);
69
+ automationBridge.on('connected', ({ metadata, port, protocol }) => {
70
+ log.info(`Automation bridge connected (port=${port}, protocol=${protocol ?? 'none'})`, metadata);
71
+ });
72
+ automationBridge.on('disconnected', ({ code, reason, port, protocol }) => {
73
+ log.info(`Automation bridge disconnected (code=${code}, reason=${reason || 'n/a'}, port=${port}, protocol=${protocol ?? 'none'})`);
74
+ });
75
+ automationBridge.on('handshakeFailed', ({ reason, port }) => {
76
+ log.warn(`Automation bridge handshake failed (port=${port}): ${reason}`);
77
+ });
78
+ automationBridge.on('message', (message) => {
79
+ log.debug('Automation bridge inbound message', message);
80
+ });
81
+ automationBridge.on('error', (error) => {
82
+ log.error('Automation bridge error', error);
83
+ });
84
+ startMetricsServer({ healthMonitor, automationBridge, logger: log });
85
+ const graphqlServer = new GraphQLServer(bridge, automationBridge);
86
+ graphqlServer.start().catch((error) => {
87
+ log.warn('GraphQL server failed to start:', error);
88
+ });
89
+ log.debug('Initializing WebAssembly integration...');
90
+ initializeWASM().then(() => {
91
+ log.info('✅ WebAssembly integration initialized (JSON parsing and math operations)');
92
+ }).catch((error) => {
93
+ log.warn('⚠️ WebAssembly initialization failed, using TypeScript fallbacks:', error);
94
+ });
126
95
  log.debug('Initializing response validation...');
127
96
  const toolDefs = consolidatedToolDefinitions;
128
97
  toolDefs.forEach((tool) => {
@@ -130,528 +99,30 @@ export function createServer() {
130
99
  responseValidator.registerSchema(tool.name, tool.outputSchema);
131
100
  }
132
101
  });
133
- // Summary at debug level to avoid repeated noisy blocks in some shells
134
102
  log.debug(`Registered ${responseValidator.getStats().totalSchemas} output schemas for validation`);
135
- // Do NOT connect to Unreal at startup; connect on demand
136
103
  log.debug('Server starting without connecting to Unreal Engine');
137
- metrics.connectionStatus = 'disconnected';
138
- // Health checks manager (only active when connected)
139
- const startHealthChecks = () => {
140
- if (healthCheckTimer)
141
- return;
142
- lastHealthSuccessAt = Date.now();
143
- healthCheckTimer = setInterval(async () => {
144
- // Only attempt health pings while connected; stay silent otherwise
145
- if (!bridge.isConnected) {
146
- // Optionally pause fully after 5 minutes of no success
147
- const FIVE_MIN_MS = 5 * 60 * 1000;
148
- if (!lastHealthSuccessAt || Date.now() - lastHealthSuccessAt > FIVE_MIN_MS) {
149
- if (healthCheckTimer) {
150
- clearInterval(healthCheckTimer);
151
- healthCheckTimer = undefined;
152
- }
153
- log.info('Health checks paused after 5 minutes without a successful response');
154
- }
155
- return;
156
- }
157
- await performHealthCheck(bridge);
158
- // Stop sending echoes if we haven't had a successful response in > 5 minutes
159
- const FIVE_MIN_MS = 5 * 60 * 1000;
160
- if (!lastHealthSuccessAt || Date.now() - lastHealthSuccessAt > FIVE_MIN_MS) {
161
- if (healthCheckTimer) {
162
- clearInterval(healthCheckTimer);
163
- healthCheckTimer = undefined;
164
- log.info('Health checks paused after 5 minutes without a successful response');
165
- }
166
- }
167
- }, CONFIG.HEALTH_CHECK_INTERVAL_MS);
168
- };
169
- // On-demand connection helper
170
- const ensureConnectedOnDemand = async () => {
171
- if (bridge.isConnected)
172
- return true;
173
- const ok = await bridge.tryConnect(3, 5000, 1000);
174
- if (ok) {
175
- metrics.connectionStatus = 'connected';
176
- startHealthChecks();
177
- }
178
- else {
179
- metrics.connectionStatus = 'disconnected';
180
- }
181
- return ok;
182
- };
183
- // Resources
184
- const assetResources = new AssetResources(bridge);
185
- const actorResources = new ActorResources(bridge);
186
- const levelResources = new LevelResources(bridge);
187
- // Tools
188
- const actorTools = new ActorTools(bridge);
189
- const assetTools = new AssetTools(bridge);
190
- const editorTools = new EditorTools(bridge);
191
- const materialTools = new MaterialTools(bridge);
192
- const animationTools = new AnimationTools(bridge);
193
- const physicsTools = new PhysicsTools(bridge);
194
- const niagaraTools = new NiagaraTools(bridge);
195
- const blueprintTools = new BlueprintTools(bridge);
196
- const levelTools = new LevelTools(bridge);
197
- const lightingTools = new LightingTools(bridge);
198
- const landscapeTools = new LandscapeTools(bridge);
199
- const foliageTools = new FoliageTools(bridge);
200
- const buildEnvAdvanced = new BuildEnvironmentAdvanced(bridge);
201
- const debugTools = new DebugVisualizationTools(bridge);
202
- const performanceTools = new PerformanceTools(bridge);
203
- const audioTools = new AudioTools(bridge);
204
- const uiTools = new UITools(bridge);
205
- const rcTools = new RcTools(bridge);
206
- const sequenceTools = new SequenceTools(bridge);
207
- const introspectionTools = new IntrospectionTools(bridge);
208
- const visualTools = new VisualTools(bridge);
209
- const engineTools = new EngineTools(bridge);
210
- const logTools = new LogTools(bridge);
104
+ healthMonitor.metrics.connectionStatus = 'disconnected';
211
105
  const server = new Server({
212
106
  name: CONFIG.SERVER_NAME,
213
107
  version: CONFIG.SERVER_VERSION
214
108
  }, {
215
109
  capabilities: {
216
- resources: {},
217
110
  tools: {},
218
- prompts: {
219
- listChanged: false
220
- },
221
- logging: {}
222
- }
223
- });
224
- // Optional elicitation helper – used only if client supports it.
225
- const elicitation = createElicitationHelper(server, log);
226
- const defaultElicitationTimeoutMs = elicitation.getDefaultTimeoutMs();
227
- const createNotConnectedResponse = (toolName) => {
228
- const payload = {
229
- success: false,
230
- error: 'UE_NOT_CONNECTED',
231
- message: 'Unreal Engine is not connected (after 3 attempts). Please open UE and try again.',
232
- retriable: false,
233
- scope: `tool-call/${toolName}`
234
- };
235
- return responseValidator.wrapResponse(toolName, {
236
- ...payload,
237
- content: [
238
- {
239
- type: 'text',
240
- text: JSON.stringify(payload, null, 2)
241
- }
242
- ]
243
- });
244
- };
245
- // Handle resource listing
246
- server.setRequestHandler(ListResourcesRequestSchema, async () => {
247
- return {
248
- resources: [
249
- {
250
- uri: 'ue://assets',
251
- name: 'Project Assets',
252
- description: 'List all assets in the project',
253
- mimeType: 'application/json'
254
- },
255
- {
256
- uri: 'ue://actors',
257
- name: 'Level Actors',
258
- description: 'List all actors in the current level',
259
- mimeType: 'application/json'
260
- },
261
- {
262
- uri: 'ue://level',
263
- name: 'Current Level',
264
- description: 'Information about the current level',
265
- mimeType: 'application/json'
266
- },
267
- {
268
- uri: 'ue://exposed',
269
- name: 'Remote Control Exposed',
270
- description: 'List all exposed properties via Remote Control',
271
- mimeType: 'application/json'
272
- },
273
- {
274
- uri: 'ue://health',
275
- name: 'Health Status',
276
- description: 'Server health and performance metrics',
277
- mimeType: 'application/json'
278
- },
279
- {
280
- uri: 'ue://version',
281
- name: 'Engine Version',
282
- description: 'Unreal Engine version and compatibility info',
283
- mimeType: 'application/json'
284
- }
285
- ]
286
- };
287
- });
288
- // Handle resource reading
289
- server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
290
- const uri = request.params.uri;
291
- if (uri === 'ue://assets') {
292
- const ok = await ensureConnectedOnDemand();
293
- if (!ok) {
294
- return { contents: [{ uri, mimeType: 'text/plain', text: 'Unreal Engine not connected (after 3 attempts).' }] };
295
- }
296
- const list = await assetResources.list('/Game', true);
297
- return {
298
- contents: [{
299
- uri,
300
- mimeType: 'application/json',
301
- text: JSON.stringify(list, null, 2)
302
- }]
303
- };
304
- }
305
- if (uri === 'ue://actors') {
306
- const ok = await ensureConnectedOnDemand();
307
- if (!ok) {
308
- return { contents: [{ uri, mimeType: 'text/plain', text: 'Unreal Engine not connected (after 3 attempts).' }] };
309
- }
310
- const list = await actorResources.listActors();
311
- return {
312
- contents: [{
313
- uri,
314
- mimeType: 'application/json',
315
- text: JSON.stringify(list, null, 2)
316
- }]
317
- };
318
- }
319
- if (uri === 'ue://level') {
320
- const ok = await ensureConnectedOnDemand();
321
- if (!ok) {
322
- return { contents: [{ uri, mimeType: 'text/plain', text: 'Unreal Engine not connected (after 3 attempts).' }] };
323
- }
324
- const level = await levelResources.getCurrentLevel();
325
- return {
326
- contents: [{
327
- uri,
328
- mimeType: 'application/json',
329
- text: JSON.stringify(level, null, 2)
330
- }]
331
- };
332
- }
333
- if (uri === 'ue://exposed') {
334
- const ok = await ensureConnectedOnDemand();
335
- if (!ok) {
336
- return { contents: [{ uri, mimeType: 'text/plain', text: 'Unreal Engine not connected (after 3 attempts).' }] };
337
- }
338
- try {
339
- const exposed = await bridge.getExposed();
340
- return {
341
- contents: [{
342
- uri,
343
- mimeType: 'application/json',
344
- text: JSON.stringify(exposed, null, 2)
345
- }]
346
- };
347
- }
348
- catch {
349
- return {
350
- contents: [{
351
- uri,
352
- mimeType: 'text/plain',
353
- text: 'Failed to get exposed properties. Ensure Remote Control is configured.'
354
- }]
355
- };
356
- }
357
- }
358
- if (uri === 'ue://health') {
359
- const uptimeMs = Date.now() - metrics.uptime;
360
- // Query engine version and feature flags only when connected
361
- let versionInfo = {};
362
- let featureFlags = {};
363
- if (bridge.isConnected) {
364
- try {
365
- versionInfo = await bridge.getEngineVersion();
366
- }
367
- catch { }
368
- try {
369
- featureFlags = await bridge.getFeatureFlags();
370
- }
371
- catch { }
372
- }
373
- const health = {
374
- status: metrics.connectionStatus,
375
- uptime: Math.floor(uptimeMs / 1000),
376
- performance: {
377
- totalRequests: metrics.totalRequests,
378
- successfulRequests: metrics.successfulRequests,
379
- failedRequests: metrics.failedRequests,
380
- successRate: metrics.totalRequests > 0 ?
381
- (metrics.successfulRequests / metrics.totalRequests * 100).toFixed(2) + '%' : 'N/A',
382
- averageResponseTime: Math.round(metrics.averageResponseTime) + 'ms'
383
- },
384
- lastHealthCheck: metrics.lastHealthCheck.toISOString(),
385
- unrealConnection: {
386
- status: bridge.isConnected ? 'connected' : 'disconnected',
387
- host: process.env.UE_HOST || 'localhost',
388
- httpPort: process.env.UE_RC_HTTP_PORT || 30010,
389
- wsPort: process.env.UE_RC_WS_PORT || 30020,
390
- engineVersion: versionInfo,
391
- features: {
392
- pythonEnabled: featureFlags.pythonEnabled === true,
393
- subsystems: featureFlags.subsystems || {},
394
- rcHttpReachable: bridge.isConnected
395
- }
396
- },
397
- recentErrors: metrics.recentErrors.slice(-5)
398
- };
399
- return {
400
- contents: [{
401
- uri,
402
- mimeType: 'application/json',
403
- text: JSON.stringify(health, null, 2)
404
- }]
405
- };
406
- }
407
- if (uri === 'ue://version') {
408
- const ok = await ensureConnectedOnDemand();
409
- if (!ok) {
410
- return { contents: [{ uri, mimeType: 'text/plain', text: 'Unreal Engine not connected (after 3 attempts).' }] };
411
- }
412
- const info = await bridge.getEngineVersion();
413
- return {
414
- contents: [{
415
- uri,
416
- mimeType: 'application/json',
417
- text: JSON.stringify(info, null, 2)
418
- }]
419
- };
420
- }
421
- throw new Error(`Unknown resource: ${uri}`);
422
- });
423
- // Handle tool listing - consolidated tools only
424
- server.setRequestHandler(ListToolsRequestSchema, async () => {
425
- log.info('Serving consolidated tools');
426
- return {
427
- tools: consolidatedToolDefinitions
428
- };
429
- });
430
- // Handle tool calls - consolidated tools only (13)
431
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
432
- const { name } = request.params;
433
- let args = request.params.arguments || {};
434
- const startTime = Date.now();
435
- let requiresEngine = true;
436
- try {
437
- const n = String(name);
438
- if (n === 'system_control') {
439
- const action = String((args || {}).action || '').trim();
440
- if (action === 'read_log') {
441
- requiresEngine = false;
442
- }
443
- }
444
- }
445
- catch { }
446
- if (requiresEngine) {
447
- const connected = await ensureConnectedOnDemand();
448
- if (!connected) {
449
- trackPerformance(startTime, false);
450
- return createNotConnectedResponse(name);
451
- }
452
- }
453
- // Create tools object for handler
454
- const tools = {
455
- actorTools,
456
- assetTools,
457
- materialTools,
458
- editorTools,
459
- animationTools,
460
- physicsTools,
461
- niagaraTools,
462
- blueprintTools,
463
- levelTools,
464
- lightingTools,
465
- landscapeTools,
466
- foliageTools,
467
- buildEnvAdvanced,
468
- debugTools,
469
- performanceTools,
470
- audioTools,
471
- uiTools,
472
- rcTools,
473
- sequenceTools,
474
- introspectionTools,
475
- visualTools,
476
- engineTools,
477
- logTools,
478
- // Elicitation (client-optional)
479
- elicit: elicitation.elicit,
480
- supportsElicitation: elicitation.supports,
481
- elicitationTimeoutMs: defaultElicitationTimeoutMs,
482
- // Resources for listing and info
483
- assetResources,
484
- actorResources,
485
- levelResources,
486
- bridge
487
- };
488
- // Execute consolidated tool handler
489
- try {
490
- log.debug(`Executing tool: ${name}`);
491
- // Opportunistic generic elicitation for missing primitive required fields
492
- try {
493
- const toolDef = consolidatedToolDefinitions.find(t => t.name === name);
494
- const inputSchema = toolDef?.inputSchema;
495
- const elicitFn = tools.elicit;
496
- if (inputSchema && typeof elicitFn === 'function') {
497
- const props = inputSchema.properties || {};
498
- const required = Array.isArray(inputSchema.required) ? inputSchema.required : [];
499
- const missing = required.filter((k) => {
500
- const v = args[k];
501
- if (v === undefined || v === null)
502
- return true;
503
- if (typeof v === 'string' && v.trim() === '')
504
- return true;
505
- return false;
506
- });
507
- // Build a flat primitive-only schema subset per MCP Elicitation rules
508
- const primitiveProps = {};
509
- for (const k of missing) {
510
- const p = props[k];
511
- if (!p || typeof p !== 'object')
512
- continue;
513
- const t = (p.type || '').toString();
514
- const isEnum = Array.isArray(p.enum);
515
- if (t === 'string' || t === 'number' || t === 'integer' || t === 'boolean' || isEnum) {
516
- primitiveProps[k] = {
517
- type: t || (isEnum ? 'string' : undefined),
518
- title: p.title,
519
- description: p.description,
520
- enum: p.enum,
521
- enumNames: p.enumNames,
522
- minimum: p.minimum,
523
- maximum: p.maximum,
524
- minLength: p.minLength,
525
- maxLength: p.maxLength,
526
- pattern: p.pattern,
527
- format: p.format,
528
- default: p.default
529
- };
530
- }
531
- }
532
- if (Object.keys(primitiveProps).length > 0) {
533
- const elicitOptions = { fallback: async () => ({ ok: false, error: 'missing-params' }) };
534
- if (typeof tools.elicitationTimeoutMs === 'number' && Number.isFinite(tools.elicitationTimeoutMs)) {
535
- elicitOptions.timeoutMs = tools.elicitationTimeoutMs;
536
- }
537
- const elicitRes = await elicitFn(`Provide missing parameters for ${name}`, { type: 'object', properties: primitiveProps, required: Object.keys(primitiveProps) }, elicitOptions);
538
- if (elicitRes && elicitRes.ok && elicitRes.value) {
539
- args = { ...args, ...elicitRes.value };
540
- }
541
- }
542
- }
543
- }
544
- catch (e) {
545
- log.debug('Generic elicitation prefill skipped', { err: e?.message || String(e) });
546
- }
547
- let result = await handleConsolidatedToolCall(name, args, tools);
548
- log.debug(`Tool ${name} returned result`);
549
- // Clean the result to remove circular references
550
- result = cleanObject(result);
551
- // Validate and enhance response
552
- result = responseValidator.wrapResponse(name, result);
553
- trackPerformance(startTime, true);
554
- log.info(`Tool ${name} completed successfully in ${Date.now() - startTime}ms`);
555
- // Log that we're returning the response
556
- const responsePreview = JSON.stringify(result).substring(0, 100);
557
- log.debug(`Returning response to MCP client: ${responsePreview}...`);
558
- return result;
559
- }
560
- catch (error) {
561
- trackPerformance(startTime, false);
562
- // Use consistent error handling
563
- const errorResponse = ErrorHandler.createErrorResponse(error, name, { ...args, scope: `tool-call/${name}` });
564
- log.error(`Tool execution failed: ${name}`, errorResponse);
565
- // Record error for health diagnostics
566
- try {
567
- metrics.recentErrors.push({
568
- time: new Date().toISOString(),
569
- scope: errorResponse.scope || `tool-call/${name}`,
570
- type: errorResponse._debug?.errorType || 'UNKNOWN',
571
- message: errorResponse.error || errorResponse.message || 'Unknown error',
572
- retriable: Boolean(errorResponse.retriable)
573
- });
574
- if (metrics.recentErrors.length > 20)
575
- metrics.recentErrors.splice(0, metrics.recentErrors.length - 20);
576
- }
577
- catch { }
578
- const sanitizedError = cleanObject(errorResponse);
579
- let errorText = '';
580
- try {
581
- errorText = JSON.stringify(sanitizedError, null, 2);
582
- }
583
- catch {
584
- errorText = sanitizedError.message || errorResponse.message || `Failed to execute ${name}`;
585
- }
586
- const wrappedError = {
587
- ...sanitizedError,
588
- isError: true,
589
- content: [{
590
- type: 'text',
591
- text: errorText
592
- }]
593
- };
594
- return responseValidator.wrapResponse(name, wrappedError);
595
- }
596
- });
597
- // Handle prompt listing
598
- server.setRequestHandler(ListPromptsRequestSchema, async () => {
599
- return {
600
- prompts: prompts.map(p => ({
601
- name: p.name,
602
- description: p.description,
603
- arguments: Object.entries(p.arguments || {}).map(([name, schema]) => {
604
- const meta = {};
605
- if (schema.type)
606
- meta.type = schema.type;
607
- if (schema.enum)
608
- meta.enum = schema.enum;
609
- if (schema.default !== undefined)
610
- meta.default = schema.default;
611
- return {
612
- name,
613
- description: schema.description,
614
- required: schema.required ?? false,
615
- ...(Object.keys(meta).length ? { _meta: meta } : {})
616
- };
617
- })
618
- }))
619
- };
620
- });
621
- // Handle prompt retrieval
622
- server.setRequestHandler(GetPromptRequestSchema, async (request) => {
623
- const prompt = prompts.find(p => p.name === request.params.name);
624
- if (!prompt) {
625
- throw new Error(`Unknown prompt: ${request.params.name}`);
111
+ resources: {},
112
+ prompts: {}
626
113
  }
627
- const args = (request.params.arguments || {});
628
- const messages = prompt.build(args);
629
- return {
630
- description: prompt.description,
631
- messages
632
- };
633
114
  });
634
- return { server, bridge };
115
+ const serverSetup = new ServerSetup(server, bridge, automationBridge, log, healthMonitor);
116
+ serverSetup.setup();
117
+ return { server, bridge, automationBridge, graphqlServer };
635
118
  }
636
- // Export configuration schema for Smithery session UI and validation
637
119
  export const configSchema = z.object({
638
- ueHost: z.string().optional().default('127.0.0.1').describe('Unreal Engine host (e.g. 127.0.0.1)'),
639
- ueHttpPort: z.number().int().optional().default(30010).describe('Remote Control HTTP port'),
640
- ueWsPort: z.number().int().optional().default(30020).describe('Remote Control WebSocket port'),
641
120
  logLevel: z.enum(['debug', 'info', 'warn', 'error']).optional().default('info').describe('Runtime log level'),
642
121
  projectPath: z.string().optional().default('C:/Users/YourName/Documents/Unreal Projects/YourProject').describe('Absolute path to your Unreal .uproject file')
643
122
  });
644
- // Default export expected by Smithery TypeScript runtime. Accepts an optional config object
645
- // and injects values into the environment before creating the server.
646
123
  export default function createServerDefault({ config } = {}) {
647
124
  try {
648
125
  if (config) {
649
- if (typeof config.ueHost === 'string' && config.ueHost.trim())
650
- process.env.UE_HOST = config.ueHost;
651
- if (config.ueHttpPort !== undefined)
652
- process.env.UE_RC_HTTP_PORT = String(config.ueHttpPort);
653
- if (config.ueWsPort !== undefined)
654
- process.env.UE_RC_WS_PORT = String(config.ueWsPort);
655
126
  if (typeof config.logLevel === 'string')
656
127
  process.env.LOG_LEVEL = config.logLevel;
657
128
  if (typeof config.projectPath === 'string' && config.projectPath.trim())
@@ -659,16 +130,57 @@ export default function createServerDefault({ config } = {}) {
659
130
  }
660
131
  }
661
132
  catch (e) {
662
- // Non-fatal: log and continue (console to avoid circular logger dependencies at top-level)
663
133
  console.debug('[createServerDefault] Failed to apply config to environment:', e?.message || e);
664
134
  }
665
135
  const { server } = createServer();
666
136
  return server;
667
137
  }
668
138
  export async function startStdioServer() {
669
- const { server } = createServer();
139
+ const { server, automationBridge, graphqlServer } = createServer();
670
140
  const transport = new StdioServerTransport();
671
- // Add debugging for transport messages
141
+ let shuttingDown = false;
142
+ const handleShutdown = async (signal) => {
143
+ if (shuttingDown) {
144
+ return;
145
+ }
146
+ shuttingDown = true;
147
+ const reason = signal ? ` due to ${signal}` : '';
148
+ log.info(`Shutting down MCP server${reason}`);
149
+ try {
150
+ automationBridge.stop();
151
+ }
152
+ catch (error) {
153
+ log.warn('Failed to stop automation bridge cleanly', error);
154
+ }
155
+ try {
156
+ await graphqlServer.stop();
157
+ }
158
+ catch (error) {
159
+ log.warn('Failed to stop GraphQL server cleanly', error);
160
+ }
161
+ try {
162
+ if (typeof server.close === 'function') {
163
+ await server.close();
164
+ }
165
+ }
166
+ catch (error) {
167
+ log.warn('Failed to close MCP server transport cleanly', error);
168
+ }
169
+ if (signal) {
170
+ process.exit(0);
171
+ }
172
+ };
173
+ ['SIGINT', 'SIGTERM'].forEach((signal) => {
174
+ process.once(signal, () => {
175
+ void handleShutdown(signal);
176
+ });
177
+ });
178
+ process.once('beforeExit', () => {
179
+ automationBridge.stop();
180
+ });
181
+ process.once('exit', () => {
182
+ automationBridge.stop();
183
+ });
672
184
  const originalWrite = process.stdout.write;
673
185
  process.stdout.write = function (...args) {
674
186
  const message = args[0];
@@ -680,5 +192,4 @@ export async function startStdioServer() {
680
192
  await server.connect(transport);
681
193
  log.info('Unreal Engine MCP Server started on stdio');
682
194
  }
683
- // Direct execution is handled via src/cli.ts to keep this module side-effect free.
684
195
  //# sourceMappingURL=index.js.map