Editor/Mcp/BridgeDebugTools.cs

Editor-side bridge debug tool wrappers for MCP (auto-generated). Exposes async methods to run console commands, draw debug primitives (box, line, ray, sphere), clear debug draws, frame the editor camera, get bridge status and profiler stats, restart the editor, and set game time scale by forwarding calls to McpGate.Run.

Networking
// AUTO-GENERATED by scripts/emit-mcp-wrappers.mjs — DO NOT EDIT.
// Regenerate: node scripts/extract-manifest.mjs && node scripts/emit-mcp-wrappers.mjs
// Source of truth: sbox-mcp-server/src/tools/ (zod schemas) → scripts/tools-manifest.json

using System.Text.Json.Nodes;
using System.Threading.Tasks;
using Editor.Mcp;

/// <summary>
/// Bridge health, editor restart, console commands, profiler stats, time scale, debug draw
/// primitives (lines, boxes, spheres), and editor camera framing.
/// </summary>
[McpToolset( "bridge_debug", "Bridge health, editor restart, console commands, profiler stats, time scale, debug draw primitives (lines, boxes, spheres), and editor camera framing." )]
public static class BridgeDebugTools
{
	/// <summary>
	/// Run an s&amp;box console command / ConCmd via Sandbox.ConsoleSystem.Run — e.g. a cvar
	/// ('sv_cheats 1') or a registered command. Also the invocation primitive behind execute_csharp.
	/// Fire-and-forget: returns only { ran, command } and does NOT capture console output — follow with
	/// read_log to see what the command printed.
	/// </summary>
	/// <param name="command">The console command line to run.</param>
	[McpTool( "console_run" )]
	public static Task<object> ConsoleRun( string command )
		=> McpGate.Run( "console_run", McpGate.Args( ( "command", command ) ) );

	/// <summary>
	/// Remove ALL debug-draw primitives at once by destroying the debug holder for the current scene
	/// (edit or play) — there is no way to remove a single shape. Call before redrawing a fresh frame
	/// of debug shapes. Returns { cleared, removed } where removed is how many primitives were
	/// destroyed.
	/// </summary>
	[McpTool( "debug_clear" )]
	public static Task<object> DebugClear()
		=> McpGate.Run( "debug_clear", McpGate.Args() );

	/// <summary>
	/// Draw a wireframe debug box centered at a point. Renders in editor and play. Ideal for
	/// visualizing a trigger_zone's bounds or a physics_overlap box volume. Accumulates until
	/// debug_clear. Returns { drawn: 'box', count, mode } — count is the total accumulated primitives,
	/// mode is 'edit' or 'play' (edit-mode gizmos are NOT in take_screenshot; use capture_view in play
	/// mode).
	/// </summary>
	/// <param name="center">Box center, world-space "x,y,z".</param>
	/// <param name="size">Full size "x,y,z" in units (default "32,32,32").</param>
	/// <param name="color">Color as "r,g,b" or "r,g,b,a" floats 0–1 (default varies per shape).</param>
	/// <param name="thickness">Line thickness for edit-mode gizmos (default 2).</param>
	[McpTool( "debug_draw_box" )]
	public static Task<object> DebugDrawBox( string center, string size = null, string color = null, double? thickness = null )
		=> McpGate.Run( "debug_draw_box", McpGate.Args( ( "center", center ), ( "size", size ), ( "color", color ), ( "thickness", thickness ) ) );

	/// <summary>
	/// Draw a debug line between two world points. Renders in the editor viewport (Gizmo) AND in play
	/// mode (DebugOverlay), accumulating until debug_clear. NB: edit-mode gizmos show in the live
	/// editor but NOT in take_screenshot/screenshot_from — use capture_view in play mode to see debug
	/// draws through the bridge.
	/// </summary>
	/// <param name="from">Start point, world-space "x,y,z".</param>
	/// <param name="to">End point, world-space "x,y,z".</param>
	/// <param name="color">Color as "r,g,b" or "r,g,b,a" floats 0–1 (default varies per shape).</param>
	/// <param name="thickness">Line thickness for edit-mode gizmos (default 2).</param>
	[McpTool( "debug_draw_line" )]
	public static Task<object> DebugDrawLine( string from, string to, string color = null, double? thickness = null )
		=> McpGate.Run( "debug_draw_line", McpGate.Args( ( "from", from ), ( "to", to ), ( "color", color ), ( "thickness", thickness ) ) );

	/// <summary>
	/// Draw a debug ray (drawn as an arrow) from an origin along a direction for a given length.
	/// Renders in editor and play. Ideal for visualizing a raycast result or a facing/normal direction.
	/// Accumulates until debug_clear. Returns { drawn: 'ray', count, mode } — count is the total
	/// accumulated primitives, mode is 'edit' or 'play' (edit-mode gizmos are NOT in take_screenshot;
	/// use capture_view in play mode).
	/// </summary>
	/// <param name="origin">Ray origin, world-space "x,y,z".</param>
	/// <param name="direction">Direction vector "x,y,z" (normalized internally).</param>
	/// <param name="length">Ray length in units (default 64).</param>
	/// <param name="color">Color as "r,g,b" or "r,g,b,a" floats 0–1 (default varies per shape).</param>
	/// <param name="thickness">Line thickness for edit-mode gizmos (default 2).</param>
	[McpTool( "debug_draw_ray" )]
	public static Task<object> DebugDrawRay( string origin, string direction, double? length = null, string color = null, double? thickness = null )
		=> McpGate.Run( "debug_draw_ray", McpGate.Args( ( "origin", origin ), ( "direction", direction ), ( "length", length ), ( "color", color ), ( "thickness", thickness ) ) );

