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.
- 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-config.yml +51 -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 +27 -0
- package/.github/workflows/labeler.yml +17 -0
- package/.github/workflows/links.yml +80 -0
- package/.github/workflows/pr-size-labeler.yml +137 -0
- package/.github/workflows/publish-mcp.yml +13 -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 +338 -31
- package/CONTRIBUTING.md +140 -0
- package/GEMINI.md +115 -0
- package/Public/Plugin_setup_guide.mp4 +0 -0
- package/README.md +189 -128
- 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 +26 -0
- package/dist/config.js +59 -0
- package/dist/constants.d.ts +16 -0
- package/dist/constants.js +16 -0
- package/dist/graphql/loaders.d.ts +64 -0
- package/dist/graphql/loaders.js +117 -0
- package/dist/graphql/resolvers.d.ts +268 -0
- package/dist/graphql/resolvers.js +746 -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 +117 -0
- package/dist/graphql/types.d.ts +9 -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 +33 -18
- package/dist/index.js +130 -619
- 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 +20 -0
- package/dist/server-setup.js +71 -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 +163 -9
- package/dist/tools/actors.js +356 -311
- package/dist/tools/animation.d.ts +135 -4
- package/dist/tools/animation.js +510 -411
- package/dist/tools/assets.d.ts +75 -29
- package/dist/tools/assets.js +265 -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.d.ts +208 -126
- 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 +198 -1027
- package/dist/tools/debug.d.ts +143 -85
- package/dist/tools/debug.js +234 -180
- package/dist/tools/dynamic-handler-registry.d.ts +13 -0
- package/dist/tools/dynamic-handler-registry.js +23 -0
- package/dist/tools/editor.d.ts +30 -83
- package/dist/tools/editor.js +247 -244
- package/dist/tools/engine.d.ts +10 -4
- package/dist/tools/engine.js +13 -5
- package/dist/tools/environment.d.ts +30 -0
- package/dist/tools/environment.js +267 -0
- package/dist/tools/foliage.d.ts +65 -99
- package/dist/tools/foliage.js +221 -331
- package/dist/tools/handlers/actor-handlers.d.ts +3 -0
- package/dist/tools/handlers/actor-handlers.js +227 -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 +54 -93
- package/dist/tools/landscape.js +284 -409
- package/dist/tools/level.d.ts +66 -27
- package/dist/tools/level.js +647 -675
- package/dist/tools/lighting.d.ts +77 -38
- package/dist/tools/lighting.js +445 -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 +194 -118
- package/dist/tools/niagara.d.ts +149 -39
- package/dist/tools/niagara.js +267 -182
- package/dist/tools/performance.d.ts +27 -13
- package/dist/tools/performance.js +203 -122
- package/dist/tools/physics.d.ts +32 -77
- package/dist/tools/physics.js +175 -582
- package/dist/tools/property-dictionary.d.ts +13 -0
- package/dist/tools/property-dictionary.js +82 -0
- package/dist/tools/sequence.d.ts +85 -60
- package/dist/tools/sequence.js +208 -747
- 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 +64 -34
- package/dist/tools/ui.js +134 -214
- package/dist/types/automation-responses.d.ts +115 -0
- package/dist/types/automation-responses.js +2 -0
- package/dist/types/env.d.ts +0 -3
- package/dist/types/env.js +0 -7
- package/dist/types/responses.d.ts +249 -0
- package/dist/types/responses.js +2 -0
- 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 +68 -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/path-security.d.ts +2 -0
- package/dist/utils/path-security.js +24 -0
- package/dist/utils/response-factory.d.ts +7 -0
- package/dist/utils/response-factory.js +27 -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 +684 -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 +67 -28
- 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 +136 -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 +58 -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 +19 -0
- package/src/graphql/loaders.ts +244 -0
- package/src/graphql/resolvers.ts +1008 -0
- package/src/graphql/schema.ts +452 -0
- package/src/graphql/server.ts +156 -0
- package/src/graphql/types.ts +10 -0
- package/src/handlers/resource-handlers.ts +186 -0
- package/src/index.ts +166 -664
- package/src/resources/actors.ts +58 -76
- package/src/resources/assets.ts +148 -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 +114 -0
- package/src/services/health-monitor.ts +132 -0
- package/src/services/metrics-server.ts +142 -0
- package/src/tools/actors.ts +426 -323
- package/src/tools/animation.ts +672 -461
- package/src/tools/assets.ts +364 -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.ts +792 -970
- package/src/tools/consolidated-tool-definitions.ts +993 -515
- package/src/tools/consolidated-tool-handlers.ts +258 -1146
- package/src/tools/debug.ts +292 -187
- package/src/tools/dynamic-handler-registry.ts +33 -0
- package/src/tools/editor.ts +329 -253
- package/src/tools/engine.ts +14 -3
- package/src/tools/environment.ts +281 -0
- package/src/tools/foliage.ts +330 -392
- package/src/tools/handlers/actor-handlers.ts +265 -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 +418 -507
- package/src/tools/level.ts +786 -708
- package/src/tools/lighting.ts +588 -984
- package/src/tools/logs.ts +9 -57
- package/src/tools/materials.ts +237 -121
- package/src/tools/niagara.ts +335 -168
- package/src/tools/performance.ts +320 -169
- package/src/tools/physics.ts +274 -613
- package/src/tools/property-dictionary.ts +98 -0
- package/src/tools/sequence.ts +276 -820
- package/src/tools/tool-definition-utils.ts +35 -0
- package/src/tools/ui.ts +205 -283
- package/src/types/automation-responses.ts +119 -0
- package/src/types/env.ts +0 -10
- package/src/types/responses.ts +355 -0
- 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 +76 -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.test.ts +162 -0
- package/src/utils/normalize.ts +60 -0
- package/src/utils/path-security.ts +43 -0
- package/src/utils/response-factory.ts +44 -0
- package/src/utils/response-validator.ts +176 -56
- package/src/utils/result-helpers.ts +21 -19
- package/src/utils/safe-json.test.ts +90 -0
- package/src/utils/safe-json.ts +14 -11
- package/src/utils/unreal-command-queue.ts +152 -0
- package/src/utils/validation.test.ts +184 -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 +369 -0
- package/tests/test-asset-advanced.mjs +82 -0
- package/tests/test-asset-errors.mjs +35 -0
- package/tests/test-asset-graph.mjs +311 -0
- package/tests/test-audio.mjs +417 -0
- package/tests/test-automation-timeouts.mjs +98 -0
- package/tests/test-behavior-tree.mjs +444 -0
- package/tests/test-blueprint-graph.mjs +410 -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 +112 -0
- package/tests/test-graphql.mjs +372 -0
- package/tests/test-input.mjs +349 -0
- package/tests/test-inspect.mjs +302 -0
- package/tests/test-landscape.mjs +316 -0
- package/tests/test-lighting.mjs +428 -0
- package/tests/test-manage-asset.mjs +438 -0
- package/tests/test-manage-level.mjs +89 -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-performance.mjs +539 -0
- package/tests/test-plugin-handshake.mjs +82 -0
- package/tests/test-runner.mjs +933 -0
- package/tests/test-sequence.mjs +104 -0
- package/tests/test-system.mjs +96 -0
- package/tests/test-wasm.mjs +283 -0
- package/tests/test-world-partition.mjs +215 -0
- package/tsconfig.json +3 -3
- package/vitest.config.ts +35 -0
- 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/prompts/index.d.ts +0 -21
- package/dist/prompts/index.js +0 -217
- 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/prompts/index.ts +0 -249
- 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
|
@@ -1,1190 +1,302 @@
|
|
|
1
|
-
// Consolidated tool handlers - maps 13 tools to all 36 operations
|
|
2
1
|
import { cleanObject } from '../utils/safe-json.js';
|
|
3
2
|
import { Logger } from '../utils/logger.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
3
|
+
import { ResponseFactory } from '../utils/response-factory.js';
|
|
4
|
+
import { ITools } from '../types/tool-interfaces.js';
|
|
5
|
+
import { toolRegistry } from './dynamic-handler-registry.js';
|
|
6
|
+
import { executeAutomationRequest, requireAction } from './handlers/common-handlers.js';
|
|
7
|
+
import { handleAssetTools } from './handlers/asset-handlers.js';
|
|
8
|
+
import { handleActorTools } from './handlers/actor-handlers.js';
|
|
9
|
+
import { handleEditorTools } from './handlers/editor-handlers.js';
|
|
10
|
+
import { handleLevelTools } from './handlers/level-handlers.js';
|
|
11
|
+
import { handleBlueprintTools, handleBlueprintGet } from './handlers/blueprint-handlers.js';
|
|
12
|
+
import { handleSequenceTools } from './handlers/sequence-handlers.js';
|
|
13
|
+
import { handleAnimationTools } from './handlers/animation-handlers.js';
|
|
14
|
+
import { handleEffectTools } from './handlers/effect-handlers.js';
|
|
15
|
+
import { handleEnvironmentTools } from './handlers/environment-handlers.js';
|
|
16
|
+
import { handleSystemTools, handleConsoleCommand } from './handlers/system-handlers.js';
|
|
17
|
+
import { handleInspectTools } from './handlers/inspect-handlers.js';
|
|
18
|
+
import { handlePipelineTools } from './handlers/pipeline-handlers.js';
|
|
19
|
+
import { handleGraphTools } from './handlers/graph-handlers.js';
|
|
20
|
+
import { handleAudioTools } from './handlers/audio-handlers.js';
|
|
21
|
+
import { handleLightingTools } from './handlers/lighting-handlers.js';
|
|
22
|
+
import { handlePerformanceTools } from './handlers/performance-handlers.js';
|
|
23
|
+
import { handleInputTools } from './handlers/input-handlers.js';
|
|
24
|
+
// import { getDynamicHandlerForTool } from './dynamic-handler-registry.js';
|
|
25
|
+
// import { consolidatedToolDefinitions } from './consolidated-tool-definitions.js';
|
|
26
|
+
|
|
27
|
+
type NormalizedToolCall = {
|
|
28
|
+
name: string;
|
|
29
|
+
action: string;
|
|
30
|
+
args: any;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const MATERIAL_GRAPH_ACTION_MAP: Record<string, string> = {
|
|
34
|
+
add_material_node: 'add_node',
|
|
35
|
+
connect_material_pins: 'connect_pins',
|
|
36
|
+
remove_material_node: 'remove_node',
|
|
37
|
+
break_material_connections: 'break_connections',
|
|
38
|
+
get_material_node_details: 'get_node_details',
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const BEHAVIOR_TREE_ACTION_MAP: Record<string, string> = {
|
|
42
|
+
add_bt_node: 'add_node',
|
|
43
|
+
connect_bt_nodes: 'connect_nodes',
|
|
44
|
+
remove_bt_node: 'remove_node',
|
|
45
|
+
break_bt_connections: 'break_connections',
|
|
46
|
+
set_bt_node_properties: 'set_node_properties'
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const NIAGARA_GRAPH_ACTION_MAP: Record<string, string> = {
|
|
50
|
+
add_niagara_module: 'add_module',
|
|
51
|
+
connect_niagara_pins: 'connect_pins',
|
|
52
|
+
remove_niagara_node: 'remove_node',
|
|
53
|
+
set_niagara_parameter: 'set_parameter'
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
function isMaterialGraphAction(action: string): boolean {
|
|
57
|
+
return (
|
|
58
|
+
Object.prototype.hasOwnProperty.call(MATERIAL_GRAPH_ACTION_MAP, action) ||
|
|
59
|
+
action.includes('material_node') ||
|
|
60
|
+
action.includes('material_pins') ||
|
|
61
|
+
action.includes('material_connections')
|
|
62
|
+
);
|
|
13
63
|
}
|
|
14
64
|
|
|
15
|
-
function
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
return action;
|
|
65
|
+
function isBehaviorTreeGraphAction(action: string): boolean {
|
|
66
|
+
return (
|
|
67
|
+
Object.prototype.hasOwnProperty.call(BEHAVIOR_TREE_ACTION_MAP, action) ||
|
|
68
|
+
action.includes('_bt_') ||
|
|
69
|
+
action.includes('behavior_tree')
|
|
70
|
+
);
|
|
22
71
|
}
|
|
23
72
|
|
|
24
|
-
function
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
73
|
+
function isNiagaraGraphAction(action: string): boolean {
|
|
74
|
+
return (
|
|
75
|
+
Object.prototype.hasOwnProperty.call(NIAGARA_GRAPH_ACTION_MAP, action) ||
|
|
76
|
+
action.includes('niagara_module') ||
|
|
77
|
+
action.includes('niagara_pins') ||
|
|
78
|
+
action.includes('niagara_node') ||
|
|
79
|
+
action.includes('niagara_parameter')
|
|
80
|
+
);
|
|
29
81
|
}
|
|
30
82
|
|
|
31
|
-
function
|
|
32
|
-
|
|
33
|
-
|
|
83
|
+
function normalizeToolCall(
|
|
84
|
+
name: string,
|
|
85
|
+
args: any
|
|
86
|
+
): NormalizedToolCall {
|
|
87
|
+
let normalizedName = name;
|
|
88
|
+
let action: string;
|
|
89
|
+
|
|
90
|
+
if (args && typeof (args as any).action === 'string') {
|
|
91
|
+
action = (args as any).action;
|
|
92
|
+
} else if (normalizedName === 'console_command') {
|
|
93
|
+
normalizedName = 'system_control';
|
|
94
|
+
action = 'console_command';
|
|
95
|
+
} else {
|
|
96
|
+
action = requireAction(args);
|
|
34
97
|
}
|
|
35
|
-
return value;
|
|
36
|
-
}
|
|
37
98
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (
|
|
43
|
-
!vector ||
|
|
44
|
-
typeof vector.x !== 'number' ||
|
|
45
|
-
typeof vector.y !== 'number' ||
|
|
46
|
-
typeof vector.z !== 'number'
|
|
47
|
-
) {
|
|
48
|
-
throw new Error(message);
|
|
99
|
+
if (normalizedName === 'create_effect') normalizedName = 'manage_effect';
|
|
100
|
+
if (normalizedName === 'console_command') {
|
|
101
|
+
normalizedName = 'system_control';
|
|
102
|
+
action = 'console_command';
|
|
49
103
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
function getElicitationTimeoutMs(tools: any): number | undefined {
|
|
54
|
-
if (!tools) return undefined;
|
|
55
|
-
const direct = tools.elicitationTimeoutMs;
|
|
56
|
-
if (typeof direct === 'number' && Number.isFinite(direct)) {
|
|
57
|
-
return direct;
|
|
104
|
+
if (normalizedName === 'manage_pipeline') {
|
|
105
|
+
normalizedName = 'system_control';
|
|
106
|
+
action = 'run_ubt';
|
|
58
107
|
}
|
|
59
|
-
if (
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
return value;
|
|
63
|
-
}
|
|
108
|
+
if (normalizedName === 'manage_tests') {
|
|
109
|
+
normalizedName = 'system_control';
|
|
110
|
+
action = 'run_tests';
|
|
64
111
|
}
|
|
65
|
-
return undefined;
|
|
66
|
-
}
|
|
67
112
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
fieldSchemas: Record<string, { type: 'string' | 'number' | 'integer' | 'boolean'; title?: string; description?: string; enum?: string[]; enumNames?: string[]; minimum?: number; maximum?: number; minLength?: number; maxLength?: number; pattern?: string; format?: string; default?: unknown }>
|
|
73
|
-
) {
|
|
74
|
-
if (
|
|
75
|
-
!tools ||
|
|
76
|
-
typeof tools.supportsElicitation !== 'function' ||
|
|
77
|
-
!tools.supportsElicitation() ||
|
|
78
|
-
typeof tools.elicit !== 'function'
|
|
79
|
-
) {
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const properties: Record<string, any> = {};
|
|
84
|
-
const required: string[] = [];
|
|
85
|
-
|
|
86
|
-
for (const [key, schema] of Object.entries(fieldSchemas)) {
|
|
87
|
-
const value = args?.[key];
|
|
88
|
-
const missing =
|
|
89
|
-
value === undefined ||
|
|
90
|
-
value === null ||
|
|
91
|
-
(typeof value === 'string' && value.trim() === '');
|
|
92
|
-
if (missing) {
|
|
93
|
-
properties[key] = schema;
|
|
94
|
-
required.push(key);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (required.length === 0) return;
|
|
99
|
-
|
|
100
|
-
const timeoutMs = getElicitationTimeoutMs(tools);
|
|
101
|
-
const options: any = {
|
|
102
|
-
fallback: async () => ({ ok: false, error: 'missing-params' })
|
|
113
|
+
return {
|
|
114
|
+
name: normalizedName,
|
|
115
|
+
action,
|
|
116
|
+
args
|
|
103
117
|
};
|
|
104
|
-
|
|
105
|
-
options.timeoutMs = timeoutMs;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
try {
|
|
109
|
-
const elicited = await tools.elicit(
|
|
110
|
-
prompt,
|
|
111
|
-
{ type: 'object', properties, required },
|
|
112
|
-
options
|
|
113
|
-
);
|
|
118
|
+
}
|
|
114
119
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
+
// Registration of default handlers
|
|
121
|
+
function registerDefaultHandlers() {
|
|
122
|
+
// 1. ASSET MANAGER
|
|
123
|
+
toolRegistry.register('manage_asset', async (args, tools) => {
|
|
124
|
+
const action = args.subAction || args.action || requireAction(args); // Fallback assumption
|
|
125
|
+
// Reroute merged functionality
|
|
126
|
+
if (['create_render_target', 'nanite_rebuild_mesh'].includes(action)) {
|
|
127
|
+
const payload = { ...args, subAction: action };
|
|
128
|
+
return cleanObject(await executeAutomationRequest(tools, 'manage_render', payload, `Automation bridge not available for ${action}`));
|
|
129
|
+
}
|
|
130
|
+
if (isMaterialGraphAction(action)) {
|
|
131
|
+
const subAction = MATERIAL_GRAPH_ACTION_MAP[action] || action;
|
|
132
|
+
return await handleGraphTools('manage_material_graph', subAction, args, tools);
|
|
133
|
+
}
|
|
134
|
+
if (isBehaviorTreeGraphAction(action)) {
|
|
135
|
+
const subAction = BEHAVIOR_TREE_ACTION_MAP[action] || action;
|
|
136
|
+
return await handleGraphTools('manage_behavior_tree', subAction, args, tools);
|
|
137
|
+
}
|
|
138
|
+
return await handleAssetTools(action, args, tools);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// 2. BLUEPRINT MANAGER
|
|
142
|
+
toolRegistry.register('manage_blueprint', async (args, tools) => {
|
|
143
|
+
const action = args.action || requireAction(args);
|
|
144
|
+
if (action === 'get_blueprint') {
|
|
145
|
+
return await handleBlueprintGet(args, tools);
|
|
146
|
+
}
|
|
147
|
+
const graphActions = ['create_node', 'delete_node', 'connect_pins', 'break_pin_links', 'set_node_property', 'create_reroute_node', 'get_node_details', 'get_graph_details', 'get_pin_details'];
|
|
148
|
+
if (graphActions.includes(action)) {
|
|
149
|
+
return await handleGraphTools('manage_blueprint_graph', action, args, tools);
|
|
150
|
+
}
|
|
151
|
+
return await handleBlueprintTools(action, args, tools);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// 3. ACTOR CONTROL
|
|
155
|
+
toolRegistry.register('control_actor', async (args, tools) => {
|
|
156
|
+
return await handleActorTools(args.action || requireAction(args), args, tools);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// 4. EDITOR CONTROL
|
|
160
|
+
toolRegistry.register('control_editor', async (args, tools) => {
|
|
161
|
+
const action = args.action || requireAction(args);
|
|
162
|
+
if (action === 'simulate_input') {
|
|
163
|
+
const payload = { ...args, subAction: action };
|
|
164
|
+
return cleanObject(await executeAutomationRequest(tools, 'manage_ui', payload, 'Automation bridge not available'));
|
|
165
|
+
}
|
|
166
|
+
return await handleEditorTools(action, args, tools);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// 5. LEVEL MANAGER
|
|
170
|
+
toolRegistry.register('manage_level', async (args, tools) => {
|
|
171
|
+
const action = args.action || requireAction(args);
|
|
172
|
+
if (['load_cells', 'set_datalayer'].includes(action)) {
|
|
173
|
+
const payload = { ...args, subAction: action };
|
|
174
|
+
return cleanObject(await executeAutomationRequest(tools, 'manage_world_partition', payload, 'Automation bridge not available'));
|
|
175
|
+
}
|
|
176
|
+
return await handleLevelTools(action, args, tools);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// 6. ANIMATION & PHYSICS
|
|
180
|
+
toolRegistry.register('animation_physics', async (args, tools) => await handleAnimationTools(args.action || requireAction(args), args, tools));
|
|
181
|
+
|
|
182
|
+
// 7. EFFECTS MANAGER
|
|
183
|
+
toolRegistry.register('manage_effect', async (args, tools) => {
|
|
184
|
+
const action = args.action || requireAction(args);
|
|
185
|
+
if (isNiagaraGraphAction(action)) {
|
|
186
|
+
// Instance check
|
|
187
|
+
const isInstanceOp = action === 'set_niagara_parameter' && (args.actorName || (args.systemName && !args.assetPath && !args.systemPath));
|
|
188
|
+
if (isInstanceOp) {
|
|
189
|
+
return await handleEffectTools(action, args, tools);
|
|
120
190
|
}
|
|
191
|
+
const subAction = NIAGARA_GRAPH_ACTION_MAP[action] || action;
|
|
192
|
+
return await handleGraphTools('manage_niagara_graph', subAction, args, tools);
|
|
121
193
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
prompt,
|
|
125
|
-
err: (err as any)?.message || String(err)
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
export async function handleConsolidatedToolCall(
|
|
131
|
-
name: string,
|
|
132
|
-
args: any,
|
|
133
|
-
tools: any
|
|
134
|
-
) {
|
|
135
|
-
const startTime = Date.now();
|
|
136
|
-
// Use scoped logger (stderr) to avoid polluting stdout JSON
|
|
137
|
-
log.debug(`Starting execution of ${name} at ${new Date().toISOString()}`);
|
|
138
|
-
|
|
139
|
-
try {
|
|
140
|
-
ensureArgsPresent(args);
|
|
194
|
+
return await handleEffectTools(action, args, tools);
|
|
195
|
+
});
|
|
141
196
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
case 'manage_asset':
|
|
145
|
-
switch (requireAction(args)) {
|
|
146
|
-
case 'list': {
|
|
147
|
-
if (args.directory !== undefined && args.directory !== null && typeof args.directory !== 'string') {
|
|
148
|
-
throw new Error('Invalid directory: must be a string');
|
|
149
|
-
}
|
|
150
|
-
const res = await tools.assetResources.list(args.directory || '/Game', false);
|
|
151
|
-
return cleanObject({ success: true, ...res });
|
|
152
|
-
}
|
|
153
|
-
case 'import': {
|
|
154
|
-
let sourcePath = typeof args.sourcePath === 'string' ? args.sourcePath.trim() : '';
|
|
155
|
-
let destinationPath = typeof args.destinationPath === 'string' ? args.destinationPath.trim() : '';
|
|
197
|
+
// 8. ENVIRONMENT BUILDER
|
|
198
|
+
toolRegistry.register('build_environment', async (args, tools) => await handleEnvironmentTools(args.action || requireAction(args), args, tools));
|
|
156
199
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
200
|
+
// 9. SYSTEM CONTROL
|
|
201
|
+
toolRegistry.register('system_control', async (args, tools) => {
|
|
202
|
+
const action = args.action || requireAction(args);
|
|
203
|
+
if (action === 'console_command') return await handleConsoleCommand(args, tools);
|
|
204
|
+
if (action === 'run_ubt') return await handlePipelineTools(action, args, tools);
|
|
160
205
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
};
|
|
167
|
-
required.push('sourcePath');
|
|
168
|
-
}
|
|
206
|
+
if (action === 'run_tests') return cleanObject(await executeAutomationRequest(tools, 'manage_tests', { ...args, subAction: action }, 'Bridge unavailable'));
|
|
207
|
+
if (action === 'subscribe' || action === 'unsubscribe') return cleanObject(await executeAutomationRequest(tools, 'manage_logs', { ...args, subAction: action }, 'Bridge unavailable'));
|
|
208
|
+
if (action === 'spawn_category') return cleanObject(await executeAutomationRequest(tools, 'manage_debug', { ...args, subAction: action }, 'Bridge unavailable'));
|
|
209
|
+
if (action === 'start_session') return cleanObject(await executeAutomationRequest(tools, 'manage_insights', { ...args, subAction: action }, 'Bridge unavailable'));
|
|
210
|
+
if (action === 'lumen_update_scene') return cleanObject(await executeAutomationRequest(tools, 'manage_render', { ...args, subAction: action }, 'Bridge unavailable'));
|
|
169
211
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
type: 'string',
|
|
173
|
-
title: 'Destination Path',
|
|
174
|
-
description: 'Unreal content path where the asset should be imported (e.g., /Game/MCP/Assets)'
|
|
175
|
-
};
|
|
176
|
-
required.push('destinationPath');
|
|
177
|
-
}
|
|
212
|
+
return await handleSystemTools(action, args, tools);
|
|
213
|
+
});
|
|
178
214
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
const options: any = { fallback: async () => ({ ok: false, error: 'missing-import-params' }) };
|
|
182
|
-
if (typeof timeoutMs === 'number') {
|
|
183
|
-
options.timeoutMs = timeoutMs;
|
|
184
|
-
}
|
|
185
|
-
const elicited = await tools.elicit(
|
|
186
|
-
'Provide the missing import parameters for manage_asset.import',
|
|
187
|
-
{ type: 'object', properties: schemaProps, required },
|
|
188
|
-
options
|
|
189
|
-
);
|
|
215
|
+
// 10. SEQUENCER
|
|
216
|
+
toolRegistry.register('manage_sequence', async (args, tools) => await handleSequenceTools(args.action || requireAction(args), args, tools));
|
|
190
217
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
sourcePath = elicited.value.sourcePath.trim();
|
|
194
|
-
}
|
|
195
|
-
if (typeof elicited.value.destinationPath === 'string') {
|
|
196
|
-
destinationPath = elicited.value.destinationPath.trim();
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
}
|
|
218
|
+
// 11. INTROSPECTION
|
|
219
|
+
toolRegistry.register('inspect', async (args, tools) => await handleInspectTools(args.action || requireAction(args), args, tools));
|
|
201
220
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
const res = await tools.assetTools.importAsset(sourcePathValidated, destinationPathValidated);
|
|
205
|
-
return cleanObject(res);
|
|
206
|
-
}
|
|
207
|
-
case 'create_material': {
|
|
208
|
-
await elicitMissingPrimitiveArgs(
|
|
209
|
-
tools,
|
|
210
|
-
args,
|
|
211
|
-
'Provide the material details for manage_asset.create_material',
|
|
212
|
-
{
|
|
213
|
-
name: {
|
|
214
|
-
type: 'string',
|
|
215
|
-
title: 'Material Name',
|
|
216
|
-
description: 'Name for the new material asset'
|
|
217
|
-
},
|
|
218
|
-
path: {
|
|
219
|
-
type: 'string',
|
|
220
|
-
title: 'Save Path',
|
|
221
|
-
description: 'Optional Unreal content path where the material should be saved'
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
);
|
|
225
|
-
const sanitizedName = typeof args.name === 'string' ? args.name.trim() : args.name;
|
|
226
|
-
const sanitizedPath = typeof args.path === 'string' ? args.path.trim() : args.path;
|
|
227
|
-
const name = requireNonEmptyString(sanitizedName, 'name', 'Invalid name: must be a non-empty string');
|
|
228
|
-
const res = await tools.materialTools.createMaterial(name, sanitizedPath || '/Game/Materials');
|
|
229
|
-
return cleanObject(res);
|
|
230
|
-
}
|
|
231
|
-
default:
|
|
232
|
-
throw new Error(`Unknown asset action: ${args.action}`);
|
|
233
|
-
}
|
|
221
|
+
// 12. AUDIO
|
|
222
|
+
toolRegistry.register('manage_audio', async (args, tools) => await handleAudioTools(args.action || requireAction(args), args, tools));
|
|
234
223
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
switch (requireAction(args)) {
|
|
238
|
-
case 'spawn': {
|
|
239
|
-
await elicitMissingPrimitiveArgs(
|
|
240
|
-
tools,
|
|
241
|
-
args,
|
|
242
|
-
'Provide the spawn parameters for control_actor.spawn',
|
|
243
|
-
{
|
|
244
|
-
classPath: {
|
|
245
|
-
type: 'string',
|
|
246
|
-
title: 'Actor Class or Asset Path',
|
|
247
|
-
description: 'Class name (e.g., StaticMeshActor) or asset path (e.g., /Engine/BasicShapes/Cube) to spawn'
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
);
|
|
251
|
-
const classPathInput = typeof args.classPath === 'string' ? args.classPath.trim() : args.classPath;
|
|
252
|
-
const classPath = requireNonEmptyString(classPathInput, 'classPath', 'Invalid classPath: must be a non-empty string');
|
|
253
|
-
const actorNameInput = typeof args.actorName === 'string' && args.actorName.trim() !== ''
|
|
254
|
-
? args.actorName
|
|
255
|
-
: (typeof args.name === 'string' ? args.name : undefined);
|
|
256
|
-
const res = await tools.actorTools.spawn({
|
|
257
|
-
classPath,
|
|
258
|
-
location: args.location,
|
|
259
|
-
rotation: args.rotation,
|
|
260
|
-
actorName: actorNameInput
|
|
261
|
-
});
|
|
262
|
-
return cleanObject(res);
|
|
263
|
-
}
|
|
264
|
-
case 'delete': {
|
|
265
|
-
await elicitMissingPrimitiveArgs(
|
|
266
|
-
tools,
|
|
267
|
-
args,
|
|
268
|
-
'Which actor should control_actor.delete remove?',
|
|
269
|
-
{
|
|
270
|
-
actorName: {
|
|
271
|
-
type: 'string',
|
|
272
|
-
title: 'Actor Name',
|
|
273
|
-
description: 'Exact label of the actor to delete'
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
);
|
|
277
|
-
const actorNameArg = typeof args.actorName === 'string' && args.actorName.trim() !== ''
|
|
278
|
-
? args.actorName
|
|
279
|
-
: (typeof args.name === 'string' ? args.name : undefined);
|
|
280
|
-
const actorName = requireNonEmptyString(actorNameArg, 'actorName', 'Invalid actorName');
|
|
281
|
-
const res = await tools.bridge.executeEditorFunction('DELETE_ACTOR', { actor_name: actorName });
|
|
282
|
-
return cleanObject(res);
|
|
283
|
-
}
|
|
284
|
-
case 'apply_force': {
|
|
285
|
-
await elicitMissingPrimitiveArgs(
|
|
286
|
-
tools,
|
|
287
|
-
args,
|
|
288
|
-
'Provide the target actor for control_actor.apply_force',
|
|
289
|
-
{
|
|
290
|
-
actorName: {
|
|
291
|
-
type: 'string',
|
|
292
|
-
title: 'Actor Name',
|
|
293
|
-
description: 'Physics-enabled actor that should receive the force'
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
);
|
|
297
|
-
const actorName = requireNonEmptyString(args.actorName, 'actorName', 'Invalid actorName');
|
|
298
|
-
const vector = requireVector3Components(args.force, 'Invalid force: must have numeric x,y,z');
|
|
299
|
-
const res = await tools.physicsTools.applyForce({
|
|
300
|
-
actorName,
|
|
301
|
-
forceType: 'Force',
|
|
302
|
-
vector
|
|
303
|
-
});
|
|
304
|
-
return cleanObject(res);
|
|
305
|
-
}
|
|
306
|
-
default:
|
|
307
|
-
throw new Error(`Unknown actor action: ${args.action}`);
|
|
308
|
-
}
|
|
224
|
+
// 13. BEHAVIOR TREE
|
|
225
|
+
toolRegistry.register('manage_behavior_tree', async (args, tools) => await handleGraphTools('manage_behavior_tree', args.action || requireAction(args), args, tools));
|
|
309
226
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
switch (requireAction(args)) {
|
|
313
|
-
case 'play': {
|
|
314
|
-
const res = await tools.editorTools.playInEditor();
|
|
315
|
-
return cleanObject(res);
|
|
316
|
-
}
|
|
317
|
-
case 'stop': {
|
|
318
|
-
const res = await tools.editorTools.stopPlayInEditor();
|
|
319
|
-
return cleanObject(res);
|
|
320
|
-
}
|
|
321
|
-
case 'pause': {
|
|
322
|
-
const res = await tools.editorTools.pausePlayInEditor();
|
|
323
|
-
return cleanObject(res);
|
|
324
|
-
}
|
|
325
|
-
case 'set_game_speed': {
|
|
326
|
-
const speed = requirePositiveNumber(args.speed, 'speed', 'Invalid speed: must be a positive number');
|
|
327
|
-
// Use console command via bridge
|
|
328
|
-
const res = await tools.bridge.executeConsoleCommand(`slomo ${speed}`);
|
|
329
|
-
return cleanObject(res);
|
|
330
|
-
}
|
|
331
|
-
case 'eject': {
|
|
332
|
-
const res = await tools.bridge.executeConsoleCommand('eject');
|
|
333
|
-
return cleanObject(res);
|
|
334
|
-
}
|
|
335
|
-
case 'possess': {
|
|
336
|
-
const res = await tools.bridge.executeConsoleCommand('viewself');
|
|
337
|
-
return cleanObject(res);
|
|
338
|
-
}
|
|
339
|
-
case 'set_camera': {
|
|
340
|
-
const res = await tools.editorTools.setViewportCamera(args.location, args.rotation);
|
|
341
|
-
return cleanObject(res);
|
|
342
|
-
}
|
|
343
|
-
case 'set_view_mode': {
|
|
344
|
-
await elicitMissingPrimitiveArgs(
|
|
345
|
-
tools,
|
|
346
|
-
args,
|
|
347
|
-
'Provide the view mode for control_editor.set_view_mode',
|
|
348
|
-
{
|
|
349
|
-
viewMode: {
|
|
350
|
-
type: 'string',
|
|
351
|
-
title: 'View Mode',
|
|
352
|
-
description: 'Viewport view mode (e.g., Lit, Unlit, Wireframe)'
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
);
|
|
356
|
-
const viewMode = requireNonEmptyString(args.viewMode, 'viewMode', 'Missing required parameter: viewMode');
|
|
357
|
-
const res = await tools.bridge.setSafeViewMode(viewMode);
|
|
358
|
-
return cleanObject(res);
|
|
359
|
-
}
|
|
360
|
-
default:
|
|
361
|
-
throw new Error(`Unknown editor action: ${args.action}`);
|
|
362
|
-
}
|
|
227
|
+
// 14. BLUEPRINT GRAPH DIRECT
|
|
228
|
+
toolRegistry.register('manage_blueprint_graph', async (args, tools) => await handleGraphTools('manage_blueprint_graph', args.action || requireAction(args), args, tools));
|
|
363
229
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
tools,
|
|
370
|
-
args,
|
|
371
|
-
'Select the level to load for manage_level.load',
|
|
372
|
-
{
|
|
373
|
-
levelPath: {
|
|
374
|
-
type: 'string',
|
|
375
|
-
title: 'Level Path',
|
|
376
|
-
description: 'Content path of the level asset to load (e.g., /Game/Maps/MyLevel)'
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
);
|
|
380
|
-
const levelPath = requireNonEmptyString(args.levelPath, 'levelPath', 'Missing required parameter: levelPath');
|
|
381
|
-
const res = await tools.levelTools.loadLevel({ levelPath, streaming: !!args.streaming });
|
|
382
|
-
return cleanObject(res);
|
|
383
|
-
}
|
|
384
|
-
case 'save': {
|
|
385
|
-
const res = await tools.levelTools.saveLevel({ levelName: args.levelName, savePath: args.savePath });
|
|
386
|
-
return cleanObject(res);
|
|
387
|
-
}
|
|
388
|
-
case 'stream': {
|
|
389
|
-
await elicitMissingPrimitiveArgs(
|
|
390
|
-
tools,
|
|
391
|
-
args,
|
|
392
|
-
'Provide the streaming level name for manage_level.stream',
|
|
393
|
-
{
|
|
394
|
-
levelName: {
|
|
395
|
-
type: 'string',
|
|
396
|
-
title: 'Level Name',
|
|
397
|
-
description: 'Streaming level name to toggle'
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
);
|
|
401
|
-
const levelName = requireNonEmptyString(args.levelName, 'levelName', 'Missing required parameter: levelName');
|
|
402
|
-
const res = await tools.levelTools.streamLevel({ levelName, shouldBeLoaded: !!args.shouldBeLoaded, shouldBeVisible: !!args.shouldBeVisible });
|
|
403
|
-
return cleanObject(res);
|
|
404
|
-
}
|
|
405
|
-
case 'create_light': {
|
|
406
|
-
await elicitMissingPrimitiveArgs(
|
|
407
|
-
tools,
|
|
408
|
-
args,
|
|
409
|
-
'Provide the light details for manage_level.create_light',
|
|
410
|
-
{
|
|
411
|
-
lightType: {
|
|
412
|
-
type: 'string',
|
|
413
|
-
title: 'Light Type',
|
|
414
|
-
description: 'Directional, Point, Spot, Rect, or Sky'
|
|
415
|
-
},
|
|
416
|
-
name: {
|
|
417
|
-
type: 'string',
|
|
418
|
-
title: 'Light Name',
|
|
419
|
-
description: 'Name for the new light actor'
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
);
|
|
423
|
-
const lightType = requireNonEmptyString(args.lightType, 'lightType', 'Missing required parameter: lightType');
|
|
424
|
-
const name = requireNonEmptyString(args.name, 'name', 'Invalid name');
|
|
425
|
-
const typeKey = lightType.toLowerCase();
|
|
426
|
-
const toVector = (value: any, fallback: [number, number, number]): [number, number, number] => {
|
|
427
|
-
if (Array.isArray(value) && value.length === 3) {
|
|
428
|
-
return [Number(value[0]) || 0, Number(value[1]) || 0, Number(value[2]) || 0];
|
|
429
|
-
}
|
|
430
|
-
if (value && typeof value === 'object') {
|
|
431
|
-
return [Number(value.x) || 0, Number(value.y) || 0, Number(value.z) || 0];
|
|
432
|
-
}
|
|
433
|
-
return fallback;
|
|
434
|
-
};
|
|
435
|
-
const toRotator = (value: any, fallback: [number, number, number]): [number, number, number] => {
|
|
436
|
-
if (Array.isArray(value) && value.length === 3) {
|
|
437
|
-
return [Number(value[0]) || 0, Number(value[1]) || 0, Number(value[2]) || 0];
|
|
438
|
-
}
|
|
439
|
-
if (value && typeof value === 'object') {
|
|
440
|
-
return [Number(value.pitch) || 0, Number(value.yaw) || 0, Number(value.roll) || 0];
|
|
441
|
-
}
|
|
442
|
-
return fallback;
|
|
443
|
-
};
|
|
444
|
-
const toColor = (value: any): [number, number, number] | undefined => {
|
|
445
|
-
if (Array.isArray(value) && value.length === 3) {
|
|
446
|
-
return [Number(value[0]) || 0, Number(value[1]) || 0, Number(value[2]) || 0];
|
|
447
|
-
}
|
|
448
|
-
if (value && typeof value === 'object') {
|
|
449
|
-
return [Number(value.r) || 0, Number(value.g) || 0, Number(value.b) || 0];
|
|
450
|
-
}
|
|
451
|
-
return undefined;
|
|
452
|
-
};
|
|
230
|
+
// 15. RENDER TOOLS
|
|
231
|
+
toolRegistry.register('manage_render', async (args, tools) => {
|
|
232
|
+
const action = args.action || requireAction(args);
|
|
233
|
+
return cleanObject(await executeAutomationRequest(tools, 'manage_render', { ...args, subAction: action }, 'Bridge unavailable'));
|
|
234
|
+
});
|
|
453
235
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
236
|
+
// 16. WORLD PARTITION
|
|
237
|
+
toolRegistry.register('manage_world_partition', async (args, tools) => {
|
|
238
|
+
const action = args.action || requireAction(args);
|
|
239
|
+
return cleanObject(await executeAutomationRequest(tools, 'manage_world_partition', { ...args, subAction: action }, 'Bridge unavailable'));
|
|
240
|
+
});
|
|
458
241
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
name,
|
|
462
|
-
intensity: args.intensity,
|
|
463
|
-
color,
|
|
464
|
-
rotation,
|
|
465
|
-
castShadows,
|
|
466
|
-
temperature: args.temperature
|
|
467
|
-
}));
|
|
468
|
-
}
|
|
469
|
-
if (typeKey === 'point') {
|
|
470
|
-
return cleanObject(await tools.lightingTools.createPointLight({
|
|
471
|
-
name,
|
|
472
|
-
location,
|
|
473
|
-
intensity: args.intensity,
|
|
474
|
-
radius: args.radius,
|
|
475
|
-
color,
|
|
476
|
-
falloffExponent: args.falloffExponent,
|
|
477
|
-
castShadows
|
|
478
|
-
}));
|
|
479
|
-
}
|
|
480
|
-
if (typeKey === 'spot') {
|
|
481
|
-
const innerCone = typeof args.innerCone === 'number' ? args.innerCone : undefined;
|
|
482
|
-
const outerCone = typeof args.outerCone === 'number' ? args.outerCone : undefined;
|
|
483
|
-
if (innerCone !== undefined && outerCone !== undefined && innerCone >= outerCone) {
|
|
484
|
-
throw new Error('innerCone must be less than outerCone');
|
|
485
|
-
}
|
|
486
|
-
return cleanObject(await tools.lightingTools.createSpotLight({
|
|
487
|
-
name,
|
|
488
|
-
location,
|
|
489
|
-
rotation,
|
|
490
|
-
intensity: args.intensity,
|
|
491
|
-
innerCone: args.innerCone,
|
|
492
|
-
outerCone: args.outerCone,
|
|
493
|
-
radius: args.radius,
|
|
494
|
-
color,
|
|
495
|
-
castShadows
|
|
496
|
-
}));
|
|
497
|
-
}
|
|
498
|
-
if (typeKey === 'rect') {
|
|
499
|
-
return cleanObject(await tools.lightingTools.createRectLight({
|
|
500
|
-
name,
|
|
501
|
-
location,
|
|
502
|
-
rotation,
|
|
503
|
-
intensity: args.intensity,
|
|
504
|
-
width: args.width,
|
|
505
|
-
height: args.height,
|
|
506
|
-
color
|
|
507
|
-
}));
|
|
508
|
-
}
|
|
509
|
-
if (typeKey === 'sky' || typeKey === 'skylight') {
|
|
510
|
-
return cleanObject(await tools.lightingTools.createSkyLight({
|
|
511
|
-
name,
|
|
512
|
-
sourceType: args.sourceType,
|
|
513
|
-
cubemapPath: args.cubemapPath,
|
|
514
|
-
intensity: args.intensity,
|
|
515
|
-
recapture: args.recapture
|
|
516
|
-
}));
|
|
517
|
-
}
|
|
518
|
-
throw new Error(`Unknown light type: ${lightType}`);
|
|
519
|
-
}
|
|
520
|
-
case 'build_lighting': {
|
|
521
|
-
const res = await tools.lightingTools.buildLighting({ quality: args.quality || 'High', buildReflectionCaptures: true });
|
|
522
|
-
return cleanObject(res);
|
|
523
|
-
}
|
|
524
|
-
default:
|
|
525
|
-
throw new Error(`Unknown level action: ${args.action}`);
|
|
526
|
-
}
|
|
242
|
+
// 17. LIGHTING
|
|
243
|
+
toolRegistry.register('manage_lighting', async (args, tools) => await handleLightingTools(args.action || requireAction(args), args, tools));
|
|
527
244
|
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
switch (requireAction(args)) {
|
|
531
|
-
case 'create_animation_bp': {
|
|
532
|
-
await elicitMissingPrimitiveArgs(
|
|
533
|
-
tools,
|
|
534
|
-
args,
|
|
535
|
-
'Provide details for animation_physics.create_animation_bp',
|
|
536
|
-
{
|
|
537
|
-
name: {
|
|
538
|
-
type: 'string',
|
|
539
|
-
title: 'Blueprint Name',
|
|
540
|
-
description: 'Name of the Animation Blueprint to create'
|
|
541
|
-
},
|
|
542
|
-
skeletonPath: {
|
|
543
|
-
type: 'string',
|
|
544
|
-
title: 'Skeleton Path',
|
|
545
|
-
description: 'Content path of the skeleton asset to bind'
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
);
|
|
549
|
-
const name = requireNonEmptyString(args.name, 'name', 'Invalid name');
|
|
550
|
-
const skeletonPath = requireNonEmptyString(args.skeletonPath, 'skeletonPath', 'Invalid skeletonPath');
|
|
551
|
-
const res = await tools.animationTools.createAnimationBlueprint({ name, skeletonPath, savePath: args.savePath });
|
|
552
|
-
return cleanObject(res);
|
|
553
|
-
}
|
|
554
|
-
case 'play_montage': {
|
|
555
|
-
await elicitMissingPrimitiveArgs(
|
|
556
|
-
tools,
|
|
557
|
-
args,
|
|
558
|
-
'Provide playback details for animation_physics.play_montage',
|
|
559
|
-
{
|
|
560
|
-
actorName: {
|
|
561
|
-
type: 'string',
|
|
562
|
-
title: 'Actor Name',
|
|
563
|
-
description: 'Actor that should play the montage'
|
|
564
|
-
},
|
|
565
|
-
montagePath: {
|
|
566
|
-
type: 'string',
|
|
567
|
-
title: 'Montage Path',
|
|
568
|
-
description: 'Montage or animation asset path to play'
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
);
|
|
572
|
-
const actorName = requireNonEmptyString(args.actorName, 'actorName', 'Invalid actorName');
|
|
573
|
-
const montagePath = args.montagePath || args.animationPath;
|
|
574
|
-
const validatedMontage = requireNonEmptyString(montagePath, 'montagePath', 'Invalid montagePath');
|
|
575
|
-
const res = await tools.animationTools.playAnimation({ actorName, animationType: 'Montage', animationPath: validatedMontage, playRate: args.playRate });
|
|
576
|
-
return cleanObject(res);
|
|
577
|
-
}
|
|
578
|
-
case 'setup_ragdoll': {
|
|
579
|
-
await elicitMissingPrimitiveArgs(
|
|
580
|
-
tools,
|
|
581
|
-
args,
|
|
582
|
-
'Provide setup details for animation_physics.setup_ragdoll',
|
|
583
|
-
{
|
|
584
|
-
skeletonPath: {
|
|
585
|
-
type: 'string',
|
|
586
|
-
title: 'Skeleton Path',
|
|
587
|
-
description: 'Content path for the skeleton asset'
|
|
588
|
-
},
|
|
589
|
-
physicsAssetName: {
|
|
590
|
-
type: 'string',
|
|
591
|
-
title: 'Physics Asset Name',
|
|
592
|
-
description: 'Name of the physics asset to apply'
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
);
|
|
596
|
-
const skeletonPath = requireNonEmptyString(args.skeletonPath, 'skeletonPath', 'Invalid skeletonPath');
|
|
597
|
-
const physicsAssetName = requireNonEmptyString(args.physicsAssetName, 'physicsAssetName', 'Invalid physicsAssetName');
|
|
598
|
-
const res = await tools.physicsTools.setupRagdoll({ skeletonPath, physicsAssetName, blendWeight: args.blendWeight, savePath: args.savePath });
|
|
599
|
-
return cleanObject(res);
|
|
600
|
-
}
|
|
601
|
-
default:
|
|
602
|
-
throw new Error(`Unknown animation/physics action: ${args.action}`);
|
|
603
|
-
}
|
|
245
|
+
// 18. PERFORMANCE
|
|
246
|
+
toolRegistry.register('manage_performance', async (args, tools) => await handlePerformanceTools(args.action || requireAction(args), args, tools));
|
|
604
247
|
|
|
605
|
-
//
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
case 'particle': {
|
|
609
|
-
await elicitMissingPrimitiveArgs(
|
|
610
|
-
tools,
|
|
611
|
-
args,
|
|
612
|
-
'Provide the particle effect details for create_effect.particle',
|
|
613
|
-
{
|
|
614
|
-
effectType: {
|
|
615
|
-
type: 'string',
|
|
616
|
-
title: 'Effect Type',
|
|
617
|
-
description: 'Preset effect type to spawn (e.g., Fire, Smoke)'
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
);
|
|
621
|
-
const res = await tools.niagaraTools.createEffect({ effectType: args.effectType, name: args.name, location: args.location, scale: args.scale, customParameters: args.customParameters });
|
|
622
|
-
return cleanObject(res);
|
|
623
|
-
}
|
|
624
|
-
case 'niagara': {
|
|
625
|
-
await elicitMissingPrimitiveArgs(
|
|
626
|
-
tools,
|
|
627
|
-
args,
|
|
628
|
-
'Provide the Niagara system path for create_effect.niagara',
|
|
629
|
-
{
|
|
630
|
-
systemPath: {
|
|
631
|
-
type: 'string',
|
|
632
|
-
title: 'Niagara System Path',
|
|
633
|
-
description: 'Asset path of the Niagara system to spawn'
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
);
|
|
637
|
-
const systemPath = requireNonEmptyString(args.systemPath, 'systemPath', 'Invalid systemPath');
|
|
638
|
-
const verifyResult = await tools.bridge.executePythonWithResult(`
|
|
639
|
-
import unreal, json
|
|
640
|
-
path = r"${systemPath}"
|
|
641
|
-
exists = unreal.EditorAssetLibrary.does_asset_exist(path)
|
|
642
|
-
print('RESULT:' + json.dumps({'success': exists, 'exists': exists, 'path': path}))
|
|
643
|
-
`.trim());
|
|
644
|
-
if (!verifyResult?.exists) {
|
|
645
|
-
return cleanObject({ success: false, error: `Niagara system not found at ${systemPath}` });
|
|
646
|
-
}
|
|
647
|
-
const loc = Array.isArray(args.location)
|
|
648
|
-
? { x: args.location[0], y: args.location[1], z: args.location[2] }
|
|
649
|
-
: args.location || { x: 0, y: 0, z: 0 };
|
|
650
|
-
const res = await tools.niagaraTools.spawnEffect({
|
|
651
|
-
systemPath,
|
|
652
|
-
location: [loc.x ?? 0, loc.y ?? 0, loc.z ?? 0],
|
|
653
|
-
rotation: Array.isArray(args.rotation) ? args.rotation : undefined,
|
|
654
|
-
scale: args.scale
|
|
655
|
-
});
|
|
656
|
-
return cleanObject(res);
|
|
657
|
-
}
|
|
658
|
-
case 'debug_shape': {
|
|
659
|
-
const shapeInput = args.shape ?? 'Sphere';
|
|
660
|
-
const shape = String(shapeInput).trim().toLowerCase();
|
|
661
|
-
const originalShapeLabel = String(shapeInput).trim() || 'shape';
|
|
662
|
-
const loc = args.location || { x: 0, y: 0, z: 0 };
|
|
663
|
-
const size = args.size || 100;
|
|
664
|
-
const color = args.color || [255, 0, 0, 255];
|
|
665
|
-
const duration = args.duration || 5;
|
|
666
|
-
if (shape === 'line') {
|
|
667
|
-
const end = args.end || { x: loc.x + size, y: loc.y, z: loc.z };
|
|
668
|
-
return cleanObject(await tools.debugTools.drawDebugLine({ start: [loc.x, loc.y, loc.z], end: [end.x, end.y, end.z], color, duration }));
|
|
669
|
-
} else if (shape === 'box') {
|
|
670
|
-
const extent = [size, size, size];
|
|
671
|
-
return cleanObject(await tools.debugTools.drawDebugBox({ center: [loc.x, loc.y, loc.z], extent, color, duration }));
|
|
672
|
-
} else if (shape === 'sphere') {
|
|
673
|
-
return cleanObject(await tools.debugTools.drawDebugSphere({ center: [loc.x, loc.y, loc.z], radius: size, color, duration }));
|
|
674
|
-
} else if (shape === 'capsule') {
|
|
675
|
-
return cleanObject(await tools.debugTools.drawDebugCapsule({ center: [loc.x, loc.y, loc.z], halfHeight: size, radius: Math.max(10, size/3), color, duration }));
|
|
676
|
-
} else if (shape === 'cone') {
|
|
677
|
-
return cleanObject(await tools.debugTools.drawDebugCone({ origin: [loc.x, loc.y, loc.z], direction: [0,0,1], length: size, angleWidth: 0.5, angleHeight: 0.5, color, duration }));
|
|
678
|
-
} else if (shape === 'arrow') {
|
|
679
|
-
const end = args.end || { x: loc.x + size, y: loc.y, z: loc.z };
|
|
680
|
-
return cleanObject(await tools.debugTools.drawDebugArrow({ start: [loc.x, loc.y, loc.z], end: [end.x, end.y, end.z], color, duration }));
|
|
681
|
-
} else if (shape === 'point') {
|
|
682
|
-
return cleanObject(await tools.debugTools.drawDebugPoint({ location: [loc.x, loc.y, loc.z], size, color, duration }));
|
|
683
|
-
} else if (shape === 'text' || shape === 'string') {
|
|
684
|
-
const text = args.text || 'Debug';
|
|
685
|
-
return cleanObject(await tools.debugTools.drawDebugString({ location: [loc.x, loc.y, loc.z], text, color, duration }));
|
|
686
|
-
}
|
|
687
|
-
// Default fallback
|
|
688
|
-
return cleanObject({ success: false, error: `Unsupported debug shape: ${originalShapeLabel}` });
|
|
689
|
-
}
|
|
690
|
-
default:
|
|
691
|
-
throw new Error(`Unknown effect action: ${args.action}`);
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
// 7. BLUEPRINT MANAGER
|
|
695
|
-
case 'manage_blueprint':
|
|
696
|
-
switch (requireAction(args)) {
|
|
697
|
-
case 'create': {
|
|
698
|
-
await elicitMissingPrimitiveArgs(
|
|
699
|
-
tools,
|
|
700
|
-
args,
|
|
701
|
-
'Provide details for manage_blueprint.create',
|
|
702
|
-
{
|
|
703
|
-
name: {
|
|
704
|
-
type: 'string',
|
|
705
|
-
title: 'Blueprint Name',
|
|
706
|
-
description: 'Name for the new Blueprint asset'
|
|
707
|
-
},
|
|
708
|
-
blueprintType: {
|
|
709
|
-
type: 'string',
|
|
710
|
-
title: 'Blueprint Type',
|
|
711
|
-
description: 'Base type such as Actor, Pawn, Character, etc.'
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
);
|
|
715
|
-
const res = await tools.blueprintTools.createBlueprint({
|
|
716
|
-
name: args.name,
|
|
717
|
-
blueprintType: args.blueprintType || 'Actor',
|
|
718
|
-
savePath: args.savePath,
|
|
719
|
-
parentClass: args.parentClass
|
|
720
|
-
});
|
|
721
|
-
return cleanObject(res);
|
|
722
|
-
}
|
|
723
|
-
case 'add_component': {
|
|
724
|
-
await elicitMissingPrimitiveArgs(
|
|
725
|
-
tools,
|
|
726
|
-
args,
|
|
727
|
-
'Provide details for manage_blueprint.add_component',
|
|
728
|
-
{
|
|
729
|
-
name: {
|
|
730
|
-
type: 'string',
|
|
731
|
-
title: 'Blueprint Name',
|
|
732
|
-
description: 'Blueprint asset to modify'
|
|
733
|
-
},
|
|
734
|
-
componentType: {
|
|
735
|
-
type: 'string',
|
|
736
|
-
title: 'Component Type',
|
|
737
|
-
description: 'Component class to add (e.g., StaticMeshComponent)'
|
|
738
|
-
},
|
|
739
|
-
componentName: {
|
|
740
|
-
type: 'string',
|
|
741
|
-
title: 'Component Name',
|
|
742
|
-
description: 'Name for the new component'
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
);
|
|
746
|
-
const res = await tools.blueprintTools.addComponent({ blueprintName: args.name, componentType: args.componentType, componentName: args.componentName });
|
|
747
|
-
return cleanObject(res);
|
|
748
|
-
}
|
|
749
|
-
default:
|
|
750
|
-
throw new Error(`Unknown blueprint action: ${args.action}`);
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
// 8. ENVIRONMENT BUILDER
|
|
754
|
-
case 'build_environment':
|
|
755
|
-
switch (requireAction(args)) {
|
|
756
|
-
case 'create_landscape': {
|
|
757
|
-
const res = await tools.landscapeTools.createLandscape({ name: args.name, sizeX: args.sizeX, sizeY: args.sizeY, materialPath: args.materialPath });
|
|
758
|
-
return cleanObject(res);
|
|
759
|
-
}
|
|
760
|
-
case 'sculpt': {
|
|
761
|
-
const res = await tools.landscapeTools.sculptLandscape({ landscapeName: args.name, tool: args.tool, brushSize: args.brushSize, strength: args.strength });
|
|
762
|
-
return cleanObject(res);
|
|
763
|
-
}
|
|
764
|
-
case 'add_foliage': {
|
|
765
|
-
const res = await tools.foliageTools.addFoliageType({ name: args.name, meshPath: args.meshPath, density: args.density });
|
|
766
|
-
return cleanObject(res);
|
|
767
|
-
}
|
|
768
|
-
case 'paint_foliage': {
|
|
769
|
-
const pos = args.position ? [args.position.x || 0, args.position.y || 0, args.position.z || 0] : [0,0,0];
|
|
770
|
-
const res = await tools.foliageTools.paintFoliage({ foliageType: args.foliageType, position: pos, brushSize: args.brushSize, paintDensity: args.paintDensity, eraseMode: args.eraseMode });
|
|
771
|
-
return cleanObject(res);
|
|
772
|
-
}
|
|
773
|
-
case 'create_procedural_terrain': {
|
|
774
|
-
const loc = args.location ? [args.location.x||0, args.location.y||0, args.location.z||0] : [0,0,0];
|
|
775
|
-
const res = await tools.buildEnvAdvanced.createProceduralTerrain({
|
|
776
|
-
name: args.name || 'ProceduralTerrain',
|
|
777
|
-
location: loc as [number,number,number],
|
|
778
|
-
sizeX: args.sizeX,
|
|
779
|
-
sizeY: args.sizeY,
|
|
780
|
-
subdivisions: args.subdivisions,
|
|
781
|
-
heightFunction: args.heightFunction,
|
|
782
|
-
material: args.materialPath
|
|
783
|
-
});
|
|
784
|
-
return cleanObject(res);
|
|
785
|
-
}
|
|
786
|
-
case 'create_procedural_foliage': {
|
|
787
|
-
if (!args.bounds || !args.bounds.location || !args.bounds.size) throw new Error('bounds.location and bounds.size are required');
|
|
788
|
-
const bounds = {
|
|
789
|
-
location: [args.bounds.location.x||0, args.bounds.location.y||0, args.bounds.location.z||0] as [number,number,number],
|
|
790
|
-
size: [args.bounds.size.x||1000, args.bounds.size.y||1000, args.bounds.size.z||100] as [number,number,number]
|
|
791
|
-
};
|
|
792
|
-
const res = await tools.buildEnvAdvanced.createProceduralFoliage({
|
|
793
|
-
name: args.name || 'ProceduralFoliage',
|
|
794
|
-
bounds,
|
|
795
|
-
foliageTypes: args.foliageTypes || [],
|
|
796
|
-
seed: args.seed
|
|
797
|
-
});
|
|
798
|
-
return cleanObject(res);
|
|
799
|
-
}
|
|
800
|
-
case 'add_foliage_instances': {
|
|
801
|
-
if (!args.foliageType) throw new Error('foliageType is required');
|
|
802
|
-
if (!Array.isArray(args.transforms)) throw new Error('transforms array is required');
|
|
803
|
-
const transforms = (args.transforms as any[]).map(t => ({
|
|
804
|
-
location: [t.location?.x||0, t.location?.y||0, t.location?.z||0] as [number,number,number],
|
|
805
|
-
rotation: t.rotation ? [t.rotation.pitch||0, t.rotation.yaw||0, t.rotation.roll||0] as [number,number,number] : undefined,
|
|
806
|
-
scale: t.scale ? [t.scale.x||1, t.scale.y||1, t.scale.z||1] as [number,number,number] : undefined
|
|
807
|
-
}));
|
|
808
|
-
const res = await tools.buildEnvAdvanced.addFoliageInstances({ foliageType: args.foliageType, transforms });
|
|
809
|
-
return cleanObject(res);
|
|
810
|
-
}
|
|
811
|
-
case 'create_landscape_grass_type': {
|
|
812
|
-
const res = await tools.buildEnvAdvanced.createLandscapeGrassType({
|
|
813
|
-
name: args.name || 'GrassType',
|
|
814
|
-
meshPath: args.meshPath,
|
|
815
|
-
density: args.density,
|
|
816
|
-
minScale: args.minScale,
|
|
817
|
-
maxScale: args.maxScale
|
|
818
|
-
});
|
|
819
|
-
return cleanObject(res);
|
|
820
|
-
}
|
|
821
|
-
default:
|
|
822
|
-
throw new Error(`Unknown environment action: ${args.action}`);
|
|
823
|
-
}
|
|
248
|
+
// 19. INPUT
|
|
249
|
+
toolRegistry.register('manage_input', async (args, tools) => await handleInputTools(args.action || requireAction(args), args, tools));
|
|
250
|
+
}
|
|
824
251
|
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
switch (requireAction(args)) {
|
|
828
|
-
case 'read_log': {
|
|
829
|
-
const filterCategoryRaw = args.filter_category;
|
|
830
|
-
const filterCategory = Array.isArray(filterCategoryRaw)
|
|
831
|
-
? filterCategoryRaw
|
|
832
|
-
: typeof filterCategoryRaw === 'string' && filterCategoryRaw.trim() !== ''
|
|
833
|
-
? filterCategoryRaw.split(',').map((s: string) => s.trim()).filter(Boolean)
|
|
834
|
-
: undefined;
|
|
835
|
-
const res = await tools.logTools.readOutputLog({
|
|
836
|
-
filterCategory,
|
|
837
|
-
filterLevel: args.filter_level,
|
|
838
|
-
lines: typeof args.lines === 'number' ? args.lines : undefined,
|
|
839
|
-
logPath: typeof args.log_path === 'string' ? args.log_path : undefined,
|
|
840
|
-
includePrefixes: Array.isArray(args.include_prefixes) ? args.include_prefixes : undefined,
|
|
841
|
-
excludeCategories: Array.isArray(args.exclude_categories) ? args.exclude_categories : undefined
|
|
842
|
-
});
|
|
843
|
-
return cleanObject(res);
|
|
844
|
-
}
|
|
845
|
-
case 'profile': {
|
|
846
|
-
const res = await tools.performanceTools.startProfiling({ type: args.profileType, duration: args.duration });
|
|
847
|
-
return cleanObject(res);
|
|
848
|
-
}
|
|
849
|
-
case 'show_fps': {
|
|
850
|
-
const res = await tools.performanceTools.showFPS({ enabled: !!args.enabled, verbose: !!args.verbose });
|
|
851
|
-
return cleanObject(res);
|
|
852
|
-
}
|
|
853
|
-
case 'set_quality': {
|
|
854
|
-
const res = await tools.performanceTools.setScalability({ category: args.category, level: args.level });
|
|
855
|
-
return cleanObject(res);
|
|
856
|
-
}
|
|
857
|
-
case 'play_sound': {
|
|
858
|
-
await elicitMissingPrimitiveArgs(
|
|
859
|
-
tools,
|
|
860
|
-
args,
|
|
861
|
-
'Provide the audio asset for system_control.play_sound',
|
|
862
|
-
{
|
|
863
|
-
soundPath: {
|
|
864
|
-
type: 'string',
|
|
865
|
-
title: 'Sound Asset Path',
|
|
866
|
-
description: 'Asset path of the sound to play'
|
|
867
|
-
}
|
|
868
|
-
}
|
|
869
|
-
);
|
|
870
|
-
const soundPath = requireNonEmptyString(args.soundPath, 'soundPath', 'Missing required parameter: soundPath');
|
|
871
|
-
if (args.location && typeof args.location === 'object') {
|
|
872
|
-
const loc = [args.location.x || 0, args.location.y || 0, args.location.z || 0];
|
|
873
|
-
const res = await tools.audioTools.playSoundAtLocation({ soundPath, location: loc as [number, number, number], volume: args.volume, pitch: args.pitch, startTime: args.startTime });
|
|
874
|
-
return cleanObject(res);
|
|
875
|
-
}
|
|
876
|
-
const res = await tools.audioTools.playSound2D({ soundPath, volume: args.volume, pitch: args.pitch, startTime: args.startTime });
|
|
877
|
-
return cleanObject(res);
|
|
878
|
-
}
|
|
879
|
-
case 'create_widget': {
|
|
880
|
-
await elicitMissingPrimitiveArgs(
|
|
881
|
-
tools,
|
|
882
|
-
args,
|
|
883
|
-
'Provide details for system_control.create_widget',
|
|
884
|
-
{
|
|
885
|
-
widgetName: {
|
|
886
|
-
type: 'string',
|
|
887
|
-
title: 'Widget Name',
|
|
888
|
-
description: 'Name for the new UI widget asset'
|
|
889
|
-
},
|
|
890
|
-
widgetType: {
|
|
891
|
-
type: 'string',
|
|
892
|
-
title: 'Widget Type',
|
|
893
|
-
description: 'Widget type such as HUD, Menu, Overlay, etc.'
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
);
|
|
897
|
-
const widgetName = requireNonEmptyString(args.widgetName ?? args.name, 'widgetName', 'Missing required parameter: widgetName');
|
|
898
|
-
const widgetType = requireNonEmptyString(args.widgetType, 'widgetType', 'Missing required parameter: widgetType');
|
|
899
|
-
const res = await tools.uiTools.createWidget({ name: widgetName, type: widgetType as any, savePath: args.savePath });
|
|
900
|
-
return cleanObject(res);
|
|
901
|
-
}
|
|
902
|
-
case 'show_widget': {
|
|
903
|
-
const res = await tools.uiTools.setWidgetVisibility({ widgetName: args.widgetName, visible: args.visible !== false });
|
|
904
|
-
return cleanObject(res);
|
|
905
|
-
}
|
|
906
|
-
case 'screenshot': {
|
|
907
|
-
const res = await tools.visualTools.takeScreenshot({ resolution: args.resolution });
|
|
908
|
-
return cleanObject(res);
|
|
909
|
-
}
|
|
910
|
-
case 'engine_start': {
|
|
911
|
-
const res = await tools.engineTools.launchEditor({ editorExe: args.editorExe, projectPath: args.projectPath });
|
|
912
|
-
return cleanObject(res);
|
|
913
|
-
}
|
|
914
|
-
case 'engine_quit': {
|
|
915
|
-
const res = await tools.engineTools.quitEditor();
|
|
916
|
-
return cleanObject(res);
|
|
917
|
-
}
|
|
918
|
-
default:
|
|
919
|
-
throw new Error(`Unknown system action: ${args.action}`);
|
|
920
|
-
}
|
|
252
|
+
// Initialize default handlers immediately
|
|
253
|
+
registerDefaultHandlers();
|
|
921
254
|
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
return { success: false, error: 'Command blocked for safety' } as any;
|
|
932
|
-
}
|
|
933
|
-
try {
|
|
934
|
-
const raw = await tools.bridge.executeConsoleCommand(cmd);
|
|
935
|
-
const summary = tools.bridge.summarizeConsoleCommand(cmd, raw);
|
|
936
|
-
const output = summary.output || '';
|
|
937
|
-
const looksInvalid = /unknown|invalid/i.test(output);
|
|
938
|
-
return cleanObject({
|
|
939
|
-
success: summary.returnValue !== false && !looksInvalid,
|
|
940
|
-
command: summary.command,
|
|
941
|
-
output: output || undefined,
|
|
942
|
-
logLines: summary.logLines?.length ? summary.logLines : undefined,
|
|
943
|
-
returnValue: summary.returnValue,
|
|
944
|
-
message: !looksInvalid
|
|
945
|
-
? (output || 'Command executed')
|
|
946
|
-
: undefined,
|
|
947
|
-
error: looksInvalid ? output : undefined,
|
|
948
|
-
raw: summary.raw
|
|
949
|
-
});
|
|
950
|
-
} catch (e: any) {
|
|
951
|
-
return cleanObject({ success: false, command: cmd, error: e?.message || String(e) });
|
|
952
|
-
}
|
|
953
|
-
|
|
255
|
+
// Export the main consolidated tool call handler
|
|
256
|
+
export async function handleConsolidatedToolCall(
|
|
257
|
+
name: string,
|
|
258
|
+
args: any,
|
|
259
|
+
tools: ITools
|
|
260
|
+
) {
|
|
261
|
+
const logger = new Logger('ConsolidatedToolHandler');
|
|
262
|
+
const startTime = Date.now();
|
|
263
|
+
logger.info(`Starting execution of ${name} at ${new Date().toISOString()}`);
|
|
954
264
|
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
if (!presetName) throw new Error('Missing required parameter: name or presetName');
|
|
967
|
-
rcResult = await tools.rcTools.createPreset({
|
|
968
|
-
name: presetName,
|
|
969
|
-
path: args.path
|
|
970
|
-
});
|
|
971
|
-
// Return consistent output with presetId for tests
|
|
972
|
-
if (rcResult.success) {
|
|
973
|
-
rcResult.message = `Remote Control preset created: ${presetName}`;
|
|
974
|
-
// Ensure presetId is set (for test compatibility)
|
|
975
|
-
if (rcResult.presetPath && !rcResult.presetId) {
|
|
976
|
-
rcResult.presetId = rcResult.presetPath;
|
|
977
|
-
}
|
|
978
|
-
}
|
|
979
|
-
break;
|
|
980
|
-
|
|
981
|
-
case 'list':
|
|
982
|
-
// List all presets - implement via RcTools
|
|
983
|
-
rcResult = await tools.rcTools.listPresets();
|
|
984
|
-
break;
|
|
985
|
-
|
|
986
|
-
case 'delete':
|
|
987
|
-
case 'delete_preset':
|
|
988
|
-
const presetIdentifier = args.presetId || args.presetPath;
|
|
989
|
-
if (!presetIdentifier) throw new Error('Missing required parameter: presetId');
|
|
990
|
-
rcResult = await tools.rcTools.deletePreset(presetIdentifier);
|
|
991
|
-
if (rcResult.success) {
|
|
992
|
-
rcResult.message = 'Preset deleted successfully';
|
|
993
|
-
}
|
|
994
|
-
break;
|
|
995
|
-
|
|
996
|
-
case 'expose_actor':
|
|
997
|
-
if (!args.presetPath) throw new Error('Missing required parameter: presetPath');
|
|
998
|
-
if (!args.actorName) throw new Error('Missing required parameter: actorName');
|
|
999
|
-
|
|
1000
|
-
rcResult = await tools.rcTools.exposeActor({
|
|
1001
|
-
presetPath: args.presetPath,
|
|
1002
|
-
actorName: args.actorName
|
|
1003
|
-
});
|
|
1004
|
-
if (rcResult.success) {
|
|
1005
|
-
rcResult.message = `Actor '${args.actorName}' exposed to preset`;
|
|
1006
|
-
}
|
|
1007
|
-
break;
|
|
1008
|
-
|
|
1009
|
-
case 'expose_property':
|
|
1010
|
-
case 'expose': // Support simplified name from tests
|
|
1011
|
-
// Support both presetPath and presetId
|
|
1012
|
-
const presetPathExp = args.presetPath || args.presetId;
|
|
1013
|
-
if (!presetPathExp) throw new Error('Missing required parameter: presetPath or presetId');
|
|
1014
|
-
if (!args.objectPath) throw new Error('Missing required parameter: objectPath');
|
|
1015
|
-
if (!args.propertyName) throw new Error('Missing required parameter: propertyName');
|
|
1016
|
-
|
|
1017
|
-
rcResult = await tools.rcTools.exposeProperty({
|
|
1018
|
-
presetPath: presetPathExp,
|
|
1019
|
-
objectPath: args.objectPath,
|
|
1020
|
-
propertyName: args.propertyName
|
|
1021
|
-
});
|
|
1022
|
-
if (rcResult.success) {
|
|
1023
|
-
rcResult.message = `Property '${args.propertyName}' exposed to preset`;
|
|
1024
|
-
}
|
|
1025
|
-
break;
|
|
1026
|
-
|
|
1027
|
-
case 'list_fields':
|
|
1028
|
-
case 'get_exposed': // Support test naming
|
|
1029
|
-
const presetPathList = args.presetPath || args.presetId;
|
|
1030
|
-
if (!presetPathList) throw new Error('Missing required parameter: presetPath or presetId');
|
|
1031
|
-
|
|
1032
|
-
rcResult = await tools.rcTools.listFields({
|
|
1033
|
-
presetPath: presetPathList
|
|
1034
|
-
});
|
|
1035
|
-
// Map 'fields' to 'exposedProperties' for test compatibility
|
|
1036
|
-
if (rcResult.success && rcResult.fields) {
|
|
1037
|
-
rcResult.exposedProperties = rcResult.fields;
|
|
1038
|
-
}
|
|
1039
|
-
break;
|
|
1040
|
-
|
|
1041
|
-
case 'set_property':
|
|
1042
|
-
case 'set_value': // Support test naming
|
|
1043
|
-
// Support both patterns
|
|
1044
|
-
const objPathSet = args.objectPath || args.presetId;
|
|
1045
|
-
const propNameSet = args.propertyName || args.propertyLabel;
|
|
1046
|
-
|
|
1047
|
-
if (!objPathSet) throw new Error('Missing required parameter: objectPath or presetId');
|
|
1048
|
-
if (!propNameSet) throw new Error('Missing required parameter: propertyName or propertyLabel');
|
|
1049
|
-
if (args.value === undefined) throw new Error('Missing required parameter: value');
|
|
1050
|
-
|
|
1051
|
-
rcResult = await tools.rcTools.setProperty({
|
|
1052
|
-
objectPath: objPathSet,
|
|
1053
|
-
propertyName: propNameSet,
|
|
1054
|
-
value: args.value
|
|
1055
|
-
});
|
|
1056
|
-
if (rcResult.success) {
|
|
1057
|
-
rcResult.message = `Property '${propNameSet}' value updated`;
|
|
1058
|
-
}
|
|
1059
|
-
break;
|
|
1060
|
-
|
|
1061
|
-
case 'get_property':
|
|
1062
|
-
case 'get_value': // Support test naming
|
|
1063
|
-
const objPathGet = args.objectPath || args.presetId;
|
|
1064
|
-
const propNameGet = args.propertyName || args.propertyLabel;
|
|
1065
|
-
|
|
1066
|
-
if (!objPathGet) throw new Error('Missing required parameter: objectPath or presetId');
|
|
1067
|
-
if (!propNameGet) throw new Error('Missing required parameter: propertyName or propertyLabel');
|
|
1068
|
-
|
|
1069
|
-
rcResult = await tools.rcTools.getProperty({
|
|
1070
|
-
objectPath: objPathGet,
|
|
1071
|
-
propertyName: propNameGet
|
|
1072
|
-
});
|
|
1073
|
-
break;
|
|
1074
|
-
|
|
1075
|
-
case 'call_function':
|
|
1076
|
-
if (!args.presetId) throw new Error('Missing required parameter: presetId');
|
|
1077
|
-
if (!args.functionLabel) throw new Error('Missing required parameter: functionLabel');
|
|
1078
|
-
|
|
1079
|
-
// For now, return not implemented
|
|
1080
|
-
rcResult = {
|
|
1081
|
-
success: false,
|
|
1082
|
-
error: 'Function calls not yet implemented'
|
|
1083
|
-
};
|
|
1084
|
-
break;
|
|
1085
|
-
|
|
1086
|
-
default:
|
|
1087
|
-
throw new Error(`Unknown RC action: ${rcAction}. Valid actions are: create_preset, expose_actor, expose_property, list_fields, set_property, get_property, or their simplified versions: create, list, delete, expose, get_exposed, set_value, get_value, call_function`);
|
|
1088
|
-
}
|
|
1089
|
-
|
|
1090
|
-
// Return result directly - MCP formatting will be handled by response validator
|
|
1091
|
-
// Clean to prevent circular references
|
|
1092
|
-
return cleanObject(rcResult);
|
|
265
|
+
try {
|
|
266
|
+
const normalized = normalizeToolCall(name, args);
|
|
267
|
+
const normalizedName = normalized.name;
|
|
268
|
+
const normalizedArgs = normalized.args;
|
|
269
|
+
// Note: action extracted inside handler usually, but here we might pass it if needed.
|
|
270
|
+
// The handlers above re-extract or use normalizedArgs.action.
|
|
271
|
+
// `normalizeToolCall` puts `action` into `normalized.action` but does NOT necessarily put it into `normalized.args.action`.
|
|
272
|
+
// Let's ensure args has action if we relied on it above.
|
|
273
|
+
if (normalized.action && !normalizedArgs.action) {
|
|
274
|
+
normalizedArgs.action = normalized.action;
|
|
275
|
+
}
|
|
1093
276
|
|
|
1094
|
-
|
|
1095
|
-
case 'manage_sequence':
|
|
1096
|
-
// Direct handling for sequence operations
|
|
1097
|
-
const seqResult = await (async () => {
|
|
1098
|
-
const sequenceTools = tools.sequenceTools;
|
|
1099
|
-
if (!sequenceTools) throw new Error('Sequence tools not available');
|
|
1100
|
-
const action = requireAction(args);
|
|
1101
|
-
|
|
1102
|
-
switch (action) {
|
|
1103
|
-
case 'create':
|
|
1104
|
-
return await sequenceTools.create({ name: args.name, path: args.path });
|
|
1105
|
-
case 'open':
|
|
1106
|
-
return await sequenceTools.open({ path: args.path });
|
|
1107
|
-
case 'add_camera':
|
|
1108
|
-
return await sequenceTools.addCamera({ spawnable: args.spawnable !== false });
|
|
1109
|
-
case 'add_actor':
|
|
1110
|
-
return await sequenceTools.addActor({ actorName: args.actorName });
|
|
1111
|
-
case 'add_actors':
|
|
1112
|
-
if (!args.actorNames) throw new Error('Missing required parameter: actorNames');
|
|
1113
|
-
return await sequenceTools.addActors({ actorNames: args.actorNames });
|
|
1114
|
-
case 'remove_actors':
|
|
1115
|
-
if (!args.actorNames) throw new Error('Missing required parameter: actorNames');
|
|
1116
|
-
return await sequenceTools.removeActors({ actorNames: args.actorNames });
|
|
1117
|
-
case 'get_bindings':
|
|
1118
|
-
return await sequenceTools.getBindings({ path: args.path });
|
|
1119
|
-
case 'add_spawnable_from_class':
|
|
1120
|
-
if (!args.className) throw new Error('Missing required parameter: className');
|
|
1121
|
-
return await sequenceTools.addSpawnableFromClass({ className: args.className, path: args.path });
|
|
1122
|
-
case 'play':
|
|
1123
|
-
return await sequenceTools.play({ loopMode: args.loopMode });
|
|
1124
|
-
case 'pause':
|
|
1125
|
-
return await sequenceTools.pause();
|
|
1126
|
-
case 'stop':
|
|
1127
|
-
return await sequenceTools.stop();
|
|
1128
|
-
case 'set_properties':
|
|
1129
|
-
return await sequenceTools.setSequenceProperties({
|
|
1130
|
-
path: args.path,
|
|
1131
|
-
frameRate: args.frameRate,
|
|
1132
|
-
lengthInFrames: args.lengthInFrames,
|
|
1133
|
-
playbackStart: args.playbackStart,
|
|
1134
|
-
playbackEnd: args.playbackEnd
|
|
1135
|
-
});
|
|
1136
|
-
case 'get_properties':
|
|
1137
|
-
return await sequenceTools.getSequenceProperties({ path: args.path });
|
|
1138
|
-
case 'set_playback_speed':
|
|
1139
|
-
if (args.speed === undefined) throw new Error('Missing required parameter: speed');
|
|
1140
|
-
return await sequenceTools.setPlaybackSpeed({ speed: args.speed });
|
|
1141
|
-
default:
|
|
1142
|
-
throw new Error(`Unknown sequence action: ${action}`);
|
|
1143
|
-
}
|
|
1144
|
-
})();
|
|
1145
|
-
|
|
1146
|
-
// Return result directly - MCP formatting will be handled by response validator
|
|
1147
|
-
// Clean to prevent circular references
|
|
1148
|
-
return cleanObject(seqResult);
|
|
1149
|
-
// 13. INTROSPECTION
|
|
1150
|
-
case 'inspect':
|
|
1151
|
-
const inspectAction = requireAction(args);
|
|
1152
|
-
switch (inspectAction) {
|
|
1153
|
-
case 'inspect_object': {
|
|
1154
|
-
const res = await tools.introspectionTools.inspectObject({ objectPath: args.objectPath, detailed: args.detailed });
|
|
1155
|
-
return cleanObject(res);
|
|
1156
|
-
}
|
|
1157
|
-
case 'set_property': {
|
|
1158
|
-
const res = await tools.introspectionTools.setProperty({ objectPath: args.objectPath, propertyName: args.propertyName, value: args.value });
|
|
1159
|
-
return cleanObject(res);
|
|
1160
|
-
}
|
|
1161
|
-
default:
|
|
1162
|
-
throw new Error(`Unknown inspect action: ${inspectAction}`);
|
|
1163
|
-
}
|
|
1164
|
-
|
|
277
|
+
const handler = toolRegistry.getHandler(normalizedName);
|
|
1165
278
|
|
|
1166
|
-
|
|
1167
|
-
|
|
279
|
+
if (handler) {
|
|
280
|
+
return await handler(normalizedArgs, tools);
|
|
1168
281
|
}
|
|
1169
282
|
|
|
1170
|
-
//
|
|
283
|
+
// Fallback or Unknown
|
|
284
|
+
return cleanObject({ success: false, error: 'UNKNOWN_TOOL', message: `Unknown consolidated tool: ${name}` });
|
|
1171
285
|
|
|
1172
286
|
} catch (err: any) {
|
|
1173
287
|
const duration = Date.now() - startTime;
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
// Return consistent error structure matching regular tool handlers
|
|
288
|
+
logger.error(`Failed execution of ${name} after ${duration}ms: ${err?.message || String(err)}`);
|
|
1177
289
|
const errorMessage = err?.message || String(err);
|
|
1178
|
-
const
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
290
|
+
const lowerError = errorMessage.toString().toLowerCase();
|
|
291
|
+
const isTimeout = lowerError.includes('timeout');
|
|
292
|
+
|
|
293
|
+
let text: string;
|
|
294
|
+
if (isTimeout) {
|
|
295
|
+
text = `Tool ${name} timed out. Please check Unreal Engine connection.`;
|
|
296
|
+
} else {
|
|
297
|
+
text = `Failed to execute ${name}: ${errorMessage}`;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return ResponseFactory.error(text);
|
|
1189
301
|
}
|
|
1190
|
-
}
|
|
302
|
+
}
|