Skip to content

Runtime MCP Target Routing Skill ​

Use this skill together with SCE DebugTools MCP when an Editor debug session can contain more than one simulated client. The purpose of this skill is to keep Runtime inspection and Runtime writes attached to the intended Gameplay/Wasmtime state instead of silently using the first available instance.

Scope and protocol ​

Connection selection has two levels: select the Editor/Runtime process by its discovered instance ID, then select a Gameplay with target_id. GUI ports are automatic; normally omit port in the outer forwarding tools. Explicit foreign ports require runtime_instance_id. See the multi-instance connection guide.

Gameplay routing selects multiple Gameplay instances inside the same Client process. It does not provide independent input focus for multiple embedded viewports and does not broker multiple independent SCE processes.

The routing field belongs to the outer Runtime MCP request envelope:

json
{
  "method": "runtime.call_tool",
  "params": {
    "target_id": "gameplay:<GameplayUUID>",
    "name": "ui.snapshot",
    "arguments": {
      "maxDepth": 4,
      "maxNodes": 200
    }
  }
}

target_id is not a Runtime tool argument. Keep it beside name and arguments; a field with the same name inside arguments belongs to the Runtime tool and must not be overwritten by the bridge.

Target IDs are process-local and short-lived. Treat them as opaque strings. Do not persist them in a project, put them in generated code, or reuse them after a Gameplay is destroyed or recreated.

Deterministic selection workflow ​

  1. Start or attach to the full Editor debug session through the normal debug_* tools.
  2. Call runtime_list_targets without a target ID.
  3. Match the returned targets in this order:
    • the simulated client explicitly named by the user;
    • an exact slot_id match;
    • an exact string player_id match;
    • a unique foreground target when it clearly matches the task.
  4. If exactly one target remains, remember its target_id for this debug session.
  5. If more than one target remains, ask the user which simulated client to inspect. Never choose the first array item as an implicit default.
  6. Call runtime_list_tools with the selected ID, then call runtime_call_tool with the same ID for every target-specific Runtime request.
  7. If the debug session ends, a Gameplay is recreated, or the bridge reports a stale target, return to step 2.

Use debug.ping to verify the selected target's live identity. On updated clients, runtime_identity_source="native_gameplay" identifies the routed target_id, gameplay_uuid, project, path, slot and user. startup_arguments describes process startup and may refer to an older project. With only updated managed dependencies, runtime_identity_source="managed_game" supplies the live project/session/player and run mode; native route/path fields are null. Treat missing or null fields as unavailable and retain target discovery evidence instead of filling them from startup argv.

Example target discovery:

json
{
  "tool": "runtime_list_targets",
  "arguments": {

    "timeout_ms": 5000
  }
}

Read result.targets from an outer Editor MCP response. Each target may contain:

FieldMeaning
target_idOpaque process-local routing ID, normally gameplay:<GameplayUUID>
gameplay_uuidGameplay instance identity for diagnostics
slot_idEditor multiplayer slot identifier
player_idPlayer identity serialized as a string to avoid integer precision loss
map_nameMap associated with the Gameplay instance
is_foregroundWhether the target is currently foreground
is_editor_gameWhether the target belongs to the Editor debug session
readyWhether its Wasmtime Runtime state is ready for calls

Only one target can omit target_id for backward compatibility. With multiple targets, omission must fail with runtime_target_ambiguous.

Keep one target for one verification loop ​

Once selected, use the same target for the whole evidence chain:

text
runtime_list_targets
  -> runtime_list_tools(target_id)
  -> runtime_call_tool(debug.ping, target_id)
  -> runtime_call_tool(debug.list_tools, target_id)
  -> runtime_call_tool(ui.snapshot or project tool, target_id)

Do not mix a target-specific ui.snapshot with a target-less debug.ping; that can produce evidence from different Gameplay instances. If the user asks to compare two simulated clients, discover once, then run the same tool sequence separately for each returned ID and label the evidence with the ID/slot/player metadata.

Background visual verification ​

After selecting a target, inspect its runtime_list_tools schema for debug.viewport_update. If available, acquire a lease for that target before a continuous animation or visual test, keep the returned string lease_id, renew before ttl_ms expires, and release in finally. Leases are bound to Gameplay and expire by wall time; ordinary per-call TCP disconnects do not release them. A stale lease cannot be renewed on another target or a new game.

