Editor/Mcp/BridgeVisualsTools.cs

Editor toolset wrapper for bridge_visuals MCP tools. It defines static methods that call McpGate.Run to add lights, fog, skybox, particles, probes, post-processing and related visual/editor operations.

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>
/// 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>
	/// 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&amp;box lights have no separate brightness field —
	/// intensity is the colour magnitude, so 'brightness' scales the colour (use &gt;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 &lt;Effect&gt; 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 -&gt; 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>
	/// 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 ) ) );
}