	/// <summary>
	/// Draw a wireframe debug sphere at a point. Renders in editor and play. Ideal for visualizing a
	/// physics_overlap radius or an NPC's hearing/sight range. Accumulates until debug_clear. Returns {
	/// drawn: 'sphere', count, mode } — count is the total accumulated primitives, mode is 'edit' or
	/// 'play' (edit-mode gizmos are NOT in take_screenshot; use capture_view in play mode).
	/// </summary>
	/// <param name="center">Sphere center, world-space "x,y,z".</param>
	/// <param name="radius">Radius in units (default 32).</param>
	/// <param name="color">Color as "r,g,b" or "r,g,b,a" floats 0–1 (default varies per shape).</param>
	/// <param name="thickness">Line thickness for edit-mode gizmos (default 2).</param>
	[McpTool( "debug_draw_sphere" )]
	public static Task<object> DebugDrawSphere( string center, double? radius = null, string color = null, double? thickness = null )
		=> McpGate.Run( "debug_draw_sphere", McpGate.Args( ( "center", center ), ( "radius", radius ), ( "color", color ), ( "thickness", thickness ) ) );

	/// <summary>
	/// Aim the s&amp;box EDITOR viewport camera at a GameObject (by id) or a world point (position +
	/// optional radius), then call take_screenshot to capture that view. This is how Claude points its
	/// own screenshots at what it's working on — frame a spawned object, then screenshot to verify it
	/// actually looks right.
	/// </summary>
	/// <param name="id">GUID of a GameObject to frame on.</param>
	/// <param name="position">World point to frame on (use instead of id) — object {x,y,z} or comma string "x,y,z". As "x,y,z" (or JSON {x,y,z}).</param>
	/// <param name="radius">Frame radius around the position, in units (default 128).</param>
	[McpTool( "frame_camera" )]
	public static Task<object> FrameCamera( string id = null, string position = null, double? radius = null )
		=> McpGate.Run( "frame_camera", McpGate.Args( ( "id", id ), ( "position", position ), ( "radius", radius ) ) );

	/// <summary>
	/// Check the s&amp;box Bridge connection — call this FIRST in a session. Returns a human summary
	/// plus JSON: connected, roundTripOk (heartbeat can be fresh while the editor's request loop is
	/// stalled — trust roundTripOk), bridgeVersion vs mcpServerVersion + versionsAligned (a mismatch
	/// means restart Claude Code / republish the addon), handlerCount, heartbeatAgeMs, latencyMs, and
	/// ipcDir (transport is file IPC; host/port are legacy fields).
	/// </summary>
	[McpTool.ReadOnly( "get_bridge_status" )]
	public static Task<object> GetBridgeStatus()
		=> McpGate.Run( "get_bridge_status", McpGate.Args() );

	/// <summary>
	/// Read live engine performance counters (ported from Unity's get_profiler_stats): FPS, frame ms,
	/// GPU ms, bytes allocated, process memory, exception count, and per-category timings
	/// (update/physics/ui/render/network/gcPause) averaged over `frames`. Reads
	/// Sandbox.Diagnostics.PerformanceStats. Most meaningful during play (call start_play first) but
	/// populated in the editor too. Read-only.
	/// </summary>
	/// <param name="frames">Averaging window (frames) for the per-category timings. Default 60.</param>
	[McpTool.ReadOnly( "get_profiler_stats" )]
	public static Task<object> GetProfilerStats( int? frames = null )
		=> McpGate.Run( "get_profiler_stats", McpGate.Args( ( "frames", frames ) ) );

	/// <summary>
	/// Restart the s&amp;box editor and wait for the bridge to reconnect — closes the C#-edit→recompile
	/// loop so addon/bridge changes apply without a manual restart. Relaunches straight back into the
	/// current project (EditorUtility.RestartEditor). Saves unsaved scenes by default (pass save:false
	/// to discard them). Blocks until the bridge is back (or waitMs elapses), then reports the handler
	/// count.
	/// </summary>
	/// <param name="save">Save unsaved scenes before restarting (default true; false discards them).</param>
	/// <param name="waitMs">Max ms to wait for reconnect (default 150000).</param>
	[McpTool( "restart_editor" )]
	public static Task<object> RestartEditor( bool? save = null, int? waitMs = null )
		=> McpGate.Run( "restart_editor", McpGate.Args( ( "save", save ), ( "waitMs", waitMs ) ) );

	/// <summary>
	/// Set the running game's time scale DURING PLAY MODE (ported from Unity's
	/// playtest_set_time_scale). 0 = pause, 1 = normal, 0.1 = slow-mo to watch a fast interaction
	/// frame-by-frame, 2+ = fast-forward idle/economy ticks. Requires start_play first — the edit scene
	/// doesn't tick, so this no-ops outside play mode and returns an error. Sets
	/// Game.ActiveScene.TimeScale (clamped to 0–100). Returns the applied and previous values.
	/// </summary>
	/// <param name="scale">Time multiplier: 0 = pause, 1 = normal speed, 0.1 = slow-mo, 2 = double speed. Clamped to 0–100.</param>
	[McpTool( "set_time_scale" )]
	public static Task<object> SetTimeScale( double scale )
		=> McpGate.Run( "set_time_scale", McpGate.Args( ( "scale", scale ) ) );
}