Editor tool method that scans the active scene for GameObjects whose names match given prefixes or have a specified tag, optionally destroys them, and returns a JSON summary of candidates and results.
#nullable enable
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using Sandbox;
namespace Editor.Mcp
{
public static partial class SboxMcpAssistant
{
/// <summary>
/// Scans and purges temporary test GameObjects, procedural hazard clutter, orbit cameras,
/// and orphaned test entities created during automated test iterations.
/// </summary>
[McpTool( "pocketknife_scene_cleanup" )]
public static string SceneCleanup(
[Description( "Comma-separated list of name prefixes to purge (default: '__temp_,Procedural_,Test_,Auto_,OrbitCamera_,Brush_,Audio_')" )] string prefixes = "__temp_,Procedural_,Test_,Auto_,OrbitCamera_,Brush_,Audio_",
[Description( "Optional tag filter (e.g. 'test' or 'temporary')" )] string? tag = null,
[Description( "If true, simulates the purge and returns candidates without destroying them" )] bool dryRun = false )
{
try
{
var scene = EditorRuntime.ResolveActiveScene();
if ( scene == null )
{
return McpJson.Write( ToolOutcome<object>.Failure( null, ToolStatus.Unavailable, "scene.not_found", "No active scene loaded." ) );
}
var prefixList = (prefixes ?? "")
.Split( new[] { ',', ';', ' ' }, StringSplitOptions.RemoveEmptyEntries )
.Select( p => p.Trim() )
.Where( p => p.Length > 0 )
.ToArray();
var allObjects = scene.GetAllObjects( false ).ToList();
var toPurge = new List<GameObject>();
foreach ( var go in allObjects )
{
if ( !go.IsValid() ) continue;
var matchesPrefix = prefixList.Any( p => go.Name.StartsWith( p, StringComparison.OrdinalIgnoreCase ) );
var matchesTag = !string.IsNullOrWhiteSpace( tag ) && go.Tags.Has( tag.Trim() );
if ( matchesPrefix || matchesTag )
{
toPurge.Add( go );
}
}
var purgedList = toPurge.Select( go => new
{
Name = go.Name,
Id = go.Id.ToString(),
Position = go.WorldPosition,
Tags = string.Join( ", ", go.Tags.TryGetAll() )
} ).ToArray();
if ( !dryRun )
{
foreach ( var go in toPurge )
{
if ( go.IsValid() )
{
go.Destroy();
}
}
}
return McpJson.Write( ToolOutcome<object>.Ready( new
{
DryRun = dryRun,
TotalPurged = purgedList.Length,
PrefixesUsed = prefixList,
TagFilter = tag,
PurgedObjects = purgedList,
Success = true
} ) );
}
catch ( Exception ex )
{
return McpJson.Write( ToolOutcome<object>.Failure( null, ToolStatus.Failed, "cleanup.failed", "Scene cleanup operation failed.", ex ) );
}
}
}
}