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.
- package/.env.example +26 -0
- package/.env.production +38 -7
- package/.eslintrc.json +0 -54
- package/.eslintrc.override.json +8 -0
- package/.github/ISSUE_TEMPLATE/bug_report.yml +94 -0
- package/.github/ISSUE_TEMPLATE/config.yml +8 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +56 -0
- package/.github/copilot-instructions.md +478 -45
- package/.github/dependabot.yml +19 -0
- package/.github/labeler.yml +24 -0
- package/.github/labels.yml +70 -0
- package/.github/pull_request_template.md +42 -0
- package/.github/release-drafter.yml +148 -0
- package/.github/workflows/auto-merge.yml +38 -0
- package/.github/workflows/ci.yml +38 -0
- package/.github/workflows/dependency-review.yml +17 -0
- package/.github/workflows/gemini-issue-triage.yml +172 -0
- package/.github/workflows/greetings.yml +23 -0
- package/.github/workflows/labeler.yml +16 -0
- package/.github/workflows/links.yml +80 -0
- package/.github/workflows/pr-size-labeler.yml +137 -0
- package/.github/workflows/publish-mcp.yml +12 -7
- package/.github/workflows/release-drafter.yml +23 -0
- package/.github/workflows/release.yml +112 -0
- package/.github/workflows/semantic-pull-request.yml +35 -0
- package/.github/workflows/smoke-test.yml +36 -0
- package/.github/workflows/stale.yml +28 -0
- package/CHANGELOG.md +267 -31
- package/CONTRIBUTING.md +140 -0
- package/README.md +166 -71
- package/claude_desktop_config_example.json +7 -6
- package/dist/automation/bridge.d.ts +50 -0
- package/dist/automation/bridge.js +452 -0
- package/dist/automation/connection-manager.d.ts +23 -0
- package/dist/automation/connection-manager.js +107 -0
- package/dist/automation/handshake.d.ts +11 -0
- package/dist/automation/handshake.js +89 -0
- package/dist/automation/index.d.ts +3 -0
- package/dist/automation/index.js +3 -0
- package/dist/automation/message-handler.d.ts +12 -0
- package/dist/automation/message-handler.js +149 -0
- package/dist/automation/request-tracker.d.ts +25 -0
- package/dist/automation/request-tracker.js +98 -0
- package/dist/automation/types.d.ts +130 -0
- package/dist/automation/types.js +2 -0
- package/dist/cli.js +32 -5
- package/dist/config.d.ts +27 -0
- package/dist/config.js +60 -0
- package/dist/constants.d.ts +12 -0
- package/dist/constants.js +12 -0
- package/dist/graphql/resolvers.d.ts +268 -0
- package/dist/graphql/resolvers.js +743 -0
- package/dist/graphql/schema.d.ts +5 -0
- package/dist/graphql/schema.js +437 -0
- package/dist/graphql/server.d.ts +26 -0
- package/dist/graphql/server.js +115 -0
- package/dist/graphql/types.d.ts +7 -0
- package/dist/graphql/types.js +2 -0
- package/dist/handlers/resource-handlers.d.ts +20 -0
- package/dist/handlers/resource-handlers.js +180 -0
- package/dist/index.d.ts +31 -18
- package/dist/index.js +119 -619
- package/dist/prompts/index.js +4 -4
- package/dist/resources/actors.d.ts +17 -12
- package/dist/resources/actors.js +56 -76
- package/dist/resources/assets.d.ts +6 -14
- package/dist/resources/assets.js +115 -147
- package/dist/resources/levels.d.ts +13 -13
- package/dist/resources/levels.js +25 -34
- package/dist/server/resource-registry.d.ts +20 -0
- package/dist/server/resource-registry.js +37 -0
- package/dist/server/tool-registry.d.ts +23 -0
- package/dist/server/tool-registry.js +322 -0
- package/dist/server-setup.d.ts +21 -0
- package/dist/server-setup.js +111 -0
- package/dist/services/health-monitor.d.ts +34 -0
- package/dist/services/health-monitor.js +105 -0
- package/dist/services/metrics-server.d.ts +11 -0
- package/dist/services/metrics-server.js +105 -0
- package/dist/tools/actors.d.ts +147 -9
- package/dist/tools/actors.js +350 -311
- package/dist/tools/animation.d.ts +135 -4
- package/dist/tools/animation.js +510 -411
- package/dist/tools/assets.d.ts +117 -19
- package/dist/tools/assets.js +259 -284
- package/dist/tools/audio.d.ts +102 -42
- package/dist/tools/audio.js +272 -685
- package/dist/tools/base-tool.d.ts +17 -0
- package/dist/tools/base-tool.js +46 -0
- package/dist/tools/behavior-tree.d.ts +94 -0
- package/dist/tools/behavior-tree.js +39 -0
- package/dist/tools/blueprint/helpers.d.ts +29 -0
- package/dist/tools/blueprint/helpers.js +182 -0
- package/dist/tools/blueprint.d.ts +228 -118
- package/dist/tools/blueprint.js +685 -832
- package/dist/tools/consolidated-tool-definitions.d.ts +5462 -1781
- package/dist/tools/consolidated-tool-definitions.js +829 -496
- package/dist/tools/consolidated-tool-handlers.d.ts +2 -1
- package/dist/tools/consolidated-tool-handlers.js +211 -1026
- package/dist/tools/debug.d.ts +143 -85
- package/dist/tools/debug.js +234 -180
- package/dist/tools/dynamic-handler-registry.d.ts +11 -0
- package/dist/tools/dynamic-handler-registry.js +101 -0
- package/dist/tools/editor.d.ts +139 -18
- package/dist/tools/editor.js +239 -244
- package/dist/tools/engine.d.ts +10 -4
- package/dist/tools/engine.js +13 -5
- package/dist/tools/environment.d.ts +36 -0
- package/dist/tools/environment.js +267 -0
- package/dist/tools/foliage.d.ts +105 -14
- package/dist/tools/foliage.js +219 -331
- package/dist/tools/handlers/actor-handlers.d.ts +3 -0
- package/dist/tools/handlers/actor-handlers.js +232 -0
- package/dist/tools/handlers/animation-handlers.d.ts +3 -0
- package/dist/tools/handlers/animation-handlers.js +185 -0
- package/dist/tools/handlers/argument-helper.d.ts +16 -0
- package/dist/tools/handlers/argument-helper.js +80 -0
- package/dist/tools/handlers/asset-handlers.d.ts +3 -0
- package/dist/tools/handlers/asset-handlers.js +496 -0
- package/dist/tools/handlers/audio-handlers.d.ts +3 -0
- package/dist/tools/handlers/audio-handlers.js +166 -0
- package/dist/tools/handlers/blueprint-handlers.d.ts +4 -0
- package/dist/tools/handlers/blueprint-handlers.js +358 -0
- package/dist/tools/handlers/common-handlers.d.ts +14 -0
- package/dist/tools/handlers/common-handlers.js +56 -0
- package/dist/tools/handlers/editor-handlers.d.ts +3 -0
- package/dist/tools/handlers/editor-handlers.js +119 -0
- package/dist/tools/handlers/effect-handlers.d.ts +3 -0
- package/dist/tools/handlers/effect-handlers.js +171 -0
- package/dist/tools/handlers/environment-handlers.d.ts +3 -0
- package/dist/tools/handlers/environment-handlers.js +170 -0
- package/dist/tools/handlers/graph-handlers.d.ts +3 -0
- package/dist/tools/handlers/graph-handlers.js +90 -0
- package/dist/tools/handlers/input-handlers.d.ts +3 -0
- package/dist/tools/handlers/input-handlers.js +21 -0
- package/dist/tools/handlers/inspect-handlers.d.ts +3 -0
- package/dist/tools/handlers/inspect-handlers.js +383 -0
- package/dist/tools/handlers/level-handlers.d.ts +3 -0
- package/dist/tools/handlers/level-handlers.js +237 -0
- package/dist/tools/handlers/lighting-handlers.d.ts +3 -0
- package/dist/tools/handlers/lighting-handlers.js +144 -0
- package/dist/tools/handlers/performance-handlers.d.ts +3 -0
- package/dist/tools/handlers/performance-handlers.js +130 -0
- package/dist/tools/handlers/pipeline-handlers.d.ts +3 -0
- package/dist/tools/handlers/pipeline-handlers.js +110 -0
- package/dist/tools/handlers/sequence-handlers.d.ts +3 -0
- package/dist/tools/handlers/sequence-handlers.js +376 -0
- package/dist/tools/handlers/system-handlers.d.ts +4 -0
- package/dist/tools/handlers/system-handlers.js +506 -0
- package/dist/tools/input.d.ts +19 -0
- package/dist/tools/input.js +89 -0
- package/dist/tools/introspection.d.ts +103 -40
- package/dist/tools/introspection.js +425 -568
- package/dist/tools/landscape.d.ts +97 -36
- package/dist/tools/landscape.js +280 -409
- package/dist/tools/level.d.ts +130 -10
- package/dist/tools/level.js +639 -675
- package/dist/tools/lighting.d.ts +77 -38
- package/dist/tools/lighting.js +441 -943
- package/dist/tools/logs.d.ts +3 -3
- package/dist/tools/logs.js +5 -57
- package/dist/tools/materials.d.ts +91 -24
- package/dist/tools/materials.js +190 -118
- package/dist/tools/niagara.d.ts +149 -39
- package/dist/tools/niagara.js +232 -182
- package/dist/tools/performance.d.ts +27 -12
- package/dist/tools/performance.js +204 -122
- package/dist/tools/physics.d.ts +32 -77
- package/dist/tools/physics.js +171 -582
- package/dist/tools/property-dictionary.d.ts +13 -0
- package/dist/tools/property-dictionary.js +82 -0
- package/dist/tools/sequence.d.ts +73 -48
- package/dist/tools/sequence.js +196 -748
- package/dist/tools/tool-definition-utils.d.ts +59 -0
- package/dist/tools/tool-definition-utils.js +35 -0
- package/dist/tools/ui.d.ts +66 -34
- package/dist/tools/ui.js +134 -214
- package/dist/types/env.d.ts +0 -3
- package/dist/types/env.js +0 -7
- package/dist/types/tool-interfaces.d.ts +898 -0
- package/dist/types/tool-interfaces.js +2 -0
- package/dist/types/tool-types.d.ts +183 -19
- package/dist/types/tool-types.js +0 -4
- package/dist/unreal-bridge.d.ts +24 -131
- package/dist/unreal-bridge.js +364 -1506
- package/dist/utils/command-validator.d.ts +9 -0
- package/dist/utils/command-validator.js +67 -0
- package/dist/utils/elicitation.d.ts +1 -1
- package/dist/utils/elicitation.js +12 -15
- package/dist/utils/error-handler.d.ts +2 -51
- package/dist/utils/error-handler.js +11 -87
- package/dist/utils/ini-reader.d.ts +3 -0
- package/dist/utils/ini-reader.js +69 -0
- package/dist/utils/logger.js +9 -6
- package/dist/utils/normalize.d.ts +3 -0
- package/dist/utils/normalize.js +56 -0
- package/dist/utils/response-factory.d.ts +7 -0
- package/dist/utils/response-factory.js +33 -0
- package/dist/utils/response-validator.d.ts +3 -24
- package/dist/utils/response-validator.js +130 -81
- package/dist/utils/result-helpers.d.ts +4 -5
- package/dist/utils/result-helpers.js +15 -16
- package/dist/utils/safe-json.js +5 -11
- package/dist/utils/unreal-command-queue.d.ts +24 -0
- package/dist/utils/unreal-command-queue.js +120 -0
- package/dist/utils/validation.d.ts +0 -40
- package/dist/utils/validation.js +1 -78
- package/dist/wasm/index.d.ts +70 -0
- package/dist/wasm/index.js +535 -0
- package/docs/GraphQL-API.md +888 -0
- package/docs/Migration-Guide-v0.5.0.md +692 -0
- package/docs/Roadmap.md +53 -0
- package/docs/WebAssembly-Integration.md +628 -0
- package/docs/editor-plugin-extension.md +370 -0
- package/docs/handler-mapping.md +242 -0
- package/docs/native-automation-progress.md +128 -0
- package/docs/testing-guide.md +423 -0
- package/mcp-config-example.json +6 -6
- package/package.json +60 -27
- package/plugins/McpAutomationBridge/Config/FilterPlugin.ini +8 -0
- package/plugins/McpAutomationBridge/McpAutomationBridge.uplugin +64 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/McpAutomationBridge.Build.cs +189 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.cpp +22 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.h +30 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeHelpers.h +1983 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeModule.cpp +72 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSettings.cpp +46 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSubsystem.cpp +581 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AnimationHandlers.cpp +2394 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetQueryHandlers.cpp +300 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetWorkflowHandlers.cpp +2807 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AudioHandlers.cpp +1087 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BehaviorTreeHandlers.cpp +488 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.cpp +643 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.h +31 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintGraphHandlers.cpp +1184 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers.cpp +5652 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers_List.cpp +152 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ControlHandlers.cpp +2614 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_DebugHandlers.cpp +42 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EditorFunctionHandlers.cpp +1237 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EffectHandlers.cpp +1701 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EnvironmentHandlers.cpp +2145 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_FoliageHandlers.cpp +954 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InputHandlers.cpp +209 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InsightsHandlers.cpp +41 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LandscapeHandlers.cpp +1164 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LevelHandlers.cpp +762 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LightingHandlers.cpp +634 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LogHandlers.cpp +136 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MaterialGraphHandlers.cpp +494 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraGraphHandlers.cpp +278 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraHandlers.cpp +625 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PerformanceHandlers.cpp +401 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PipelineHandlers.cpp +67 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ProcessRequest.cpp +735 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PropertyHandlers.cpp +2634 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_RenderHandlers.cpp +189 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.cpp +917 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.h +39 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequenceHandlers.cpp +2670 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequencerHandlers.cpp +519 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_TestHandlers.cpp +38 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_UiHandlers.cpp +668 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_WorldPartitionHandlers.cpp +346 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.cpp +1330 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.h +149 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpConnectionManager.cpp +783 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSettings.h +115 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSubsystem.h +796 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpConnectionManager.h +117 -0
- package/scripts/check-unreal-connection.mjs +19 -0
- package/scripts/clean-tmp.js +23 -0
- package/scripts/patch-wasm.js +26 -0
- package/scripts/run-all-tests.mjs +131 -0
- package/scripts/smoke-test.ts +94 -0
- package/scripts/sync-mcp-plugin.js +143 -0
- package/scripts/test-no-plugin-alternates.mjs +113 -0
- package/scripts/validate-server.js +46 -0
- package/scripts/verify-automation-bridge.js +200 -0
- package/server.json +57 -21
- package/src/automation/bridge.ts +558 -0
- package/src/automation/connection-manager.ts +130 -0
- package/src/automation/handshake.ts +99 -0
- package/src/automation/index.ts +2 -0
- package/src/automation/message-handler.ts +167 -0
- package/src/automation/request-tracker.ts +123 -0
- package/src/automation/types.ts +107 -0
- package/src/cli.ts +33 -6
- package/src/config.ts +73 -0
- package/src/constants.ts +12 -0
- package/src/graphql/resolvers.ts +1010 -0
- package/src/graphql/schema.ts +452 -0
- package/src/graphql/server.ts +154 -0
- package/src/graphql/types.ts +7 -0
- package/src/handlers/resource-handlers.ts +186 -0
- package/src/index.ts +152 -663
- package/src/prompts/index.ts +4 -4
- package/src/resources/actors.ts +58 -76
- package/src/resources/assets.ts +147 -134
- package/src/resources/levels.ts +28 -33
- package/src/server/resource-registry.ts +47 -0
- package/src/server/tool-registry.ts +354 -0
- package/src/server-setup.ts +148 -0
- package/src/services/health-monitor.ts +132 -0
- package/src/services/metrics-server.ts +142 -0
- package/src/tools/actors.ts +417 -322
- package/src/tools/animation.ts +671 -461
- package/src/tools/assets.ts +353 -289
- package/src/tools/audio.ts +323 -766
- package/src/tools/base-tool.ts +52 -0
- package/src/tools/behavior-tree.ts +45 -0
- package/src/tools/blueprint/helpers.ts +189 -0
- package/src/tools/blueprint.ts +787 -965
- package/src/tools/consolidated-tool-definitions.ts +993 -515
- package/src/tools/consolidated-tool-handlers.ts +272 -1139
- package/src/tools/debug.ts +292 -187
- package/src/tools/dynamic-handler-registry.ts +151 -0
- package/src/tools/editor.ts +309 -246
- package/src/tools/engine.ts +14 -3
- package/src/tools/environment.ts +287 -0
- package/src/tools/foliage.ts +314 -379
- package/src/tools/handlers/actor-handlers.ts +271 -0
- package/src/tools/handlers/animation-handlers.ts +237 -0
- package/src/tools/handlers/argument-helper.ts +142 -0
- package/src/tools/handlers/asset-handlers.ts +532 -0
- package/src/tools/handlers/audio-handlers.ts +194 -0
- package/src/tools/handlers/blueprint-handlers.ts +380 -0
- package/src/tools/handlers/common-handlers.ts +87 -0
- package/src/tools/handlers/editor-handlers.ts +123 -0
- package/src/tools/handlers/effect-handlers.ts +220 -0
- package/src/tools/handlers/environment-handlers.ts +183 -0
- package/src/tools/handlers/graph-handlers.ts +116 -0
- package/src/tools/handlers/input-handlers.ts +28 -0
- package/src/tools/handlers/inspect-handlers.ts +450 -0
- package/src/tools/handlers/level-handlers.ts +252 -0
- package/src/tools/handlers/lighting-handlers.ts +147 -0
- package/src/tools/handlers/performance-handlers.ts +132 -0
- package/src/tools/handlers/pipeline-handlers.ts +127 -0
- package/src/tools/handlers/sequence-handlers.ts +415 -0
- package/src/tools/handlers/system-handlers.ts +564 -0
- package/src/tools/input.ts +101 -0
- package/src/tools/introspection.ts +493 -584
- package/src/tools/landscape.ts +394 -489
- package/src/tools/level.ts +752 -694
- package/src/tools/lighting.ts +583 -984
- package/src/tools/logs.ts +9 -57
- package/src/tools/materials.ts +231 -121
- package/src/tools/niagara.ts +293 -168
- package/src/tools/performance.ts +320 -168
- package/src/tools/physics.ts +268 -613
- package/src/tools/property-dictionary.ts +98 -0
- package/src/tools/sequence.ts +255 -815
- package/src/tools/tool-definition-utils.ts +35 -0
- package/src/tools/ui.ts +207 -283
- package/src/types/env.ts +0 -10
- package/src/types/tool-interfaces.ts +250 -0
- package/src/types/tool-types.ts +243 -21
- package/src/unreal-bridge.ts +460 -1550
- package/src/utils/command-validator.ts +75 -0
- package/src/utils/elicitation.ts +10 -7
- package/src/utils/error-handler.ts +14 -90
- package/src/utils/ini-reader.ts +86 -0
- package/src/utils/logger.ts +8 -3
- package/src/utils/normalize.ts +60 -0
- package/src/utils/response-factory.ts +39 -0
- package/src/utils/response-validator.ts +176 -56
- package/src/utils/result-helpers.ts +21 -19
- package/src/utils/safe-json.ts +14 -11
- package/src/utils/unreal-command-queue.ts +152 -0
- package/src/utils/validation.ts +4 -1
- package/src/wasm/index.ts +838 -0
- package/test-server.mjs +100 -0
- package/tests/run-unreal-tool-tests.mjs +242 -14
- package/tests/test-animation.mjs +44 -0
- package/tests/test-asset-advanced.mjs +82 -0
- package/tests/test-asset-errors.mjs +35 -0
- package/tests/test-audio.mjs +219 -0
- package/tests/test-automation-timeouts.mjs +98 -0
- package/tests/test-behavior-tree.mjs +261 -0
- package/tests/test-blueprint-events.mjs +35 -0
- package/tests/test-blueprint-graph.mjs +79 -0
- package/tests/test-blueprint.mjs +577 -0
- package/tests/test-client-mode.mjs +86 -0
- package/tests/test-console-command.mjs +56 -0
- package/tests/test-control-actor.mjs +425 -0
- package/tests/test-control-editor.mjs +80 -0
- package/tests/test-extra-tools.mjs +38 -0
- package/tests/test-graphql.mjs +322 -0
- package/tests/test-inspect.mjs +72 -0
- package/tests/test-landscape.mjs +60 -0
- package/tests/test-manage-asset.mjs +438 -0
- package/tests/test-manage-level.mjs +70 -0
- package/tests/test-materials.mjs +356 -0
- package/tests/test-niagara.mjs +185 -0
- package/tests/test-no-inline-python.mjs +122 -0
- package/tests/test-plugin-handshake.mjs +82 -0
- package/tests/test-render.mjs +33 -0
- package/tests/test-runner.mjs +933 -0
- package/tests/test-search-assets.mjs +66 -0
- package/tests/test-sequence.mjs +68 -0
- package/tests/test-system.mjs +57 -0
- package/tests/test-wasm.mjs +193 -0
- package/tests/test-world-partition.mjs +215 -0
- package/tsconfig.json +3 -3
- package/wasm/Cargo.lock +363 -0
- package/wasm/Cargo.toml +42 -0
- package/wasm/LICENSE +21 -0
- package/wasm/README.md +253 -0
- package/wasm/src/dependency_resolver.rs +377 -0
- package/wasm/src/lib.rs +153 -0
- package/wasm/src/property_parser.rs +271 -0
- package/wasm/src/transform_math.rs +396 -0
- package/wasm/tests/integration.rs +109 -0
- package/.github/workflows/smithery-build.yml +0 -29
- package/dist/tools/build_environment_advanced.d.ts +0 -65
- package/dist/tools/build_environment_advanced.js +0 -633
- package/dist/tools/rc.d.ts +0 -110
- package/dist/tools/rc.js +0 -437
- package/dist/tools/visual.d.ts +0 -40
- package/dist/tools/visual.js +0 -282
- package/dist/utils/http.d.ts +0 -6
- package/dist/utils/http.js +0 -151
- package/dist/utils/python-output.d.ts +0 -18
- package/dist/utils/python-output.js +0 -290
- package/dist/utils/python.d.ts +0 -2
- package/dist/utils/python.js +0 -4
- package/dist/utils/stdio-redirect.d.ts +0 -2
- package/dist/utils/stdio-redirect.js +0 -20
- package/docs/unreal-tool-test-cases.md +0 -574
- package/smithery.yaml +0 -29
- package/src/tools/build_environment_advanced.ts +0 -732
- package/src/tools/rc.ts +0 -515
- package/src/tools/visual.ts +0 -281
- package/src/utils/http.ts +0 -187
- package/src/utils/python-output.ts +0 -351
- package/src/utils/python.ts +0 -3
- package/src/utils/stdio-redirect.ts +0 -18
package/src/index.ts
CHANGED
|
@@ -1,156 +1,120 @@
|
|
|
1
|
-
|
|
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 {
|
|
7
|
-
import {
|
|
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 {
|
|
46
|
-
import {
|
|
47
|
-
import {
|
|
48
|
-
import {
|
|
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
|
|
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
|
-
|
|
53
|
-
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
|
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
|
-
//
|
|
81
|
-
|
|
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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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
|
-
|
|
253
|
-
|
|
254
|
-
},
|
|
255
|
-
logging: {}
|
|
138
|
+
resources: {},
|
|
139
|
+
prompts: {}
|
|
256
140
|
}
|
|
257
141
|
}
|
|
258
142
|
);
|
|
259
143
|
|
|
260
|
-
//
|
|
261
|
-
const
|
|
262
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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.
|
|
709
|
-
if (config.
|
|
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
|
-
|
|
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
|
}
|