Read rendered_engine_frame from debug.viewport_update status to distinguish engine-time progression from completed viewport drawing. On targets advertising the extended screenshot schema, debug.capture_screenshot waits for a new viewport submission by default; frame_timeout_ms controls this wait and the outer timeout must also cover file completion. The result captures the selected target's full window, not a cropped viewport. Its rendered_engine_frame is a completed submission identifier, not an exact GPU readback identifier.

Treat window_minimized, window_hidden, viewport_hidden, graphics_unavailable, and viewport_frame_timeout as explicit unavailable/timeout results. The embedded viewport inherits its Editor host window's minimized/hidden state. Restore the host or show the selected tab before retrying. A lease does not activate windows, switch hidden multiplayer tabs, change input routing, or override a deliberately paused simulation.

For animation acceptance, retain both advancing rendered_engine_frame values and changing native bone positions or visible poses in completed screenshots. Advancing game time or animation ticks alone does not prove drawing. A single fresh screenshot requests its own temporary update; use a continuous lease for a sequence of visual checks. Disconnecting an unfinished screenshot request cleans up that temporary update, while explicit leases remain valid until release or expiry.

Input boundary ​

Target routing selects the Runtime/Wasmtime state for debug.*, ui.*, scenegraph.*, material.*, and project-specific Runtime tools. input.pointer, input.key, input.text, and input.reset still go through process-level input and are affected by the active window and foreground focus. Do not claim that two embedded viewports can receive independent simultaneous synthetic input in phase one.

For a held pointer/key sequence, keep the selected Runtime target fixed and call input.reset if the sequence is interrupted. After changing the foreground embedded viewport, rediscover targets if the client recreated Gameplay state, then verify the target with debug.ping before continuing.

Fallback commands ​

When native Editor MCP actions are unavailable, use the versioned SDK fallback:

powershell
.\tools\runtime-mcp\Invoke-SceRuntimeMcpClient.ps1 -ListTargets -Port $runtimePort -RuntimeInstanceId $runtimeId
.\tools\runtime-mcp\Invoke-SceRuntimeMcpClient.ps1 -ListTools -TargetId gameplay:42 -Port $runtimePort -RuntimeInstanceId $runtimeId
.\tools\runtime-mcp\Invoke-SceRuntimeMcpClient.ps1 -Tool debug.ping -TargetId gameplay:42 -Port $runtimePort -RuntimeInstanceId $runtimeId
.\tools\runtime-mcp\Invoke-SceRuntimeMcpClient.ps1 -Tool ui.snapshot -TargetId gameplay:42 -ArgumentsJson '{"maxDepth":4,"maxNodes":200}' -Port $runtimePort -RuntimeInstanceId $runtimeId

The project-local ai/tools/Invoke-SceRuntimeMcp.ps1 is a thin shim over this shared client. Use Invoke-SceEditorMcp.ps1 when the full Editor MCP HTTP bridge is available; use the direct Runtime fallback only when the outer bridge is unavailable or is itself being diagnosed.

Error recovery ​

ErrorMeaningRecovery
runtime_target_ambiguousMultiple Gameplay targets are active and no target was suppliedCall runtime_list_targets, select deterministically, retry with target_id
runtime_target_not_foundThe target ended or was recreatedDiscover again; do not retry the stale ID
runtime_target_routing_unavailableClient native bridge predates target routingUpdate the Client build; a single-target call without target_id may still work
client_runtime_unavailableNo Runtime bridge is ready on the selected portWait for debug startup, check the port, then rediscover
invalid_target_idThe bridge received a non-string or blank target IDSend a non-empty string in the outer params.target_id field

When runtime_list_targets returns an empty list, treat the Runtime as not ready or inactive; do not invent a target ID. Check the newest client log and the debug operation state before retrying.

Evidence rules ​

Before claiming that a specific simulated client was inspected or changed, retain:

  • the target discovery result containing target_id, slot_id, and player_id;
  • a successful debug.ping or debug.list_tools response using that same ID;
  • the target-specific inspection or write response;
  • a fresh discovery result after any debug restart or Gameplay recreation.

For the complete Runtime tool catalog and coordinate/input semantics, read Runtime MCP 调试指南 and SCE Editor MCP 工具速查.