unity-open-mcp 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -2
- package/dist/agent-identity.d.ts +12 -0
- package/dist/agent-identity.js +2 -0
- package/dist/agent-identity.js.map +1 -1
- package/dist/batch-spawn.d.ts +39 -0
- package/dist/batch-spawn.js +287 -69
- package/dist/batch-spawn.js.map +1 -1
- package/dist/capabilities/build-capabilities.js +2 -0
- package/dist/capabilities/build-capabilities.js.map +1 -1
- package/dist/capabilities/discovery.d.ts +51 -0
- package/dist/capabilities/discovery.js +87 -0
- package/dist/capabilities/discovery.js.map +1 -0
- package/dist/capabilities/lifecycle.d.ts +6 -0
- package/dist/capabilities/lifecycle.js +17 -1
- package/dist/capabilities/lifecycle.js.map +1 -1
- package/dist/capabilities/tool-groups.js +1 -0
- package/dist/capabilities/tool-groups.js.map +1 -1
- package/dist/child-supervision.d.ts +6 -0
- package/dist/child-supervision.js +12 -0
- package/dist/child-supervision.js.map +1 -1
- package/dist/cli/args.d.ts +24 -2
- package/dist/cli/args.js +97 -2
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/cli.js +42 -3
- package/dist/cli/cli.js.map +1 -1
- package/dist/cli/help-text.d.ts +1 -0
- package/dist/cli/help-text.js +71 -3
- package/dist/cli/help-text.js.map +1 -1
- package/dist/cli/json-guards.d.ts +2 -0
- package/dist/cli/json-guards.js +5 -0
- package/dist/cli/json-guards.js.map +1 -0
- package/dist/cli/setup-command.d.ts +88 -0
- package/dist/cli/setup-command.js +444 -0
- package/dist/cli/setup-command.js.map +1 -0
- package/dist/cli/update-command.d.ts +30 -0
- package/dist/cli/update-command.js +271 -0
- package/dist/cli/update-command.js.map +1 -0
- package/dist/compile-verify.d.ts +3 -0
- package/dist/compile-verify.js +4 -0
- package/dist/compile-verify.js.map +1 -1
- package/dist/constants.d.ts +10 -2
- package/dist/constants.js +10 -2
- package/dist/constants.js.map +1 -1
- package/dist/dialog-dismiss.d.ts +108 -12
- package/dist/dialog-dismiss.js +294 -109
- package/dist/dialog-dismiss.js.map +1 -1
- package/dist/dialog-policy.d.ts +55 -9
- package/dist/dialog-policy.js +85 -15
- package/dist/dialog-policy.js.map +1 -1
- package/dist/editor-search.d.ts +2 -0
- package/dist/editor-search.js +8 -0
- package/dist/editor-search.js.map +1 -0
- package/dist/gate-error.d.ts +3 -0
- package/dist/gate-error.js.map +1 -1
- package/dist/index.js +55 -1
- package/dist/index.js.map +1 -1
- package/dist/instance-discovery.d.ts +16 -0
- package/dist/instance-discovery.js +45 -0
- package/dist/instance-discovery.js.map +1 -1
- package/dist/jobs/adapters.d.ts +15 -0
- package/dist/jobs/adapters.js +147 -0
- package/dist/jobs/adapters.js.map +1 -0
- package/dist/jobs/handler.d.ts +3 -0
- package/dist/jobs/handler.js +29 -0
- package/dist/jobs/handler.js.map +1 -0
- package/dist/jobs/job-manager.d.ts +123 -0
- package/dist/jobs/job-manager.js +290 -0
- package/dist/jobs/job-manager.js.map +1 -0
- package/dist/live-client.d.ts +50 -2
- package/dist/live-client.js +221 -26
- package/dist/live-client.js.map +1 -1
- package/dist/local-tools.d.ts +17 -0
- package/dist/local-tools.js +47 -0
- package/dist/local-tools.js.map +1 -0
- package/dist/process-diagnostics.d.ts +47 -20
- package/dist/process-diagnostics.js +30 -15
- package/dist/process-diagnostics.js.map +1 -1
- package/dist/project-path.d.ts +86 -0
- package/dist/project-path.js +159 -0
- package/dist/project-path.js.map +1 -0
- package/dist/release-lookup.d.ts +32 -0
- package/dist/release-lookup.js +147 -0
- package/dist/release-lookup.js.map +1 -0
- package/dist/routers.d.ts +17 -3
- package/dist/routers.js +28 -8
- package/dist/routers.js.map +1 -1
- package/dist/running-unity.d.ts +21 -0
- package/dist/running-unity.js +50 -0
- package/dist/running-unity.js.map +1 -1
- package/dist/schema-defaults.d.ts +4 -3
- package/dist/schema-defaults.js +20 -6
- package/dist/schema-defaults.js.map +1 -1
- package/dist/server-instructions.js +7 -1
- package/dist/server-instructions.js.map +1 -1
- package/dist/server.d.ts +10 -3
- package/dist/server.js +24 -25
- package/dist/server.js.map +1 -1
- package/dist/setup/portable-config.d.ts +60 -0
- package/dist/setup/portable-config.js +249 -0
- package/dist/setup/portable-config.js.map +1 -0
- package/dist/skill/SKILL.md +68 -0
- package/dist/skill/copy-references.d.ts +2 -0
- package/dist/skill/copy-references.js +16 -0
- package/dist/skill/copy-references.js.map +1 -0
- package/dist/skill/generate-skill.js +5 -3
- package/dist/skill/generate-skill.js.map +1 -1
- package/dist/skill/references/batch-and-gates.md +142 -0
- package/dist/skill/references/compile-and-safe-mode.md +163 -0
- package/dist/skill/references/discovery-and-groups.md +30 -0
- package/dist/skill/references/routing-and-lifecycle.md +62 -0
- package/dist/skill/references/senses-and-tests.md +41 -0
- package/dist/skill/references/yaml-and-offline-work.md +33 -0
- package/dist/templates/mcp-wrapper.sh +35 -0
- package/dist/tool-contract.d.ts +8 -0
- package/dist/tool-contract.js +253 -0
- package/dist/tool-contract.js.map +1 -0
- package/dist/tool-router.d.ts +62 -7
- package/dist/tool-router.js +535 -61
- package/dist/tool-router.js.map +1 -1
- package/dist/tool-session-state.d.ts +6 -0
- package/dist/tool-session-state.js +14 -0
- package/dist/tool-session-state.js.map +1 -1
- package/dist/tools/agent-capabilities.js +9 -0
- package/dist/tools/agent-capabilities.js.map +1 -1
- package/dist/tools/batch-execute.js +17 -14
- package/dist/tools/batch-execute.js.map +1 -1
- package/dist/tools/bridge-status.js +4 -5
- package/dist/tools/bridge-status.js.map +1 -1
- package/dist/tools/compile-check.js +6 -4
- package/dist/tools/compile-check.js.map +1 -1
- package/dist/tools/component-get.js +2 -2
- package/dist/tools/component-get.js.map +1 -1
- package/dist/tools/component-modify.js +4 -4
- package/dist/tools/component-modify.js.map +1 -1
- package/dist/tools/execute-csharp.js +8 -4
- package/dist/tools/execute-csharp.js.map +1 -1
- package/dist/tools/gameobject-modify.js +2 -1
- package/dist/tools/gameobject-modify.js.map +1 -1
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.js +10 -2
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/inputsim-key.js +12 -14
- package/dist/tools/inputsim-key.js.map +1 -1
- package/dist/tools/inputsim-pointer.js +3 -3
- package/dist/tools/inputsim-pointer.js.map +1 -1
- package/dist/tools/inputsim-step.js +7 -8
- package/dist/tools/inputsim-step.js.map +1 -1
- package/dist/tools/inputsim-touch.js +9 -10
- package/dist/tools/inputsim-touch.js.map +1 -1
- package/dist/tools/jobs.d.ts +40 -0
- package/dist/tools/jobs.js +18 -0
- package/dist/tools/jobs.js.map +1 -0
- package/dist/tools/manage-tools.js +10 -5
- package/dist/tools/manage-tools.js.map +1 -1
- package/dist/tools/project-commands.d.ts +40 -0
- package/dist/tools/project-commands.js +23 -0
- package/dist/tools/project-commands.js.map +1 -0
- package/dist/tools/read-compile-errors.js +14 -93
- package/dist/tools/read-compile-errors.js.map +1 -1
- package/dist/tools/resource-pressure.js +27 -17
- package/dist/tools/resource-pressure.js.map +1 -1
- package/dist/tools/scan-paths.js +4 -4
- package/dist/tools/scan-paths.js.map +1 -1
- package/dist/tools/schema-fragments.d.ts +15 -3
- package/dist/tools/schema-fragments.js +23 -13
- package/dist/tools/schema-fragments.js.map +1 -1
- package/dist/tools/upgrade.d.ts +40 -0
- package/dist/tools/upgrade.js +55 -0
- package/dist/tools/upgrade.js.map +1 -0
- package/dist/unity-install-discovery.d.ts +24 -0
- package/dist/unity-install-discovery.js +113 -4
- package/dist/unity-install-discovery.js.map +1 -1
- package/package.json +4 -3
- package/templates/mcp-wrapper.sh +35 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Routing and lifecycle
|
|
2
|
+
|
|
3
|
+
## Lifecycle & scene safety
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
| Policy | Meaning | Tools |
|
|
7
|
+
| --------------------- | ----------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
8
|
+
| `none` | Read-only, returns immediately | `ping`, `find_members`, `validate_edit`, `checkpoint_create`, `delta`, `find_references`, `dependencies`, `scan_paths`, `read_asset`, `search_assets`, `list_assets`, `editor_status`, `read_console`, `screenshot`, `profiler_*`, `spatial_query`, `visual_compare` |
|
|
9
|
+
| `editor_settle` | Mutating; bridge waits for asset refresh/serialization to finish | `apply_fix`, `reserialize` |
|
|
10
|
+
| `restart_then_settle` | Mutating; may trigger domain reload; bridge blocks until compile finishes (cap 60s) | `execute_csharp`, `invoke_method`, `execute_menu`, `compile_check` |
|
|
11
|
+
| `custom_confirmation` | Async; returns immediately, result via external completion signal you poll | `run_tests` |
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
> The table above is the bridge's settle-timing surface (how long it blocks before returning). For the **recovery-axis** taxonomy — `none` / `compile-reload` / `modal-dialog` / `scene-dirty` / `process-stale`, describing what to do when a call fails or stalls — read `capabilities.tools[].lifecycle` (+ `lifecycleNote`) per tool, and `capabilities.lifecycleBlock` for the full class table. See the docs §Lifecycle policy for the `compile-reload` agent limits (notably `compile_check` is batch-only and returns `editor_instance_locked` when a live Editor holds the project lock).
|
|
15
|
+
|
|
16
|
+
**Active-scene dirty guard.** Before any `restart_then_settle` op, the bridge preflights loaded scenes. If any scene has unsaved changes, the call refuses with `error.code = "scene_dirty"` + `dirtyScenes[]` + `agentNextSteps` so Unity's native save modal never interrupts. Recover by: saving first (`scene_save`), discarding (`EditorSceneManager.RestoreSavedSceneState()`), or passing `ignore_scene_dirty: true` on `execute_csharp` / `invoke_method` / `execute_menu` / `scene_create` / `scene_open` / `editor_set_state` / `build_set_target` / `build_set_defines` / `settings_set_player`.
|
|
17
|
+
|
|
18
|
+
`scene_create` / `scene_open` are guarded only in their default Single mode (it closes every open scene without saving); `mode: "additive"` keeps open scenes and skips the guard. `apply_fix`, `reserialize`, and the other scene mutators (`scene_save` / `scene_unload` / `scene_set_active` / `scene_focus` / `sceneview_set_camera`) are **not** guarded.
|
|
19
|
+
|
|
20
|
+
**Power-tool deny heuristic.** `execute_csharp` / `execute_menu` are blocked from destructive patterns by default (`EditorApplication.Exit`, `Application.Quit`, `AssetDatabase.DeleteAsset`, `BuildPipeline.BuildPlayer`, `File/Quit`, `TestRunnerApi`). Refused calls return `error.code = "denied_by_policy"` (csharp) or `"menu_blocked"` (menu) with the matched pattern + alternative. If you genuinely need one, set **both** `gate: "off"` and `confirm_bypass: true` — the bypass is audited. `execute_menu` also tells `menu_not_found` (no item at that path — fix the path) from `menu_disabled` (the item exists but its validate function rejected the current Editor state, e.g. nothing selected — change the selection/state, not the path).
|
|
21
|
+
|
|
22
|
+
**`execute_csharp` runs on Unity's main thread — never block it.** The snippet executes synchronously inline on `EditorApplication.update`, so any blocking primitive wedges the editor **unrecoverably**: `WaitOne` / `.Result` / `Thread.Sleep` / `while(!done)` waiting for a callback, and driving `TestRunnerApi` (which delivers its callbacks on the same main thread), all deadlock. The HTTP timeout fires on the worker thread and **cannot self-heal a stuck main thread** — no further editor tick runs, so the editor must be killed externally. The timeout envelope's `agentNextSteps` will tell you this; heed it (check `editor_status` / `bridge_status` before retrying, **don't** just raise `timeout_ms`). For test execution use `unity_senses_run_tests` (async, does not block). For everything else, prefer a typed tool or write the snippet to be non-blocking (fire-and-forget + poll via a follow-up call).
|
|
23
|
+
|
|
24
|
+
## Routing rules
|
|
25
|
+
|
|
26
|
+
Treat `capabilities.routePolicy` + `batchCapable` as source of truth. `batchCapable` is about the **headless batch-spawn fallback** only (`capabilities.routing.perToolFlagMeaning` says so inline) — it does not decide what may be a nested `batch_execute` step.
|
|
27
|
+
|
|
28
|
+
- **Live is the default** — when the bridge is connected, most tools route to `POST /tools/{name}` on the Editor.
|
|
29
|
+
- **Batch fallback** — spawns headless Unity (`-batchmode`) **only** for `batchCapable: true` tools when the live bridge is unavailable. `find_members`, `execute_csharp`, `invoke_method`, and the allowed `execute_menu` subset can use this route; mutating power tools do not get the live gate/checkpoint flow in headless mode.
|
|
30
|
+
- **Exact Editor guard** — before a headless spawn, the server reads `ProjectSettings/ProjectVersion.txt` and requires that exact installed Unity. `unity_version_not_installed` / `unity_version_mismatch` mean nothing was spawned; install or select the exact Editor, then retry (no server restart needed). Use `UNITY_OPEN_MCP_ALLOW_VERSION_MISMATCH=1` only for an intentional upgrade or compatibility run because a different Unity can rewrite package/project metadata during load.
|
|
31
|
+
- **Senses are live-only** — `run_tests`, screenshots, profiler, console, spatial queries have no batch form.
|
|
32
|
+
- **Offline reads** (`list_assets`, `find_references`, `read_asset`, `search_assets`) parse the project from disk and never need Unity. Coverage: text-serialized Unity YAML (`.prefab`/`.unity`/`.asset`/`.mat`/`.controller`/`.anim`/`.playable`/`.preset`/`.spriteatlas`/`.terrainlayer`/`.vfx`) **plus** JSON assets not covered by YAML-only parsers (`.asmdef`, `.shadergraph`/`.shadersubgraph`). `read_asset` also reconstructs the full hierarchy offline, parses prefab-variant overrides (matching `prefab_get_overrides`), and surfaces `integrity[]` signals (missing refs, missing scripts, malformed JSON, orphaned prefab instances) on the response — act on them before a `validate_edit` round-trip.
|
|
33
|
+
- **`dependencies` is live-only** — it reuses the verify `Dependencies.Scanner` (forward) + `ReferenceGraph` (reverse), both of which call AssetDatabase. No offline form.
|
|
34
|
+
- **`compile_check` is always batch** — spawns a fresh headless Unity that recompiles from scratch, even when the live bridge is up.
|
|
35
|
+
- **Verify scans (`scan_all`, `baseline_create`, `regression_check`) are always batch** — they run in the headless verify package and are not registered on the live bridge, so even with the bridge up they spawn fresh Unity (`_route.fallbackReason: "verify_always_batch"`). Expect `editor_instance_locked` when a live Editor holds the project lock — close it and retry, or use the live `validate_edit` / `scan_paths` for a scoped health check instead.
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
Snippets import `Object = UnityEngine.Object` automatically unless the caller supplies an Object alias. Snippets run in a separate assembly and can access public APIs. Use explicit reflection for inspection of internals; assembly-access bypass is not supported.
|
|
39
|
+
|
|
40
|
+
## Session-owned jobs
|
|
41
|
+
|
|
42
|
+
`unity_open_mcp_jobs` stays local and visible while Unity reloads or disconnects.
|
|
43
|
+
Use it only when an operation has an explicit job adapter; declarations alone do
|
|
44
|
+
not enable a job. `wait` observes for at most 30 seconds without owning execution.
|
|
45
|
+
A lost connection is not proof of failure: `orphaned` requires operation-specific
|
|
46
|
+
evidence before any retry. A `running` job with lifecycle `disconnected` is still
|
|
47
|
+
being observed through a busy or unreachable Editor; keep waiting.
|
|
48
|
+
`cancel_requested` is not cancellation confirmation.
|
|
49
|
+
Terminal records and idempotency keys survive for 30 minutes in the same server
|
|
50
|
+
session, but not across a server restart. Keep routing and agent metadata stable.
|
|
51
|
+
While a project job runs, conflicting calls return `job_busy` with `error.job`
|
|
52
|
+
(`jobId`, `commandId`, `phase`) and `agentNextSteps`. `execute_csharp` is refused
|
|
53
|
+
even with `read_only: true`: the bridge cannot verify a snippet writes nothing.
|
|
54
|
+
The starting agent waits with that `jobId` (others get `job_not_found`); use typed
|
|
55
|
+
reads meanwhile and retry once after a terminal state, never in a loop.
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
For project commands, exact describe precedes invoke/start. After async start,
|
|
59
|
+
call status and bounded wait; inspect terminal gate results even on failure or
|
|
60
|
+
cancellation. A job does not weaken scope, deny or dirty-scene policy. Never use
|
|
61
|
+
reflection to bypass an unavailable declaration. Builds/imports/packages/bakes
|
|
62
|
+
are unsupported job targets.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Senses and tests
|
|
2
|
+
|
|
3
|
+
## Agent senses (live-only, no batch fallback)
|
|
4
|
+
|
|
5
|
+
- `**unity_senses_run_tests`** — EditMode + PlayMode test runner with per-test pass/fail. Filter by assembly / namespace / class / method. Set `include_passes: false` on large suites to avoid truncation. **Never fire a second `run_tests` before the first resolves** (no concurrency guard; results interleave). Results are retrieved by the server polling a results file (NOT via `unity_senses_pull_events`). A run that is superseded by a newer run, or aborted when play mode is entered mid-EditMode-run, writes a terminal file with `status:"aborted"` + `reason` (`superseded_by_run` | `playmode_entered`) — the poll resolves to that instead of timing out.
|
|
6
|
+
- **Zero-match looks like "no results".** When the filter matches zero tests, `TestRunnerApi.Execute` runs nothing and the result is `status:"completed"` with `summary.total:0` and a `note` — that is a no-match filter, not a failure. `test_class` maps to NUnit `groupNames` (a partial/group match), and `assembly_name` must name a real test assembly.
|
|
7
|
+
- **Single-test workaround.** To run one test method deterministically (and get NUnit assertion failures verbatim), invoke the test class/method directly via `unity_open_mcp_invoke_method` instead of `run_tests`.
|
|
8
|
+
- **Brief reloading window after a run.** A test run (PlayMode especially) can trigger a Unity domain reload that freezes the bridge's heartbeat for a few seconds. The MCP server recognizes an in-flight run and treats that window as a normal reload rather than a dead bridge, so subsequent tools keep waiting instead of failing fast with `bridge_compile_failed`. For robustness, after `run_tests` returns `{status:"started", runId}`, poll the result file (or call `ping` / `editor_status`) once before issuing other tools — the run itself is async and the bridge may still be settling.
|
|
9
|
+
- **Headless / CI alternative.** `run_tests` has no MCP batch form (it needs a live Editor). For headless CI, invoke Unity's CLI directly with `-runTests`. **Omit `-quit`** — with `-quit` the editor exits after the initial asset refresh, *before* the test runner starts, so no tests run and no XML is written despite an exit code of 0. The correct invocation:
|
|
10
|
+
```
|
|
11
|
+
Unity -batchmode -projectPath <project> -runTests -testPlatform EditMode \
|
|
12
|
+
-testResults out.xml -logFile run.log -nographics
|
|
13
|
+
```
|
|
14
|
+
Exit codes: `0` = all tests passed, `2` = test failures present (NOT a process error — do not treat 2 as a crash), `3` = test runner setup failure. Known **batchmode-only** red tests that pass in the GUI editor — don't try to "fix" these, validate them in the GUI editor instead: in-process HTTP-server lifecycle (socket-disposed), GPU/SceneView (no graphic device under `-nographics`), Unity-6's removed `LogEntries.GetEntries(List<LogEntry>)` internal API (console/capture), MSBuild/Roslyn `Microsoft.Build.Utilities.Core` host assembly load, and TimeManager SerializedProperty float access.
|
|
15
|
+
- `**unity_senses_read_console`** — console entries via reflection. Filter `type: "error"` to confirm clean compile. `detail: "summary"` for messages only (saves tokens); `detail: "verbose"` includes Unity-internal frames.
|
|
16
|
+
- `**unity_senses_screenshot**` — Scene / Game / **composed** / isolated 2×2 composite of one GameObject. `view: "game"` renders `Camera.main` **only** — Screen-Space Overlay UI and multi-camera compositions are NOT in the frame; use `view: "composed"` to verify UI edits (the full player frame: all cameras by depth + Overlay canvases).
|
|
17
|
+
- `**unity_senses_screenshot_camera**` — render from an arbitrary world-space pose (position + rotation + fov) without moving the scene/game camera; transient camera, scene camera untouched.
|
|
18
|
+
- `**unity_senses_capture_inline**` — same targets as `screenshot` but returns the PNG as an inline base64 image (no temp file) for agents that don't read the filesystem.
|
|
19
|
+
- `**unity_senses_visual_compare**` — visual regression: capture a named reference snapshot (`action: save`) from scene / game / isolated view, then `action: compare` a later capture against it to check whether an edit visually broke anything. Returns `pixelDiffPercent`, `mismatchedPixels`, `perceptualDistance` (8×8 average-hash Hamming distance — tolerates minor anti-aliasing drift), and `match` (true when `pixelDiffPercent <= sensitivity * 100`; default `sensitivity: 0.01` = 1%). On a non-zero diff, returns an inline diff image (mismatched pixels highlighted red over the current frame). References persist under `~/.unity-open-mcp/screenshots/references/`; `action: list` / `delete` manage them. The `compare` resamples the current capture to the reference dimensions if they differ, so the diff is resolution-stable.
|
|
20
|
+
- `**unity_senses_screenshot_window**` — capture an Editor window (Console / Hierarchy / Inspector / Project / Scene / Game / custom). **Windows-only**: on macOS/Linux returns `not_supported` (the editor-window backbuffer is not bound during bridge dispatch, so a readback produces a blank frame) — use the OS screen-capture tool, or `screenshot {view:"composed"}` for the rendered Game view. On Windows, a blank/solid-color frame is rejected with `blank_capture` rather than reported as `ok`.
|
|
21
|
+
- `**unity_senses_frame_debugger**` — control Unity's Frame Debugger: `action: enable` opens the window and starts capturing, `action: disable` stops it, `action: list` returns the draw-call list (shader / pass / material / render target / vertex/index/instance counts per call). Read-only (gate off); the response reports `windowOpened` so you know Editor UI may have changed. Enable, render a frame, then call `list` to inspect draw calls.
|
|
22
|
+
- `**unity_senses_profiler_capture**` / `profiler_memory` / `profiler_rendering` — frame hierarchy, memory allocators, rendering env.
|
|
23
|
+
- `**unity_senses_profiler_capture_frame`** — single-frame deep profiler capture. Returns one (or a few, via `frame_count`) frame's full sample tree, optionally filtered by `modules` (Profiler category names, e.g. "CPU,Rendering"). Deeper than `profiler_capture`; bound output with `max_depth` (default 8) and `max_items` (default 200). If the Profiler is off, the tool enables it for the capture, then restores it to disabled once data is captured; `profilerWasEnabled` reports the PRIOR state. On a cold capture (Profiler was off and no frame is recorded yet) the tool returns `status:"warming_up"` instead of blocking — it leaves the Profiler enabled, so re-issue the call shortly to receive the frame data.
|
|
24
|
+
- `**unity_senses_spatial_query**` — physics-based raycast / overlap / bounds / ground_check / nearest against the live scene.
|
|
25
|
+
- `**unity_senses_pull_events**` — incremental console logs + editor-state transitions (compile start/stop, play-mode). Cheaper than `read_console` for "what happened since my last call"; first call opens the stream, later calls return only new events. Returns `bridge_unavailable` when the bridge is down. The stream self-heals across Unity restarts and domain reloads (the server re-reads the bridge port+token from the instance lock on every reconnect, backing off 2s→30s), so after a reload keep polling instead of assuming the stream is dead.
|
|
26
|
+
|
|
27
|
+
**Verification habit:** after any C# change, run `read_console` with `type: "error"` (or `run_tests` on the affected assembly) to confirm compile + tests pass before declaring done.
|
|
28
|
+
|
|
29
|
+
### Return serialization (execute_csharp / invoke_method)
|
|
30
|
+
|
|
31
|
+
Results are walked by a depth-limited reflective serializer before becoming `mutation.output`:
|
|
32
|
+
|
|
33
|
+
- Structs/POCOs → JSON objects with public fields/props (`return new Vector3(1,2,3)` → `{"$type":"Vector3","x":1,"y":2,"z":3}`).
|
|
34
|
+
- Lists truncate to 100 items (`max_items` configurable); truncated arrays report `{"items":[...],"truncated":N}`.
|
|
35
|
+
- Recursion caps at depth 4 (`max_depth` configurable).
|
|
36
|
+
- Cycles / `UnityEngine.Object` refs never infinite-loop — back-edges become `{"$ref":"TypeName"}`, Unity objects become `{"$type":...,"name":...,"instanceId":...}`.
|
|
37
|
+
- **Malformed output is rejected, not trusted.** The per-member walk is try/catch'd, but an exception can still escape mid-walk (e.g. a `TypeLoadException` when a field references a missing assembly), leaving unbalanced JSON. `execute_csharp` validates the serialized output before it enters the envelope; if it isn't a balanced JSON object the call fails with `error.code = "execution_error"` and a diagnostic naming the return type. On the rare chance the body is still corrupted in transit, the MCP server surfaces `bridge_response_unparsable` instead of a fake success — either way you'll never see a `_compileVerify`-annotated result missing its `mutation` block.
|
|
38
|
+
|
|
39
|
+
### `execute_csharp` read-only probe flag (`read_only`)
|
|
40
|
+
|
|
41
|
+
`execute_csharp` defaults to mutating and requires a non-empty `paths_hint`. For pure inspection, pass `read_only: true`: no scope, checkpoint, or validation is needed, even when the scene is dirty or a hint was supplied. This is a caller assertion, not a sandbox: never use it for writes or disruptive lifecycle transitions. Known compile/refresh, scene-switch, and play-transition calls retain protection, and the deny heuristic still applies. See the request and batch guidance below.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# YAML and offline work
|
|
2
|
+
|
|
3
|
+
### Reserialize after direct YAML edits
|
|
4
|
+
|
|
5
|
+
When you edit `.prefab` / `.unity` / `.asset` / `.mat` / `.controller` / `.anim` directly as YAML text, run `**unity_open_mcp_reserialize`** with the touched `paths` (the `paths` array doubles as gate scope). Round-trip rewrites canonically so missing fields, wrong indentation, and stale `fileID` references surface in `gate.delta`. Whole-project reserialize is intentionally unsupported — enumerate assets you edited. Default targets asset YAML only (no `.meta` diff on body-only edits); pass `include_meta: true` only for upgrade/importer-change workflows.
|
|
6
|
+
|
|
7
|
+
Prefer a typed live tool while the Editor is reachable. Never directly edit an open or dirty scene or asset YAML. If direct editing is necessary, explicitly identify the offline-file route and confirm the target is not open/dirty. Offline work remains supported when the Editor is unavailable or the user chooses file-level repair. Preserve GUID/fileID relationships and verify the intended scene: wrong-scene edits can silently damage a different asset. Reimport/reserialize the touched files and run scoped validation before trusting the result.
|
|
8
|
+
|
|
9
|
+
### read_asset: map, not dump
|
|
10
|
+
|
|
11
|
+
Raw Unity YAML is enormous. `read_asset` returns counts, a `cmp` table declaring repeated component sets once (referenced by `c1`/`c2` codes), and a folded `tree`. Drill down with `field_limit` + `component` / `path` / `profile=full` (or `detail=verbose`) instead of re-reading raw YAML. Session cache reuses the parsed model (`_cache: "hit"`). `profile: full` disables render-only folding; `field_limit: 0` (default) returns names only — bump it before `component` drill-down so fields are available. `page_size`/`cursor` page the `tree` rows.
|
|
12
|
+
|
|
13
|
+
Use `**search_assets`** to locate prefabs/components/GUIDs; each result tags *why* it matched so you know which `read_asset` drill-down to run next.
|
|
14
|
+
|
|
15
|
+
### Reads that save tokens
|
|
16
|
+
|
|
17
|
+
Raw Unity data is large. Prefer the cheap, structured reads before reaching for verbose output:
|
|
18
|
+
|
|
19
|
+
- **`read_asset`** returns a folded `tree` + `cmp` table + counts, not raw YAML. Drill into a subtree with `component` / `path` + `field_limit` instead of re-reading the whole asset; the parsed model is session-cached (`_cache: "hit"`). `field_limit: 0` (default) returns field names only — bump it only for a `component` drill-down where you need values.
|
|
20
|
+
- **`manage_tools(action="list_groups")`** — sessions start with two default-on groups (`core` + `gate-and-verify`); activate only the other group you need so the full **250+ tool** surface stays out of the prompt. Prefer `action="activate_for"` with a free-text `intent` (or `action="suggest"` to preview) over hand-picking ids — it brings the right groups online in one call.
|
|
21
|
+
- **`read_console`** with `detail: "summary"` returns messages only; reserve `detail: "verbose"` for when you need Unity-internal stack frames.
|
|
22
|
+
- **`search_assets`** tags *why* each result matched, so you skip broad reads and go straight to the right drill-down.
|
|
23
|
+
- **Output profiles + paging.** The heavy tools (`read_asset`, `search_assets`, `scene_get_data`, `find_references`, `validate_edit`, `scan_paths`, `component_get`) take `profile: compact|balanced|full` (`compact` is the default — the cheap folded/counts shape). Raise to `balanced`/`full` to expand. Page large results with `page_size` + the `pagination.next_cursor` token (omit `page_size` to get the whole payload at once). For `component_get`, start compact then use `property_path` or paging to expand one subtree. For `find_references` / `validate_edit` / `scan_paths`, the compact default returns counts/groupings only — pass `profile: "balanced"` to get the per-asset / per-issue lists those tools previously returned by default. (The legacy `detail` / `max_results` / `max_nodes` caps remain as aliases.)
|
|
24
|
+
- **`capabilities`** before assuming tool names/schemas/route policy — cheaper than discovering a tool's real signature by trial and error.
|
|
25
|
+
- **Prefer typed tools** (`component_modify`, `scene_get_data`, …) over `execute_csharp` / `invoke_method` — explicit schemas, smaller request/response envelopes, same gate.
|
|
26
|
+
|
|
27
|
+
### find_references before delete
|
|
28
|
+
|
|
29
|
+
Before deleting or moving an asset, call `**find_references*`* to see who depends on it. Offline-first (no live bridge needed for text-serialized assets).
|
|
30
|
+
|
|
31
|
+
### dependencies: both directions in one call
|
|
32
|
+
|
|
33
|
+
When you need **both** directions (what this asset depends on AND what depends on it), or the broken-edge / cycle view, call `**dependencies*`*. It returns the forward + reverse edge sets plus the same `broken_dependency` GUIDs and `dependency_cycle` trails the `dependencies` verify rule computes — no second dependency graph is built (it reuses the verify scanner + the `find_references` reverse walker). Live bridge only (the scanners call AssetDatabase); pass `detail: "summary"` for counts only when you just need the magnitude.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# unity-open-mcp MCP wrapper.
|
|
3
|
+
#
|
|
4
|
+
# Resolves UNITY_PROJECT_PATH from THIS SCRIPT'S location, so it works no
|
|
5
|
+
# matter what working directory the AI client spawns the MCP server in. Use it
|
|
6
|
+
# for clients that expand neither ${workspaceFolder} nor environment variables
|
|
7
|
+
# in their MCP config (for example Codex and ZCode).
|
|
8
|
+
#
|
|
9
|
+
# Committed to the repository; contains no machine-specific paths.
|
|
10
|
+
# Regenerate with: unity-open-mcp setup --wrapper …
|
|
11
|
+
#
|
|
12
|
+
# Override the Unity subfolder for a one-off run:
|
|
13
|
+
# UNITY_SUBPATH=OtherClient bash <this script>
|
|
14
|
+
set -euo pipefail
|
|
15
|
+
|
|
16
|
+
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
17
|
+
workspace_root="$(cd "${script_dir}/__WORKSPACE_FROM_SCRIPT__" && pwd)"
|
|
18
|
+
subpath="${UNITY_SUBPATH-__UNITY_SUBPATH__}"
|
|
19
|
+
|
|
20
|
+
if [ -n "${subpath}" ]; then
|
|
21
|
+
project_path="${workspace_root}/${subpath}"
|
|
22
|
+
else
|
|
23
|
+
project_path="${workspace_root}"
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
for marker in __UNITY_ROOT_MARKERS__; do
|
|
27
|
+
if [ ! -d "${project_path}/${marker}" ]; then
|
|
28
|
+
echo "unity-open-mcp: ${project_path} is not a Unity project root (missing ${marker}/)." >&2
|
|
29
|
+
echo "unity-open-mcp: set UNITY_SUBPATH to the Unity folder relative to ${workspace_root}." >&2
|
|
30
|
+
exit 1
|
|
31
|
+
fi
|
|
32
|
+
done
|
|
33
|
+
|
|
34
|
+
export UNITY_PROJECT_PATH="${project_path}"
|
|
35
|
+
exec npx -y "unity-open-mcp@__UNITY_OPEN_MCP_VERSION__" "$@"
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Tool } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
type Schema = Record<string, any>;
|
|
3
|
+
/** Locator names are explicit in the published contract; aliases retain their wire key. */
|
|
4
|
+
export declare function canonicalSchema(name: string, input: Schema): Tool["inputSchema"];
|
|
5
|
+
/** Validate before dispatch, including nested records; arbitrary value payloads stay opaque. */
|
|
6
|
+
export declare function validateSchema(value: any, schema: Schema, path?: string): string[];
|
|
7
|
+
export declare function wireArguments(value: any, schema: Schema, notes?: string[]): any;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { implementedRules } from "./capabilities/rule-catalog.js";
|
|
2
|
+
// Explicit semantic inventory: prose edits must never change a locator contract.
|
|
3
|
+
const GAME_OBJECT_PATH_TOOLS = new Set([
|
|
4
|
+
"unity_senses_spatial_query",
|
|
5
|
+
"unity_open_mcp_prefab_create",
|
|
6
|
+
"unity_open_mcp_prefab_apply",
|
|
7
|
+
"unity_open_mcp_prefab_revert",
|
|
8
|
+
"unity_open_mcp_prefab_unpack",
|
|
9
|
+
"unity_open_mcp_prefab_get_overrides",
|
|
10
|
+
"unity_open_mcp_prefab_status",
|
|
11
|
+
"unity_open_mcp_gameobject_destroy",
|
|
12
|
+
"unity_open_mcp_gameobject_duplicate",
|
|
13
|
+
"unity_open_mcp_gameobject_find",
|
|
14
|
+
"unity_open_mcp_gameobject_modify",
|
|
15
|
+
"unity_open_mcp_gameobject_set_parent",
|
|
16
|
+
"unity_open_mcp_component_add",
|
|
17
|
+
"unity_open_mcp_component_destroy",
|
|
18
|
+
"unity_open_mcp_component_get",
|
|
19
|
+
"unity_open_mcp_component_modify",
|
|
20
|
+
"unity_open_mcp_scene_focus",
|
|
21
|
+
"unity_open_mcp_selection_set",
|
|
22
|
+
"unity_open_mcp_navigation_surface_add",
|
|
23
|
+
"unity_open_mcp_navigation_set_bake_settings",
|
|
24
|
+
"unity_open_mcp_navigation_surface_bake",
|
|
25
|
+
"unity_open_mcp_navigation_modifier_add",
|
|
26
|
+
"unity_open_mcp_navigation_modifier_volume_add",
|
|
27
|
+
"unity_open_mcp_navigation_link_add",
|
|
28
|
+
"unity_open_mcp_navigation_agent_add",
|
|
29
|
+
"unity_open_mcp_navigation_agent_set_destination",
|
|
30
|
+
"unity_open_mcp_navigation_get",
|
|
31
|
+
"unity_open_mcp_navigation_modify",
|
|
32
|
+
"unity_open_mcp_probuilder_get_mesh_info",
|
|
33
|
+
"unity_open_mcp_probuilder_extrude",
|
|
34
|
+
"unity_open_mcp_probuilder_delete_faces",
|
|
35
|
+
"unity_open_mcp_probuilder_set_face_material",
|
|
36
|
+
"unity_open_mcp_particle_system_get",
|
|
37
|
+
"unity_open_mcp_particle_system_modify",
|
|
38
|
+
"unity_open_mcp_splines_add_knot",
|
|
39
|
+
"unity_open_mcp_splines_set_knot",
|
|
40
|
+
"unity_open_mcp_splines_set_tangent_mode",
|
|
41
|
+
"unity_open_mcp_splines_evaluate",
|
|
42
|
+
"unity_open_mcp_splines_get_knots",
|
|
43
|
+
"unity_open_mcp_splines_modify",
|
|
44
|
+
"unity_open_mcp_light_add",
|
|
45
|
+
"unity_open_mcp_light_set",
|
|
46
|
+
"unity_open_mcp_light_modify",
|
|
47
|
+
"unity_open_mcp_reflection_probe_bake",
|
|
48
|
+
"unity_open_mcp_reflection_probe_get",
|
|
49
|
+
"unity_open_mcp_audio_source_add",
|
|
50
|
+
"unity_open_mcp_audio_source_modify",
|
|
51
|
+
"unity_open_mcp_ui_canvas_add",
|
|
52
|
+
"unity_open_mcp_ui_layout_group_add",
|
|
53
|
+
"unity_open_mcp_ui_element_modify",
|
|
54
|
+
"unity_open_mcp_constraint_add",
|
|
55
|
+
"unity_open_mcp_lod_group_configure",
|
|
56
|
+
"unity_open_mcp_lod_add_level",
|
|
57
|
+
"unity_open_mcp_terrain_set_heights",
|
|
58
|
+
"unity_open_mcp_terrain_paint_layer",
|
|
59
|
+
"unity_open_mcp_terrain_place_trees",
|
|
60
|
+
"unity_open_mcp_terrain_set_neighbors",
|
|
61
|
+
"unity_open_mcp_cinemachine_set_targets",
|
|
62
|
+
"unity_open_mcp_cinemachine_set_lens",
|
|
63
|
+
"unity_open_mcp_cinemachine_set_body",
|
|
64
|
+
"unity_open_mcp_cinemachine_set_noise",
|
|
65
|
+
"unity_open_mcp_cinemachine_brain_ensure",
|
|
66
|
+
"unity_open_mcp_timeline_director_bind",
|
|
67
|
+
"unity_open_mcp_tilemap_set_tile",
|
|
68
|
+
"unity_open_mcp_tilemap_box_fill",
|
|
69
|
+
]);
|
|
70
|
+
const ASSET_PATH_TOOLS = new Set([
|
|
71
|
+
"unity_open_mcp_scene_create",
|
|
72
|
+
"unity_open_mcp_scene_open",
|
|
73
|
+
"unity_open_mcp_scene_save",
|
|
74
|
+
"unity_open_mcp_scene_unload",
|
|
75
|
+
"unity_open_mcp_scene_set_active",
|
|
76
|
+
"unity_open_mcp_scene_get_data",
|
|
77
|
+
]);
|
|
78
|
+
// GameObject host selectors for the "supply only one selector" rule. `name`
|
|
79
|
+
// selects by name unless the tool uses it as a payload; such tools name their
|
|
80
|
+
// by-name selector here and publish their full set as `x-gameobject-selectors`,
|
|
81
|
+
// which the bridge's BatchSchemaValidator reads from the generated schema.
|
|
82
|
+
const DEFAULT_GAMEOBJECT_SELECTORS = ["instance_id", "game_object_path", "path", "target_path", "name"];
|
|
83
|
+
const NAME_SELECTOR_OVERRIDES = {
|
|
84
|
+
// `name` is the new name; the target is resolved by name_target.
|
|
85
|
+
unity_open_mcp_gameobject_modify: "name_target",
|
|
86
|
+
};
|
|
87
|
+
/** Selector keys the one-selector rule applies to; null when the schema has no GameObject host. */
|
|
88
|
+
function gameObjectSelectors(schema) {
|
|
89
|
+
if (Array.isArray(schema["x-gameobject-selectors"]))
|
|
90
|
+
return schema["x-gameobject-selectors"];
|
|
91
|
+
if (schema["x-project-command"] || !schema.properties?.game_object_path)
|
|
92
|
+
return null;
|
|
93
|
+
return DEFAULT_GAMEOBJECT_SELECTORS.filter(k => schema.properties[k]);
|
|
94
|
+
}
|
|
95
|
+
/** Locator names are explicit in the published contract; aliases retain their wire key. */
|
|
96
|
+
export function canonicalSchema(name, input) {
|
|
97
|
+
const schema = structuredClone(input);
|
|
98
|
+
const props = schema.properties;
|
|
99
|
+
// offline_integrity is a reporting aggregator, not a registered VerifyRunner rule.
|
|
100
|
+
for (const key of ["include_rules", "exclude_rules", ...(name === "unity_open_mcp_scan_paths" ? ["categories"] : [])])
|
|
101
|
+
if (props[key]?.items)
|
|
102
|
+
props[key].items.enum = implementedRules().filter(r => r.id !== "offline_integrity").map(r => r.id);
|
|
103
|
+
const rename = (s, old, key) => {
|
|
104
|
+
if (!s.properties?.[old] || s.properties[key])
|
|
105
|
+
return;
|
|
106
|
+
s.properties[key] = { ...s.properties[old], "x-wire-key": old };
|
|
107
|
+
s.properties[old] = { ...s.properties[old], deprecated: true, "x-alias-for": key,
|
|
108
|
+
description: `Deprecated alias; use ${key}.` };
|
|
109
|
+
delete s.properties[old].default;
|
|
110
|
+
if (s.required?.includes(old)) {
|
|
111
|
+
s.required = s.required.filter((k) => k !== old);
|
|
112
|
+
s.allOf = [...(s.allOf ?? []), { anyOf: [{ required: [key] }, { required: [old] }] }];
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
if (GAME_OBJECT_PATH_TOOLS.has(name))
|
|
116
|
+
rename(schema, "path", "game_object_path");
|
|
117
|
+
if (ASSET_PATH_TOOLS.has(name))
|
|
118
|
+
rename(schema, "path", "asset_path");
|
|
119
|
+
rename(schema, "prefab_asset_path", "asset_path");
|
|
120
|
+
if (props.prefab_asset_path)
|
|
121
|
+
props.prefab_path = { type: "string", deprecated: true, "x-alias-for": "asset_path", description: "Deprecated alias; use asset_path." };
|
|
122
|
+
if (props.game_object_path && !props.target_path)
|
|
123
|
+
props.target_path = { type: "string", deprecated: true, "x-alias-for": "game_object_path", description: "Deprecated alias; use game_object_path." };
|
|
124
|
+
if (/component_(get|modify|add)/.test(name))
|
|
125
|
+
rename(schema, "type_name", "component_type");
|
|
126
|
+
if (name.endsWith("component_modify"))
|
|
127
|
+
rename(props.fields.items, "path", "property_path");
|
|
128
|
+
if (name === "unity_open_mcp_find_references" || name === "unity_open_mcp_dependencies") {
|
|
129
|
+
props.path = { type: "string", deprecated: true, "x-alias-for": "asset_path", description: "Deprecated alias; use asset_path." };
|
|
130
|
+
for (const branch of schema.oneOf ?? [])
|
|
131
|
+
if (branch.required?.includes("asset_path")) {
|
|
132
|
+
branch.required = branch.required.filter((k) => k !== "asset_path");
|
|
133
|
+
branch.anyOf = [{ required: ["asset_path"] }, { required: ["path"] }];
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (props.prefab_path)
|
|
137
|
+
for (const clause of schema.allOf ?? [])
|
|
138
|
+
if (clause.anyOf?.some((b) => b.required?.includes("asset_path")))
|
|
139
|
+
clause.anyOf.push({ required: ["prefab_path"] });
|
|
140
|
+
if (props.game_object_path) {
|
|
141
|
+
const nameSelector = NAME_SELECTOR_OVERRIDES[name] ?? "name";
|
|
142
|
+
if (nameSelector !== "name")
|
|
143
|
+
schema["x-gameobject-selectors"] = DEFAULT_GAMEOBJECT_SELECTORS.map(k => k === "name" ? nameSelector : k).filter(k => props[k]);
|
|
144
|
+
props.game_object_path.description = `GameObject hierarchy path, e.g. Root/Child. Supply only one host selector: instance_id, game_object_path, or ${nameSelector}.`;
|
|
145
|
+
}
|
|
146
|
+
if (props.component_type)
|
|
147
|
+
props.component_type.description = "Component type (full name preferred); alternative to component_instance_id.";
|
|
148
|
+
return schema;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* An explicit `null` for an OPTIONAL property whose schema declares a
|
|
152
|
+
* non-null type is read as "omitted": MCP clients routinely serialize an
|
|
153
|
+
* absent optional argument as null, and the bridge's own readers treat such a
|
|
154
|
+
* value as absent. A required property, a property without a declared type
|
|
155
|
+
* (opaque patch values such as component_modify's `value`) and a type that
|
|
156
|
+
* lists `null` keep the value as sent.
|
|
157
|
+
*/
|
|
158
|
+
function isNullOmitted(schema, key, value) {
|
|
159
|
+
if (value !== null)
|
|
160
|
+
return false;
|
|
161
|
+
if ((schema.required ?? []).includes(key))
|
|
162
|
+
return false;
|
|
163
|
+
const type = schema.properties?.[key]?.type;
|
|
164
|
+
if (!type)
|
|
165
|
+
return false;
|
|
166
|
+
return !(Array.isArray(type) ? type : [type]).includes("null");
|
|
167
|
+
}
|
|
168
|
+
/** `value` without its null-omitted properties; the input object is not mutated. */
|
|
169
|
+
function withoutNullOmitted(value, schema) {
|
|
170
|
+
if (!schema.properties)
|
|
171
|
+
return value;
|
|
172
|
+
const kept = Object.entries(value).filter(([key, v]) => !isNullOmitted(schema, key, v));
|
|
173
|
+
return kept.length === Object.keys(value).length ? value : Object.fromEntries(kept);
|
|
174
|
+
}
|
|
175
|
+
/** Validate before dispatch, including nested records; arbitrary value payloads stay opaque. */
|
|
176
|
+
export function validateSchema(value, schema, path = "args") {
|
|
177
|
+
if (value && typeof value === "object" && !Array.isArray(value))
|
|
178
|
+
value = withoutNullOmitted(value, schema);
|
|
179
|
+
const errors = [];
|
|
180
|
+
for (const op of ["anyOf", "oneOf", "allOf"])
|
|
181
|
+
if (schema[op]) {
|
|
182
|
+
const n = schema[op].filter((s) => validateSchema(value, s, path).length === 0).length;
|
|
183
|
+
if ((op === "anyOf" && !n) || (op === "oneOf" && n !== 1) || (op === "allOf" && n !== schema[op].length))
|
|
184
|
+
errors.push(`${path} must satisfy ${op}`);
|
|
185
|
+
}
|
|
186
|
+
const matches = (t) => t === "array" ? Array.isArray(value) : t === "object" ? value !== null && typeof value === "object" && !Array.isArray(value) : t === "integer" ? Number.isInteger(value) : t === "null" ? value === null : typeof value === t;
|
|
187
|
+
if (schema.type && !(Array.isArray(schema.type) ? schema.type : [schema.type]).some(matches))
|
|
188
|
+
return [`${path} must be ${schema.type}`];
|
|
189
|
+
if (schema.enum && !schema.enum.includes(value))
|
|
190
|
+
errors.push(`${path} must be one of ${schema.enum.join(", ")}`);
|
|
191
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
192
|
+
for (const key of schema.required ?? [])
|
|
193
|
+
if (value[key] === undefined)
|
|
194
|
+
errors.push(`${path}.${key} is required`);
|
|
195
|
+
const selectorKeys = gameObjectSelectors(schema);
|
|
196
|
+
if (selectorKeys) {
|
|
197
|
+
const supplied = (k) => value[k] !== undefined && value[k] !== null && value[k] !== "" && value[k] !== 0 && value[k] !== "0";
|
|
198
|
+
const selectors = selectorKeys.filter(supplied);
|
|
199
|
+
if (selectors.length > 1)
|
|
200
|
+
errors.push(`${path}: supply only one GameObject selector (prefer game_object_path); ignored keys: ${selectors.join(", ")}`);
|
|
201
|
+
if (supplied("component_instance_id") && ["component_type", "type_name"].some(supplied))
|
|
202
|
+
errors.push(`${path}: component_instance_id and component_type are alternatives`);
|
|
203
|
+
}
|
|
204
|
+
if (schema.properties)
|
|
205
|
+
for (const [key, child] of Object.entries(value)) {
|
|
206
|
+
const prop = Object.prototype.hasOwnProperty.call(schema.properties, key) ? schema.properties[key] : undefined;
|
|
207
|
+
if (!prop && schema.additionalProperties === false)
|
|
208
|
+
errors.push(`${path}.${key} is unknown; canonical keys: ${Object.keys(schema.properties).filter(k => !schema.properties[k].deprecated).join(", ")}`);
|
|
209
|
+
if (prop) {
|
|
210
|
+
if (prop["x-alias-for"] && Object.keys(value).some(other => other !== key && (other === prop["x-alias-for"] || schema.properties[other]?.["x-alias-for"] === prop["x-alias-for"])))
|
|
211
|
+
errors.push(`${path}.${key} conflicts with another selector; use only ${prop["x-alias-for"]}`);
|
|
212
|
+
errors.push(...validateSchema(child, prop, `${path}.${key}`));
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (Array.isArray(value) && schema.items)
|
|
217
|
+
value.forEach((v, i) => errors.push(...validateSchema(v, schema.items, `${path}[${i}]`)));
|
|
218
|
+
if (typeof value === "string" && schema.pattern && !new RegExp(schema.pattern).test(value))
|
|
219
|
+
errors.push(`${path} does not match ${schema.pattern}`);
|
|
220
|
+
const size = typeof value === "number" ? value : typeof value === "string" || Array.isArray(value) ? value.length : undefined;
|
|
221
|
+
if (size !== undefined) {
|
|
222
|
+
for (const k of ["minimum", "minItems", "minLength"])
|
|
223
|
+
if (schema[k] !== undefined && size < schema[k])
|
|
224
|
+
errors.push(`${path} violates ${k}`);
|
|
225
|
+
if (typeof value === "number" && schema.exclusiveMinimum !== undefined && value <= schema.exclusiveMinimum)
|
|
226
|
+
errors.push(`${path} violates exclusiveMinimum`);
|
|
227
|
+
if (typeof value === "number" && schema.exclusiveMaximum !== undefined && value >= schema.exclusiveMaximum)
|
|
228
|
+
errors.push(`${path} violates exclusiveMaximum`);
|
|
229
|
+
for (const k of ["maximum", "maxItems", "maxLength"])
|
|
230
|
+
if (schema[k] !== undefined && size > schema[k])
|
|
231
|
+
errors.push(`${path} violates ${k}`);
|
|
232
|
+
}
|
|
233
|
+
return errors;
|
|
234
|
+
}
|
|
235
|
+
export function wireArguments(value, schema, notes = []) {
|
|
236
|
+
if (Array.isArray(value))
|
|
237
|
+
return value.map(v => wireArguments(v, schema.items ?? {}, notes));
|
|
238
|
+
if (!value || typeof value !== "object" || !schema.properties)
|
|
239
|
+
return value;
|
|
240
|
+
const out = {};
|
|
241
|
+
// Null-omitted properties never reach the wire: the bridge validates the
|
|
242
|
+
// same schema and would otherwise refuse the null the server just waived.
|
|
243
|
+
for (const [key, v] of Object.entries(withoutNullOmitted(value, schema))) {
|
|
244
|
+
const p = schema.properties[key] ?? {};
|
|
245
|
+
const canonical = p["x-alias-for"];
|
|
246
|
+
if (canonical)
|
|
247
|
+
notes.push(`${key} is deprecated; use ${canonical}.`);
|
|
248
|
+
const target = p["x-wire-key"] ?? (canonical ? schema.properties[canonical]?.["x-wire-key"] ?? canonical : key);
|
|
249
|
+
out[target] = wireArguments(v, p, notes);
|
|
250
|
+
}
|
|
251
|
+
return out;
|
|
252
|
+
}
|
|
253
|
+
//# sourceMappingURL=tool-contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-contract.js","sourceRoot":"","sources":["../src/tool-contract.ts"],"names":[],"mappings":"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"}
|
package/dist/tool-router.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { JobManager } from "./jobs/job-manager.js";
|
|
2
|
+
import { type RequestIdentity } from "./agent-identity.js";
|
|
3
|
+
import { type BridgeTagConfirmation } from "./release-lookup.js";
|
|
1
4
|
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
2
5
|
import type { Router } from "./router.js";
|
|
3
|
-
import
|
|
4
|
-
import type { BatchSpawn } from "./batch-spawn.js";
|
|
6
|
+
import { LiveClient } from "./live-client.js";
|
|
5
7
|
import type { BridgeEventStream } from "./event-stream.js";
|
|
6
8
|
export declare function resolveRouteLogEnabled(env?: NodeJS.ProcessEnv): boolean;
|
|
7
9
|
export declare const ROUTE_LOG_ENABLED: boolean;
|
|
@@ -23,7 +25,8 @@ export interface HubControlBackend {
|
|
|
23
25
|
getInstallPath(): InstallPathResult;
|
|
24
26
|
setInstallPath(path: string): ReturnType<typeof setInstallPath>;
|
|
25
27
|
}
|
|
26
|
-
import
|
|
28
|
+
import { BatchSpawn } from "./batch-spawn.js";
|
|
29
|
+
import { type ToolSessionState } from "./tool-session-state.js";
|
|
27
30
|
export type SourceTag = "live" | "offline" | "local";
|
|
28
31
|
export interface RouteMeta {
|
|
29
32
|
/**
|
|
@@ -45,6 +48,19 @@ export interface RouteMeta {
|
|
|
45
48
|
*/
|
|
46
49
|
editorPid?: number;
|
|
47
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Release lookups behind `unity_open_mcp_upgrade`. Injectable so unit tests
|
|
53
|
+
* never touch npm or GitHub; production uses the shared `release-lookup`
|
|
54
|
+
* module with the global fetch.
|
|
55
|
+
*/
|
|
56
|
+
export interface ReleaseLookup {
|
|
57
|
+
latest(): Promise<{
|
|
58
|
+
version: string;
|
|
59
|
+
}>;
|
|
60
|
+
confirmBridgeTag(version: string): Promise<BridgeTagConfirmation>;
|
|
61
|
+
}
|
|
62
|
+
/** Test seam for the upgrade route's network lookups. Returns a restore function. */
|
|
63
|
+
export declare function setReleaseLookupForTest(lookup: ReleaseLookup | null): () => void;
|
|
48
64
|
export declare class ToolRouter implements Router {
|
|
49
65
|
private live;
|
|
50
66
|
private batch;
|
|
@@ -54,6 +70,10 @@ export declare class ToolRouter implements Router {
|
|
|
54
70
|
private onToolListChanged?;
|
|
55
71
|
private hubBackend;
|
|
56
72
|
private globalLogPathOverride?;
|
|
73
|
+
private jobManager?;
|
|
74
|
+
get jobs(): JobManager;
|
|
75
|
+
closeJobs(): void;
|
|
76
|
+
routeJobs(args: Record<string, unknown>, identity?: RequestIdentity): Promise<CallToolResult>;
|
|
57
77
|
private readonly modelCache;
|
|
58
78
|
/**
|
|
59
79
|
* M28-refactoring Plan 3 (T3.2) — named-tool dispatch map. Replaces the
|
|
@@ -68,17 +88,41 @@ export declare class ToolRouter implements Router {
|
|
|
68
88
|
private routeHandlers?;
|
|
69
89
|
private getRouteHandlers;
|
|
70
90
|
constructor(live: LiveClient, batch: BatchSpawn, projectPath: string, eventStream: BridgeEventStream, sessionState: ToolSessionState, onToolListChanged?: (() => void | Promise<void>) | undefined, hubBackend?: HubControlBackend, globalLogPathOverride?: string | undefined);
|
|
71
|
-
route(toolName: string, args: Record<string, unknown
|
|
91
|
+
route(toolName: string, args: Record<string, unknown>, identity?: RequestIdentity): Promise<CallToolResult>;
|
|
92
|
+
/**
|
|
93
|
+
* `capabilities` / `bridge_status` with an explicit project_path, from any
|
|
94
|
+
* entry point (direct, manage_tools invoke, port override). The probe router
|
|
95
|
+
* gets the arguments without project_path, so it dispatches normally. A
|
|
96
|
+
* per-request port override is refused rather than dropped or mixed in:
|
|
97
|
+
* project_path brings its own port/auth discovery, and pinging the override
|
|
98
|
+
* port would present another bridge's answer as the probed project's.
|
|
99
|
+
*/
|
|
100
|
+
private routeProjectProbe;
|
|
101
|
+
/**
|
|
102
|
+
* Router for a read-only probe of another project: its own port/auth
|
|
103
|
+
* discovery and a throwaway copy of this session. Discovery still reconciles
|
|
104
|
+
* auto-activation against the probed project's inventory, so the probe
|
|
105
|
+
* reports that project's view, but only the copy changes — this session's
|
|
106
|
+
* active groups and tools/list stay put, and no list_changed is wired. The
|
|
107
|
+
* probe tools are local-only and never spawn Unity; the probe's own batch
|
|
108
|
+
* router only answers headless availability for that project's editor.
|
|
109
|
+
*/
|
|
110
|
+
private projectProbeRouter;
|
|
111
|
+
private static readonly PROBE_BATCH_CACHE_SIZE;
|
|
112
|
+
private readonly probeBatchByProject;
|
|
113
|
+
private projectProbeBatch;
|
|
72
114
|
/**
|
|
73
115
|
* M23 Plan 3 — route with a per-call live-client override. Used when a
|
|
74
116
|
* per-request `_meta.port` override targets a different bridge instance than
|
|
75
117
|
* the default. The override client bypasses shared session state (it is a
|
|
76
118
|
* fresh LiveClient aimed at the override port); all other routing logic is
|
|
77
|
-
* identical to {@link route}.
|
|
78
|
-
*
|
|
119
|
+
* identical to {@link route}. `identity` carries the override port and the
|
|
120
|
+
* request's agent id.
|
|
79
121
|
*/
|
|
80
|
-
routeOverride(toolName: string, args: Record<string, unknown>, overrideLive: LiveClient): Promise<CallToolResult>;
|
|
122
|
+
routeOverride(toolName: string, args: Record<string, unknown>, overrideLive: LiveClient, identity: RequestIdentity): Promise<CallToolResult>;
|
|
81
123
|
private routeCore;
|
|
124
|
+
private routeValidated;
|
|
125
|
+
private routeUnchecked;
|
|
82
126
|
private batchPingResult;
|
|
83
127
|
/**
|
|
84
128
|
* feedback-fable-04-08 §7 — when a `live_unavailable` batch fallback lands
|
|
@@ -106,6 +150,17 @@ export declare class ToolRouter implements Router {
|
|
|
106
150
|
private routeListRules;
|
|
107
151
|
private routeGenerateSkill;
|
|
108
152
|
private routeManageTools;
|
|
153
|
+
/**
|
|
154
|
+
* `unity_open_mcp_upgrade` is a live tool, but its network lookups run HERE
|
|
155
|
+
* rather than inside the bridge: the bridge tool executes on the Editor main
|
|
156
|
+
* thread, where a 10 s registry timeout would freeze the Editor and every
|
|
157
|
+
* queued bridge request. The bridge only ever receives a resolved
|
|
158
|
+
* `target_version`, and an apply that will re-pin through UPM is forwarded
|
|
159
|
+
* only after the `bridge-v<version>` release tag was confirmed to exist —
|
|
160
|
+
* the same preview → confirm → apply order the bridge window follows.
|
|
161
|
+
*/
|
|
162
|
+
private routeUpgrade;
|
|
163
|
+
private routeProjectCommands;
|
|
109
164
|
private manageToolsListGroups;
|
|
110
165
|
private manageToolsSuggest;
|
|
111
166
|
private manageToolsActivateFor;
|