unity-open-mcp 0.1.2 → 0.3.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/dist/agent-identity.d.ts +52 -0
- package/dist/agent-identity.js +115 -0
- package/dist/agent-identity.js.map +1 -0
- package/dist/batch-spawn.d.ts +7 -0
- package/dist/batch-spawn.js +155 -29
- package/dist/batch-spawn.js.map +1 -1
- package/dist/capabilities/build-capabilities.d.ts +45 -0
- package/dist/capabilities/build-capabilities.js +262 -27
- package/dist/capabilities/build-capabilities.js.map +1 -1
- package/dist/capabilities/cost-hints.d.ts +82 -0
- package/dist/capabilities/cost-hints.js +185 -0
- package/dist/capabilities/cost-hints.js.map +1 -0
- package/dist/capabilities/lifecycle.d.ts +40 -0
- package/dist/capabilities/lifecycle.js +353 -0
- package/dist/capabilities/lifecycle.js.map +1 -0
- package/dist/capabilities/rule-catalog.d.ts +13 -1
- package/dist/capabilities/rule-catalog.js +620 -75
- package/dist/capabilities/rule-catalog.js.map +1 -1
- package/dist/capabilities/tool-groups.d.ts +21 -0
- package/dist/capabilities/tool-groups.js +411 -14
- package/dist/capabilities/tool-groups.js.map +1 -1
- package/dist/cli/args.d.ts +32 -1
- package/dist/cli/args.js +201 -4
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/cli.d.ts +21 -0
- package/dist/cli/cli.js +68 -5
- package/dist/cli/cli.js.map +1 -1
- package/dist/cli/commands.d.ts +33 -0
- package/dist/cli/commands.js +400 -0
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli/exit-codes.d.ts +43 -0
- package/dist/cli/exit-codes.js +78 -0
- package/dist/cli/exit-codes.js.map +1 -0
- package/dist/compile-verify.d.ts +65 -0
- package/dist/compile-verify.js +94 -0
- package/dist/compile-verify.js.map +1 -0
- package/dist/compressible-router.js +64 -11
- package/dist/compressible-router.js.map +1 -1
- package/dist/compression/asset-model.d.ts +15 -0
- package/dist/compression/compact.d.ts +5 -4
- package/dist/compression/compact.js +13 -10
- package/dist/compression/compact.js.map +1 -1
- package/dist/dialog-dismiss.d.ts +127 -74
- package/dist/dialog-dismiss.js +427 -170
- package/dist/dialog-dismiss.js.map +1 -1
- package/dist/dialog-policy.d.ts +141 -0
- package/dist/dialog-policy.js +345 -0
- package/dist/dialog-policy.js.map +1 -0
- package/dist/gate-error.d.ts +6 -0
- package/dist/gate-error.js +13 -2
- package/dist/gate-error.js.map +1 -1
- package/dist/hub-control.d.ts +247 -0
- package/dist/hub-control.js +703 -0
- package/dist/hub-control.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +46 -12
- package/dist/index.js.map +1 -1
- package/dist/live-client.d.ts +123 -3
- package/dist/live-client.js +555 -69
- package/dist/live-client.js.map +1 -1
- package/dist/offline.d.ts +105 -0
- package/dist/offline.js +816 -23
- package/dist/offline.js.map +1 -1
- package/dist/output-profile.d.ts +110 -0
- package/dist/output-profile.js +255 -0
- package/dist/output-profile.js.map +1 -0
- package/dist/retry-policy.d.ts +72 -0
- package/dist/retry-policy.js +146 -0
- package/dist/retry-policy.js.map +1 -0
- package/dist/routers.d.ts +4 -0
- package/dist/routers.js +2 -2
- package/dist/routers.js.map +1 -1
- package/dist/server-instructions.d.ts +18 -0
- package/dist/server-instructions.js +145 -0
- package/dist/server-instructions.js.map +1 -0
- package/dist/tool-router.d.ts +38 -1
- package/dist/tool-router.js +690 -37
- package/dist/tool-router.js.map +1 -1
- package/dist/tool-session-state.d.ts +37 -0
- package/dist/tool-session-state.js +78 -1
- package/dist/tool-session-state.js.map +1 -1
- package/dist/tools/apply-fix.js +16 -2
- package/dist/tools/apply-fix.js.map +1 -1
- package/dist/tools/asmdef-create.d.ts +2 -0
- package/dist/tools/asmdef-create.js +114 -0
- package/dist/tools/asmdef-create.js.map +1 -0
- package/dist/tools/asmdef-get.d.ts +2 -0
- package/dist/tools/asmdef-get.js +31 -0
- package/dist/tools/asmdef-get.js.map +1 -0
- package/dist/tools/asmdef-list.d.ts +2 -0
- package/dist/tools/asmdef-list.js +41 -0
- package/dist/tools/asmdef-list.js.map +1 -0
- package/dist/tools/asmdef-modify.d.ts +2 -0
- package/dist/tools/asmdef-modify.js +108 -0
- package/dist/tools/asmdef-modify.js.map +1 -0
- package/dist/tools/audio-listener-get.d.ts +2 -0
- package/dist/tools/audio-listener-get.js +16 -0
- package/dist/tools/audio-listener-get.js.map +1 -0
- package/dist/tools/audio-mixer-get-parameter.d.ts +2 -0
- package/dist/tools/audio-mixer-get-parameter.js +25 -0
- package/dist/tools/audio-mixer-get-parameter.js.map +1 -0
- package/dist/tools/audio-mixer-set-parameter.d.ts +2 -0
- package/dist/tools/audio-mixer-set-parameter.js +44 -0
- package/dist/tools/audio-mixer-set-parameter.js.map +1 -0
- package/dist/tools/audio-source-add.d.ts +2 -0
- package/dist/tools/audio-source-add.js +94 -0
- package/dist/tools/audio-source-add.js.map +1 -0
- package/dist/tools/audio-source-modify.d.ts +2 -0
- package/dist/tools/audio-source-modify.js +52 -0
- package/dist/tools/audio-source-modify.js.map +1 -0
- package/dist/tools/bridge-status.js +16 -6
- package/dist/tools/bridge-status.js.map +1 -1
- package/dist/tools/build-get-active-target.js +1 -2
- package/dist/tools/build-get-active-target.js.map +1 -1
- package/dist/tools/build-get-defines.js +1 -1
- package/dist/tools/build-get-defines.js.map +1 -1
- package/dist/tools/build-get-scenes.js +1 -2
- package/dist/tools/build-get-scenes.js.map +1 -1
- package/dist/tools/build-get-targets.js +1 -1
- package/dist/tools/build-get-targets.js.map +1 -1
- package/dist/tools/build-set-defines.js +1 -2
- package/dist/tools/build-set-defines.js.map +1 -1
- package/dist/tools/build-set-scenes.js +1 -1
- package/dist/tools/build-set-scenes.js.map +1 -1
- package/dist/tools/build-set-target.js +1 -1
- package/dist/tools/build-set-target.js.map +1 -1
- package/dist/tools/build-start.js +0 -1
- package/dist/tools/build-start.js.map +1 -1
- package/dist/tools/cinemachine-brain-ensure.d.ts +2 -0
- package/dist/tools/cinemachine-brain-ensure.js +27 -0
- package/dist/tools/cinemachine-brain-ensure.js.map +1 -0
- package/dist/tools/cinemachine-camera-list.d.ts +2 -0
- package/dist/tools/cinemachine-camera-list.js +15 -0
- package/dist/tools/cinemachine-camera-list.js.map +1 -0
- package/dist/tools/cinemachine-create-camera.d.ts +2 -0
- package/dist/tools/cinemachine-create-camera.js +55 -0
- package/dist/tools/cinemachine-create-camera.js.map +1 -0
- package/dist/tools/cinemachine-set-body.d.ts +2 -0
- package/dist/tools/cinemachine-set-body.js +33 -0
- package/dist/tools/cinemachine-set-body.js.map +1 -0
- package/dist/tools/cinemachine-set-lens.d.ts +2 -0
- package/dist/tools/cinemachine-set-lens.js +41 -0
- package/dist/tools/cinemachine-set-lens.js.map +1 -0
- package/dist/tools/cinemachine-set-noise.d.ts +2 -0
- package/dist/tools/cinemachine-set-noise.js +31 -0
- package/dist/tools/cinemachine-set-noise.js.map +1 -0
- package/dist/tools/cinemachine-set-targets.d.ts +2 -0
- package/dist/tools/cinemachine-set-targets.js +30 -0
- package/dist/tools/cinemachine-set-targets.js.map +1 -0
- package/dist/tools/console-log.js +2 -2
- package/dist/tools/console-log.js.map +1 -1
- package/dist/tools/constraint-add.d.ts +2 -0
- package/dist/tools/constraint-add.js +85 -0
- package/dist/tools/constraint-add.js.map +1 -0
- package/dist/tools/dependencies.d.ts +2 -0
- package/dist/tools/dependencies.js +76 -0
- package/dist/tools/dependencies.js.map +1 -0
- package/dist/tools/editor-add-tag.js +2 -3
- package/dist/tools/editor-add-tag.js.map +1 -1
- package/dist/tools/editor-clear-history.d.ts +2 -0
- package/dist/tools/editor-clear-history.js +25 -0
- package/dist/tools/editor-clear-history.js.map +1 -0
- package/dist/tools/editor-get-layers.js +3 -3
- package/dist/tools/editor-get-layers.js.map +1 -1
- package/dist/tools/editor-get-tags.js +2 -2
- package/dist/tools/editor-get-tags.js.map +1 -1
- package/dist/tools/editor-redo.js +2 -3
- package/dist/tools/editor-redo.js.map +1 -1
- package/dist/tools/editor-undo-history.d.ts +2 -0
- package/dist/tools/editor-undo-history.js +21 -0
- package/dist/tools/editor-undo-history.js.map +1 -0
- package/dist/tools/editor-undo.js +3 -3
- package/dist/tools/editor-undo.js.map +1 -1
- package/dist/tools/editorprefs-delete.d.ts +2 -0
- package/dist/tools/editorprefs-delete.js +23 -0
- package/dist/tools/editorprefs-delete.js.map +1 -0
- package/dist/tools/editorprefs-get.d.ts +2 -0
- package/dist/tools/editorprefs-get.js +28 -0
- package/dist/tools/editorprefs-get.js.map +1 -0
- package/dist/tools/editorprefs-set.d.ts +2 -0
- package/dist/tools/editorprefs-set.js +33 -0
- package/dist/tools/editorprefs-set.js.map +1 -0
- package/dist/tools/find-references.js +24 -4
- package/dist/tools/find-references.js.map +1 -1
- package/dist/tools/frame-debugger.d.ts +2 -0
- package/dist/tools/frame-debugger.js +48 -0
- package/dist/tools/frame-debugger.js.map +1 -0
- package/dist/tools/gameobject-modify.js +69 -2
- package/dist/tools/gameobject-modify.js.map +1 -1
- package/dist/tools/gate-budget-estimate.js +2 -1
- package/dist/tools/gate-budget-estimate.js.map +1 -1
- package/dist/tools/hub-available-releases.d.ts +2 -0
- package/dist/tools/hub-available-releases.js +23 -0
- package/dist/tools/hub-available-releases.js.map +1 -0
- package/dist/tools/hub-get-install-path.d.ts +2 -0
- package/dist/tools/hub-get-install-path.js +20 -0
- package/dist/tools/hub-get-install-path.js.map +1 -0
- package/dist/tools/hub-install-editor.d.ts +2 -0
- package/dist/tools/hub-install-editor.js +47 -0
- package/dist/tools/hub-install-editor.js.map +1 -0
- package/dist/tools/hub-install-modules.d.ts +2 -0
- package/dist/tools/hub-install-modules.js +44 -0
- package/dist/tools/hub-install-modules.js.map +1 -0
- package/dist/tools/hub-list-editors.d.ts +2 -0
- package/dist/tools/hub-list-editors.js +25 -0
- package/dist/tools/hub-list-editors.js.map +1 -0
- package/dist/tools/hub-set-install-path.d.ts +2 -0
- package/dist/tools/hub-set-install-path.js +30 -0
- package/dist/tools/hub-set-install-path.js.map +1 -0
- package/dist/tools/impact-preview.js +2 -2
- package/dist/tools/impact-preview.js.map +1 -1
- package/dist/tools/index.d.ts +14 -0
- package/dist/tools/index.js +534 -24
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/light-add.d.ts +2 -0
- package/dist/tools/light-add.js +73 -0
- package/dist/tools/light-add.js.map +1 -0
- package/dist/tools/light-modify.d.ts +2 -0
- package/dist/tools/light-modify.js +36 -0
- package/dist/tools/light-modify.js.map +1 -0
- package/dist/tools/light-set.d.ts +2 -0
- package/dist/tools/light-set.js +54 -0
- package/dist/tools/light-set.js.map +1 -0
- package/dist/tools/list-assets-of-type.d.ts +2 -0
- package/dist/tools/list-assets-of-type.js +54 -0
- package/dist/tools/list-assets-of-type.js.map +1 -0
- package/dist/tools/lod-add-level.d.ts +2 -0
- package/dist/tools/lod-add-level.js +66 -0
- package/dist/tools/lod-add-level.js.map +1 -0
- package/dist/tools/lod-group-configure.d.ts +2 -0
- package/dist/tools/lod-group-configure.js +64 -0
- package/dist/tools/lod-group-configure.js.map +1 -0
- package/dist/tools/manage-tools.js +7 -5
- package/dist/tools/manage-tools.js.map +1 -1
- package/dist/tools/material-set-property.js +5 -1
- package/dist/tools/material-set-property.js.map +1 -1
- package/dist/tools/memory-snapshot-capture.d.ts +2 -0
- package/dist/tools/memory-snapshot-capture.js +61 -0
- package/dist/tools/memory-snapshot-capture.js.map +1 -0
- package/dist/tools/mutation-explain.js +2 -2
- package/dist/tools/mutation-explain.js.map +1 -1
- package/dist/tools/playerprefs-delete.d.ts +2 -0
- package/dist/tools/playerprefs-delete.js +24 -0
- package/dist/tools/playerprefs-delete.js.map +1 -0
- package/dist/tools/playerprefs-get.d.ts +2 -0
- package/dist/tools/playerprefs-get.js +28 -0
- package/dist/tools/playerprefs-get.js.map +1 -0
- package/dist/tools/playerprefs-set.d.ts +2 -0
- package/dist/tools/playerprefs-set.js +37 -0
- package/dist/tools/playerprefs-set.js.map +1 -0
- package/dist/tools/profiler-capture-frame.d.ts +2 -0
- package/dist/tools/profiler-capture-frame.js +75 -0
- package/dist/tools/profiler-capture-frame.js.map +1 -0
- package/dist/tools/profiler-clear-data.js +1 -2
- package/dist/tools/profiler-clear-data.js.map +1 -1
- package/dist/tools/profiler-enable-module.js +4 -5
- package/dist/tools/profiler-enable-module.js.map +1 -1
- package/dist/tools/profiler-get-status.js +5 -6
- package/dist/tools/profiler-get-status.js.map +1 -1
- package/dist/tools/profiler-load-data.js +4 -5
- package/dist/tools/profiler-load-data.js.map +1 -1
- package/dist/tools/profiler-save-data.js +3 -3
- package/dist/tools/profiler-save-data.js.map +1 -1
- package/dist/tools/profiler-set-config.js +5 -5
- package/dist/tools/profiler-set-config.js.map +1 -1
- package/dist/tools/profiler-start.js +4 -4
- package/dist/tools/profiler-start.js.map +1 -1
- package/dist/tools/profiler-stop.js +2 -3
- package/dist/tools/profiler-stop.js.map +1 -1
- package/dist/tools/read-asset.js +21 -3
- package/dist/tools/read-asset.js.map +1 -1
- package/dist/tools/reflection-probe-bake.d.ts +2 -0
- package/dist/tools/reflection-probe-bake.js +47 -0
- package/dist/tools/reflection-probe-bake.js.map +1 -0
- package/dist/tools/reflection-probe-get.d.ts +2 -0
- package/dist/tools/reflection-probe-get.js +19 -0
- package/dist/tools/reflection-probe-get.js.map +1 -0
- package/dist/tools/reimport-package.d.ts +2 -0
- package/dist/tools/reimport-package.js +53 -0
- package/dist/tools/reimport-package.js.map +1 -0
- package/dist/tools/scan-paths.js +19 -0
- package/dist/tools/scan-paths.js.map +1 -1
- package/dist/tools/scene-focus.js +1 -1
- package/dist/tools/scene-focus.js.map +1 -1
- package/dist/tools/scene-get-data.js +31 -13
- package/dist/tools/scene-get-data.js.map +1 -1
- package/dist/tools/scene-get-dirty-summary.js +1 -1
- package/dist/tools/scene-get-dirty-summary.js.map +1 -1
- package/dist/tools/sceneview-get-camera.d.ts +2 -0
- package/dist/tools/sceneview-get-camera.js +16 -0
- package/dist/tools/sceneview-get-camera.js.map +1 -0
- package/dist/tools/sceneview-set-camera.d.ts +2 -0
- package/dist/tools/sceneview-set-camera.js +58 -0
- package/dist/tools/sceneview-set-camera.js.map +1 -0
- package/dist/tools/script-write.js +2 -2
- package/dist/tools/script-write.js.map +1 -1
- package/dist/tools/scriptableobject-create.d.ts +2 -0
- package/dist/tools/scriptableobject-create.js +86 -0
- package/dist/tools/scriptableobject-create.js.map +1 -0
- package/dist/tools/search-assets.js +20 -3
- package/dist/tools/search-assets.js.map +1 -1
- package/dist/tools/settings-get-lighting.js +3 -4
- package/dist/tools/settings-get-lighting.js.map +1 -1
- package/dist/tools/settings-get-player.js +2 -2
- package/dist/tools/settings-get-player.js.map +1 -1
- package/dist/tools/settings-get-quality.js +1 -2
- package/dist/tools/settings-get-quality.js.map +1 -1
- package/dist/tools/settings-get-render-pipeline.d.ts +2 -0
- package/dist/tools/settings-get-render-pipeline.js +19 -0
- package/dist/tools/settings-get-render-pipeline.js.map +1 -0
- package/dist/tools/settings-get-time.d.ts +2 -0
- package/dist/tools/settings-get-time.js +18 -0
- package/dist/tools/settings-get-time.js.map +1 -0
- package/dist/tools/settings-set-lighting.js +5 -5
- package/dist/tools/settings-set-lighting.js.map +1 -1
- package/dist/tools/settings-set-physics.js +2 -2
- package/dist/tools/settings-set-physics.js.map +1 -1
- package/dist/tools/settings-set-player.js +2 -2
- package/dist/tools/settings-set-player.js.map +1 -1
- package/dist/tools/settings-set-quality-level.d.ts +2 -0
- package/dist/tools/settings-set-quality-level.js +44 -0
- package/dist/tools/settings-set-quality-level.js.map +1 -0
- package/dist/tools/settings-set-quality.js +2 -2
- package/dist/tools/settings-set-quality.js.map +1 -1
- package/dist/tools/settings-set-time.d.ts +2 -0
- package/dist/tools/settings-set-time.js +53 -0
- package/dist/tools/settings-set-time.js.map +1 -0
- package/dist/tools/shader-get-data.js +3 -3
- package/dist/tools/shader-get-data.js.map +1 -1
- package/dist/tools/shader-graph-create.d.ts +2 -0
- package/dist/tools/shader-graph-create.js +40 -0
- package/dist/tools/shader-graph-create.js.map +1 -0
- package/dist/tools/shader-graph-node-add.d.ts +2 -0
- package/dist/tools/shader-graph-node-add.js +50 -0
- package/dist/tools/shader-graph-node-add.js.map +1 -0
- package/dist/tools/shader-graph-node-connect.d.ts +2 -0
- package/dist/tools/shader-graph-node-connect.js +60 -0
- package/dist/tools/shader-graph-node-connect.js.map +1 -0
- package/dist/tools/shader-graph-open.d.ts +2 -0
- package/dist/tools/shader-graph-open.js +29 -0
- package/dist/tools/shader-graph-open.js.map +1 -0
- package/dist/tools/skybox-get.d.ts +2 -0
- package/dist/tools/skybox-get.js +13 -0
- package/dist/tools/skybox-get.js.map +1 -0
- package/dist/tools/skybox-set.d.ts +2 -0
- package/dist/tools/skybox-set.js +34 -0
- package/dist/tools/skybox-set.js.map +1 -0
- package/dist/tools/spriteatlas-add-packable.d.ts +2 -0
- package/dist/tools/spriteatlas-add-packable.js +40 -0
- package/dist/tools/spriteatlas-add-packable.js.map +1 -0
- package/dist/tools/spriteatlas-create.d.ts +2 -0
- package/dist/tools/spriteatlas-create.js +41 -0
- package/dist/tools/spriteatlas-create.js.map +1 -0
- package/dist/tools/spriteatlas-delete.d.ts +2 -0
- package/dist/tools/spriteatlas-delete.js +32 -0
- package/dist/tools/spriteatlas-delete.js.map +1 -0
- package/dist/tools/spriteatlas-get.d.ts +2 -0
- package/dist/tools/spriteatlas-get.js +23 -0
- package/dist/tools/spriteatlas-get.js.map +1 -0
- package/dist/tools/spriteatlas-list.d.ts +2 -0
- package/dist/tools/spriteatlas-list.js +21 -0
- package/dist/tools/spriteatlas-list.js.map +1 -0
- package/dist/tools/spriteatlas-modify.d.ts +2 -0
- package/dist/tools/spriteatlas-modify.js +55 -0
- package/dist/tools/spriteatlas-modify.js.map +1 -0
- package/dist/tools/spriteatlas-remove-packable.d.ts +2 -0
- package/dist/tools/spriteatlas-remove-packable.js +39 -0
- package/dist/tools/spriteatlas-remove-packable.js.map +1 -0
- package/dist/tools/terrain-create.d.ts +2 -0
- package/dist/tools/terrain-create.js +73 -0
- package/dist/tools/terrain-create.js.map +1 -0
- package/dist/tools/terrain-paint-layer.d.ts +2 -0
- package/dist/tools/terrain-paint-layer.js +85 -0
- package/dist/tools/terrain-paint-layer.js.map +1 -0
- package/dist/tools/terrain-place-trees.d.ts +2 -0
- package/dist/tools/terrain-place-trees.js +92 -0
- package/dist/tools/terrain-place-trees.js.map +1 -0
- package/dist/tools/terrain-set-heights.d.ts +2 -0
- package/dist/tools/terrain-set-heights.js +68 -0
- package/dist/tools/terrain-set-heights.js.map +1 -0
- package/dist/tools/terrain-set-neighbors.d.ts +2 -0
- package/dist/tools/terrain-set-neighbors.js +65 -0
- package/dist/tools/terrain-set-neighbors.js.map +1 -0
- package/dist/tools/texture-get-importer.d.ts +2 -0
- package/dist/tools/texture-get-importer.js +25 -0
- package/dist/tools/texture-get-importer.js.map +1 -0
- package/dist/tools/texture-get.d.ts +2 -0
- package/dist/tools/texture-get.js +23 -0
- package/dist/tools/texture-get.js.map +1 -0
- package/dist/tools/texture-reimport.d.ts +2 -0
- package/dist/tools/texture-reimport.js +33 -0
- package/dist/tools/texture-reimport.js.map +1 -0
- package/dist/tools/texture-set-import.d.ts +2 -0
- package/dist/tools/texture-set-import.js +52 -0
- package/dist/tools/texture-set-import.js.map +1 -0
- package/dist/tools/tilemap-box-fill.d.ts +2 -0
- package/dist/tools/tilemap-box-fill.js +32 -0
- package/dist/tools/tilemap-box-fill.js.map +1 -0
- package/dist/tools/tilemap-create-rule-tile.d.ts +2 -0
- package/dist/tools/tilemap-create-rule-tile.js +39 -0
- package/dist/tools/tilemap-create-rule-tile.js.map +1 -0
- package/dist/tools/tilemap-create-tile-asset.d.ts +2 -0
- package/dist/tools/tilemap-create-tile-asset.js +35 -0
- package/dist/tools/tilemap-create-tile-asset.js.map +1 -0
- package/dist/tools/tilemap-create.d.ts +2 -0
- package/dist/tools/tilemap-create.js +32 -0
- package/dist/tools/tilemap-create.js.map +1 -0
- package/dist/tools/tilemap-set-tile.d.ts +2 -0
- package/dist/tools/tilemap-set-tile.js +33 -0
- package/dist/tools/tilemap-set-tile.js.map +1 -0
- package/dist/tools/timeline-clip-add.d.ts +2 -0
- package/dist/tools/timeline-clip-add.js +43 -0
- package/dist/tools/timeline-clip-add.js.map +1 -0
- package/dist/tools/timeline-create.d.ts +2 -0
- package/dist/tools/timeline-create.js +32 -0
- package/dist/tools/timeline-create.js.map +1 -0
- package/dist/tools/timeline-director-bind.d.ts +2 -0
- package/dist/tools/timeline-director-bind.js +34 -0
- package/dist/tools/timeline-director-bind.js.map +1 -0
- package/dist/tools/timeline-modify.d.ts +2 -0
- package/dist/tools/timeline-modify.js +44 -0
- package/dist/tools/timeline-modify.js.map +1 -0
- package/dist/tools/timeline-track-add.d.ts +2 -0
- package/dist/tools/timeline-track-add.js +37 -0
- package/dist/tools/timeline-track-add.js.map +1 -0
- package/dist/tools/ui-canvas-add.d.ts +2 -0
- package/dist/tools/ui-canvas-add.js +66 -0
- package/dist/tools/ui-canvas-add.js.map +1 -0
- package/dist/tools/ui-element-add.d.ts +2 -0
- package/dist/tools/ui-element-add.js +65 -0
- package/dist/tools/ui-element-add.js.map +1 -0
- package/dist/tools/ui-element-modify.d.ts +2 -0
- package/dist/tools/ui-element-modify.js +59 -0
- package/dist/tools/ui-element-modify.js.map +1 -0
- package/dist/tools/ui-layout-group-add.d.ts +2 -0
- package/dist/tools/ui-layout-group-add.js +80 -0
- package/dist/tools/ui-layout-group-add.js.map +1 -0
- package/dist/tools/validate-edit.js +22 -1
- package/dist/tools/validate-edit.js.map +1 -1
- package/dist/tools/vfx-block-edit.d.ts +2 -0
- package/dist/tools/vfx-block-edit.js +51 -0
- package/dist/tools/vfx-block-edit.js.map +1 -0
- package/dist/tools/vfx-list.d.ts +2 -0
- package/dist/tools/vfx-list.js +31 -0
- package/dist/tools/vfx-list.js.map +1 -0
- package/dist/tools/vfx-open.d.ts +2 -0
- package/dist/tools/vfx-open.js +29 -0
- package/dist/tools/vfx-open.js.map +1 -0
- package/package.json +1 -1
package/dist/tool-router.js
CHANGED
|
@@ -1,16 +1,26 @@
|
|
|
1
1
|
import { AssetModelCache, isCompressible, routeCompressible } from "./compressible-router.js";
|
|
2
|
-
import { listAssetsOffline, findReferencesOffline } from "./offline.js";
|
|
2
|
+
import { listAssetsOffline, findReferencesOffline, dependenciesOffline } from "./offline.js";
|
|
3
3
|
import { editorLogPath, readLogTail, DEFAULT_LOG_TAIL_BYTES } from "./unity-log.js";
|
|
4
4
|
import { summarizeProjectHealth } from "./project-health.js";
|
|
5
5
|
import { buildCapabilities } from "./capabilities/build-capabilities.js";
|
|
6
6
|
import { RULE_CATALOG, FIX_CATALOG } from "./capabilities/rule-catalog.js";
|
|
7
|
-
import { GROUP_IDS, TOOL_GROUPS, groupToTools, } from "./capabilities/tool-groups.js";
|
|
7
|
+
import { GROUP_IDS, TOOL_GROUPS, AUTO_ACTIVATE_GROUPS, groupToTools, } from "./capabilities/tool-groups.js";
|
|
8
8
|
import { listRules } from "./capabilities/list-rules.js";
|
|
9
9
|
import { generateSkill } from "./skill/generate-skill.js";
|
|
10
10
|
import { knownClientKeys } from "./skill/client-paths.js";
|
|
11
11
|
import { ALL_TOOLS } from "./tools/index.js";
|
|
12
|
-
import { lockPath, readInstanceLock, classifyInstance } from "./instance-discovery.js";
|
|
12
|
+
import { lockPath, readInstanceLock, classifyInstance, isPidAlive } from "./instance-discovery.js";
|
|
13
|
+
import { listInstalledEditors, fetchAvailableReleases, openInstallDeepLink, getInstallPath, setInstallPath, } from "./hub-control.js";
|
|
14
|
+
/** Default Hub-control backend — calls the real hub-control functions. */
|
|
15
|
+
const defaultHubBackend = {
|
|
16
|
+
listInstalledEditors: () => listInstalledEditors(),
|
|
17
|
+
fetchAvailableReleases: () => fetchAvailableReleases(),
|
|
18
|
+
openInstallDeepLink: (version, changeset) => openInstallDeepLink(version, changeset),
|
|
19
|
+
getInstallPath: () => getInstallPath(),
|
|
20
|
+
setInstallPath: (path) => setInstallPath(path),
|
|
21
|
+
};
|
|
13
22
|
import { BATCH_TOOL_NAMES } from "./batch-spawn.js";
|
|
23
|
+
import { readProfileAndDetail, applyPaging, attachPagination, foldVerifyResult, parseResultBody, withResultBody, } from "./output-profile.js";
|
|
14
24
|
function injectRouteMeta(result, meta) {
|
|
15
25
|
if (result.content.length === 0)
|
|
16
26
|
return result;
|
|
@@ -35,6 +45,43 @@ function injectRouteMeta(result, meta) {
|
|
|
35
45
|
return result;
|
|
36
46
|
}
|
|
37
47
|
}
|
|
48
|
+
// M26 Plan 2 — local-routed error result (used by the Hub control tools for
|
|
49
|
+
// missing-parameter refusals before any side effect runs). Tagged _source=local
|
|
50
|
+
// because it is resolved entirely in the MCP server.
|
|
51
|
+
function localError(code, message) {
|
|
52
|
+
return {
|
|
53
|
+
content: [
|
|
54
|
+
{ type: "text", text: JSON.stringify({ error: { code, message }, _source: "local" }) },
|
|
55
|
+
],
|
|
56
|
+
isError: true,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
// M26 Plan 2 — gate-consistent envelope shape for the mutating Hub control
|
|
60
|
+
// tools (install_editor / install_modules / set_install_path). These are
|
|
61
|
+
// system-level ops, not project-asset mutations, so paths_hint is N/A and the
|
|
62
|
+
// gate is skipped (gate.skipped=true). The shape matches MutationEnvelope so
|
|
63
|
+
// agents parse it uniformly alongside bridge-routed mutating tools.
|
|
64
|
+
function hubMutationEnvelope(opts) {
|
|
65
|
+
const body = {
|
|
66
|
+
mutation: {
|
|
67
|
+
success: opts.success,
|
|
68
|
+
output: opts.output,
|
|
69
|
+
error: opts.error,
|
|
70
|
+
},
|
|
71
|
+
gate: {
|
|
72
|
+
mode: "off",
|
|
73
|
+
skipped: true,
|
|
74
|
+
validation: null,
|
|
75
|
+
delta: null,
|
|
76
|
+
},
|
|
77
|
+
agentNextSteps: opts.nextSteps,
|
|
78
|
+
_source: "local",
|
|
79
|
+
};
|
|
80
|
+
return {
|
|
81
|
+
content: [{ type: "text", text: JSON.stringify(body) }],
|
|
82
|
+
isError: opts.isError,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
38
85
|
function activeGroupsEqual(a, b) {
|
|
39
86
|
if (a.length !== b.length)
|
|
40
87
|
return false;
|
|
@@ -77,6 +124,66 @@ function summarizeBridgeStatusLock(lock) {
|
|
|
77
124
|
unityVersion: lock.unityVersion,
|
|
78
125
|
};
|
|
79
126
|
}
|
|
127
|
+
// ===========================================================================
|
|
128
|
+
// M22 — heavy-tool result post-processors (find_references + verify tools).
|
|
129
|
+
//
|
|
130
|
+
// These run after the live/batch/offline hop returns. The bridge emits the full
|
|
131
|
+
// result regardless of profile; we fold + page server-side so the wire shape
|
|
132
|
+
// matches the profile/paging contract without a bridge change. Each returns a
|
|
133
|
+
// new CallToolResult (never mutates the input); injectRouteMeta runs after to
|
|
134
|
+
// append `_route`.
|
|
135
|
+
// ===========================================================================
|
|
136
|
+
/** Page the offline find_references `referencedBy` list and attach pagination. */
|
|
137
|
+
function pageFindReferencesResult(result, pageSize, cursor) {
|
|
138
|
+
const referencedBy = Array.isArray(result.referencedBy) ? result.referencedBy : [];
|
|
139
|
+
const { page, block } = applyPaging(referencedBy, "find_references", { page_size: pageSize, cursor });
|
|
140
|
+
return attachPagination({ ...result, referencedBy: page }, block);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Fold + page the LIVE find_references result. The bridge returns a flat
|
|
144
|
+
* `referencedBy` path list (no byKind/byFolder groupings, no locations); we
|
|
145
|
+
* derive the compact grouping server-side and page when requested.
|
|
146
|
+
*/
|
|
147
|
+
function foldFindReferencesLive(result, profile, wantPaging, args) {
|
|
148
|
+
const body = parseResultBody(result);
|
|
149
|
+
if (body === null)
|
|
150
|
+
return result;
|
|
151
|
+
if (body.error)
|
|
152
|
+
return result;
|
|
153
|
+
// Bridge shape: { referencedBy: string[], totalCount, ... } (flat paths).
|
|
154
|
+
const rawList = Array.isArray(body.referencedBy) ? body.referencedBy : [];
|
|
155
|
+
const pageSize = typeof args.page_size === "number" && args.page_size > 0 ? args.page_size : 0;
|
|
156
|
+
const cursor = typeof args.cursor === "string" ? args.cursor : undefined;
|
|
157
|
+
if (profile === "compact") {
|
|
158
|
+
// compact: counts + byKind grouping only — drop the per-asset list.
|
|
159
|
+
const { referencedBy: _dropped, ...rest } = body;
|
|
160
|
+
void _dropped;
|
|
161
|
+
const byKind = groupLiveRefsByKind(rawList);
|
|
162
|
+
return withResultBody(result, {
|
|
163
|
+
...rest,
|
|
164
|
+
totalCount: rawList.length,
|
|
165
|
+
byKind,
|
|
166
|
+
referencedBy: [],
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
// balanced / full: keep the path list (paged when requested).
|
|
170
|
+
if (pageSize > 0) {
|
|
171
|
+
const { page, block } = applyPaging(rawList, "find_references", { page_size: pageSize, cursor });
|
|
172
|
+
const withPage = attachPagination({ ...body, referencedBy: page, totalCount: rawList.length }, block);
|
|
173
|
+
return withResultBody(result, withPage);
|
|
174
|
+
}
|
|
175
|
+
return withResultBody(result, { ...body, totalCount: rawList.length });
|
|
176
|
+
}
|
|
177
|
+
/** Derive a byKind grouping from flat referenced paths for the live compact view. */
|
|
178
|
+
function groupLiveRefsByKind(paths) {
|
|
179
|
+
const counts = {};
|
|
180
|
+
for (const path of paths) {
|
|
181
|
+
const dot = path.lastIndexOf(".");
|
|
182
|
+
const kind = dot > 0 ? path.slice(dot + 1).toLowerCase() : "other";
|
|
183
|
+
counts[kind] = (counts[kind] ?? 0) + 1;
|
|
184
|
+
}
|
|
185
|
+
return counts;
|
|
186
|
+
}
|
|
80
187
|
// Operator-facing "what to do next" hint for each coarse status. Kept short
|
|
81
188
|
// and action-oriented so the Validation Suite can render it inline.
|
|
82
189
|
function bridgeStatusNextStep(status) {
|
|
@@ -87,10 +194,41 @@ function bridgeStatusNextStep(status) {
|
|
|
87
194
|
return "Unity is compiling. Wait for the bridge to return to idle, or poll unity_open_mcp_bridge_status again.";
|
|
88
195
|
case "stopped":
|
|
89
196
|
return "Bridge listener is not reachable. Open the bridge window (Unity menu: Tools/Unity Open MCP Bridge) and ensure it is started, or launch Unity if it is not running, then call unity_open_mcp_bridge_status again to confirm.";
|
|
197
|
+
case "unreachable":
|
|
198
|
+
// M20 Plan 4-5 / T-fix-3 — distinct from "stopped": the Unity process is
|
|
199
|
+
// alive (lock PID live) but the listener did not respond. Usually a
|
|
200
|
+
// transient domain-reload window (the bridge tears down its HTTP socket
|
|
201
|
+
// for the reload duration) — retry shortly rather than treating it as a
|
|
202
|
+
// clean stop. This makes the reload-window flakiness visible instead of
|
|
203
|
+
// being masked by a clean-looking "stopped".
|
|
204
|
+
return "Bridge listener is not responding but Unity is running — likely a transient domain-reload window (the bridge tears down its HTTP socket during compiles). Wait a moment and call unity_open_mcp_bridge_status again; if it persists, call unity_open_mcp_read_compile_errors to check for a failed recompile.";
|
|
90
205
|
case "dead_bridge":
|
|
91
|
-
|
|
206
|
+
// M23 Plan 2 — safe-mode surfacing (feedback 2026-06-29). A dead_bridge
|
|
207
|
+
// signature (live PID + stale heartbeat) almost always means Unity is
|
|
208
|
+
// sitting in Safe Mode / showing compile errors; the generic "open the
|
|
209
|
+
// bridge window" hint was misleading. Name safe mode explicitly and
|
|
210
|
+
// point at the one channel that works in that state.
|
|
211
|
+
return "Unity is likely in Safe Mode or showing compile errors — the bridge " +
|
|
212
|
+
"assembly failed to recompile, so the HTTP listener will not return on its " +
|
|
213
|
+
"own. Call unity_open_mcp_read_compile_errors to retrieve the compiler " +
|
|
214
|
+
"errors from Editor.log, fix the cited file/line, then trigger a recompile " +
|
|
215
|
+
"(e.g. a no-op edit + focus Unity, or unity_open_mcp_compile_check once the " +
|
|
216
|
+
"source compiles). If Unity is NOT in safe mode, the bridge toolbar toggle " +
|
|
217
|
+
"may be off — open the bridge window (Unity menu: Tools/Unity Open MCP " +
|
|
218
|
+
"Bridge) and confirm it is started.";
|
|
92
219
|
}
|
|
93
220
|
}
|
|
221
|
+
function bridgeStatusRecoveryHint(status) {
|
|
222
|
+
if (status === "dead_bridge") {
|
|
223
|
+
return {
|
|
224
|
+
tool: "unity_open_mcp_read_compile_errors",
|
|
225
|
+
reason: "Unity is likely in Safe Mode / compile failure — read_compile_errors " +
|
|
226
|
+
"reads Editor.log offline (the only channel that works with the bridge " +
|
|
227
|
+
"assembly dead).",
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
94
232
|
export class ToolRouter {
|
|
95
233
|
live;
|
|
96
234
|
batch;
|
|
@@ -98,16 +236,36 @@ export class ToolRouter {
|
|
|
98
236
|
eventStream;
|
|
99
237
|
sessionState;
|
|
100
238
|
onToolListChanged;
|
|
239
|
+
hubBackend;
|
|
101
240
|
modelCache = new AssetModelCache();
|
|
102
|
-
constructor(live, batch, projectPath, eventStream, sessionState, onToolListChanged
|
|
241
|
+
constructor(live, batch, projectPath, eventStream, sessionState, onToolListChanged,
|
|
242
|
+
// M26 Plan 2 — injectable Hub-control backend. Omit in production (the
|
|
243
|
+
// default calls the real hub-control functions); tests pass a fake to
|
|
244
|
+
// avoid real subprocess + network side effects.
|
|
245
|
+
hubBackend = defaultHubBackend) {
|
|
103
246
|
this.live = live;
|
|
104
247
|
this.batch = batch;
|
|
105
248
|
this.projectPath = projectPath;
|
|
106
249
|
this.eventStream = eventStream;
|
|
107
250
|
this.sessionState = sessionState;
|
|
108
251
|
this.onToolListChanged = onToolListChanged;
|
|
252
|
+
this.hubBackend = hubBackend;
|
|
109
253
|
}
|
|
110
254
|
async route(toolName, args) {
|
|
255
|
+
return this.routeCore(this.live, toolName, args);
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* M23 Plan 3 — route with a per-call live-client override. Used when a
|
|
259
|
+
* per-request `_meta.port` override targets a different bridge instance than
|
|
260
|
+
* the default. The override client bypasses shared session state (it is a
|
|
261
|
+
* fresh LiveClient aimed at the override port); all other routing logic is
|
|
262
|
+
* identical to {@link route}. When `overrideLive` is null the default client
|
|
263
|
+
* is used (identical to {@link route}).
|
|
264
|
+
*/
|
|
265
|
+
async routeOverride(toolName, args, overrideLive) {
|
|
266
|
+
return this.routeCore(overrideLive, toolName, args);
|
|
267
|
+
}
|
|
268
|
+
async routeCore(live, toolName, args) {
|
|
111
269
|
// list_assets — always offline (no live equivalent needed).
|
|
112
270
|
if (toolName === "unity_open_mcp_list_assets") {
|
|
113
271
|
return this.routeListAssets(args);
|
|
@@ -125,11 +283,11 @@ export class ToolRouter {
|
|
|
125
283
|
// bridge is reachable; an unreachable bridge returns a clear error instead
|
|
126
284
|
// of hanging on connect.
|
|
127
285
|
if (toolName === "unity_senses_pull_events") {
|
|
128
|
-
return this.routePullEvents(args);
|
|
286
|
+
return this.routePullEvents(args, live);
|
|
129
287
|
}
|
|
130
288
|
// capabilities — local capability-discovery surface (no live/batch hop).
|
|
131
289
|
if (toolName === "unity_open_mcp_capabilities") {
|
|
132
|
-
return this.routeCapabilities(args);
|
|
290
|
+
return this.routeCapabilities(args, live);
|
|
133
291
|
}
|
|
134
292
|
// list_rules — local rule catalog (no live/batch hop). Lets an agent
|
|
135
293
|
// discover which rules apply to which asset kinds before calling
|
|
@@ -148,7 +306,7 @@ export class ToolRouter {
|
|
|
148
306
|
// server applies the activate / deactivate filter to ListTools. Always
|
|
149
307
|
// visible regardless of the current active set (meta-tool).
|
|
150
308
|
if (toolName === "unity_open_mcp_manage_tools") {
|
|
151
|
-
return this.routeManageTools(args);
|
|
309
|
+
return this.routeManageTools(args, live);
|
|
152
310
|
}
|
|
153
311
|
// testsuite-tauri phase-3 — bridge_status. Operator-only health snapshot.
|
|
154
312
|
// Server-resolved (classifyInstance + one /ping via the LiveClient); never
|
|
@@ -156,18 +314,59 @@ export class ToolRouter {
|
|
|
156
314
|
// `status` token (running/compiling/stopped/dead_bridge) the Validation
|
|
157
315
|
// Suite app drives its offline-scenario gate off. Read-only, gate-free.
|
|
158
316
|
if (toolName === "unity_open_mcp_bridge_status") {
|
|
159
|
-
return this.routeBridgeStatus(args);
|
|
317
|
+
return this.routeBridgeStatus(args, live);
|
|
318
|
+
}
|
|
319
|
+
// M26 Plan 2 — Unity Hub control tools. Local-routed: resolved inside the
|
|
320
|
+
// MCP server (filesystem discovery + Unity archive feed + unityhub:// deep
|
|
321
|
+
// link + Hub CLI). Never hit the bridge tool endpoint, never spawn Unity.
|
|
322
|
+
// Mutating members are system-level ops (paths_hint N/A, gate-free).
|
|
323
|
+
if (toolName === "unity_open_mcp_hub_list_editors") {
|
|
324
|
+
return this.routeHubListEditors();
|
|
325
|
+
}
|
|
326
|
+
if (toolName === "unity_open_mcp_hub_available_releases") {
|
|
327
|
+
return this.routeHubAvailableReleases();
|
|
328
|
+
}
|
|
329
|
+
if (toolName === "unity_open_mcp_hub_install_editor") {
|
|
330
|
+
return this.routeHubInstallEditor(args);
|
|
331
|
+
}
|
|
332
|
+
if (toolName === "unity_open_mcp_hub_install_modules") {
|
|
333
|
+
return this.routeHubInstallModules(args);
|
|
334
|
+
}
|
|
335
|
+
if (toolName === "unity_open_mcp_hub_get_install_path") {
|
|
336
|
+
return this.routeHubGetInstallPath();
|
|
337
|
+
}
|
|
338
|
+
if (toolName === "unity_open_mcp_hub_set_install_path") {
|
|
339
|
+
return this.routeHubSetInstallPath(args);
|
|
160
340
|
}
|
|
161
341
|
// find_references — offline-first when no bridge is connected; live
|
|
162
342
|
// ReferenceGraph remains available when the bridge is up.
|
|
163
343
|
if (toolName === "unity_open_mcp_find_references") {
|
|
164
|
-
return this.routeFindReferences(args);
|
|
344
|
+
return this.routeFindReferences(args, live);
|
|
345
|
+
}
|
|
346
|
+
// dependencies — offline-first (M24 Plan 2 / T24.2). Forward + reverse
|
|
347
|
+
// edges + impact analysis all compute from YAML on disk when no bridge is
|
|
348
|
+
// connected; the live DependenciesTool remains available when the bridge
|
|
349
|
+
// is up. The offline path is the only one that exposes include_impact
|
|
350
|
+
// (transitive reverse closure) today.
|
|
351
|
+
if (toolName === "unity_open_mcp_dependencies") {
|
|
352
|
+
return this.routeDependencies(args, live);
|
|
353
|
+
}
|
|
354
|
+
// M22 — verify tools. The bridge returns the full issues[] list; we fold +
|
|
355
|
+
// page server-side per the output profile (compact = counts only).
|
|
356
|
+
if (toolName === "unity_open_mcp_validate_edit" || toolName === "unity_open_mcp_scan_paths") {
|
|
357
|
+
return this.routeVerifyResult(toolName, args, live);
|
|
358
|
+
}
|
|
359
|
+
// M22 — scene_get_data. Resolves the output profile (onto detail) and pages
|
|
360
|
+
// the node stream server-side; the bridge detail/max_nodes params keep
|
|
361
|
+
// working as back-compat aliases.
|
|
362
|
+
if (toolName === "unity_open_mcp_scene_get_data") {
|
|
363
|
+
return this.routeSceneGetData(args, live);
|
|
165
364
|
}
|
|
166
365
|
// Compact drill-down reads: offline-first for text-serialized assets, fall
|
|
167
366
|
// back to live bridge for binary formats. The compressible-router handles
|
|
168
367
|
// the source selection internally.
|
|
169
368
|
if (isCompressible(toolName)) {
|
|
170
|
-
const result = await routeCompressible(toolName, args,
|
|
369
|
+
const result = await routeCompressible(toolName, args, live, this.modelCache, this.projectPath);
|
|
171
370
|
return injectRouteMeta(result, { route: "live" });
|
|
172
371
|
}
|
|
173
372
|
const canBatch = this.batch.isBatchTool(toolName);
|
|
@@ -186,13 +385,13 @@ export class ToolRouter {
|
|
|
186
385
|
});
|
|
187
386
|
}
|
|
188
387
|
if (!canBatch && !isPing) {
|
|
189
|
-
const result = await
|
|
388
|
+
const result = await live.route(toolName, args);
|
|
190
389
|
return injectRouteMeta(result, { route: "live" });
|
|
191
390
|
}
|
|
192
|
-
const liveAvailable = await
|
|
391
|
+
const liveAvailable = await live.isLiveAvailable();
|
|
193
392
|
if (liveAvailable) {
|
|
194
393
|
console.error(`[unity-open-mcp] Route: ${toolName} -> live`);
|
|
195
|
-
const result = await
|
|
394
|
+
const result = await live.route(toolName, args);
|
|
196
395
|
return injectRouteMeta(result, { route: "live" });
|
|
197
396
|
}
|
|
198
397
|
console.error(`[unity-open-mcp] Route: ${toolName} -> batch (live bridge unavailable)`);
|
|
@@ -334,8 +533,8 @@ export class ToolRouter {
|
|
|
334
533
|
// (a fresh subscriber id is minted per process), so a restarted MCP server
|
|
335
534
|
// begins "now" — agents that care about historical logs should still call
|
|
336
535
|
// unity_senses_read_console.
|
|
337
|
-
async routePullEvents(args) {
|
|
338
|
-
const liveAvailable = await
|
|
536
|
+
async routePullEvents(args, live) {
|
|
537
|
+
const liveAvailable = await live.isLiveAvailable();
|
|
339
538
|
if (!liveAvailable) {
|
|
340
539
|
return {
|
|
341
540
|
content: [
|
|
@@ -365,7 +564,7 @@ export class ToolRouter {
|
|
|
365
564
|
isError: false,
|
|
366
565
|
};
|
|
367
566
|
}
|
|
368
|
-
async routeCapabilities(args) {
|
|
567
|
+
async routeCapabilities(args, live) {
|
|
369
568
|
const kind = args.kind === "tools" || args.kind === "rules" || args.kind === "fixes"
|
|
370
569
|
? args.kind
|
|
371
570
|
: undefined;
|
|
@@ -376,12 +575,18 @@ export class ToolRouter {
|
|
|
376
575
|
// Capabilities stays local-route; the bridge probe is a read-only fetch
|
|
377
576
|
// that does not change the route classification.
|
|
378
577
|
let availableBridgeTools;
|
|
379
|
-
const liveAvailable = await
|
|
578
|
+
const liveAvailable = await live.isLiveAvailable();
|
|
380
579
|
if (liveAvailable) {
|
|
381
|
-
const inventory = await
|
|
580
|
+
const inventory = await live.listBridgeTools();
|
|
382
581
|
if (inventory)
|
|
383
582
|
availableBridgeTools = inventory.tools;
|
|
384
583
|
}
|
|
584
|
+
// M20 Plan 7 / T20.7.0 — reconcile auto-activation before reporting the
|
|
585
|
+
// group catalog so the capability output reflects packages that came
|
|
586
|
+
// online since the last call. Auto-activated groups surface with
|
|
587
|
+
// `autoActivated: true` + `packageDependency` (see build-capabilities).
|
|
588
|
+
// Reuse the inventory already fetched above (no second GET /tools hop).
|
|
589
|
+
await this.reconcileAutoActivation(availableBridgeTools ? { tools: availableBridgeTools } : null);
|
|
385
590
|
const result = buildCapabilities({
|
|
386
591
|
tools: ALL_TOOLS,
|
|
387
592
|
batchToolNames: BATCH_TOOL_NAMES,
|
|
@@ -459,8 +664,8 @@ export class ToolRouter {
|
|
|
459
664
|
// M18 Plan 2 / T18.2.2 — manage_tools routes local. Mutates the per-
|
|
460
665
|
// session tool-group visibility state (ToolSessionState) that ListTools
|
|
461
666
|
// consults to filter tools. The bridge does not see these calls — it is
|
|
462
|
-
// session-state the MCP server owns (
|
|
463
|
-
//
|
|
667
|
+
// session-state the MCP server owns (the resolved decision in M18
|
|
668
|
+
// execution-plan.md).
|
|
464
669
|
//
|
|
465
670
|
// Compiled-state availability (whether the bridge compiled the domain in)
|
|
466
671
|
// is reported in the `available` + `availableReason` fields per group. The
|
|
@@ -468,11 +673,11 @@ export class ToolRouter {
|
|
|
468
673
|
// when reachable; when the bridge is offline it reports `available: null`
|
|
469
674
|
// (unknown) and the agent should fall back to `unity_open_mcp_capabilities`
|
|
470
675
|
// for the authoritative compiled-state report.
|
|
471
|
-
async routeManageTools(args) {
|
|
676
|
+
async routeManageTools(args, live) {
|
|
472
677
|
const action = typeof args.action === "string" ? args.action : "";
|
|
473
678
|
const group = typeof args.group === "string" ? args.group.trim() : "";
|
|
474
679
|
if (action === "list_groups") {
|
|
475
|
-
return this.manageToolsListGroups();
|
|
680
|
+
return this.manageToolsListGroups(live);
|
|
476
681
|
}
|
|
477
682
|
if (action === "reset") {
|
|
478
683
|
const before = this.sessionState.activeGroups();
|
|
@@ -516,9 +721,20 @@ export class ToolRouter {
|
|
|
516
721
|
}
|
|
517
722
|
return this.manageToolsError("unknown_action", `Unknown action '${action}'. Valid actions: list_groups, activate, deactivate, reset.`);
|
|
518
723
|
}
|
|
519
|
-
async manageToolsListGroups() {
|
|
724
|
+
async manageToolsListGroups(live) {
|
|
725
|
+
// M20 Plan 7 / T20.7.0 — fetch the compiled-state inventory once and
|
|
726
|
+
// reuse it for both auto-activation reconciliation and compiled-state
|
|
727
|
+
// availability (two GET /tools hops → one). When the bridge is offline
|
|
728
|
+
// both paths degrade gracefully (no auto-activation; availability=null).
|
|
729
|
+
const liveAvailable = await live.isLiveAvailable();
|
|
730
|
+
const inventory = liveAvailable
|
|
731
|
+
? await live.listBridgeTools()
|
|
732
|
+
: null;
|
|
733
|
+
// Reconcile auto-activation first so the listed active set reflects
|
|
734
|
+
// packages that came online since the last call.
|
|
735
|
+
await this.reconcileAutoActivation(inventory ? { tools: inventory.tools } : null);
|
|
520
736
|
const groupsList = groupToTools();
|
|
521
|
-
const compiledAvailability =
|
|
737
|
+
const compiledAvailability = this.computeCompiledAvailability(inventory, groupsList);
|
|
522
738
|
const groups = TOOL_GROUPS.map((g) => {
|
|
523
739
|
const tools = groupsList[g.id] ?? [];
|
|
524
740
|
const availability = compiledAvailability.get(g.id);
|
|
@@ -526,6 +742,12 @@ export class ToolRouter {
|
|
|
526
742
|
id: g.id,
|
|
527
743
|
description: g.description,
|
|
528
744
|
active: this.sessionState.isGroupActive(g.id),
|
|
745
|
+
// M20 Plan 7 / T20.7.0 — why the group is active (default / manual /
|
|
746
|
+
// auto). `null` when the group is not active. Lets an agent learn
|
|
747
|
+
// that a group is visible because its package is installed, not
|
|
748
|
+
// because the operator opted in.
|
|
749
|
+
activationSource: this.sessionState.activationSource(g.id),
|
|
750
|
+
autoActivated: this.sessionState.activationSource(g.id) === "auto",
|
|
529
751
|
defaultEnabled: g.defaultEnabled,
|
|
530
752
|
// Compiled-state availability of the domain dependency. `true` for
|
|
531
753
|
// groups without a domainDefine (always compiled in); `false` /
|
|
@@ -536,6 +758,7 @@ export class ToolRouter {
|
|
|
536
758
|
available: availability?.available ?? null,
|
|
537
759
|
availableReason: availability?.reason ?? null,
|
|
538
760
|
unityPackage: g.unityPackage ?? null,
|
|
761
|
+
packageDependency: g.unityPackage ?? null,
|
|
539
762
|
toolCount: tools.length,
|
|
540
763
|
tools,
|
|
541
764
|
};
|
|
@@ -573,16 +796,69 @@ export class ToolRouter {
|
|
|
573
796
|
return;
|
|
574
797
|
await this.onToolListChanged();
|
|
575
798
|
}
|
|
799
|
+
// M20 Plan 7 / T20.7.0 — reconcile the auto-activated set against the
|
|
800
|
+
// currently-compiled-in bridge inventory. A group with `autoActivate: true`
|
|
801
|
+
// is considered package-present when any of its compiled-in tool names
|
|
802
|
+
// appears in the bridge's `GET /tools` inventory — the same signal
|
|
803
|
+
// `resolveCompiledAvailability` uses, so no extra package_list round-trip.
|
|
804
|
+
// The session store records the outcome; we fire the listChanged
|
|
805
|
+
// notification exactly once when the active set changed. Returns true when
|
|
806
|
+
// the active set changed (so callers like capabilities / list_groups can
|
|
807
|
+
// report a fresh snapshot).
|
|
808
|
+
//
|
|
809
|
+
// Callers that have ALREADY fetched the bridge inventory (capabilities,
|
|
810
|
+
// list_groups) pass it via `prefetchedInventory` to avoid a second
|
|
811
|
+
// `GET /tools` round-trip on the same call.
|
|
812
|
+
async reconcileAutoActivation(prefetchedInventory) {
|
|
813
|
+
if (AUTO_ACTIVATE_GROUPS.length === 0)
|
|
814
|
+
return false;
|
|
815
|
+
let inventory = prefetchedInventory ?? null;
|
|
816
|
+
if (!inventory) {
|
|
817
|
+
const liveAvailable = await this.live.isLiveAvailable();
|
|
818
|
+
if (!liveAvailable)
|
|
819
|
+
return false;
|
|
820
|
+
inventory = await this.live.listBridgeTools();
|
|
821
|
+
if (!inventory)
|
|
822
|
+
return false;
|
|
823
|
+
}
|
|
824
|
+
const groupsList = groupToTools();
|
|
825
|
+
const satisfied = new Set();
|
|
826
|
+
for (const entry of AUTO_ACTIVATE_GROUPS) {
|
|
827
|
+
const groupTools = groupsList[entry.groupId] ?? [];
|
|
828
|
+
const anyCompiledIn = groupTools.length > 0 &&
|
|
829
|
+
groupTools.some((t) => inventory.tools.has(t));
|
|
830
|
+
if (anyCompiledIn)
|
|
831
|
+
satisfied.add(entry.groupId);
|
|
832
|
+
}
|
|
833
|
+
const before = this.sessionState.activeGroups();
|
|
834
|
+
const changed = this.sessionState.reconcileAutoActivation(satisfied);
|
|
835
|
+
if (changed.length > 0) {
|
|
836
|
+
await this.maybeNotifyToolListChanged(before);
|
|
837
|
+
return true;
|
|
838
|
+
}
|
|
839
|
+
return false;
|
|
840
|
+
}
|
|
576
841
|
// Resolve compiled-state availability per group from the live bridge. The
|
|
577
842
|
// bridge reports which tools it compiled in via `GET /tools`; a domain-
|
|
578
843
|
// gated group is `available: true` when any of its compiled-in tool names
|
|
579
844
|
// appears in that set, `false` when none do (Unity domain package not
|
|
580
845
|
// installed), and `null` (unknown) when the bridge is offline.
|
|
846
|
+
//
|
|
847
|
+
// Thin async wrapper over {@link computeCompiledAvailability}: fetches the
|
|
848
|
+
// inventory (or null when the bridge is offline) and delegates. Kept for
|
|
849
|
+
// the few call sites that don't already have an inventory in hand.
|
|
581
850
|
async resolveCompiledAvailability() {
|
|
582
|
-
const out = new Map();
|
|
583
851
|
const liveAvailable = await this.live.isLiveAvailable();
|
|
584
|
-
const inventory = liveAvailable
|
|
585
|
-
|
|
852
|
+
const inventory = liveAvailable
|
|
853
|
+
? await this.live.listBridgeTools()
|
|
854
|
+
: null;
|
|
855
|
+
return this.computeCompiledAvailability(inventory, groupToTools());
|
|
856
|
+
}
|
|
857
|
+
// Pure per-group availability computation given a (possibly null) bridge
|
|
858
|
+
// tool inventory. Extracted so callers that already fetched the inventory
|
|
859
|
+
// (capabilities, list_groups) can avoid a second GET /tools hop.
|
|
860
|
+
computeCompiledAvailability(inventory, groupsList) {
|
|
861
|
+
const out = new Map();
|
|
586
862
|
for (const g of TOOL_GROUPS) {
|
|
587
863
|
if (!g.domainDefine) {
|
|
588
864
|
out.set(g.id, { available: true, reason: null });
|
|
@@ -640,6 +916,7 @@ export class ToolRouter {
|
|
|
640
916
|
// lock="dead_bridge" → "dead_bridge"
|
|
641
917
|
// /ping reachable, compiling=true → "compiling"
|
|
642
918
|
// /ping reachable, connected=true → "running"
|
|
919
|
+
// /ping UNreachable + Unity running → "unreachable" (T-fix-3)
|
|
643
920
|
// otherwise (offline / toolbar off / gone) → "stopped"
|
|
644
921
|
//
|
|
645
922
|
// "stopped" intentionally folds two indistinguishable cases from the
|
|
@@ -648,7 +925,15 @@ export class ToolRouter {
|
|
|
648
925
|
// lock summary in the response disambiguates them (`lock === null` → no
|
|
649
926
|
// Unity; `lock.pid` alive but no listener → toolbar off). The tool never
|
|
650
927
|
// errors on an offline bridge — `stopped` IS the answer in that case.
|
|
651
|
-
|
|
928
|
+
//
|
|
929
|
+
// M20 Plan 4-5 / T-fix-3 — "unreachable" splits out the case where Unity IS
|
|
930
|
+
// running (lock PID alive, often mid-reload) but the listener did not
|
|
931
|
+
// respond. Pre-fix this collapsed to "stopped", hiding transient reload-
|
|
932
|
+
// window flakiness behind a clean-looking status. "unreachable" makes it
|
|
933
|
+
// visible so agents retry instead of treating it as a clean stop. The
|
|
934
|
+
// `isError:false` contract is preserved — this is richer status, not an
|
|
935
|
+
// error path.
|
|
936
|
+
async routeBridgeStatus(_args, live) {
|
|
652
937
|
const lockOnDisk = lockPath(this.projectPath);
|
|
653
938
|
let lock = null;
|
|
654
939
|
try {
|
|
@@ -664,11 +949,17 @@ export class ToolRouter {
|
|
|
664
949
|
// — both shapes are valid inputs here. A compile-in-progress bridge
|
|
665
950
|
// returns HTTP 503 from /ping which isLiveAvailable() treats as reachable;
|
|
666
951
|
// the ping body's `compiling` field is the authoritative signal.
|
|
667
|
-
const pingResult = await
|
|
952
|
+
const pingResult = await live.route("unity_open_mcp_ping", {});
|
|
668
953
|
const pingBody = parsePingBody(pingResult);
|
|
669
954
|
const pingReachable = !pingResult.isError && pingBody !== null;
|
|
670
955
|
const compiling = pingReachable === true && pingBody?.compiling === true;
|
|
671
956
|
const connected = pingReachable === true && pingBody?.connected === true;
|
|
957
|
+
// T-fix-3 — when the ping is unreachable, distinguish "Unity is running but
|
|
958
|
+
// the listener is momentarily down" (lock PID alive / reload window) from a
|
|
959
|
+
// genuine "nothing there" stop. classification "reloading" covers the
|
|
960
|
+
// domain-reload window; a live-PID "healthy"/"gone"-with-alive-pid lock
|
|
961
|
+
// also indicates Unity is up.
|
|
962
|
+
const lockPidAlive = lock !== null && isPidAlive(lock.pid);
|
|
672
963
|
let status;
|
|
673
964
|
if (classification === "dead_bridge") {
|
|
674
965
|
status = "dead_bridge";
|
|
@@ -679,6 +970,12 @@ export class ToolRouter {
|
|
|
679
970
|
else if (connected) {
|
|
680
971
|
status = "running";
|
|
681
972
|
}
|
|
973
|
+
else if (!pingReachable && (classification === "reloading" || lockPidAlive)) {
|
|
974
|
+
// Unity is running but the listener didn't respond — transient (reload
|
|
975
|
+
// window) or toolbar-off. Surface as unreachable so it isn't masked by
|
|
976
|
+
// a clean "stopped".
|
|
977
|
+
status = "unreachable";
|
|
978
|
+
}
|
|
682
979
|
else {
|
|
683
980
|
status = "stopped";
|
|
684
981
|
}
|
|
@@ -689,6 +986,17 @@ export class ToolRouter {
|
|
|
689
986
|
// uses for poll termination).
|
|
690
987
|
ready: status === "running",
|
|
691
988
|
projectPath: this.projectPath,
|
|
989
|
+
// M23 Plan 2 — top-level mirror of the instance-lock classification
|
|
990
|
+
// (healthy | reloading | dead_bridge | gone) + a structured recovery
|
|
991
|
+
// hint. Mirrors `instance.classification` at the top level so agents
|
|
992
|
+
// can branch on a single field without digging into the instance
|
|
993
|
+
// sub-object; `recoveryHint` is null unless the status has a specific
|
|
994
|
+
// recovery tool to call (today: dead_bridge → read_compile_errors).
|
|
995
|
+
// Feedback 2026-06-29: a dead_bridge must read as "Unity likely in
|
|
996
|
+
// Safe Mode / compile failure", not the generic "stopped" the bare
|
|
997
|
+
// status used to suggest.
|
|
998
|
+
classification,
|
|
999
|
+
recoveryHint: bridgeStatusRecoveryHint(status),
|
|
692
1000
|
instance: {
|
|
693
1001
|
lockPath: lockOnDisk,
|
|
694
1002
|
classification,
|
|
@@ -716,12 +1024,146 @@ export class ToolRouter {
|
|
|
716
1024
|
isError: false,
|
|
717
1025
|
};
|
|
718
1026
|
}
|
|
719
|
-
|
|
720
|
-
|
|
1027
|
+
// ── M26 Plan 2 — Unity Hub control (local-routed) ────────────────
|
|
1028
|
+
//
|
|
1029
|
+
// Each of these resolves entirely inside the MCP server (no bridge tool
|
|
1030
|
+
// endpoint, no batch Unity) → every response is tagged `_source: "local"`.
|
|
1031
|
+
// Read-only members are plain JSON bodies; mutating members return the
|
|
1032
|
+
// gate-consistent mutation/gate/agentNextSteps envelope shape so agents
|
|
1033
|
+
// parse them uniformly, with gate.skipped=true because these are system-
|
|
1034
|
+
// level ops where the project-asset gate does not apply (paths_hint N/A).
|
|
1035
|
+
async routeHubListEditors() {
|
|
1036
|
+
const editors = this.hubBackend.listInstalledEditors();
|
|
1037
|
+
const body = {
|
|
1038
|
+
editors,
|
|
1039
|
+
count: editors.length,
|
|
1040
|
+
_source: "local",
|
|
1041
|
+
};
|
|
1042
|
+
return {
|
|
1043
|
+
content: [{ type: "text", text: JSON.stringify(body) }],
|
|
1044
|
+
isError: false,
|
|
1045
|
+
};
|
|
1046
|
+
}
|
|
1047
|
+
async routeHubAvailableReleases() {
|
|
1048
|
+
const result = await this.hubBackend.fetchAvailableReleases();
|
|
1049
|
+
const body = {
|
|
1050
|
+
entries: result.entries,
|
|
1051
|
+
count: result.entries.length,
|
|
1052
|
+
stale: result.stale,
|
|
1053
|
+
fetchedAt: result.fetchedAt,
|
|
1054
|
+
_source: "local",
|
|
1055
|
+
};
|
|
1056
|
+
return {
|
|
1057
|
+
content: [{ type: "text", text: JSON.stringify(body) }],
|
|
1058
|
+
isError: false,
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
1061
|
+
async routeHubInstallEditor(args) {
|
|
1062
|
+
const version = typeof args.version === "string" ? args.version : "";
|
|
1063
|
+
const changeset = typeof args.changeset === "string" ? args.changeset : undefined;
|
|
1064
|
+
if (!version.trim()) {
|
|
1065
|
+
return localError("missing_parameter", "version is required.");
|
|
1066
|
+
}
|
|
1067
|
+
const res = this.hubBackend.openInstallDeepLink(version, changeset);
|
|
1068
|
+
return hubMutationEnvelope({
|
|
1069
|
+
success: res.opened,
|
|
1070
|
+
output: { deepLink: res.deepLink, version, changeset: changeset ?? null },
|
|
1071
|
+
error: res.error,
|
|
1072
|
+
nextSteps: res.opened
|
|
1073
|
+
? [
|
|
1074
|
+
"Unity Hub opened at the install dialog for " +
|
|
1075
|
+
version +
|
|
1076
|
+
". The download runs inside the Hub (watch its progress UI).",
|
|
1077
|
+
"There is no in-call completion detection — poll " +
|
|
1078
|
+
"unity_open_mcp_hub_list_editors after the Hub finishes to confirm the new editor.",
|
|
1079
|
+
]
|
|
1080
|
+
: [
|
|
1081
|
+
"Ensure Unity Hub is installed and registered as the unityhub:// handler, then retry.",
|
|
1082
|
+
"Fallback: open the release-notes URL from hub_available_releases in a browser and install manually.",
|
|
1083
|
+
],
|
|
1084
|
+
isError: !res.opened,
|
|
1085
|
+
});
|
|
1086
|
+
}
|
|
1087
|
+
async routeHubInstallModules(args) {
|
|
1088
|
+
const version = typeof args.version === "string" ? args.version : "";
|
|
1089
|
+
const changeset = typeof args.changeset === "string" ? args.changeset : undefined;
|
|
1090
|
+
const modules = Array.isArray(args.modules)
|
|
1091
|
+
? args.modules.filter((m) => typeof m === "string")
|
|
1092
|
+
: [];
|
|
1093
|
+
if (!version.trim()) {
|
|
1094
|
+
return localError("missing_parameter", "version is required.");
|
|
1095
|
+
}
|
|
1096
|
+
const res = this.hubBackend.openInstallDeepLink(version, changeset);
|
|
1097
|
+
return hubMutationEnvelope({
|
|
1098
|
+
success: res.opened,
|
|
1099
|
+
output: {
|
|
1100
|
+
deepLink: res.deepLink,
|
|
1101
|
+
version,
|
|
1102
|
+
changeset: changeset ?? null,
|
|
1103
|
+
modulesRequested: modules,
|
|
1104
|
+
note: "The unityhub:// scheme does not expose a module-specific deep link; the Hub opens at the version's install dialog where the operator selects the modules.",
|
|
1105
|
+
},
|
|
1106
|
+
error: res.error,
|
|
1107
|
+
nextSteps: res.opened
|
|
1108
|
+
? [
|
|
1109
|
+
`Unity Hub opened at the install dialog for ${version}. Select the requested modules (${modules.join(", ") || "none specified"}) in the Hub UI.`,
|
|
1110
|
+
"Poll unity_open_mcp_hub_list_editors (which scans Data/PlaybackEngines) afterwards to confirm the new modules.",
|
|
1111
|
+
]
|
|
1112
|
+
: [
|
|
1113
|
+
"Ensure Unity Hub is installed and registered as the unityhub:// handler, then retry.",
|
|
1114
|
+
],
|
|
1115
|
+
isError: !res.opened,
|
|
1116
|
+
});
|
|
1117
|
+
}
|
|
1118
|
+
async routeHubGetInstallPath() {
|
|
1119
|
+
const res = this.hubBackend.getInstallPath();
|
|
1120
|
+
const body = {
|
|
1121
|
+
path: res.path,
|
|
1122
|
+
source: res.source,
|
|
1123
|
+
error: res.error,
|
|
1124
|
+
_source: "local",
|
|
1125
|
+
};
|
|
1126
|
+
return {
|
|
1127
|
+
content: [{ type: "text", text: JSON.stringify(body) }],
|
|
1128
|
+
isError: res.error !== null,
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
async routeHubSetInstallPath(args) {
|
|
1132
|
+
const path = typeof args.path === "string" ? args.path : "";
|
|
1133
|
+
if (!path.trim()) {
|
|
1134
|
+
return localError("missing_parameter", "path is required.");
|
|
1135
|
+
}
|
|
1136
|
+
const res = this.hubBackend.setInstallPath(path);
|
|
1137
|
+
return hubMutationEnvelope({
|
|
1138
|
+
success: res.success,
|
|
1139
|
+
output: { path, hubCliOutput: res.output },
|
|
1140
|
+
error: res.error,
|
|
1141
|
+
nextSteps: res.success
|
|
1142
|
+
? [`Default Unity Editor install path set to ${path}.`]
|
|
1143
|
+
: [
|
|
1144
|
+
"Set the UNITY_HUB_PATH env var to the Unity Hub binary if the CLI was not found, then retry.",
|
|
1145
|
+
],
|
|
1146
|
+
isError: !res.success,
|
|
1147
|
+
});
|
|
1148
|
+
}
|
|
1149
|
+
async routeFindReferences(args, live) {
|
|
1150
|
+
// M22 — resolve profile -> detail. compact (default) maps to summary
|
|
1151
|
+
// (counts/byKind/byFolder only); balanced -> normal; full -> verbose.
|
|
1152
|
+
const { detail, profile } = readProfileAndDetail(args, "summary");
|
|
1153
|
+
const wantPaging = typeof args.page_size === "number" && args.page_size > 0;
|
|
1154
|
+
const liveAvailable = await live.isLiveAvailable();
|
|
721
1155
|
if (liveAvailable) {
|
|
722
1156
|
console.error("[unity-open-mcp] Route: find_references -> live");
|
|
723
|
-
|
|
724
|
-
|
|
1157
|
+
// The live bridge only honors max_results (no detail/per-file/threshold).
|
|
1158
|
+
// Ask for the full set when paging so the cursor can walk it; otherwise
|
|
1159
|
+
// honor the legacy max_results cap.
|
|
1160
|
+
const liveArgs = {
|
|
1161
|
+
...args,
|
|
1162
|
+
max_results: wantPaging ? 0 : (typeof args.max_results === "number" ? args.max_results : 100),
|
|
1163
|
+
};
|
|
1164
|
+
const result = await live.route("unity_open_mcp_find_references", liveArgs);
|
|
1165
|
+
const folded = foldFindReferencesLive(result, profile, wantPaging, args);
|
|
1166
|
+
return injectRouteMeta(folded, { route: "live" });
|
|
725
1167
|
}
|
|
726
1168
|
console.error("[unity-open-mcp] Route: find_references -> offline (live bridge unavailable)");
|
|
727
1169
|
const assetPath = typeof args.asset_path === "string" ? args.asset_path : undefined;
|
|
@@ -746,16 +1188,98 @@ export class ToolRouter {
|
|
|
746
1188
|
const result = await findReferencesOffline({
|
|
747
1189
|
assetPath,
|
|
748
1190
|
guid,
|
|
749
|
-
detail
|
|
750
|
-
|
|
1191
|
+
detail,
|
|
1192
|
+
// When paging, fetch the full set; otherwise honor the legacy cap.
|
|
1193
|
+
maxResults: wantPaging ? 0 : (typeof args.max_results === "number" ? args.max_results : 100),
|
|
751
1194
|
maxPerFile: typeof args.max_per_file === "number" ? args.max_per_file : 5,
|
|
752
1195
|
patternThreshold: typeof args.pattern_threshold === "number" ? args.pattern_threshold : 0,
|
|
753
1196
|
projectRoot: this.projectPath,
|
|
754
1197
|
});
|
|
1198
|
+
// M22 — page the referencedBy list when requested (balanced/full only;
|
|
1199
|
+
// compact returns an empty list by design).
|
|
1200
|
+
const withPaging = wantPaging
|
|
1201
|
+
? pageFindReferencesResult(result, args.page_size, typeof args.cursor === "string" ? args.cursor : undefined)
|
|
1202
|
+
: result;
|
|
1203
|
+
return {
|
|
1204
|
+
content: [
|
|
1205
|
+
{ type: "text", text: JSON.stringify({ ...withPaging, _source: "offline" }) },
|
|
1206
|
+
],
|
|
1207
|
+
isError: false,
|
|
1208
|
+
};
|
|
1209
|
+
}
|
|
1210
|
+
catch (err) {
|
|
1211
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1212
|
+
return {
|
|
1213
|
+
content: [
|
|
1214
|
+
{
|
|
1215
|
+
type: "text",
|
|
1216
|
+
text: JSON.stringify({
|
|
1217
|
+
error: { code: "offline_error", message },
|
|
1218
|
+
}),
|
|
1219
|
+
},
|
|
1220
|
+
],
|
|
1221
|
+
isError: true,
|
|
1222
|
+
};
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
// M24 Plan 2 / T24.2 — dependencies (forward + reverse edges + impact
|
|
1226
|
+
// analysis). Offline-first when no bridge is connected; the live
|
|
1227
|
+
// DependenciesTool is the fallback when the bridge is up. The live tool does
|
|
1228
|
+
// not yet honor include_impact / max_impact_depth — those are offline-only
|
|
1229
|
+
// today, so when the bridge is up and impact is requested we route offline
|
|
1230
|
+
// regardless (the offline path is the only one with the transitive closure).
|
|
1231
|
+
async routeDependencies(args, live) {
|
|
1232
|
+
const { detail } = readProfileAndDetail(args, "normal");
|
|
1233
|
+
const assetPath = typeof args.asset_path === "string" ? args.asset_path : undefined;
|
|
1234
|
+
const guid = typeof args.guid === "string" ? args.guid : undefined;
|
|
1235
|
+
const includeImpact = args.include_impact === true;
|
|
1236
|
+
if (!assetPath && !guid) {
|
|
755
1237
|
return {
|
|
756
1238
|
content: [
|
|
757
|
-
{
|
|
1239
|
+
{
|
|
1240
|
+
type: "text",
|
|
1241
|
+
text: JSON.stringify({
|
|
1242
|
+
error: {
|
|
1243
|
+
code: "missing_parameter",
|
|
1244
|
+
message: "Either 'asset_path' or 'guid' is required.",
|
|
1245
|
+
},
|
|
1246
|
+
}),
|
|
1247
|
+
},
|
|
758
1248
|
],
|
|
1249
|
+
isError: true,
|
|
1250
|
+
};
|
|
1251
|
+
}
|
|
1252
|
+
const liveAvailable = await live.isLiveAvailable();
|
|
1253
|
+
// Impact analysis is offline-only. When requested, go straight to the
|
|
1254
|
+
// offline path even if the bridge is up — the live DependenciesTool has no
|
|
1255
|
+
// transitive-closure surface yet.
|
|
1256
|
+
if (!includeImpact && liveAvailable) {
|
|
1257
|
+
console.error("[unity-open-mcp] Route: dependencies -> live");
|
|
1258
|
+
// The live tool honors asset_path/guid/detail/max_results only.
|
|
1259
|
+
const liveArgs = {
|
|
1260
|
+
asset_path: assetPath,
|
|
1261
|
+
guid,
|
|
1262
|
+
detail,
|
|
1263
|
+
max_results: typeof args.max_results === "number" ? args.max_results : 100,
|
|
1264
|
+
};
|
|
1265
|
+
const result = await live.route("unity_open_mcp_dependencies", liveArgs);
|
|
1266
|
+
return injectRouteMeta(result, { route: "live" });
|
|
1267
|
+
}
|
|
1268
|
+
console.error(includeImpact && liveAvailable
|
|
1269
|
+
? "[unity-open-mcp] Route: dependencies -> offline (include_impact is offline-only)"
|
|
1270
|
+
: "[unity-open-mcp] Route: dependencies -> offline (live bridge unavailable)");
|
|
1271
|
+
try {
|
|
1272
|
+
const result = await dependenciesOffline({
|
|
1273
|
+
assetPath,
|
|
1274
|
+
guid,
|
|
1275
|
+
detail,
|
|
1276
|
+
maxResults: typeof args.max_results === "number" ? args.max_results : 100,
|
|
1277
|
+
includeImpact,
|
|
1278
|
+
maxImpactDepth: typeof args.max_impact_depth === "number" ? args.max_impact_depth : 5,
|
|
1279
|
+
projectRoot: this.projectPath,
|
|
1280
|
+
});
|
|
1281
|
+
return {
|
|
1282
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
759
1283
|
isError: false,
|
|
760
1284
|
};
|
|
761
1285
|
}
|
|
@@ -774,5 +1298,134 @@ export class ToolRouter {
|
|
|
774
1298
|
};
|
|
775
1299
|
}
|
|
776
1300
|
}
|
|
1301
|
+
// M22 — verify tools (validate_edit / scan_paths). The bridge always returns
|
|
1302
|
+
// the full issues[] list; we fold + page server-side per the output profile
|
|
1303
|
+
// (compact = counts only, balanced/full keep + page issues).
|
|
1304
|
+
async routeVerifyResult(toolName, args, live) {
|
|
1305
|
+
const { profile } = readProfileAndDetail(args, "summary");
|
|
1306
|
+
const pageSize = typeof args.page_size === "number" && args.page_size > 0 ? args.page_size : 0;
|
|
1307
|
+
const cursor = typeof args.cursor === "string" ? args.cursor : undefined;
|
|
1308
|
+
// Forward to live/batch exactly as the generic path would. The bridge
|
|
1309
|
+
// ignores the unknown profile/page_size/cursor keys (JsonBody substring-
|
|
1310
|
+
// searches only for keys it asks for), so they pass through harmlessly.
|
|
1311
|
+
const canBatch = this.batch.isBatchTool(toolName);
|
|
1312
|
+
let raw;
|
|
1313
|
+
if (!canBatch) {
|
|
1314
|
+
raw = await live.route(toolName, args);
|
|
1315
|
+
raw = injectRouteMeta(raw, { route: "live" });
|
|
1316
|
+
}
|
|
1317
|
+
else {
|
|
1318
|
+
const liveAvailable = await live.isLiveAvailable();
|
|
1319
|
+
if (liveAvailable) {
|
|
1320
|
+
raw = await live.route(toolName, args);
|
|
1321
|
+
raw = injectRouteMeta(raw, { route: "live" });
|
|
1322
|
+
}
|
|
1323
|
+
else {
|
|
1324
|
+
raw = await this.batch.route(toolName, args);
|
|
1325
|
+
raw = injectRouteMeta(raw, { route: "batch", fallbackReason: "live_unavailable" });
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
const body = parseResultBody(raw);
|
|
1329
|
+
if (body === null)
|
|
1330
|
+
return raw;
|
|
1331
|
+
if (body.error)
|
|
1332
|
+
return raw;
|
|
1333
|
+
const folded = foldVerifyResult(body, toolName, profile, { page_size: pageSize, cursor });
|
|
1334
|
+
return withResultBody(raw, folded);
|
|
1335
|
+
}
|
|
1336
|
+
// M22 — scene_get_data. Resolves the output profile (onto detail) and pages
|
|
1337
|
+
// the node stream server-side. The bridge detail/max_nodes params keep
|
|
1338
|
+
// working as back-compat aliases; profile wins when both are present.
|
|
1339
|
+
async routeSceneGetData(args, live) {
|
|
1340
|
+
const { detail } = readProfileAndDetail(args, "summary");
|
|
1341
|
+
const wantPaging = typeof args.page_size === "number" && args.page_size > 0;
|
|
1342
|
+
// Build the args the bridge sees: profile resolved to detail (the bridge's
|
|
1343
|
+
// real knob), legacy max_nodes honored, paging keys stripped (the bridge
|
|
1344
|
+
// would ignore them anyway, but keep the call clean).
|
|
1345
|
+
const bridgeArgs = { ...args };
|
|
1346
|
+
delete bridgeArgs.profile;
|
|
1347
|
+
delete bridgeArgs.page_size;
|
|
1348
|
+
delete bridgeArgs.cursor;
|
|
1349
|
+
bridgeArgs.detail = detail;
|
|
1350
|
+
const canBatch = this.batch.isBatchTool("unity_open_mcp_scene_get_data");
|
|
1351
|
+
let raw;
|
|
1352
|
+
if (!canBatch) {
|
|
1353
|
+
raw = await live.route("unity_open_mcp_scene_get_data", bridgeArgs);
|
|
1354
|
+
raw = injectRouteMeta(raw, { route: "live" });
|
|
1355
|
+
}
|
|
1356
|
+
else {
|
|
1357
|
+
const liveAvailable = await live.isLiveAvailable();
|
|
1358
|
+
if (liveAvailable) {
|
|
1359
|
+
raw = await live.route("unity_open_mcp_scene_get_data", bridgeArgs);
|
|
1360
|
+
raw = injectRouteMeta(raw, { route: "live" });
|
|
1361
|
+
}
|
|
1362
|
+
else {
|
|
1363
|
+
raw = await this.batch.route("unity_open_mcp_scene_get_data", bridgeArgs);
|
|
1364
|
+
raw = injectRouteMeta(raw, { route: "batch", fallbackReason: "live_unavailable" });
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
if (!wantPaging)
|
|
1368
|
+
return raw;
|
|
1369
|
+
const body = parseResultBody(raw);
|
|
1370
|
+
if (body === null)
|
|
1371
|
+
return raw;
|
|
1372
|
+
if (body.error)
|
|
1373
|
+
return raw;
|
|
1374
|
+
const pageSize = args.page_size;
|
|
1375
|
+
const cursor = typeof args.cursor === "string" ? args.cursor : undefined;
|
|
1376
|
+
const paged = pageSceneNodes(body, pageSize, cursor);
|
|
1377
|
+
return withResultBody(raw, paged);
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
/**
|
|
1381
|
+
* Page the scene_get_data node stream. The bridge emits a per-root `roots`
|
|
1382
|
+
* array (summary: root roster) or a nested hierarchy (normal/verbose). We page
|
|
1383
|
+
* the FLATTENED node stream so the cursor is a stable position regardless of
|
|
1384
|
+
* the detail mode, then re-hang the page as a flat `nodes` array with the
|
|
1385
|
+
* original scene metadata preserved.
|
|
1386
|
+
*/
|
|
1387
|
+
function pageSceneNodes(body, pageSize, cursor) {
|
|
1388
|
+
const flat = flattenSceneBody(body);
|
|
1389
|
+
const { page, block } = applyPaging(flat, "scene_get_data", { page_size: pageSize, cursor });
|
|
1390
|
+
// Preserve scene-level metadata; replace the structural root/hierarchy fields
|
|
1391
|
+
// with the flat paged node list + a note explaining the page shape.
|
|
1392
|
+
const { roots, rootGameObjects, nodes, truncated, pagination: _existing, ...meta } = body;
|
|
1393
|
+
void roots;
|
|
1394
|
+
void rootGameObjects;
|
|
1395
|
+
void nodes;
|
|
1396
|
+
void truncated;
|
|
1397
|
+
void _existing;
|
|
1398
|
+
return attachPagination({
|
|
1399
|
+
...meta,
|
|
1400
|
+
nodes: page,
|
|
1401
|
+
pagingNote: "page_size returns a flattened node stream; re-read without page_size for the structural root/hierarchy view",
|
|
1402
|
+
}, block);
|
|
1403
|
+
}
|
|
1404
|
+
/**
|
|
1405
|
+
* Flatten the scene body into a node stream for paging. Handles both the
|
|
1406
|
+
* summary root roster (array of root objects) and the nested normal/verbose
|
|
1407
|
+
* hierarchy (objects with `children`).
|
|
1408
|
+
*/
|
|
1409
|
+
function flattenSceneBody(body) {
|
|
1410
|
+
const out = [];
|
|
1411
|
+
const walk = (node) => {
|
|
1412
|
+
if (!node || typeof node !== "object")
|
|
1413
|
+
return;
|
|
1414
|
+
const obj = node;
|
|
1415
|
+
out.push(obj);
|
|
1416
|
+
const children = obj.children;
|
|
1417
|
+
if (Array.isArray(children)) {
|
|
1418
|
+
for (const child of children)
|
|
1419
|
+
walk(child);
|
|
1420
|
+
}
|
|
1421
|
+
};
|
|
1422
|
+
// The bridge emits `roots` (summary) or `rootGameObjects` (normal/verbose);
|
|
1423
|
+
// accept either.
|
|
1424
|
+
const rootField = Array.isArray(body.roots) ? body.roots : body.rootGameObjects;
|
|
1425
|
+
if (Array.isArray(rootField)) {
|
|
1426
|
+
for (const root of rootField)
|
|
1427
|
+
walk(root);
|
|
1428
|
+
}
|
|
1429
|
+
return out;
|
|
777
1430
|
}
|
|
778
1431
|
//# sourceMappingURL=tool-router.js.map
|