Editor/Mcp/BridgeScaffoldGameplayTools.cs

Editor toolset bridge for generating gameplay scaffolding C# components. It defines many McpTool methods that call McpGate.Run with named arguments to emit various ready-made gameplay scripts (interaction prompt, stations, carry system, pickups, wallets, directors, save systems, player/NPC controllers, etc.). Each method is an async Task<object> wrapper that forwards parameters to the MCP bridge.

File AccessNetworking
// 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>
/// Generate complete, compile-verified gameplay C# components: player/NPC controllers, game
/// managers, health, pickups, inventory, save systems, economy, loot tables, round/phase machines,
/// interaction systems, placement mode, and more. Each tool writes a .cs file into the project;
/// follow with trigger_hotload + compile_status.
/// </summary>
[McpToolset( "bridge_scaffold_gameplay", "Generate complete, compile-verified gameplay C# components: player/NPC controllers, game managers, health, pickups, inventory, save systems, economy, loot tables, round/phase machines, interaction systems, placement mode, and more. Each tool writes a .cs file into the project; follow with trigger_hotload + compile_status." )]
public static class BridgeScaffoldGameplayTools
{
	/// <summary>
	/// Generate an eye-traced interaction-prompt HUD — a PanelComponent (.razor + .razor.scss pair,
	/// like create_leaderboard_panel) that every frame traces a ray from the scene camera
	/// (Scene.Trace.Ray, out to [Property] float Range) and, when the crosshair is on a component
	/// implementing Component.IPressable, shows a centered "Press E"-style pill. The prompt text comes
	/// from the target's IPressable.GetTooltip() when it overrides it (most don't), else a [Property]
	/// DefaultPrompt built from the action. This is the visible half of the interaction loop: it PAIRS
	/// with create_interactable / add_interaction_station (which implement IPressable) — this tool
	/// tells the player they CAN press, those tools handle the press. Host it under a ScreenPanel
	/// (add_screen_panel), then add the component to that panel object. The generated Razor is
	/// razor_lint-safe by construction: PanelComponent + BuildHash override folding the visible state,
	/// no switch-expressions and no non-ASCII in @code, and a class root selector in the SCSS.
	/// LOCAL/visual-only (no [Sync]).
	/// </summary>
	/// <param name="name">Class/file name for the generated .razor. Defaults to 'InteractionPrompt'.</param>
	/// <param name="directory">Subdirectory for the generated .razor + .razor.scss. Defaults to 'Code/UI'.</param>
	/// <param name="action">Verb woven into the default prompt text ('Press E to &lt;action&gt;'). Defaults to 'use'.</param>
	/// <param name="range">Eye-trace reach in world units — how close the crosshair must be to a pressable to show the prompt. Defaults to 120.</param>
	[McpTool( "add_interaction_prompt" )]
	public static Task<object> AddInteractionPrompt( string name = null, string directory = null, string action = null, double? range = null )
		=> McpGate.Run( "add_interaction_prompt", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "action", action ), ( "range", range ) ) );

	/// <summary>
	/// Generate a Component.IPressable 'station' prop (crafting bench / shop till / arcade cabinet)
	/// that ONE user occupies at a time. Occupancy is host-authoritative: the occupant is a
	/// [Sync(SyncFlags.FromHost)] Guid (GameObject/Connection aren't [Sync]-able) and Press() routes
	/// the claim to the host via an [Rpc.Host] Occupy(). Includes a reservation grace window (the
	/// station stays reserved for its last user for graceSeconds after they leave, so a brief walk-away
	/// can't jump the queue), an optional unlock-level gate (users below requiredLevel can't use it —
	/// wire the static ResolveUserLevel hook to your progression system to activate it), and an
	/// overlay-open hook (a static OnStationOpened(GameObject) event to open your UI, plus an opt-in
	/// [Rpc.Broadcast] mirror). Single-player safe. Optionally attached to an existing GameObject by
	/// GUID (only after a trigger_hotload). Give the prop a Collider so the player's use key can
	/// raycast it. Mined from interaction-station patterns across shipped s&amp;box games.
	/// </summary>
	/// <param name="name">Class name for the generated component. Defaults to 'InteractionStation'.</param>
	/// <param name="directory">Subdirectory for the .cs file (path override). Defaults to 'Code'.</param>
	/// <param name="graceSeconds">Seconds the station stays reserved for its last user after they leave, before anyone else can claim it. 0 = no grace window. Defaults to 5.</param>
	/// <param name="requiredLevel">Unlock-level gate: users below this level can't use the station. 0 = no gate. The gate only bites once you wire the static ResolveUserLevel hook to your progression system. Defaults to 0.</param>
	/// <param name="targetId">GUID of an existing GameObject to attach the station component to (only attaches if the type is already loaded — generate, trigger_hotload, then it places; otherwise add it after the hotload).</param>
	[McpTool( "add_interaction_station" )]
	public static Task<object> AddInteractionStation( string name = null, string directory = null, double? graceSeconds = null, int? requiredLevel = null, string targetId = null )
		=> McpGate.Run( "add_interaction_station", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "graceSeconds", graceSeconds ), ( "requiredLevel", requiredLevel ), ( "targetId", targetId ) ) );

	/// <summary>
	/// Generate a first-person pickup / carry / throw component (sealed Component) for physics props.
	/// Attach it to the PLAYER (the object that owns the camera). It eye-traces from Scene.Camera for a
	/// Rigidbody-bearing GameObject tagged [Property] CarryTag (default 'carryable') within [Property]
	/// Range; grabbing routes a host-authoritative [Rpc.Host] request that re-validates the target and
	/// caller, hands the object's network ownership to the carrier
	/// (GameObject.Network.AssignOwnership), and disables the rigidbody's MotionEnabled while held. The
	/// held object follows a hold point ([Property] Vector3 HoldOffset in front of the camera) each
	/// FixedUpdate; dropping restores physics, throwing applies an impulse ([Property] float
	/// ThrowForce). The held-object id is [Sync(SyncFlags.FromHost)] so proxies see the carrying state,
	/// and static OnPickedUp / OnDropped events fire uniformly for SFX/VFX. PAIRS with physics props —
	/// give each carryable a Rigidbody + Collider and the CarryTag (set_tags); network-spawn them for
	/// multiplayer so ownership + transform replicate. Single-player safe (IsProxy is false and RPCs
	/// run locally with no session). Inputs: GrabAction (default 'use') grabs/drops, ThrowAction
	/// (default 'attack1') throws. Optionally attach to an existing player GameObject by GUID after a
	/// hotload.
	/// </summary>
	/// <param name="name">Class name for the generated component. Defaults to 'CarrySystem'.</param>
	/// <param name="directory">Subdirectory for the generated .cs file. Defaults to 'Code'.</param>
	/// <param name="range">Eye-trace reach for grabbing a carryable, in world units. Defaults to 130.</param>
	/// <param name="throwForce">Impulse magnitude applied on throw (scales with the prop's mass — tune per game). Defaults to 20000.</param>
	/// <param name="carryTag">Only objects with this tag (and a Rigidbody) can be picked up; lower-cased/underscored to match s&amp;box tag convention. Defaults to 'carryable'.</param>
	/// <param name="targetId">GUID of the PLAYER GameObject (the one with the camera) to attach to (only attaches if the type is already loaded — hotload first).</param>
	[McpTool( "create_carry_system" )]
	public static Task<object> CreateCarrySystem( string name = null, string directory = null, double? range = null, double? throwForce = null, string carryTag = null, string targetId = null )
		=> McpGate.Run( "create_carry_system", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "range", range ), ( "throwForce", throwForce ), ( "carryTag", carryTag ), ( "targetId", targetId ) ) );

	/// <summary>
	/// Generate a networked coin / currency pickup component (sealed, Component.ITriggerListener).
	/// Host-spawned; when a GameObject carrying PlayerTag ('player') enters its trigger the HOST
	/// validates and grants Value (default 1) into a wallet on the player, then destroys the pickup
	/// network-wide (the host Destroy() replicates — there is no NetworkDestroy on this SDK). Optional
	/// magnet: while MagnetRadius (default 0 = off) is &gt; 0 the coin accelerates toward the nearest
	/// player each FixedUpdate (host-side, capped by MaxMagnetSpeed). IsProxy guards keep the grant +
	/// despawn host-only in multiplayer (NetworkSpawn the coin on the host); single-player works with
	/// no networking. The deposit is reflection-free and dependency-free: a static Grant seam is wired
	/// ONCE to the direct typed call — player.Components.Get&lt;EconomyWallet&gt;()?.AddMoney(amount) —
	/// so the component compiles with NO hard reference to a specific wallet class (rename the wallet
	/// type if yours differs; mirrors create_pickup's self-contained convention). WalletComponentName
	/// (default 'EconomyWallet') is used to locate the wallet and name the fix if Grant is left unwired
	/// (never silent). Pairs with create_economy_wallet (AddMoney/TrySpend/CanAfford) and
	/// create_floating_combat_text (spawn a '+N' popup from OnCollected).
	/// </summary>
	/// <param name="name">Class name for the generated component. Defaults to 'CurrencyPickup'.</param>
	/// <param name="directory">Subdirectory for the generated .cs file. Defaults to 'Code'.</param>
	/// <param name="value">How much currency the pickup grants into the wallet. Defaults to 1.</param>
	/// <param name="magnetRadius">Magnet range in world units — within it the coin flies to the nearest player each FixedUpdate. 0 = magnet off. Defaults to 0.</param>
	/// <param name="walletComponentName">Type name of the wallet component to deposit into (used to locate it and to name the fix if the Grant seam is left unwired). Defaults to 'EconomyWallet'.</param>
	/// <param name="targetId">GUID of a coin GameObject to attach to — give it a trigger Collider (SphereCollider, IsTrigger=true). Only attaches if the type is already loaded — hotload first.</param>
	[McpTool( "create_currency_pickup" )]
	public static Task<object> CreateCurrencyPickup( string name = null, string directory = null, int? value = null, double? magnetRadius = null, string walletComponentName = null, string targetId = null )
		=> McpGate.Run( "create_currency_pickup", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "value", value ), ( "magnetRadius", magnetRadius ), ( "walletComponentName", walletComponentName ), ( "targetId", targetId ) ) );

	/// <summary>
	/// Generate a host-authoritative time-of-day clock: [Sync(SyncFlags.FromHost)] TimeOfDay (0–24) +
	/// Day advancing by Time.Delta, IsDay/IsNight from sunrise/sunset hours, and static OnNewDay /
	/// OnDayNightChanged events to drive lighting, NPC schedules, or spawns. Single-player safe. Pairs
	/// with create_round_phase_machine. Optionally attached to a GameObject by GUID (after a hotload).
	/// </summary>
	/// <param name="name">Class name. Defaults to 'DayNightClock'.</param>
	/// <param name="directory">Subdirectory for the .cs file. Defaults to 'Code'.</param>
	/// <param name="dayLengthSeconds">Real seconds per in-game day. Defaults to 600 (10 min).</param>
	/// <param name="startHour">Hour the clock starts at (0–24). Defaults to 8.</param>
	/// <param name="sunriseHour">Hour day begins. Defaults to 6.</param>
	/// <param name="sunsetHour">Hour night begins. Defaults to 20.</param>
	/// <param name="targetId">GUID of an existing GameObject to attach to (hotload first).</param>
	[McpTool( "create_day_night_clock" )]
	public static Task<object> CreateDayNightClock( string name = null, string directory = null, double? dayLengthSeconds = null, double? startHour = null, double? sunriseHour = null, double? sunsetHour = null, string targetId = null )
		=> McpGate.Run( "create_day_night_clock", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "dayLengthSeconds", dayLengthSeconds ), ( "startHour", startHour ), ( "sunriseHour", sunriseHour ), ( "sunsetHour", sunsetHour ), ( "targetId", targetId ) ) );

	/// <summary>
	/// Generate a host-authoritative currency Wallet component: a [Sync(SyncFlags.FromHost)] Money
	/// balance (only the host can write it — plain [Sync] money is the classic economy exploit) with
	/// AddMoney / TrySpend / SetMoney / CanAfford and an OnMoneyChanged event. Single-player safe.
	/// Optionally attached to an existing GameObject by GUID (after a hotload). Pairs with a save
	/// system for persistence. Mined from the most-requested currency pattern across 51 games.
	/// </summary>
	/// <param name="name">Class name. Defaults to 'Wallet'.</param>
	/// <param name="directory">Subdirectory for the .cs file. Defaults to 'Code'.</param>
	/// <param name="startingMoney">Initial balance the host seeds on start. Defaults to 0.</param>
	/// <param name="targetId">GUID of an existing GameObject to attach the Wallet to (only attaches if the type is already loaded — hotload first).</param>
	[McpTool( "create_economy_wallet" )]
	public static Task<object> CreateEconomyWallet( string name = null, string directory = null, int? startingMoney = null, string targetId = null )
		=> McpGate.Run( "create_economy_wallet", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "startingMoney", startingMoney ), ( "targetId", targetId ) ) );

	/// <summary>
	/// Generate a generalized L4D-style AI/pacing director component (host-authoritative). On a
	/// configurable interval the host rolls a weighted pick over a [Property] List&lt;GameObject&gt;
	/// EventPrefabs (with a parallel List&lt;float&gt; Weights), skips any event already active
	/// (dedupe) and anything past a MaxActive concurrency cap, clones the chosen prefab, NetworkSpawns
	/// it, and attaches a generated {name}TimedEvent companion so each spawned event self-destructs
	/// after EventLifetime seconds. Great for ambient events, waves, and world events. Single-player
	/// safe (IsProxy guard; NetworkSpawn falls back to a local clone). Fill EventPrefabs/Weights in the
	/// inspector or via the bridge after a hotload; edit the RollInterval() stub to make pacing
	/// adaptive (player-count/inactivity/time-pressure factors) per the ai-director cookbook.
	/// Optionally attached to an existing GameObject by GUID (after a hotload). NOTE: emits ONE .cs
	/// file containing two classes ({name} + {name}TimedEvent); the type only resolves after
	/// trigger_hotload.
	/// </summary>
	/// <param name="name">Class name for the director (a {name}TimedEvent companion is generated alongside it). Defaults to 'EventDirector'.</param>
	/// <param name="path">Subdirectory for the generated .cs file. Defaults to 'Code'.</param>
	/// <param name="intervalSeconds">Base seconds between director rolls. Defaults to 30 (clamped to &gt;= 0.1).</param>
	/// <param name="maxActive">Maximum number of concurrently-live events. Defaults to 3 (clamped to &gt;= 1).</param>
	/// <param name="eventLifetime">Seconds before each spawned event self-destructs. Defaults to 60 (clamped to &gt;= 0.1).</param>
	/// <param name="targetId">GUID of an existing GameObject to attach the director to (only attaches if the type is already loaded — hotload first).</param>
	[McpTool( "create_event_director" )]
	public static Task<object> CreateEventDirector( string name = null, string path = null, double? intervalSeconds = null, int? maxActive = null, double? eventLifetime = null, string targetId = null )
		=> McpGate.Run( "create_event_director", McpGate.Args( ( "name", name ), ( "path", path ), ( "intervalSeconds", intervalSeconds ), ( "maxActive", maxActive ), ( "eventLifetime", eventLifetime ), ( "targetId", targetId ) ) );

	/// <summary>
	/// Generate a host-authoritative gacha / loot-box roller component. Two-level pick: parallel
	/// [Property] lists RarityNames + RarityWeights select a RARITY by cumulative weight (the
	/// create_weighted_loot_table shape), then a flat 'Rarity:Item' [Property] list (e.g.
	/// 'Legendary:Dragon Fang') picks an ITEM uniformly within that rarity — simple and
	/// inspector-editable. A pity counter (PityAfter, default 50) guarantees the rarest tier (the LAST
	/// entry in RarityNames) after N rolls without it and resets on a hit. Duplicate detection against
	/// an owned-items set fires a host-side OnDuplicate hook (marked TODO: convert dupes to
	/// shards/currency). Roll() routes to the host via an [Rpc.Host] RequestRoll (Rpc.Caller
	/// re-validated — NetFlags is not security) and the result fans out via [Rpc.Broadcast] so every
	/// machine fires the static OnRolled(rarity, item, isDuplicate) event; single-player safe (RPCs run
	/// locally). Use create_weighted_loot_table instead for a simpler single-tier weighted pick with no
	/// pity/dupe/networking. Pairs with create_economy_wallet (spend currency to roll) and
	/// create_inventory (store the pulls).
	/// </summary>
	/// <param name="name">Class name for the generated component. Defaults to 'GachaDropTable'.</param>
	/// <param name="directory">Subdirectory for the generated .cs file. Defaults to 'Code'.</param>
	/// <param name="pityAfter">Rolls without a rarest-tier hit before the next roll is guaranteed rarest. 0 disables pity. Defaults to 50.</param>
	/// <param name="targetId">GUID of a per-player/manager GameObject to attach to (only attaches if the type is already loaded — hotload first).</param>
	[McpTool( "create_gacha_drop_table" )]
	public static Task<object> CreateGachaDropTable( string name = null, string directory = null, int? pityAfter = null, string targetId = null )
		=> McpGate.Run( "create_gacha_drop_table", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "pityAfter", pityAfter ), ( "targetId", targetId ) ) );

	/// <summary>
	/// Generate a minimal game-manager Component: a static Instance singleton, [Property] MaxPlayers /
	/// GameState, and a Component.INetworkListener OnActive hook that logs player connects. Writes
	/// &lt;name&gt;.cs and returns { created, path, className }. NOTE: the
	/// includeScore/includeTimer/includeSpawning params are not currently applied — the same minimal
	/// manager is always generated (for richer game-loop scaffolds see create_round_phase_machine /
	/// create_objective_system / create_economy_wallet). Follow with trigger_hotload, then
	/// get_compile_errors, then place via add_component_to_new_object.
	/// </summary>
	/// <param name="name">Class name. Defaults to 'GameManager'.</param>
	/// <param name="directory">Subdirectory under code/ for the file.</param>
	/// <param name="includeScore">Include score tracking (currently not applied by the handler).</param>
	/// <param name="includeTimer">Include round timer with countdown (currently not applied by the handler).</param>
	/// <param name="includeSpawning">Include player spawning from prefab at spawn point (currently not applied by the handler).</param>
	[McpTool( "create_game_manager" )]
	public static Task<object> CreateGameManager( string name = null, string directory = null, bool? includeScore = null, bool? includeTimer = null, bool? includeSpawning = null )
		=> McpGate.Run( "create_game_manager", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "includeScore", includeScore ), ( "includeTimer", includeTimer ), ( "includeSpawning", includeSpawning ) ) );

	/// <summary>
	/// Generate a Health component: MaxHealth, [Sync] CurrentHealth, TakeDamage/Heal, an OnDeath event,
	/// optional regen and respawn. Host-authoritative damage when networked, single-player safe.
	/// Optionally attached to an existing GameObject by GUID.
	/// </summary>
	/// <param name="name">Class name. Defaults to 'Health'.</param>
	/// <param name="directory">Subdirectory for the .cs file. Defaults to 'Code'.</param>
	/// <param name="maxHealth">Starting/maximum health. Defaults to 100.</param>
	/// <param name="regen">Include passive health regeneration after a delay. Defaults to false.</param>
	/// <param name="respawn">On death, respawn at a RespawnPoint (wire it with set_component_reference) instead of disabling. Defaults to false.</param>
	/// <param name="targetId">GUID of an existing GameObject to attach the Health component to (only attaches if the type is already loaded — hotload first).</param>
	[McpTool( "create_health_system" )]
	public static Task<object> CreateHealthSystem( string name = null, string directory = null, double? maxHealth = null, bool? regen = null, bool? respawn = null, string targetId = null )
		=> McpGate.Run( "create_health_system", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "maxHealth", maxHealth ), ( "regen", regen ), ( "respawn", respawn ), ( "targetId", targetId ) ) );

	/// <summary>
	/// Generate a hold-to-confirm action component (sealed Component). While a named input action is
	/// held (Input.Down), a public Progress value fills 0→1 over [Property] float HoldSeconds;
	/// releasing early snaps back to 0, or drains down if [Property] bool DecayOnRelease. Reaching 1
	/// fires the static OnConfirmed(GameObject) event, then a short CooldownSeconds blocks
	/// re-triggering. The classic 'hold E to disarm / open / revive' interaction. No UI is generated —
	/// read the public Progress (0..1) from your own HUD to draw a radial or bar; a #region Feedback
	/// hook marks where to tie in a sound/effect. LOCAL/owner-only: input is IsProxy-guarded so it
	/// never fires on proxies and is single-player safe. For a host-authoritative outcome, call an
	/// [Rpc.Host] from inside the OnConfirmed subscriber. Attach to the player (or any owned object
	/// that reads input); optionally attach to an existing GameObject by GUID after a hotload.
	/// </summary>
	/// <param name="name">Class name for the generated component. Defaults to 'HoldToConfirm'.</param>
	/// <param name="directory">Subdirectory for the generated .cs file. Defaults to 'Code'.</param>
	/// <param name="action">Input action name that must be held (must exist in the project's Input settings — see ensure_input_action). Defaults to 'use'.</param>
	/// <param name="holdSeconds">Seconds of continuous hold required to confirm. Defaults to 1.5.</param>
	/// <param name="decayOnRelease">Baked default for DecayOnRelease: if true, releasing early drains Progress back down instead of snapping to 0 (editable per-instance). Defaults to false.</param>
	/// <param name="targetId">GUID of a GameObject to attach the component to (only attaches if the type is already loaded — hotload first).</param>
	[McpTool( "create_hold_to_confirm" )]
	public static Task<object> CreateHoldToConfirm( string name = null, string directory = null, string action = null, double? holdSeconds = null, bool? decayOnRelease = null, string targetId = null )
		=> McpGate.Run( "create_hold_to_confirm", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "action", action ), ( "holdSeconds", holdSeconds ), ( "decayOnRelease", decayOnRelease ), ( "targetId", targetId ) ) );

	/// <summary>
	/// Generate a host-authoritative passive income component: every tickSeconds the host grants
	/// incomePerTick × Multiplier, auto-wiring the first sibling component with an AddMoney(int) method
	/// (a create_economy_wallet scaffold plugs in with zero code) or an overridable Grant() seam;
	/// TotalEarned is [Sync(FromHost)] and static OnIncomeTick fires per grant. The idle-game kit:
	/// wallet (create_economy_wallet) + this + create_offline_progress. Writes a .cs file and returns {
	/// created, path, className, nextSteps } — follow with trigger_hotload + compile_status.
	/// </summary>
	/// <param name="name">Class/file name (default 'IdleIncome' -&gt; Code/IdleIncome.cs). Errors if the file exists.</param>
	/// <param name="directory">Directory for the .cs file. Default 'Code'.</param>
	/// <param name="incomePerTick">Amount granted per tick. Default 1.</param>
	/// <param name="tickSeconds">Seconds between grants. Default 1.</param>
	[McpTool( "create_idle_income" )]
	public static Task<object> CreateIdleIncome( string name = null, string directory = null, double? incomePerTick = null, double? tickSeconds = null )
		=> McpGate.Run( "create_idle_income", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "incomePerTick", incomePerTick ), ( "tickSeconds", tickSeconds ) ) );

	/// <summary>
	/// Generate a Component.IPressable interactable: the built-in PlayerController 'use' key drives
	/// Press()/Hover()/Blur() with no custom player code. Includes a static OnPressed event, an
	/// optional cooldown (TimeUntil), and a private OnPress() extensionpoint for effects. For
	/// host-authoritative side-effects call an [Rpc.Host] from OnPress(). The Prompt property is left
	/// to your game's HUD. Optionally attached to an existing GameObject by GUID (after a hotload).
	/// </summary>
	/// <param name="name">Class name. Defaults to 'Interactable'.</param>
	/// <param name="directory">Subdirectory for the .cs file. Defaults to 'Code'.</param>
	/// <param name="prompt">Prompt string shown by the game's HUD when hovering. Defaults to 'Press'.</param>
	/// <param name="cooldownSeconds">Seconds before the interactable can be pressed again. 0 = no cooldown. Defaults to 0.</param>
	/// <param name="targetId">GUID of an existing GameObject to attach the component to (only attaches if the type is already loaded — hotload first).</param>
	[McpTool( "create_interactable" )]
	public static Task<object> CreateInteractable( string name = null, string directory = null, string prompt = null, double? cooldownSeconds = null, string targetId = null )
		=> McpGate.Run( "create_interactable", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "prompt", prompt ), ( "cooldownSeconds", cooldownSeconds ), ( "targetId", targetId ) ) );

	/// <summary>
	/// Generate a slot-based inventory component using parallel List&lt;string&gt; ItemIds /
	/// List&lt;int&gt; Counts (serialization-safe, inspector-editable). Includes TryAdd (stack-first,
	/// partial-add rejected), TryRemove, CountOf, Move (swap or merge same-id slots), and Clear. Static
	/// OnChanged event fires after every successful mutation. Host-authoritative usage note: mutate on
	/// the host in multiplayer, replicate via your own [Sync]/RPC. Pairs with create_pickup.
	/// </summary>
	/// <param name="name">Class name. Defaults to 'Inventory'.</param>
	/// <param name="directory">Subdirectory for the .cs file. Defaults to 'Code'.</param>
	/// <param name="capacity">Total slot count. Defaults to 24.</param>
	/// <param name="maxStack">Maximum items per slot (stack cap). Defaults to 99.</param>
	/// <param name="targetId">GUID of an existing GameObject to attach to (hotload first).</param>
	[McpTool( "create_inventory" )]
	public static Task<object> CreateInventory( string name = null, string directory = null, int? capacity = null, int? maxStack = null, string targetId = null )
		=> McpGate.Run( "create_inventory", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "capacity", capacity ), ( "maxStack", maxStack ), ( "targetId", targetId ) ) );

	/// <summary>
	/// Generate a Razor PanelComponent that fetches and displays a Sandbox.Services leaderboard derived
	/// from a stat name. Produces TWO files: {name}.razor and {name}.razor.scss. The panel
	/// auto-refreshes every 30 s, shows rank/displayName/value rows, handles loading state, and
	/// includes a BuildHash() override (razor-lint clean). Must be hosted under a ScreenPanel or
	/// WorldPanel. Stats must be configured for the project ident on sbox.game. Uses
	/// Leaderboards.Get(statName) + board.Refresh() -- the exact API from ServicesQueryHandler. Returns
	/// { created, razorPath, scssPath, className, note }. Follow with trigger_hotload, then
	/// get_compile_errors, then host it via add_screen_panel (panelComponent=className).
	/// </summary>
	/// <param name="name">Class name for the panel component. Defaults to 'LeaderboardPanel'.</param>
	/// <param name="directory">Subdirectory for the generated files. Defaults to 'Code/UI'.</param>
	/// <param name="statName">Sandbox.Services stat name the leaderboard is derived from. Defaults to 'score'.</param>
	/// <param name="title">Display title shown at the top of the panel. Defaults to 'Leaderboard'.</param>
	/// <param name="maxRows">Maximum leaderboard rows to fetch and display. Defaults to 10.</param>
	[McpTool( "create_leaderboard_panel" )]
	public static Task<object> CreateLeaderboardPanel( string name = null, string directory = null, string statName = null, string title = null, int? maxRows = null )
		=> McpGate.Run( "create_leaderboard_panel", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "statName", statName ), ( "title", title ), ( "maxRows", maxRows ) ) );

	/// <summary>
	/// Generate an NPC controller script with NavMeshAgent pathfinding. Supports patrol, chase, and
	/// patrol-chase behaviors.
	/// </summary>
	/// <param name="name">Class name. Defaults to 'NpcController'.</param>
	/// <param name="directory">Subdirectory under code/ for the file.</param>
	/// <param name="behavior">AI behavior: 'patrol' (follow waypoints), 'chase' (follow player), 'patrol_chase' (patrol until player nearby). Defaults to 'patrol'. One of: patrol | chase | patrol_chase.</param>
	/// <param name="moveSpeed">Movement speed. Defaults to 150.</param>
	/// <param name="chaseRange">Detection range for chase behavior. Defaults to 500.</param>
	[McpTool( "create_npc_controller" )]
	public static Task<object> CreateNpcController( string name = null, string directory = null, string behavior = null, double? moveSpeed = null, double? chaseRange = null )
		=> McpGate.Run( "create_npc_controller", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "behavior", behavior ), ( "moveSpeed", moveSpeed ), ( "chaseRange", chaseRange ) ) );

	/// <summary>
	/// Generate an ObjectiveManager component — the win/lose brain of a game. Tracks an objective
	/// (collect_all / reach_goal / survive_time / eliminate_all), fires a win, and handles a lose
	/// condition (fall below kill-Z / timer / out of lives). Self-contained C#; other systems call
	/// ObjectiveManager.Instance. Optionally placed as a scene singleton. Returns { created, path,
	/// className, gameObject, note } — gameObject is the placed singleton, or null with a note when the
	/// fresh type isn't in the TypeLibrary yet. Follow with trigger_hotload, then get_compile_errors;
	/// if placement was skipped, place with add_component_to_new_object after the hotload.
	/// </summary>
	/// <param name="name">Class name. Defaults to 'ObjectiveManager'.</param>
	/// <param name="directory">Subdirectory for the .cs file. Defaults to 'Code'.</param>
	/// <param name="objective">Win condition. Defaults to 'reach_goal'. One of: collect_all | reach_goal | survive_time | eliminate_all.</param>
	/// <param name="targetCount">How many to collect/eliminate (for collect_all / eliminate_all). Defaults to 3.</param>
	/// <param name="timeLimit">Seconds — survive this long to win (survive_time) or before losing (loseOn=timer). Defaults to 60.</param>
	/// <param name="loseOn">Lose condition. 'fall' = player drops below killZ. Defaults to 'fall'. One of: fall | timer | lives | none.</param>
	/// <param name="killZ">World Z below which the player is considered fallen out of the world. Defaults to -1000.</param>
	/// <param name="lives">Lives before game over (loseOn=lives). Defaults to 1.</param>
	/// <param name="placeInScene">Place the manager as a scene singleton. Defaults to true. (Only attaches if the type is already loaded — generate, hotload, then it places; otherwise add it after hotload.).</param>
	[McpTool( "create_objective_system" )]
	public static Task<object> CreateObjectiveSystem( string name = null, string directory = null, string objective = null, int? targetCount = null, double? timeLimit = null, string loseOn = null, double? killZ = null, int? lives = null, bool? placeInScene = null )
		=> McpGate.Run( "create_objective_system", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "objective", objective ), ( "targetCount", targetCount ), ( "timeLimit", timeLimit ), ( "loseOn", loseOn ), ( "killZ", killZ ), ( "lives", lives ), ( "placeInScene", placeInScene ) ) );

	/// <summary>
	/// Generate an offline / idle-progress component (sealed, owner/host-only) — the idle-game staple.
	/// Persists LastSeenUtc (DateTime) to FileSystem.Data JSON on a dirty-flag autosave heartbeat
	/// (AutosaveSeconds) and on OnDisabled, copying create_save_system's persistence patterns. On
	/// enable it computes elapsed = now − LastSeenUtc, guards a clock rollback (negative → 0), clamps
	/// to MaxOfflineHours (default 8), then replays that time through a SimulateOffline(double seconds)
	/// TODO hook in fixed TickSeconds chunks (default 1) so idle accumulation is deterministic
	/// (frame-rate independent), and fires the static OnOfflineProgressApplied(seconds) event (drive a
	/// 'welcome back, you earned X' screen). IsProxy-guarded so a client can't author their own offline
	/// earnings. Fill in the SimulateOffline hook with your idle math (e.g.
	/// wallet.AddMoney(rate*seconds)). Pairs with create_economy_wallet / create_save_system /
	/// create_stat_modifier_system.
	/// </summary>
	/// <param name="name">Class name for the generated component. Defaults to 'OfflineProgress'.</param>
	/// <param name="directory">Subdirectory for the generated .cs file. Defaults to 'Code'.</param>
	/// <param name="maxOfflineHours">Offline time is clamped to this many hours (stops a week-away paying out a week). Defaults to 8.</param>
	/// <param name="tickSeconds">SimulateOffline chunk size in seconds — smaller = finer-grained deterministic replay (floored at 0.1). Defaults to 1.</param>
	/// <param name="targetId">GUID of an idle/save-manager GameObject to attach to (only attaches if the type is already loaded — hotload first).</param>
	[McpTool( "create_offline_progress" )]
	public static Task<object> CreateOfflineProgress( string name = null, string directory = null, double? maxOfflineHours = null, double? tickSeconds = null, string targetId = null )
		=> McpGate.Run( "create_offline_progress", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "maxOfflineHours", maxOfflineHours ), ( "tickSeconds", tickSeconds ), ( "targetId", targetId ) ) );

	/// <summary>
	/// Generate a trigger-based collectible component. On enter by a tagged object it raises
	/// OnCollected (wire it to your objective/score system) and despawns. Optionally builds a visible
	/// pickup GameObject with a trigger SphereCollider (+ a model) in one call. Returns { created,
	/// path, className, gameObject, note } — gameObject is the placed pickup (null unless
	/// placeInScene=true); a note flags when the component couldn't attach because the fresh type needs
	/// a hotload. Follow with trigger_hotload, then get_compile_errors.
	/// </summary>
	/// <param name="name">Class name. Defaults to 'Pickup'.</param>
	/// <param name="directory">Subdirectory for the .cs file. Defaults to 'Code'.</param>
	/// <param name="action">Effect flavour (all self-contained; the heal/item branches show the typed call to a companion system in comments). Defaults to 'score'. One of: score | heal | item | custom.</param>
	/// <param name="amount">Magnitude of the effect (score points, heal amount). Defaults to 1.</param>
	/// <param name="filterTag">Only collect for objects with this tag. Defaults to 'player'.</param>
	/// <param name="placeInScene">Also build a pickup GameObject (trigger SphereCollider + optional model). Defaults to false.</param>
	/// <param name="position">World position when placeInScene is true. As "x,y,z" (or JSON {x,y,z}).</param>
	/// <param name="radius">Trigger sphere radius when placed. Defaults to 24.</param>
	/// <param name="model">Optional model path for a visible pickup (e.g. 'models/dev/box.vmdl'). Cloud assets must be installed first.</param>
	[McpTool( "create_pickup" )]
	public static Task<object> CreatePickup( string name = null, string directory = null, string action = null, double? amount = null, string filterTag = null, bool? placeInScene = null, string position = null, double? radius = null, string model = null )
		=> McpGate.Run( "create_pickup", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "action", action ), ( "amount", amount ), ( "filterTag", filterTag ), ( "placeInScene", placeInScene ), ( "position", position ), ( "radius", radius ), ( "model", model ) ) );

	/// <summary>
	/// Generate a ghost-preview + commit placement component (single class). StartPlacing() clones
	/// GhostPrefab as a NetworkMode.Never preview with colliders disabled and ModelRenderers tinted
	/// semi-transparent. Each frame while placing: ray from Scene.Camera.GetMouseRay(),
	/// IgnoreGameObjectHierarchy(ghost), snap hit position to GridSize (0 = freeform), move ghost. On
	/// Input.Pressed('attack1') TryPlace() re-validates distance and commits a real clone.
	/// StopPlacing() destroys the ghost. Static OnPlaced(GameObject, Vector3) event. Includes a
	/// multiplayer RPC note. API grounded in building-placement cookbook (enifun.shop_manager pattern).
	/// </summary>
	/// <param name="name">Class name. Defaults to 'PlacementMode'.</param>
	/// <param name="directory">Subdirectory for the .cs file. Defaults to 'Code'.</param>
	/// <param name="gridSize">Snap grid size in world units (0 = freeform placement). Defaults to 0.</param>
	/// <param name="targetId">GUID of an existing GameObject to attach to (hotload first).</param>
	[McpTool( "create_placement_mode" )]
	public static Task<object> CreatePlacementMode( string name = null, string directory = null, double? gridSize = null, string targetId = null )
		=> McpGate.Run( "create_placement_mode", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "gridSize", gridSize ), ( "targetId", targetId ) ) );

	/// <summary>
	/// Generate a player controller script with WASD movement, mouse look, jumping, and sprint.
	/// Supports first-person, third-person, and top-down movement modes. Optionally places a player rig
	/// (GameObject + CharacterController + Camera) in the scene — note the generated component is
	/// attached AFTER a trigger_hotload (it isn't in the TypeLibrary until a recompile).
	/// </summary>
	/// <param name="name">Class name. Defaults to 'PlayerController'.</param>
	/// <param name="directory">Subdirectory under code/ for the file.</param>
	/// <param name="type">Movement mode: 'first_person' (mouse-look body+camera, WASD relative to facing), 'third_person' (mouse yaw, WASD relative to facing, boom camera), or 'top_down' (screen-relative WASD, fixed overhead camera, no jump). Defaults to 'first_person'. One of: first_person | third_person | top_down.</param>
	/// <param name="moveSpeed">Movement speed in units/sec. Defaults to 300.</param>
	/// <param name="jumpForce">Jump force (ignored for top_down). Defaults to 350.</param>
	/// <param name="sprintMultiplier">Sprint speed multiplier (held 'run' action). Defaults to 1.5.</param>
	/// <param name="placeInScene">If true, build a player rig in the scene: a GameObject (tagged 'player') with a CharacterController and (unless createCamera=false) a Camera. The generated controller component is NOT attached in this call — trigger_hotload then add_component_with_properties on the returned GameObject. Defaults to false (file-only).</param>
	/// <param name="createCamera">When placeInScene is true, also create a Camera (FP/TP: child at eye/boom offset; top_down: fixed overhead). Defaults to true.</param>
	/// <param name="spawnPosition">When placeInScene is true, the world position to spawn the player rig at — object {x,y,z} or comma string "x,y,z". Defaults to the origin. As "x,y,z" (or JSON {x,y,z}).</param>
	[McpTool( "create_player_controller" )]
	public static Task<object> CreatePlayerController( string name = null, string directory = null, string type = null, double? moveSpeed = null, double? jumpForce = null, double? sprintMultiplier = null, bool? placeInScene = null, bool? createCamera = null, string spawnPosition = null )
		=> McpGate.Run( "create_player_controller", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "type", type ), ( "moveSpeed", moveSpeed ), ( "jumpForce", jumpForce ), ( "sprintMultiplier", sprintMultiplier ), ( "placeInScene", placeInScene ), ( "createCamera", createCamera ), ( "spawnPosition", spawnPosition ) ) );

	/// <summary>
	/// Generate a host-authoritative round/phase machine: a [Sync(SyncFlags.FromHost)] CurrentPhase
	/// cycled through your named phases on a per-phase timer (host-only), with a static OnPhaseChanged
	/// event that fires on every machine. Great for round/match flow, match phases, or a day/night
	/// cycle. Single-player safe. Optionally attached to an existing GameObject by GUID (after a
	/// hotload). Mined from the round-flow pattern across the 51 games.
	/// </summary>
	/// <param name="name">Class name. Defaults to 'GameDirector'.</param>
	/// <param name="directory">Subdirectory for the .cs file. Defaults to 'Code'.</param>
	/// <param name="phases">Ordered phase names (become an enum), e.g. ["Lobby","Day","Night","Payout"]. Defaults to ["Lobby","Active","Ended"].</param>
	/// <param name="duration">Default seconds per phase (each phase also gets its own tunable [Property]). Defaults to 60.</param>
	/// <param name="loop">Loop back to the first phase after the last (true) or hold on the last phase (false). Defaults to true.</param>
	/// <param name="targetId">GUID of an existing GameObject to attach to (only if the type is already loaded — hotload first).</param>
	[McpTool( "create_round_phase_machine" )]
	public static Task<object> CreateRoundPhaseMachine( string name = null, string directory = null, string[] phases = null, double? duration = null, bool? loop = null, string targetId = null )
		=> McpGate.Run( "create_round_phase_machine", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "phases", phases ), ( "duration", duration ), ( "loop", loop ), ( "targetId", targetId ) ) );

	/// <summary>
	/// Generate a host-authoritative MULTI-STATE round machine (the complex variant of
	/// create_round_phase_machine). Produces one .cs file: a RoundManager singleton component + an
	/// abstract RoundState base (Begin/Tick/OnTimeUp/Finish lifecycle with a per-state
	/// [Sync(SyncFlags.FromHost)] TimeUntil timer) + one sealed stub class per named state. The manager
	/// auto-attaches the state components on start (you only place the manager), ticks ONLY the active
	/// state on the host, Advance()s on timeout with index-wrap, SKIPS any state whose CanEnter()
	/// returns false, and announces every transition via a static OnStateChanged event plus an
	/// [Rpc.Broadcast] mirror so the host fires immediately and proxies converge without waiting a
	/// snapshot (the [Sync] index reconciles late joiners). Single-player safe. USE THIS (not
	/// create_round_phase_machine) when each phase needs its OWN behaviour — entry side-effects,
	/// per-frame Tick logic, a skip condition, or copy-data-out-on-exit; use the phase machine for 3–5
	/// light phases that differ only in duration. Optionally attached to an existing GameObject by GUID
	/// (after a hotload).
	/// </summary>
	/// <param name="name">Manager class name. Defaults to 'RoundManager'. The abstract base is derived from it (RoundManager → RoundState).</param>
	/// <param name="directory">Subdirectory for the .cs file (path override). Defaults to 'Code'.</param>
	/// <param name="states">Ordered state names — each becomes a sealed {Name}State stub class. Defaults to ["Waiting","Active","PostRound"].</param>
	/// <param name="duration">Default seconds each state lasts (each state also gets its own tunable [Property] Duration). 0 = no auto-advance for a state. Defaults to 30.</param>
	/// <param name="durations">Optional per-state duration override: an array aligned to `states` ([10,120,8]) OR an object keyed by state name ({"Waiting":10,"Active":120}). Any state not covered falls back to `duration`. JSON value.</param>
	/// <param name="loop">Loop back to the first state after the last (true) or hold on the last state (false). Defaults to true.</param>
	/// <param name="targetId">GUID of an existing GameObject to attach the manager to (only if the type is already loaded — trigger_hotload first).</param>
	[McpTool( "create_round_state_machine" )]
	public static Task<object> CreateRoundStateMachine( string name = null, string directory = null, string[] states = null, double? duration = null, JsonNode durations = null, bool? loop = null, string targetId = null )
		=> McpGate.Run( "create_round_state_machine", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "states", states ), ( "duration", duration ), ( "durations", durations ), ( "loop", loop ), ( "targetId", targetId ) ) );

	/// <summary>
	/// Generate a multi-slot save MANAGER component (the slot-picker sibling of create_save_system).
	/// Use this when the game needs SEVERAL named save slots the player chooses between (New Game /
	/// Load Game menu, per-character or per-run saves) — not one silent autosave. Use
	/// create_save_system instead when a single implicit save file is enough. Emits one sealed
	/// Component that lists / creates / loads / saves / deletes N slots: a lightweight manifest file
	/// (saveslots.json) holds per-slot metadata for the picker (Used flag + Name + SavedAtUnix
	/// timestamp + PlaytimeSeconds) so listing never loads a heavy payload, and each slot's game state
	/// lives in its own saveslot_&lt;i&gt;.json. Versioned SlotData POCO with clamp-on-load Sanitize()
	/// and delete-on-version-mismatch; runs only on the owning machine (IsProxy guard). Static
	/// OnSlotLoaded / OnSlotSaved / OnSlotDeleted hooks for HUD. Storage stays within the verified
	/// FileSystem.Data.ReadJsonOrDefault / WriteJson / DeleteFile surface (index-file pattern, no
	/// directory enumeration). Set sceneReconciliation:true to also reconcile scene objects by
	/// GameObject.Id on load — records the save marks destroyed are destroyed, survivors repositioned,
	/// missing skipped (good for a placeable-world tycoon). Optionally attached to an existing
	/// GameObject by GUID (after a hotload).
	/// </summary>
	/// <param name="name">Class name for the generated component. Defaults to 'SaveSlotManager'.</param>
	/// <param name="directory">Subdirectory for the generated .cs file. Defaults to 'Code'.</param>
	/// <param name="maxSlots">How many save slots the manager manages (manifest is normalized to exactly this many, indexed 0..N-1). Clamped to 1..100. Defaults to 3.</param>
	/// <param name="sceneReconciliation">If true, saved records carry each object's GameObject.Id GUID and load reconciles the live scene against them (destroy the save's destroyed records via Scene.Directory.FindByGuid, reposition survivors, skip missing) — call RecordObject(go) to track a placeable. If false (default), the slot save is a plain payload with no scene reconciliation. Defaults to false.</param>
	/// <param name="targetId">GUID of an existing GameObject to attach the manager to (only attaches if the type is already loaded — hotload first).</param>
	[McpTool( "create_save_slots" )]
	public static Task<object> CreateSaveSlots( string name = null, string directory = null, int? maxSlots = null, bool? sceneReconciliation = null, string targetId = null )
		=> McpGate.Run( "create_save_slots", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "maxSlots", maxSlots ), ( "sceneReconciliation", sceneReconciliation ), ( "targetId", targetId ) ) );

	/// <summary>
	/// Generate a versioned save-system component: a SaveData POCO with Version bump on schema change,
	/// dirty-flag autosave on a TimeUntil timer, clamp-on-load Sanitize() for corrupt/hand-edited
	/// saves, and delete-on-version-mismatch to start fresh instead of crashing. Runs only on the
	/// owning machine (IsProxy guard). Fires static OnLoaded/OnSaved hooks for HUD and analytics.
	/// FileSystem.Data.ReadJsonOrDefault/WriteJson verified live on the current SDK. Optionally
	/// attached to an existing GameObject by GUID (after a hotload).
	/// </summary>
	/// <param name="name">Class name. Defaults to 'SaveSystem'.</param>
	/// <param name="directory">Subdirectory for the .cs file. Defaults to 'Code'.</param>
	/// <param name="fileName">Save file name under FileSystem.Data (e.g. 'save.json'). Defaults to 'save.json'.</param>
	/// <param name="version">Schema version embedded in SaveData. Old saves with a different version start fresh. Defaults to 1.</param>
	/// <param name="autosaveSeconds">Seconds between autosave ticks (0 disables autosave). Defaults to 10.</param>
	/// <param name="targetId">GUID of an existing GameObject to attach to (only if the type is already loaded -- hotload first).</param>
	[McpTool( "create_save_system" )]
	public static Task<object> CreateSaveSystem( string name = null, string directory = null, string fileName = null, int? version = null, double? autosaveSeconds = null, string targetId = null )
		=> McpGate.Run( "create_save_system", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "fileName", fileName ), ( "version", version ), ( "autosaveSeconds", autosaveSeconds ), ( "targetId", targetId ) ) );

	/// <summary>
	/// Generate an enum-keyed stat modifier system with three modifier layers: SET
	/// (highest-priority-wins hard override), ADD (summed bonuses), MULT (multiplied factors applied
	/// last). Modifier storage uses parallel private Lists of primitive types (serialization-safe).
	/// RemoveModifiersFrom(source) cleans up all mods from a buff/debuff source by reference. Static
	/// OnStatChanged(stat, value) event fires after every add/remove. Mined from RPG/buff/debuff
	/// patterns across shipped s&amp;box games. Returns { created, path, className, stats, placedOn,
	/// note } — stats echoes the sanitized stat names ({name}Stat enum values); placedOn is the target
	/// GameObject when attached (needs the type hotloaded). Follow with trigger_hotload, then
	/// get_compile_errors.
	/// </summary>
	/// <param name="name">Class name prefix -- generates {name}Stat enum + {name} Component. Defaults to 'StatSystem'.</param>
	/// <param name="directory">Subdirectory for the .cs file. Defaults to 'Code'.</param>
	/// <param name="stats">Stat names as a JSON array or comma-separated string. Defaults to 'Health,Speed,Damage'. JSON value.</param>
	/// <param name="targetId">GUID of an existing GameObject to attach to (hotload first).</param>
	[McpTool( "create_stat_modifier_system" )]
	public static Task<object> CreateStatModifierSystem( string name = null, string directory = null, JsonNode stats = null, string targetId = null )
		=> McpGate.Run( "create_stat_modifier_system", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "stats", stats ), ( "targetId", targetId ) ) );

	/// <summary>
	/// Generate a host-authoritative balanced team assigner component (smallest-bucket draft):
	/// AssignSmallest(steamId) drops a joining player into the emptiest team, announces via
	/// [Rpc.Broadcast] so every client's roster agrees, and fires static OnTeamAssigned(steamId, index,
	/// name); plus Rebalance(), GetTeam, GetMembers. Writes a .cs file and returns { created, path,
	/// className, teams, nextSteps } — follow with trigger_hotload + compile_status, attach to your
	/// game manager, call AssignSmallest from your join hook (e.g. INetworkListener.OnActive).
	/// </summary>
	/// <param name="name">Class/file name (default 'TeamAssigner' -&gt; Code/TeamAssigner.cs). Errors if the file exists.</param>
	/// <param name="directory">Directory for the .cs file. Default 'Code'.</param>
	/// <param name="teams">Team names in index order. Default ["Red", "Blue"].</param>
	[McpTool( "create_team_assigner" )]
	public static Task<object> CreateTeamAssigner( string name = null, string directory = null, string[] teams = null )
		=> McpGate.Run( "create_team_assigner", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "teams", teams ) ) );

	/// <summary>
	/// Generate a trigger-zone Component (Component.ITriggerListener): auto-adds a trigger BoxCollider
	/// on start, filters entrants by a TriggerTag [Property] (default 'player'), and logs enter/exit
	/// via private OnPlayerEnter/OnPlayerExit extension points you fill in. Writes &lt;name&gt;.cs and
	/// returns { created, path, className }. NOTE: the action/filterTag params are not currently
	/// applied at generation time — the zone always logs; implement teleport/damage/spawn in the
	/// generated methods (edit_script). Follow with trigger_hotload, then get_compile_errors.
	/// </summary>
	/// <param name="name">Class name. Defaults to 'TriggerZone'.</param>
	/// <param name="directory">Subdirectory under code/ for the file.</param>
	/// <param name="action">What happens on trigger (currently not applied by the handler — the generated zone always logs; implement the effect in OnPlayerEnter yourself). One of: log | teleport | damage | spawn.</param>
	/// <param name="filterTag">Only trigger for objects with this tag (currently not applied at generation — the generated TriggerTag [Property] defaults to 'player'; change it per-instance with set_property).</param>
	[McpTool( "create_trigger_zone" )]
	public static Task<object> CreateTriggerZone( string name = null, string directory = null, string action = null, string filterTag = null )
		=> McpGate.Run( "create_trigger_zone", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "action", action ), ( "filterTag", filterTag ) ) );

	/// <summary>
	/// Generate a cumulative-weight random loot picker: parallel Name/Weight lists
	/// (inspector-editable), a Roll() method that returns a winning entry name and fires a static
	/// OnLoot event, and optional pity (guarantee the last/rarest entry after PityAfter consecutive
	/// non-rare rolls). Roll() is host-authoritative -- only call it on the host and replicate the
	/// result (clients rolling their own loot is equivalent to clients writing their own money
	/// balance). Optionally attached to an existing GameObject by GUID (after a hotload).
	/// </summary>
	/// <param name="name">Class name. Defaults to 'LootTable'.</param>
	/// <param name="directory">Subdirectory for the .cs file. Defaults to 'Code'.</param>
	/// <param name="entries">Loot table entries. Defaults to common:70 / uncommon:25 / rare:5. JSON value.</param>
	/// <param name="pity">If true, guarantee the last (rarest) entry after PityAfter consecutive non-rare rolls. Defaults to false.</param>
	/// <param name="targetId">GUID of an existing GameObject to attach to (only if the type is already loaded -- hotload first).</param>
	[McpTool( "create_weighted_loot_table" )]
	public static Task<object> CreateWeightedLootTable( string name = null, string directory = null, JsonNode entries = null, bool? pity = null, string targetId = null )
		=> McpGate.Run( "create_weighted_loot_table", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "entries", entries ), ( "pity", pity ), ( "targetId", targetId ) ) );
}