Skip to content

SCE DebugTools MCP Skill ​

For silent gameplay failures, use the installed tool capabilities and the configuration-to-runtime guide. gameplay.snapshot includes range provenance, execution/aim targets, abilities and Buff modifications. Opt in with trace_seconds (1–60), reproduce, then request a new snapshot to read the bounded order/command trace; polling the original request only reads its original report. Generated GDATA001 warnings indicate known values omitted from C#, while custom JSON fields are intentionally ignored by generation.

Use this skill to generate and compile a project or dependency library, start or stop an editor debug session, diagnose a failed build, handle a shared message dialog, run resource statistics and Humanoid animation bake, or inspect the live debug client through Runtime MCP.

Choose the host using the development entry rules: prefer the user's already-open target-project GUI Editor unless the user explicitly requests isolation; prefer Headless for AI build, connected-server debug and publish work only when no GUI is open or isolation is requested; use GUI for graphical editing. For multiple clients, inspect the installed dynamic-client tools. GUI and Headless must not write the same project concurrently.

Connection selection ​

After switching Headless installations, prepare the project with the selected installation's sce.cmd. Default docs/.client-debug.json launcher records follow that installation/API; custom records and explicit --client-* arguments retain their settings. launcherBinding=custom pins an intentional custom record. On AI_CONTEXT_FAILED inspect diagnostics.detail for the selected PowerShell and script exception; preparation prefers installed PowerShell 7 with 5.1 fallback. If a client Launcher stalls in residual-process cleanup, check the installation-root Launcher version as well as the editor version. Root Launcher replacement requires a signed update; collect the launcher log rather than assuming a process was killed.

GUI editors and their debug runtimes allocate ports automatically. Verify editor_get_context project and mcp.editor_instance_id first; select a specific editor using editor_list_instances or HTTP helper -ListInstances / -InstanceId. GUI debug start tools accept game_in_editor (false = connected-server standalone) and runtime_mcp_port (0 = auto). Priority is per-launch override, editor startup parameter, automatic allocation; one launch does not change future defaults. Wait for this operation’s runtime_mcp.state=ready, then normally omit Runtime port. A foreign explicit connection requires runtime_instance_id as well as port. Follow the multi-instance connection guide; Headless still uses its own current schema.

GUI project orientation ​

After verifying editor_get_context.project.path, use project_get_orientation, then project_set_orientation with orientation=portrait or landscape. A successful setter returns saved=true and changed. It persists config.ini [map].is_landscape through the GUI configuration service and refreshes the open panel. An unsaved direction edit returns project_settings_unsaved; ask the user to save or cancel it in the panel first. Other pending settings remain pending. Wait for project switching or publication to finish. Older GUIs use the project settings panel; Headless does not implement these GUI tools and uses the INI workflow below. This setting does not resize a running client. See the GUI orientation guide.

Remote Debug Project Scope ​

Matched API 2000+ GUI/Headless and allocation services scope remote Hosts by the authenticated account and canonical project ID (ProjectName in project settings). Different projects keep separate matches; restarting the same account/project reuses its Host and ends its previous match, including across login sessions. Each Host still serves one project and allocation depends on available capacity. A copied folder with the same project ID is the same project. Keep separate local hosts and operation/session/client/Runtime identities for parallel projects; never write the same project from two hosts. Multiplayer clients within one project use that match's client/target tools instead.

On host_lease_scope_unsupported, require a matching allocation service before starting; do not silently retry with legacy allocation. On host_lease_project_invalid, repair canonical project metadata instead of using a directory name. Old clients retain legacy allocation; stop their matches before migration. This contract alone does not prove a deployment or real parallel run.

Tools ​

ToolPurpose
project_get_orientationGUI: reads saved portrait/landscape orientation and whether the settings panel has an unsaved direction edit.
project_set_orientationGUI: saves orientation as portrait or landscape, reads it back, and refreshes the panel without saving unrelated pending settings.
debug_startSaves the map, generates GameData/trigger code, compiles both GameEntry configurations, deploys, then starts debug.
debug_start_no_compileUses existing AppBundles; skips GameData/trigger code generation and compile/deploy.
debug_stopStops the selected host's debug run; GUI destroys the embedded game, Headless releases its owned client/helper/session resources.
debug_status / debug_waitTrack the debug operation and its associated build.
build_status / build_diagnosticsQuery build phases, diagnostics and log paths.
build_startSaves and generates GameData/trigger code, compiles and deploys both Debug configurations, without starting a game. Supports game projects and dependency libraries.
build_wait / build_cancelWait for or cancel a build; a wait timeout leaves compilation running.
resource_statistics_generate_animationsSaves/generates, rebuilds Client-Resource manifests, and generates Humanoid animations and model collision. Headless returns an asynchronous operation ID.
resource_statistics_status / resource_statistics_wait / resource_statistics_cancelTrack or cancel the Headless resource operation without resubmitting it.
debug_start_with_resource_statisticsRuns resource statistics and Humanoid animation bake first, then starts editor debug. Supports skip_compile.
runtime_list_targetsLists active Runtime targets in the current client process. Use it before Runtime calls when embedded multiplayer debugging may have multiple Gameplay instances.
runtime_call_toolCalls one Runtime MCP tool inside the currently running debug client. The inner runtime tools are not listed in outer MCP tools/list.
editor_dialog_snapshotReads the current shared editor message dialog, its content, stable ID and available buttons.
editor_dialog_chooseSelects an available button on the exact dialog ID through its normal callback.

