Editor toolset wrapper for visuals-related MCP (bridge_visuals) tools. It exposes static async methods (AddBeam, AddLight, SpawnVpcf, etc.) that forward arguments to McpGate.Run to invoke editor operations for lights, fog, skybox, probes, particles and related scene edits.
// 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>
/// Lighting, fog, post-processing, skyboxes, envmap probes, particles (.vpcf), and one-call
/// atmosphere/look presets.
/// </summary>
[McpToolset( "bridge_visuals", "Lighting, fog, post-processing, skyboxes, envmap probes, particles (.vpcf), and one-call atmosphere/look presets." )]
public static class BridgeVisualsTools
{
/// <summary>
/// EXPERIMENTAL — create an energy/laser beam (BeamEffect) from a position to a target point
/// (default: 128u straight up) — additive, tintable. Returns { created, gameObject } —
/// gameObject.id is the beam object's GUID. Like the other runtime particle tools, rendering
/// through the bridge is unverified; use spawn_vpcf when you need a guaranteed-visible effect.
/// </summary>
/// <param name="position">Beam start (world position of the beam object). As "x,y,z" (or JSON {x,y,z}).</param>
/// <param name="target">Beam end point in world space (default: 128u up). As "x,y,z" (or JSON {x,y,z}).</param>
/// <param name="width">Beam width/scale (default 4).</param>
/// <param name="color">Beam colour (default white). As "r,g,b[,a]" (0-1 floats).</param>
/// <param name="name">GameObject name.</param>
[McpTool( "add_beam" )]
public static Task<object> AddBeam( string position = null, string target = null, double? width = null, string color = null, string name = null )
=> McpGate.Run( "add_beam", McpGate.Args( ( "position", position ), ( "target", target ), ( "width", width ), ( "color", color ), ( "name", name ) ) );
/// <summary>
/// Generate a sun/sky driver component for the clock made by create_day_night_clock — a COMPANION,
/// not a replacement: the clock owns TimeOfDay, this renders it. Every frame it reads
/// TimeOfDay/SunriseHour/SunsetHour off the clock (sibling first, else first in scene, retrying
/// 1/s) and drives a DirectionalLight: sunrise→sunset maps to a 0-180° pitch arc (continuing below
/// the horizon at night), LightColor runs a hardcoded gradient (warm dawn → white noon → orange
/// dusk → dim blue night, HDR-scaled by Intensity since s&box lights have no Brightness field),
/// and optionally lerps the SkyBox2D Tint between night blue and day white. The clock CLASS NAME is
/// baked in as a typed reference — the call ERRORS if `clockClass` isn't found in the TypeLibrary
/// or any project .cs (run create_day_night_clock first; a bad token would break the whole
/// game-assembly compile). Returns {created, path, className, clockClass, sunYaw, intensity,
/// driveSkybox, placedOn, note, nextSteps[]}. Next: trigger_hotload → attach to the
/// DirectionalLight's GameObject → start_play, set_runtime_property TimeOfDay on the clock
/// (6/12/20/0) + capture_view to verify the looks. File/scene-mutating — refused during play mode.
/// </summary>
/// <param name="name">Class name for the generated component. Defaults to 'DayNightSun'.</param>
/// <param name="directory">Subdirectory for the generated .cs file. Defaults to 'Code'.</param>
/// <param name="clockClass">Class name of the day-night clock to bind to (the `name` you gave create_day_night_clock). Defaults to 'DayNightClock'. Must already exist as a compiled type or a project .cs file.</param>
/// <param name="sunYaw">Compass heading of the sun's arc in degrees. Defaults to 30.</param>
/// <param name="intensity">Brightness multiplier on the colour gradient (HDR, >1 valid; intensity = colour magnitude). Defaults to 1.</param>
/// <param name="driveSkybox">Also tint the scene's SkyBox2D between night blue and day white. Defaults to true.</param>
/// <param name="targetId">GUID of a GameObject to attach to (only attaches if the type is already loaded — hotload first).</param>
[McpTool( "add_daynight_sun" )]
public static Task<object> AddDaynightSun( string name = null, string directory = null, string clockClass = null, double? sunYaw = null, double? intensity = null, bool? driveSkybox = null, string targetId = null )
=> McpGate.Run( "add_daynight_sun", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "clockClass", clockClass ), ( "sunYaw", sunYaw ), ( "intensity", intensity ), ( "driveSkybox", driveSkybox ), ( "targetId", targetId ) ) );
/// <summary>
/// Add an environment reflection/ambient probe (EnvmapProbe) at a position with a cubic influence
/// volume — captures local reflections and indirect light for nearby surfaces. IMPORTANT: a placed
/// probe captures NOTHING until baked — follow with bake_reflections. Returns { created, gameObject
/// } — gameObject.id is the probe object's GUID.
/// </summary>
/// <param name="name">GameObject name.</param>
/// <param name="position">World position (centre of the probe). As "x,y,z" (or JSON {x,y,z}).</param>
/// <param name="size">Cubic influence size in units (default 1024).</param>
/// <param name="tint">Tint applied to the captured environment. As "r,g,b[,a]" (0-1 floats).</param>
/// <param name="feathering">Edge feathering 0-1 for blending between overlapping probes.</param>
[McpTool( "add_envmap_probe" )]
public static Task<object> AddEnvmapProbe( string name = null, string position = null, double? size = null, string tint = null, double? feathering = null )
=> McpGate.Run( "add_envmap_probe", McpGate.Args( ( "name", name ), ( "position", position ), ( "size", size ), ( "tint", tint ), ( "feathering", feathering ) ) );
/// <summary>
/// Add a light to the active scene. NOTE: s&box lights have no separate brightness field —
/// intensity is the colour magnitude, so 'brightness' scales the colour (use >1 for bright/HDR).
/// Types: directional = sun (aim it with rotation), point = omni-directional (range), spot = cone
/// (range + coneInner/coneOuter degrees), ambient = global fill light.
/// </summary>
/// <param name="type">Light type. One of: directional | point | spot | ambient.</param>
/// <param name="name">GameObject name.</param>
/// <param name="color">Light colour (default white). As "r,g,b[,a]" (0-1 floats).</param>
/// <param name="brightness">Intensity multiplier on the colour (default 1; try 2-10 for point/spot).</param>
/// <param name="range">point/spot only: falloff radius in units (maps to Radius).</param>
/// <param name="coneInner">spot only: inner cone angle in degrees.</param>
/// <param name="coneOuter">spot only: outer cone angle in degrees.</param>
/// <param name="shadows">Cast shadows (default true).</param>
/// <param name="skyColor">directional only: ambient sky colour for the upper hemisphere. As "r,g,b[,a]" (0-1 floats).</param>
/// <param name="position">World position. As "x,y,z" (or JSON {x,y,z}).</param>
/// <param name="rotation">World rotation — sets the aim direction for directional/spot lights. As "pitch,yaw,roll" degrees.</param>
/// <param name="parentId">GUID of a parent GameObject.</param>
[McpTool( "add_light" )]
public static Task<object> AddLight( string type, string name = null, string color = null, double? brightness = null, double? range = null, double? coneInner = null, double? coneOuter = null, bool? shadows = null, string skyColor = null, string position = null, string rotation = null, string parentId = null )
=> McpGate.Run( "add_light", McpGate.Args( ( "type", type ), ( "name", name ), ( "color", color ), ( "brightness", brightness ), ( "range", range ), ( "coneInner", coneInner ), ( "coneOuter", coneOuter ), ( "shadows", shadows ), ( "skyColor", skyColor ), ( "position", position ), ( "rotation", rotation ), ( "parentId", parentId ) ) );
/// <summary>
/// Add (or update) a post-processing effect on the scene's main camera (auto-enables
/// post-processing). Generic: pass the effect component name + any of its properties. Examples —
/// Bloom {Strength, Threshold, Tint}, Tonemapping, ColorAdjustments {Saturation, Brightness,
/// Contrast}, Vignette {Intensity, Color}, FilmGrain, DepthOfField, ChromaticAberration,
/// MotionBlur, Sharpen, AmbientOcclusion. Call describe_type <Effect> to discover a given
/// effect's properties.
/// </summary>
/// <param name="effect">Post-process component type name, e.g. 'Bloom', 'Vignette', 'ColorAdjustments'.</param>
/// <param name="properties">Property name -> value. Floats/ints/bools as numbers/bools, colours as {r,g,b,a}, enums as their string name. JSON value.</param>
/// <param name="cameraId">GUID of a specific camera GameObject (default: the scene's main camera).</param>
[McpTool( "add_post_process" )]
public static Task<object> AddPostProcess( string effect, JsonNode properties = null, string cameraId = null )
=> McpGate.Run( "add_post_process", McpGate.Args( ( "effect", effect ), ( "properties", properties ), ( "cameraId", cameraId ) ) );
/// <summary>
/// CCTV / security monitor / mirror / simple portal: creates a secondary camera
/// (IsMainCamera=false, Priority param) positioned/rotated or aimed at a target object, a display
/// surface (scaled box with a ModelRenderer), and GENERATES a sealed wiring component that at
/// runtime creates the render-target Texture (Texture.CreateRenderTarget builder), assigns
/// camera.RenderTarget and sets the texture onto an anonymous complex-shader material on the
/// display (attribute 'Color' = albedo; the screen is scene-lit, not emissive/unlit). Returns
/// {created, camera{id,...}, display{id,...}|null, className, path, generatedCode,
/// attachedAndWired, resolution, nextSteps[]}. FIRST call generates the .cs → follow nextSteps:
/// trigger_hotload, then add_component_with_properties on the display with properties
/// {SourceCamera: <camera GUID>}; a REPEAT call reusing the same `name` after the hotload
/// attaches AND wires everything automatically (attachedAndWired=true). The feed only renders in
/// play mode — verify with start_play + capture_view. If `name`'s .cs file exists but isn't
/// compiled yet, the call errors — hotload first. Scene/file-mutating — refused during play mode.
/// </summary>
/// <param name="name">Class name for the generated wiring component. Defaults to 'RenderTargetDisplay'. Reuse the same name for extra cameras — after a hotload the class is reused and wired in one shot.</param>
/// <param name="directory">Subdirectory for the generated .cs file. Defaults to 'Code'.</param>
/// <param name="cameraName">GameObject name for the camera. Defaults to 'RT Camera'.</param>
/// <param name="position">World position of the camera. As "x,y,z" (or JSON {x,y,z}).</param>
/// <param name="rotation">World rotation of the camera. As "pitch,yaw,roll" degrees.</param>
/// <param name="lookAtId">GUID of a GameObject to aim the camera at (overrides rotation; from get_scene_hierarchy / find_objects).</param>
/// <param name="priority">CameraComponent.Priority (integer). Defaults to 1.</param>
/// <param name="fieldOfView">Camera field of view in degrees (clamped 5-170). Engine default when omitted.</param>
/// <param name="resolution">Render-target size as 'width,height' pixels (min 16). Defaults to '512,512'.</param>
/// <param name="backgroundColor">Camera background colour. Engine default when omitted. As "r,g,b[,a]" (0-1 floats).</param>
/// <param name="createDisplay">Create the display surface GameObject. Defaults to true. Pass false to wire your own surface later.</param>
/// <param name="displayPosition">World position of the display surface. As "x,y,z" (or JSON {x,y,z}).</param>
/// <param name="displayRotation">World rotation of the display surface. As "pitch,yaw,roll" degrees.</param>
/// <param name="displaySize">Display box extents as 'x,y,z' world units (thin x = a wall screen facing ±x). Defaults to '4,128,72'.</param>
/// <param name="attribute">Material attribute that receives the texture. Defaults to 'Color' (albedo on the complex shader). Sanitized to [A-Za-z0-9_].</param>
[McpTool( "add_render_target_camera" )]
public static Task<object> AddRenderTargetCamera( string name = null, string directory = null, string cameraName = null, string position = null, string rotation = null, string lookAtId = null, double? priority = null, double? fieldOfView = null, string resolution = null, string backgroundColor = null, bool? createDisplay = null, string displayPosition = null, string displayRotation = null, string displaySize = null, string attribute = null )
=> McpGate.Run( "add_render_target_camera", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "cameraName", cameraName ), ( "position", position ), ( "rotation", rotation ), ( "lookAtId", lookAtId ), ( "priority", priority ), ( "fieldOfView", fieldOfView ), ( "resolution", resolution ), ( "backgroundColor", backgroundColor ), ( "createDisplay", createDisplay ), ( "displayPosition", displayPosition ), ( "displayRotation", displayRotation ), ( "displaySize", displaySize ), ( "attribute", attribute ) ) );
/// <summary>
/// Attach a motion trail (TrailRenderer) to an existing GameObject (via targetId) so it leaves a
/// trail as it moves — or create a standalone trail object. Only visible while the object is
/// moving.
/// </summary>
/// <param name="targetId">GUID of the GameObject to attach the trail to (else a new 'Trail' object is made).</param>
/// <param name="position">World position when creating a standalone trail. As "x,y,z" (or JSON {x,y,z}).</param>
/// <param name="lifetime">How long (seconds) trail points persist.</param>
/// <param name="maxPoints">Max points in the trail.</param>
/// <param name="pointDistance">Min distance between trail points.</param>
/// <param name="name">GameObject name when creating a new one.</param>
[McpTool( "add_trail" )]
public static Task<object> AddTrail( string targetId = null, string position = null, double? lifetime = null, double? maxPoints = null, double? pointDistance = null, string name = null )
=> McpGate.Run( "add_trail", McpGate.Args( ( "targetId", targetId ), ( "position", position ), ( "lifetime", lifetime ), ( "maxPoints", maxPoints ), ( "pointDistance", pointDistance ), ( "name", name ) ) );
/// <summary>
/// One-call scene mood: composes ambient + directional light, gradient fog, and a camera post-fx
/// stack (tonemap + colour grade + vignette) tuned for the chosen mood. Idempotent — re-runs update
/// the same 'Atmosphere *' objects. Returns { applied, mood, components, postFxCamera } — the
/// post-fx stack is only applied when a camera exists (postFxCamera is null otherwise; add a camera
/// and re-run). Screenshot to verify.
/// </summary>
/// <param name="mood">Atmosphere preset. One of: horror-night | foggy-dawn | overcast | warm-interior.</param>
[McpTool( "apply_atmosphere" )]
public static Task<object> ApplyAtmosphere( string mood )
=> McpGate.Run( "apply_atmosphere", McpGate.Args( ( "mood", mood ) ) );
/// <summary>
/// Apply just a camera post-processing look (no lights/fog): cinematic (tonemap + bloom + soft
/// vignette), filmic-horror (desaturated, high-contrast, heavy vignette, film grain), or clean
/// (tonemap only). Errors if the scene has no CameraComponent. Returns { applied, look, components,
/// camera } listing the effect components added to the main camera — tune them individually
/// afterwards with add_post_process.
/// </summary>
/// <param name="look">Post-fx look preset. One of: cinematic | filmic-horror | clean.</param>
[McpTool( "apply_post_fx_look" )]
public static Task<object> ApplyPostFxLook( string look )
=> McpGate.Run( "apply_post_fx_look", McpGate.Args( ( "look", look ) ) );
/// <summary>
/// Bake all EnvmapProbe reflection probes in the scene (EnvmapProbe.BakeAll) so they actually
/// capture their surroundings — placing a probe with add_envmap_probe does nothing visible until
/// it's baked. This is a real editor compute step, not a component setter. Runs async — returns {
/// baking, count, note, probes } immediately (each probe: id, name, mode, hasBaked), or { baked:
/// false, count: 0 } when the scene has no probes; re-screenshot after a moment to see reflections
/// appear.
/// </summary>
[McpTool( "bake_reflections" )]
public static Task<object> BakeReflections()
=> McpGate.Run( "bake_reflections", McpGate.Args() );
/// <summary>
/// Build a custom additive particle effect from raw params (ParticleEffect + cone emitter + sprite
/// renderer). Use this when the spawn_particle presets aren't what you want. Texture-free (additive
/// Texture.White glow).
/// </summary>
/// <param name="position">World position. As "x,y,z" (or JSON {x,y,z}).</param>
/// <param name="color">Particle tint (default white). As "r,g,b[,a]" (0-1 floats).</param>
/// <param name="rate">Particles per second when looping (default 30).</param>
/// <param name="burst">Particle count for a one-shot burst when loop=false (default 30).</param>
/// <param name="loop">Continuous emission (default true) vs a single burst.</param>
/// <param name="lifetime">Particle lifetime in seconds (default 2).</param>
/// <param name="size">Particle size (default 4).</param>
/// <param name="speed">Emission speed along the cone (default 100).</param>
/// <param name="coneAngle">Cone half-angle in degrees; ~85 ≈ hemisphere (default 40).</param>
/// <param name="gravity">Downward force (default 0 = none).</param>
/// <param name="additive">Additive (glow) blending (default true).</param>
/// <param name="maxParticles">Max live particles (default 500).</param>
/// <param name="name">GameObject name.</param>
[McpTool( "create_particle_effect" )]
public static Task<object> CreateParticleEffect( string position = null, string color = null, double? rate = null, double? burst = null, bool? loop = null, double? lifetime = null, double? size = null, double? speed = null, double? coneAngle = null, double? gravity = null, bool? additive = null, double? maxParticles = null, string name = null )
=> McpGate.Run( "create_particle_effect", McpGate.Args( ( "position", position ), ( "color", color ), ( "rate", rate ), ( "burst", burst ), ( "loop", loop ), ( "lifetime", lifetime ), ( "size", size ), ( "speed", speed ), ( "coneAngle", coneAngle ), ( "gravity", gravity ), ( "additive", additive ), ( "maxParticles", maxParticles ), ( "name", name ) ) );
/// <summary>
/// Add or update fog in the active scene. Types: 'gradient' (distance haze — great for
/// mood/horror), 'cubemap' (sky-tinted distance fog), 'volumetric' (a localized fog volume).
/// Re-running with the same targetId updates it rather than duplicating; without targetId each call
/// creates a new fog object. Returns { created, type, gameObject } — gameObject.id addresses the
/// fog object for later set_property/delete_gameobject; screenshot to verify the look.
/// </summary>
/// <param name="type">Fog type (default gradient). One of: gradient | cubemap | volumetric.</param>
/// <param name="name">GameObject name when creating a new fog object.</param>
/// <param name="targetId">GUID of an existing GameObject to host the fog (else a new fog object is created).</param>
/// <param name="color">Fog colour (gradient/volumetric Color, cubemap Tint). As "r,g,b[,a]" (0-1 floats).</param>
/// <param name="startDistance">gradient/cubemap: distance (units) where fog begins.</param>
/// <param name="endDistance">gradient/cubemap: distance (units) where fog reaches full density.</param>
/// <param name="height">gradient: world height the fog settles around.</param>
/// <param name="falloff">Distance/density falloff exponent (higher = sharper onset).</param>
/// <param name="blur">cubemap: sky blur amount.</param>
/// <param name="heightStart">cubemap: world height where height-fog starts.</param>
/// <param name="heightWidth">cubemap: height-fog band width.</param>
/// <param name="heightExponent">cubemap: height-fog falloff exponent.</param>
/// <param name="strength">volumetric: fog density/strength.</param>
/// <param name="size">volumetric: bounds size (units) centred on the object — object {x,y,z} or comma string "x,y,z". As "x,y,z" (or JSON {x,y,z}).</param>
[McpTool( "set_fog" )]
public static Task<object> SetFog( string type = null, string name = null, string targetId = null, string color = null, double? startDistance = null, double? endDistance = null, double? height = null, double? falloff = null, double? blur = null, double? heightStart = null, double? heightWidth = null, double? heightExponent = null, double? strength = null, string size = null )
=> McpGate.Run( "set_fog", McpGate.Args( ( "type", type ), ( "name", name ), ( "targetId", targetId ), ( "color", color ), ( "startDistance", startDistance ), ( "endDistance", endDistance ), ( "height", height ), ( "falloff", falloff ), ( "blur", blur ), ( "heightStart", heightStart ), ( "heightWidth", heightWidth ), ( "heightExponent", heightExponent ), ( "strength", strength ), ( "size", size ) ) );
/// <summary>
/// Set the scene's 2D skybox tint / indirect lighting (re-uses an existing SkyBox2D or creates
/// one). Darken the tint for night/dusk. Optionally point it at a .vmat sky material (silently kept
/// as-is if the material fails to load). Returns { created, gameObject } — gameObject.id is the sky
/// object; screenshot to verify the change.
/// </summary>
/// <param name="tint">Sky tint colour. As "r,g,b[,a]" (0-1 floats).</param>
/// <param name="indirectLighting">Whether the sky contributes indirect/ambient light.</param>
/// <param name="material">Path to a .vmat sky material (optional).</param>
/// <param name="name">Name for the sky GameObject if one is created.</param>
[McpTool( "set_skybox" )]
public static Task<object> SetSkybox( string tint = null, bool? indirectLighting = null, string material = null, string name = null )
=> McpGate.Run( "set_skybox", McpGate.Args( ( "tint", tint ), ( "indirectLighting", indirectLighting ), ( "material", material ), ( "name", name ) ) );
/// <summary>
/// EXPERIMENTAL — build an additive runtime ParticleEffect (no texture asset needed): kind = fire,
/// embers, sparks, magic, dust, blood, or snow ('smoke' returns an error). Returns { created, kind,
/// gameObject } — the component graph is created, but this runtime particle path has NOT been
/// verified to render through the bridge; for particles you can actually see, prefer spawn_vpcf.
/// </summary>
/// <param name="kind">Particle preset. One of: fire | embers | sparks | magic | dust | blood | snow.</param>
/// <param name="position">World position. As "x,y,z" (or JSON {x,y,z}).</param>
/// <param name="color">Override the particle tint. As "r,g,b[,a]" (0-1 floats).</param>
/// <param name="name">GameObject name.</param>
[McpTool( "spawn_particle" )]
public static Task<object> SpawnParticle( string kind, string position = null, string color = null, string name = null )
=> McpGate.Run( "spawn_particle", McpGate.Args( ( "kind", kind ), ( "position", position ), ( "color", color ), ( "name", name ) ) );
/// <summary>
/// Spawn a REAL particle system by playing a compiled .vpcf asset through LegacyParticleSystem —
/// the reliable path that actually renders, unlike spawn_particle/create_particle_effect (which
/// build a runtime ParticleEffect graph that shows nothing). Defaults to
/// 'particles/impact.generic.vpcf' (a sparks/impact burst — the only particle .vpcf reliably
/// present; set looped + a warm tint for a fire-ish effect). Pass your own compiled .vpcf logical
/// path if you have one. Screenshot-verifiable in edit mode.
/// </summary>
/// <param name="vpcf">Logical .vpcf path (default 'particles/impact.generic.vpcf'). NOT the .vpcf_c or .sbox/cloud cache path.</param>
/// <param name="position">World position (default origin). As "x,y,z" (or JSON {x,y,z}).</param>
/// <param name="name">GameObject name.</param>
/// <param name="looped">Loop the effect (default true).</param>
/// <param name="playbackSpeed">Playback speed multiplier.</param>
/// <param name="tint">Color tint (e.g. orange for fire); applied to the live SceneObject if it's ready this frame. As "r,g,b[,a]" (0-1 floats).</param>
[McpTool( "spawn_vpcf" )]
public static Task<object> SpawnVpcf( string vpcf = null, string position = null, string name = null, bool? looped = null, double? playbackSpeed = null, string tint = null )
=> McpGate.Run( "spawn_vpcf", McpGate.Args( ( "vpcf", vpcf ), ( "position", position ), ( "name", name ), ( "looped", looped ), ( "playbackSpeed", playbackSpeed ), ( "tint", tint ) ) );
}