For GUI debugging, debug_start and debug_start_no_compile require the full SCE Editor MCP with an editor helper and an open project. The Windows Headless host also exposes debug tools when its native/client dependencies and account are ready; inspect that host's actual schemas and capabilities. Independent TriggerMcpHost does not provide either debug runtime.

Headless Build, Debug And Publish ​

Shared Headless sessions ​

For multiple AI conversations on one project, reuse one Headless host only after its capabilities advertise mcp.multi_session and mcp.operation_ownership. Each HTTP client initializes independently and retains its own Mcp-Session-Id; each WebSocket connection has its own persistent session. Do not share session IDs. Header-less legacy calls are request-scoped and cannot cancel another request through a cross-request JSON-RPC notification. Follow the shared-project guide.

Read owner_session_id before stopping or cancelling an operation. Other sessions may query status; an authorized handoff uses headless_takeover_operation with the original operation_id, exact expected_owner_session_id and a reason. A reconnect creates a new session; do not resubmit a start after losing the reply. Taking over a parent also transfers its descendants, including children created later. Taking over one child leaves its original parent's control available; this is coordination, not exclusive account authorization. A handoff never stops or reroutes the task. Continue normal status/stop/cancel and verify owned cleanup.

Coordinate external source files, save/generate, builds, debug and publishing. Project data, selection and Undo history are shared. Model tools and resources/read use the same authoring gate: editor_busy needs the current activity to finish; project_context_changed needs a fresh project read. A transport timeout or disconnect does not prove that a task stopped. GUI collaboration is a separate scope. Do not start a second host to bypass project ownership.

Prepare and operate the host ​

At the start of Headless development, use the known installation's sce.cmd prepare <project> online before edits/builds to refresh SDK, docs and AI context for that channel/API while retaining project version pins. Verify success/readiness and reread refreshed project instructions. Successful init or prepare in this session is sufficient; reuse the prepared host for iterations. If the installation path is unknown, locate it or explicitly report preparation was skipped; do not guess or claim the existing SDK is current. Save and close any project owner normally before prepare, without forcing unsaved user work. serve does not refresh SDK/docs; offline preparation uses installed resources only. See the guide's preparation section for results and conflict handling.

If CloudData or MailSystem reports TableNotFound after changing the project ID or moving it to another deployment, first check that project's publication history in the selected deployment. Normal project creation provisions mail tables; a first successful publication in a new deployment also provisions them. Prepare and compile do not. Explain this expected migration state and follow the CloudData diagnosis; do not infer a Headless failure, treat missing tables as empty saves, or publish without authorization for that project/environment.

For early native helper exits, retain diagnostics_status paths and collect the owned PID's process stdout/stderr capture, startup stages and engine log. New packages default these to installation logs/headless, independently of build and publish logs under editor-root. Preserve the hex exit code; 0xC0000409 alone does not prove a multi-project bug or identify a stack/buffer failure. Themis initialized is not delivery confirmation. Follow the guide's log collection section and stop only the owned operation/processes after preserving evidence. Supporting debug snapshots return log_discovery_error separately from Runtime readiness. Missing log directories are normal during startup; a discovery failure reports its operation/system error while Runtime probing continues. Preserve this field with the owned client's PID and log_path when collecting feedback. Matched newer native helpers resolve WinUI only for GUI message preprocessing, so native headless startup does not require WinUI loading. Keep the complete installation; managed .NET and visual-client OS requirements still apply.

Native Control is the private loopback TCP channel between Headless and its owned native child, not another MCP server for the AI. Matched newer managed/native packages default --native-service-port and --publish-service-port to 0. Omit these flags for automatic OS allocation; the child binds and retains its port, reports headless_native_ready on its redirected stdout, and the parent validates PID, service, actual port and project health before operations. A positive explicit port is strict: occupation fails without connecting to the existing service or silently changing ports. Do not probe and release a port yourself. Discovery failure/cancellation must use owned-child cleanup. Read resolved control ports from startup logs; they are independent of Editor MCP and client Runtime MCP. Older complete packages default to fixed 18766/18767 and need a matching editor package update; an SDK helper update alone does not add native port-zero support.

Use the Headless Editor guide for installation, device login, project/player setup, single-client connected-server debug, publishing and historical recovery. Connect to the installed SCE.HeadlessEditor using HTTP/WebSocket, then inspect headless_get_capabilities, headless_get_project_state and tools/list. Resolve degraded dependencies before starting a run. The GUI launch, embedded-game cleanup and modal-window instructions below apply to the full Editor.

  • Reuse the authenticated host for repeated build/debug/query runs and project switches. After each debug run, use debug_stop and verify owned client/helper/lease cleanup; retain the managed host only for upcoming work when no editor binary update is pending. Supporting versions with --login validate the installation's saved account before requesting device authorization. --account-root selects the installation account root independently of --editor-root; the exact editor server name selects its account file, without merging environments or migrating e.* aliases. Do not copy credentials from another process or request another scan on transport/server errors.
  • Close unused test hosts and close hosts before updating editor binaries: running SCE.exe / SCE.HeadlessEditor.exe may lock the installed files. Save and finish/cancel active work, stop owned debug operations, then close the GUI normally and use the helper's -Action stop for helper-managed hosts or the original terminal/cooperative shutdown for directly started hosts. Verify actual process exit by installation path/PID/start time and endpoint closure, including owned helpers/clients; an unavailable MCP endpoint alone is insufficient. Do not force-stop unrelated or unsaved user work. Follow host shutdown and updates; account-cache reuse does not require keeping the process alive.
  • Track the original operation IDs through status/wait. Confirm the Host session, client and verified Runtime target belong to the same launch. A successful build or started client is insufficient. Hosts exposing debug_create_game/debug_attach_client/debug_clients/debug_client_stop support a reusable match with dynamic clients from frozen opened human slots. Attach launches a new process; its runtime_mcp_port is a startup override, not a live change through Runtime MCP. Check each returned client identity/endpoint separately and use debug_stop to finish the whole match; a client exit alone leaves it reusable.
  • Configure custom game test identities in config.ini [debug_setting].user_ids before creating a match. IDs follow opened human-slot order, must be unique within the match and in 1..2147483647; they do not change the authenticated account or --user-id. Normal save/generate and dual-end builds preserve an authored table; only an absent table gets the default 100 onward. Test retention through debug_create_game with no_compile=false, rather than patching after compile or skipping it. debug_attach_client.user_id is optional and asserts the frozen ID; a mismatch returns roster_user_id_mismatch before launch. Stop/recreate to change IDs. Verify Host-confirmed IDs, Runtime identity and unchanged account status.
  • Project orientation is config.ini [map].is_landscape: 0 portrait, 1 landscape, absent defaults to landscape. On an idle Headless-owned project, edit only this key in the existing section, preserve other settings/encoding, read it back, then use normal save/generate/build. There is no dedicated orientation MCP tool; use the GUI setting when the GUI owns the project. The player/team projection preserves [map], and publishing reads this key. Do not confuse it with the desktop debug viewport: the current native bridge requests 1280x720 by default. Check actual Runtime dimensions/screenshots instead of promising that editing orientation automatically swaps the window dimensions. See orientation configuration.
  • For concurrent projects, use distinct local hosts/Editor MCP ports and a separate Runtime port for each independent client. Route explicit Runtime calls with the actual port, runtime_instance_id and target_id from that client's current connection; re-read identity after restart. Official master 7270 passed two projects with three clients, full builds preserving custom IDs, and stop/recreate of one project without disrupting the other. Follow the combined example; this result does not establish GUI multiplayer or every deployment. Finish each original debug operation at stopped with cleanup_verified=true and cleanup_pending=false, then close unused owned hosts and check process/port exit.
  • For an explicit account change on a supporting host, inspect headless_account_status, then call headless_account_switch_start without credentials or endpoint arguments and track its operation_id with switch_status/cancel. Show its complete public verification link and expiry once (prefer verification_uri_complete; for TapTap with only a base URL + user_code, build .../device?qrcode=1&user_code=<encoded current code>), not the bare device page; pending starts reuse the same request. Require succeeded and the expected user_id. Busy builds/debug/preparation or unresolved publish resources must finish before switching. Failed or pre-commit cancelled authorization retains the current host account. An expired device request is retried only when the user explicitly requests it; do not reacquire automatically.
  • PC author-test quick launch (pd test channel, updated lib_lobby): start the platform with -wx_lobby=app_box -tag=test -author_test_project=<project id> -author_test_mode=<mode id>. It is cold start, single instance, SINGLE modes only, and the project id must equal the real map name; an already running platform never picks up new arguments and the flags cannot bypass test eligibility. Verify the AUTHOR_TEST_LAUNCH receipt (result=success, actual_session_id, requested project equals actual map) together with the actual mode/match_info and scene load; mode_confirmed is always false. The formal channel still runs the older package. See PC single-player author-test quick launch.
  • Publishing must target the installation's own deployment. A pd/online installation normalizes the legacy production manifest value to pd; there is no separate production publish backend. An explicit --publish-environment wins; otherwise the host resolves its installation root from --install-root or the executable location and reads the manifest env, so a resolvable raw/direct host needs no extra flag and only an unresolved manifest returns publish_environment_required until restarted with --publish-environment pd. A requested environment that differs from the host's already configured one returns publish_environment_mismatch before any upload; it does not re-check the manifest.
  • An uncertain publish stays on its original operation. Do not resubmit or delete its registry. Success requires correlated final_result with finish=true/result=true, exact publish_identity, persisted state=succeeded/publish_completed=true and owned cleanup.
  • An unknown result that observed an accepted upload keeps upload_accepted=true, and it never reverts to false. That confirms only that the upload may have been accepted; it is not success and must not clear the unknown state or allow a new publish. A missing field or false does not prove that nothing was uploaded.
  • project_publish_reconcile reads an explicitly configured external recovery store using the new host's own login. Require exact project/request/environment as well as operation ID. backend_verified=true with source_store_readonly=true/persistence_applied=false verifies the backend without changing the old record.
  • project_publish_finalize requires all four original fields: operation_id, project_id, request_id, environment. It verifies the backend and writes only its host's own store. Require persistence_applied=true, finalization_completed=true and cleanup_pending=false, then verify status. A legacy store handoff first requires authorized normal owner shutdown and confirmed resource release; an available writer lock alone cannot prove an older owner exited.

See the guide for historical publish recovery, idempotent same-store finalization and error handling. Recovery queries and finalization operate on the original request without uploading again.

Launching Full Editor MCP On A Map ​

When the task needs GUI behavior and no suitable editor session is open, start the full SCE Editor with the target map/project directory and an MCP port:

powershell
$EditorExe = "<path-to-your-Spark-Editor-launcher>"
$ProjectRoot = "C:\Users\<user>\Documents\SCE Projects\my_game"
Start-Process -FilePath $EditorExe -ArgumentList @(
  "--mcp-port=8765",
  "-map_path=`"$ProjectRoot`""
)

Use the launcher installed with your Spark Editor distribution. The full editor uses -map_path=<ProjectRoot> to load a map on startup. Do not use -1map_path; that form is only a local shortcut convention some users may use to disable an argument temporarily, not an automation contract.

PowerShell's Start-Process -ArgumentList accepts separate argument values, but a key=value argument still needs quotes around a value containing spaces. Keep the executable path and each argument as separate array elements, and quote the path inside the argument itself as shown above. This is especially important for the common C:\Users\<user>\Documents\SCE Projects\... location. After launch, inspect the actual process command line if project loading is unexpected; -map_path="C:\Users\<user>\Documents\SCE Projects\my_game" must remain one argument.

Do not confuse this with TriggerMcpHost, whose standalone command line uses --map, or with pure client runtime debugging, whose spawned client process also contains -map_path=<ProjectRoot> but is not the full editor.

Choosing The Start Tool ​

Use build_start for generation and compilation without a game session. Follow its build_id with build_wait or build_status; use build_diagnostics for errors and build_cancel to stop it. A returned ID is acceptance, not build success. On build_start_timeout, query the returned operation_id through build_status before issuing another start. This workflow is suitable for dependency libraries; validate runtime behavior in a consuming game project. Direct library debug fails at preflight with dependency_library_requires_test_project. A configured test-map menu route remains supported. An old library launch error mentioning sight.bat does not establish a data or compile failure.

Confirm the current project through GUI editor_get_context or Headless headless_get_project_state before starting. A pure build is complete when its own build reaches terminal succeeded; do not wait for a Runtime target or call debug_start merely to make data generation happen. Detailed steps: generation and compile-only workflow.

Use debug_start for the normal build-and-launch loop. It returns a queued operation, not a successful build. Pass its operation_id to debug_wait or debug_status; the associated build contains the build ID, phase, diagnostics and log paths. Use build_diagnostics with that build_id to page through errors and warnings (offset, limit, up to 500). Build tools require a supporting GUI or Headless version with ready project dependencies.

Use debug_start_no_compile after the agent or user has already built both configurations and deployed the latest DLLs into the runtime AppBundle, when an externally controlled build is needed.

Use debug_start_with_resource_statistics when a debug session needs fresh resource statistics or Humanoid animation bake output before launch. Typical symptoms: a skill animation such as dash.ani is requested, official Humanoid source animation exists, but the project has no corresponding baked file under res/anim/<ModelName>/....

Use resource_statistics_generate_animations when you only need to refresh ref/model_animation.json and baked Humanoid animations, without launching the game.

On Headless, follow its operation_id with resource_statistics_wait (at most 20000 ms per call) or resource_statistics_status; require state=succeeded and cleanup_pending=false. The operation merges project and loaded dependency animation manifests, generates model collision from project configuration, and uses an owned native helper without login or a game session. Keep the matching complete editor package and installed source resources. Cancel through resource_statistics_cancel; pending cleanup retains project ownership, so retry cleanup on the same operation before starting another one. Failed generation or pre-commit cancellation preserves previously committed animations/collision. skip_compile=true still runs the Client-Resource build and generation; it only skips subsequent Client/Server script compilation.

Dependency libraries support save, generate, build and resource statistics with a matching SDK resource analyzer. Newly created libraries use the project ID for their csproj and assembly/DLL name; ordinary game projects keep GameEntry. Test a library in a consuming game. GUI library store publishing also needs metadata and review through its library publishing entry; Headless game project_publish_* tools are not that store workflow.

Do not spam either start tool. Wait for the editor/debug client to settle, then use runtime_call_tool to verify the runtime is reachable.

For a single connected-server client in its own window, the GUI offers 调试/独立客户端调试 and 调试/独立客户端调试(不编译) without changing editor startup arguments. The latter reuses existing script binaries but still saves and prepares the map. Neither menu starts multiplayer or selects client-only debugging; compile first after script changes. A library needs a configured test map or a consuming game project.

Use debug_stop after the run. GUI-internal debugging destroys the embedded game through its host. Closing a standalone debug client notifies its exact Host session; closing one embedded multiplayer window keeps the shared session until the last window closes. Headless stops its owned standalone client and releases debug resources; check cleanup before the next run. Retain the managed host only for upcoming iterations when no editor binary update is pending; otherwise close it through the shutdown flow above.

json
{ "tool": "debug_stop", "arguments": {} }

Build Failure Diagnosis ​

  1. Follow the original build_start or debug_start operation through its status/wait tools. A queued request is not a successful build or game startup; do not start a debug run just to diagnose a compile-only build.
  2. Read the build result, or the debug operation's associated build. Query build_diagnostics with its build_id and paginate with offset / limit; keep the configuration, error code and source file/line/column together. If needed, use build_status to obtain log_paths and result_path for the same task.
  3. Read the configuration log if the diagnostic has no source location or more output is needed. A failure in generating_code precedes compiler execution and may have no per-configuration log; inspect the diagnostic and result_path instead. Do not assume a missing parsed compiler error means success.
  4. Fix the owning source, data or trigger definition. For generated code, use the appropriate editor/MCP workflow rather than editing generated files. Retry the selected build/debug tool after the prior operation reaches a terminal state.
  5. For a debug run, confirm the operation succeeds and the Runtime target is ready. A successful build only confirms compilation and deployment.

To help a user find the same errors, direct them to 信息列表 > 编辑器 > 编译. A failed or timed-out build opens the window, selects the editor tab and the compile source filter, clears search text and enables all severity levels. This selects the tab/filter, not a particular error row. A focus request made before the panel initializes is applied when it loads.

  • A source path with line/column, such as Player.cs(12,3), is clickable on the right of a located diagnostic row. It opens the file using the system's registered application; it does not automatically jump to the IDE line. Some diagnostics have no source location.
  • A full log path is clickable on the right of a build phase/status message. Before compiler logs exist, that message may link to result.json.

Logs ​

Build output is saved as UTF-8 under <EditorRoot>/logs/build/<project-path-hash>/<timestamp>-<build-id>/, with per-configuration logs, request.json and result.json. Query build_status for exact paths instead of searching the project root or choosing files only by modification time. Diagnostics include configuration, severity, code, message and source file/line/column when available.

Build state is queued, running, succeeded, failed, cancelled or timed_out. Compilation remains asynchronous, but the progress window is modal and stays above its scene-editor owner. Other applications can cover it when the user switches away. Editor windows are temporarily disabled and restored when progress ends. MCP queries and build_cancel remain available; use them rather than asking a user to operate disabled editor controls. A successful build means the requested configurations compiled and deployed; verify actual game startup with the debug operation and Runtime MCP. build_wait returning a nonterminal state means its wait elapsed. After build_cancel, wait for the terminal state before rebuilding. Closing or switching projects through MCP during compilation requests cancellation and asks the caller to retry when it completes.

All required configurations finish before deployment begins. A compiler failure preserves prior deployed DLLs; deployment failures attempt rollback and report rollback errors. Builds use the actual MSBuild output path and retain bin/obj caches.

For editor-internal debug runs, inspect wasm logs under the editor runtime directory:

  • Server wasm logs: <EditorRoot>\logs\server
  • Client wasm logs: <EditorRoot>\logs\client
  • Lua/editor host logs are siblings under <EditorRoot>\logs, such as lua, game, and CSharp.

When looking for script-side smoke output, search the newest server/client wasm logs first. Use Game.FlushLogs() in smoke code before ending a run when immediate log inspection matters.

Humanoid Animation Bake Flow ​

For editor-debug animation failures, first distinguish source animation from baked animation:

  1. Check whether the original Humanoid animation exists, for example anim/.../dash.ani.
  2. Check whether the project contains the baked output under res/anim/<ModelName>/....
  3. If the source exists but the baked output is missing, call:
json
{ "tool": "debug_start_with_resource_statistics", "arguments": {} }

To skip the later GameEntry compile after the resource step:

json
{ "tool": "debug_start_with_resource_statistics", "arguments": { "skip_compile": true } }

If no launch is needed:

json
{ "tool": "resource_statistics_generate_animations", "arguments": {} }

After completion, inspect ref/model_animation.json and ref/model_animation_data.json, project res/anim/<ModelName>/..., and the operation's returned build/log paths (or the GUI information panel). Model collision configuration produces ref/model_collision.json and block/model_collision/*.bin with companion .roles.dat files. When a game is running, use its current client log to distinguish a missing source animation from a missing baked animation. Resource generation alone does not start a client or upload the project.

No-Compile Build And Deploy ​

From the map project root:

powershell
$ProjectRoot = "D:\Maps\MyMap"
dotnet build (Join-Path $ProjectRoot "src\GameEntry.csproj") -c Server-Debug
dotnet build (Join-Path $ProjectRoot "src\GameEntry.csproj") -c Client-Debug
Copy-Item -LiteralPath (Join-Path $ProjectRoot "src\bin\Server-Debug\net9.0\GameEntry.dll") -Destination (Join-Path $ProjectRoot "AppBundle\managed\GameEntry.dll") -Force
Copy-Item -LiteralPath (Join-Path $ProjectRoot "src\bin\Client-Debug\net9.0\GameEntry.dll") -Destination (Join-Path $ProjectRoot "ui\AppBundle\managed\GameEntry.dll") -Force

Then call:

json
{ "tool": "debug_start_no_compile", "arguments": {} }

If the copy step is skipped, the launched client/server can run an old GameEntry.dll even though dotnet build just succeeded.

For the same open project, replacing GameEntry.dll does not require restarting the editor. Build and copy the DLLs, then call debug_start_no_compile again; the next debug run loads the current files.

For project-owned extra managed assemblies, add them as normal GameEntry.csproj dependencies (ProjectReference, PackageReference, or a deliberate Reference) and let the editor build/debug/publish flow deploy them. Use project-unique assembly names that do not match SDK-provided assemblies or dependency library IDs. If a user says their custom DLL is present under managed but does not take effect, first ask them to check whether the DLL file name matches an SDK-provided assembly or a dependency library ID. If a DLL name matches a dependency library ID, it is supplied by that dependency library, not by the main project AppBundle. If you intentionally use debug_start_no_compile, build the matching Client/Server configurations first, then copy only the project's own output assemblies alongside GameEntry.dll in the corresponding managed directory. See docs/guides/CustomManagedAssemblies.md.

Game projects only manage their own assemblies. SDK and runtime components are provided by the installed editor/SDK version.

For an SCE dependency-library project, check both libs.json and the consumer csproj reference. Local development additionally maps the declared library ID to its project root in local_libs.json; Headless managed data/trigger loading needs the matching explicit --lib context. The csproj path is compile-only: runtime resolves the library ID and loads the library package's matching assembly. Rebuild/check the library's own Client/Server AppBundles, not just a copied DLL beside GameEntry. See Dependency libraries for examples and path resolution.

Runtime MCP Inspection ​

Record debug.ping.session_id before restarting a game and verify that it changes after the debug operation succeeds. Match the selected runtime_list_targets entry to the requested map/player; a successful build does not prove the currently inspected game uses that build. Native Host fixes also need Host deployment and a new server game.

Server Unit, AI And Order Evidence ​

Discover gameplay.snapshot with debug.list_tools. In a connected server-backed debug game, call it through runtime_call_tool:

json
{ "name": "gameplay.snapshot", "arguments": { "limit": 8 } }
{ "name": "gameplay.snapshot", "arguments": { "request_id": "<returned GUID>" } }

The first call sends one request and returns its ID. Poll that ID until completed, failed, or timed_out; polling does not resend. Keep the same Runtime target throughout. Optional scene_id selects a server scene (default: the main-unit scene, then the player's current scene), unit_id filters one unit, and offset/limit page results (maximum 16). Read report.next_offset for the next page; pages are separate observations, not an atomic whole-scene snapshot.

The report carries server session/time, scene space model, authoritative XYZ, final move/turn speeds, vitals, AI target/combat state, and current order/stage/identity/turn ownership. Compare successive captures to detect movement, persistent Preswing, or repeated order replacement. runtime_session_id identifies the client receiving the report; server_session_id identifies the authoritative game. Snapshot completion is diagnostic evidence, not proof that every action works.

Pure-client or production games return debug_server_required. scene_not_found includes a bounded available_scenes catalog and available_scene_count; select a listed server scene with scene_id; snapshot_too_large needs a smaller page. If the tool is absent, refresh the published runtime dependencies; use project event diagnostics only for evidence this snapshot does not expose.

When the debug session can contain multiple Gameplay instances, also read Runtime MCP Target Routing. It defines the deterministic target-selection order and the evidence required before attributing a Runtime result to a specific simulated client.

When embedded multiplayer debugging may create more than one simulated client, call runtime_list_targets before any target-specific Runtime tool:

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

    "timeout_ms": 5000
  }
}

Read the target list from parsed.result.targets. A target includes a process-local target_id such as gameplay:42, plus slot_id, player_id, map_name, and is_foreground. If there is one target, target_id remains optional for backward compatibility. If there are multiple targets, pass the selected ID to both runtime_list_tools and runtime_call_tool; never silently use the first target. Prefer the unique foreground target when it clearly matches the requested simulated client. If the user did not identify a client and there is no unique match by slot_id, player_id, or foreground state, ask which client to inspect.

json
{
  "tool": "runtime_call_tool",
  "arguments": {
    "target_id": "gameplay:42",
    "name": "debug.ping",
    "arguments": {},

    "timeout_ms": 5000
  }
}

Target IDs are valid only for the current Runtime bridge process. Re-run discovery after a Gameplay ends or is recreated. Phase-one target routing selects the correct Wasmtime state, but input.* still uses process-level input and foreground focus; do not claim independent simultaneous input injection into multiple embedded viewports. Independent SCE processes receive separate automatic ports and instance IDs; discover the intended editor before selecting its Runtime and Gameplay target.

After debug_start or debug_start_no_compile, call runtime tools through the outer MCP bridge:

json
{
  "tool": "runtime_call_tool",
  "arguments": {
    "name": "debug.ping",
    "arguments": {},

    "timeout_ms": 5000
  }
}

Useful runtime tool names commonly include:

  • debug.ping: verify the client Runtime MCP bridge is reachable.
  • debug.list_tools: list runtime tools registered inside the client.
  • debug.capture_screenshot: save a live client screenshot.
  • scenegraph.materials: list current client StaticMeshComponent materials from session-owned and replicated SceneGraphs. Use this when RenderDoc or screenshots suggest a material, shader, pass, cull, fill, stencil, or render-order problem.
  • material.probe: return the first matching StaticMeshComponent material snapshot by query, nodeId, and optional materialIndex. Pass passNames such as ["base","alpha","depth","singlelayerwater"] to compare the runtime technique with the active RenderPath.
  • ui.snapshot: read the current GameUI control tree.
  • ui.find: locate controls by text, name, type, or id.
  • ui.get_rect: get one control's screen pixel rectangle.
  • ui.click: queue an engine-path pointer click by screen coordinates, control id, or Label linkId.
  • input.pointer: queue stateful pointer move, down, up, or wheel actions for hover and drag workflows.
  • input.key: queue key down, up, or atomic press actions; press supports ctrl, shift, alt, and meta modifiers.
  • input.text: deliver Unicode text to the currently focused engine UI element.
  • input.reset: release keys and pointer buttons held by Runtime MCP after an interrupted sequence.

ui.snapshot and ui.find include rich_text_spans for Label controls. Use the rendered link_id and rects_px values instead of guessing rich-text hit coordinates.

Always prefer debug.list_tools as the source of truth for the current client. Project-specific tools may appear there, and unavailable tools should not be guessed.

When using the outer Editor MCP bridge, parse tools/call.content[0].text as JSON first. runtime_list_tools and runtime_call_tool with name: "debug.list_tools" return the runtime payload under result, so read parsed.result.tools / parsed.result.count, not top-level parsed.tools:

js
const parsed = JSON.parse(contentText);
const runtime = parsed.result ?? parsed;
const tools = runtime.tools ?? [];

The parsed.result ?? parsed pattern also works with the direct TCP fallback, which returns the runtime payload directly. Treat the top-level success from the Editor MCP bridge as bridge status; the inner runtime tool result is the object under result.

Prefer ui.click for ordinary controls. Use input.pointer only when the behavior depends on hover, a held button, dragging, or wheel input. Use input.key for shortcuts/navigation and input.text for typed text. Wrap multi-call held-input sequences so input.reset is attempted when an intermediate call fails.

Stateful Input Automation ​

All pointer coordinates use the same game-UI pixel coordinate space as ui.snapshot, ui.find, and ui.get_rect. This contract is the same in embedded editor debugging and pure-client debugging. Use returned rect_px / rich_text_spans[].rects_px values rather than desktop-window coordinates or screenshot coordinates after image resizing.

input.pointer actions:

ActionRequired argumentsOptional argumentsBehavior
movex, y-Moves the engine pointer and sends the normal move/hover path.
down-x + y, buttonOptionally moves first, then holds the selected button.
up-x + y, buttonOptionally moves first, then releases a button held by Runtime MCP.
wheelnon-zero deltax + yOptionally moves first, then sends the wheel event.

Buttons are 1 left, 2 middle, and 4 right; omitted buttons default to left. A drag must preserve call order:

json
{ "name": "input.pointer", "arguments": { "action": "move", "x": 300, "y": 240 } }
{ "name": "input.pointer", "arguments": { "action": "down", "button": 1 } }
{ "name": "input.pointer", "arguments": { "action": "move", "x": 520, "y": 360 } }
{ "name": "input.pointer", "arguments": { "action": "up", "button": 1 } }

input.key accepts down, up, or atomic press. Use key names returned by the tool schema, such as A, Enter, Escape, LeftCtrl, F5, or ArrowUp. Only press accepts temporary modifiers: ctrl, shift, alt, and meta.

json
{ "name": "input.key", "arguments": { "action": "down", "key": "ArrowUp" } }
{ "name": "input.key", "arguments": { "action": "up", "key": "ArrowUp" } }
{ "name": "input.key", "arguments": { "action": "press", "key": "S", "modifiers": ["ctrl"] } }

input.text sends one Unicode E_TEXTINPUT payload to the currently focused engine UI element. Focus the input first with ui.click; use input.key for navigation, Enter/Escape, and shortcuts. An Input using the normal lost-focus update mode may not expose the new committed Text in ui.snapshot until focus moves away; while it remains focused, verify OnInputTextChanged or the corresponding business event instead.

json
{ "name": "input.text", "arguments": { "text": "Runtime MCP text" } }

Always attempt input.reset after an interrupted held-input sequence. It releases only keys and pointer buttons acquired by Runtime MCP and relinquishes its synthetic pointer position; it does not release unrelated physical user input.

json
{ "name": "input.reset", "arguments": {} }

queued=true proves only that the action entered the client main-thread queue. Confirm the resulting control state, event log, or screenshot before claiming the interaction succeeded.

Material inspection examples:

json
{
  "tool": "runtime_call_tool",
  "arguments": {
    "name": "material.probe",
    "arguments": {
      "query": "BlackHole",
      "passNames": ["base", "alpha", "depth", "singlelayerwater"]
    },

    "timeout_ms": 5000
  }
}
json
{
  "tool": "runtime_call_tool",
  "arguments": {
    "name": "scenegraph.materials",
    "arguments": {
      "query": "Unlit",
      "includeSession": true,
      "includeReplicated": true,
      "maxResults": 20,
        "passNames": ["base", "alpha", "depth", "singlelayerwater"]
    },

    "timeout_ms": 5000
  }
}

If the outer editor MCP does not expose runtime_call_tool, use the project or SDK ai/tools/Invoke-SceRuntimeMcp.ps1 fallback:

powershell
.\ai\tools\Invoke-SceRuntimeMcp.ps1 -Ping -Wait
.\ai\tools\Invoke-SceRuntimeMcp.ps1 -ListTargets
.\ai\tools\Invoke-SceRuntimeMcp.ps1 -ListTools
.\ai\tools\Invoke-SceRuntimeMcp.ps1 -ListTools -TargetId gameplay:42
.\ai\tools\Invoke-SceRuntimeMcp.ps1 -Tool debug.capture_screenshot -ArgumentsJson '{"path":"RuntimeMcpScreenshots/ui.png","overwrite":true,"maxWidth":1280,"maxHeight":720}' -Wait
.\ai\tools\Invoke-SceRuntimeMcp.ps1 -Tool ui.snapshot -TargetId gameplay:42
.\ai\tools\Invoke-SceRuntimeMcp.ps1 -Tool material.probe -ArgumentsJson '{"query":"BlackHole","passNames":["base","alpha","depth","singlelayerwater"]}'

The project script is a thin shim over the versioned client in WasiCoreSDK/tools/runtime-mcp. -Wait waits for bridge readiness and, for pending/path results such as screenshots, waits for the fresh file instead of requiring handwritten polling. Local fallback failures include a stable stage field.

If the AI environment has HTTP access to the full editor MCP port but does not surface MCP tools as native actions, prefer the project ai/tools/Invoke-SceEditorMcp.ps1 helper before falling back to direct Runtime TCP. It performs MCP initialization, lists schemas, refuses TriggerMcpHost by default, and still uses the official outer editor bridge:

powershell
.\ai\tools\Invoke-SceEditorMcp.ps1 -Context -Pretty
.\ai\tools\Invoke-SceEditorMcp.ps1 -ListTools -ToolPrefix runtime_ -Pretty
.\ai\tools\Invoke-SceEditorMcp.ps1 -Tool runtime_call_tool -ArgumentsJson '{"name":"ui.snapshot","arguments":{"maxDepth":4,"maxNodes":200},"timeout_ms":10000}' -Pretty
.\ai\tools\Invoke-SceEditorMcp.ps1 -Smoke RuntimeMcp -RuntimeDebugStartMode NoCompile -Pretty

The RuntimeMcp smoke starts editor debugging according to -RuntimeDebugStartMode, waits for the returned operation through debug_wait, then polls through short-lived startup states such as runtime_state_unavailable, client_runtime_timeout, or client_runtime_unavailable before calling Runtime tools. Prefer this smoke when validating the full editor-to-client Runtime MCP path; do not hand-roll a sequence that calls runtime_list_tools immediately after debug_start_no_compile.

Use Invoke-SceRuntimeMcp.ps1 only when the full editor MCP bridge itself is unavailable or you are specifically diagnosing the direct Runtime TCP path.

  1. Modify code or data.
  2. Build and deploy DLLs yourself if using debug_start_no_compile; call debug_start_with_resource_statistics if resource statistics or Humanoid animation bake must run before launch; otherwise call debug_start.
  3. Wait for the returned operation with debug_wait, then allow a short retry window for the client Runtime MCP state to become available.
  4. If embedded multiplayer debugging may be active, call runtime_list_targets and select one target explicitly.
  5. Call runtime_call_tool with debug.ping, carrying the selected target_id when applicable.
  6. Call debug.list_tools, then the specific runtime inspection tool, using the same target.
  7. Use screenshots, UI snapshots, logs, and runtime tool results as evidence before making the next change.
  8. Call debug_stop to close the embedded editor debug game when the automated run is finished.

Error Handling ​

If editor_close reports another_message_dialog_active, call editor_dialog_snapshot and inspect the message and available buttons. For an understood informational error or a decision already authorized by the task, call editor_dialog_choose with the returned dialog_id and choice (confirm, cancel, or close), then retry the original operation. A stale ID returns dialog_changed; read another snapshot before choosing. Hidden or disabled buttons return dialog_button_unavailable. Do not blindly confirm a dialog. These tools cover the shared editor message component, not operating-system dialogs or progress windows; discover them in tools/list before use.

After switching projects within an editor process, debug.ping.map_path can still contain its original launch argument. Use editor_get_context for the current authoring project and runtime_list_targets.map_name for the actual game, together with the runtime session ID.

For all MCP calls, check outer result.isError first.

debug_start and debug_start_no_compile usually return plain text. runtime_call_tool forwards the runtime response; if the inner runtime tool returns { "success": false, ... }, the outer call marks isError and the content[0].text should be parsed as JSON.

Common failures:

  • Editor helper not available: the full editor MCP is not attached to an open editor project.
  • client_runtime_unavailable: no debug client is listening on the selected runtime bridge port, or debug startup has not finished.
  • runtime_target_ambiguous: multiple Gameplay targets are active; call runtime_list_targets and retry with target_id.
  • runtime_target_not_found: the selected Gameplay ended or was recreated; discover targets again instead of reusing the stale ID.
  • runtime_target_routing_unavailable: the Client native bridge is too old for target discovery/routing; update the Client build. A single-target call without target_id remains the compatibility fallback when only one Gameplay is running.
  • debug_stop only requests shutdown through the editor host. It does not forcibly kill external processes. The runtime bridge may still answer debug.ping after stop; treat debug:false with empty game / map_path as an inactive debug state rather than proof that the embedded game is still running.
  • Stale DLL behavior after debug_start_no_compile: rebuild and copy both Server-Debug and Client-Debug GameEntry.dll files into AppBundle.
  • Humanoid baked animation still missing after debug_start_with_resource_statistics: inspect ref/model_animation.json; if the requested animation is not listed, the model data or skill animation reference did not enter resource statistics.