Editor/AssetToolHandlers.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using Editor;
using Sandbox;

namespace SboxMcpServer;

/// <summary>
/// Handlers for asset-browsing and editor-context MCP tools:
/// browse_assets and get_editor_context.
/// </summary>
internal static class AssetToolHandlers
{
	// ── browse_assets ──────────────────────────────────────────────────────

	internal static object BrowseAssets( JsonElement args, JsonSerializerOptions jsonOptions )
	{
		string typeFilter = null;
		string nameFilter = null;
		int    maxResults = 100;

		if ( args.ValueKind != JsonValueKind.Undefined )
		{
			if ( args.TryGetProperty( "type",        out var tp ) ) typeFilter = tp.GetString();
			if ( args.TryGetProperty( "nameContains", out var np ) ) nameFilter = np.GetString();
			if ( args.TryGetProperty( "maxResults",   out var mr ) ) maxResults = Math.Clamp( mr.GetInt32(), 1, 500 );
		}

		var results   = new List<Dictionary<string, object>>();
		int totalSeen = 0;

		try
		{
			foreach ( var asset in AssetSystem.All )
			{
				totalSeen++;

				var assetType = asset.AssetType?.FileExtension ?? "";
				var assetName = asset.Name ?? "";

				// Type filter: match by file extension or friendly type name (case-insensitive)
				if ( !string.IsNullOrEmpty( typeFilter ) )
				{
					bool typeMatch =
						assetType.IndexOf( typeFilter, StringComparison.OrdinalIgnoreCase ) >= 0 ||
						( asset.AssetType?.FriendlyName ?? "" ).IndexOf( typeFilter, StringComparison.OrdinalIgnoreCase ) >= 0;
					if ( !typeMatch ) continue;
				}

				// Name filter: match basename (case-insensitive)
				if ( !string.IsNullOrEmpty( nameFilter ) &&
					assetName.IndexOf( nameFilter, StringComparison.OrdinalIgnoreCase ) < 0 ) continue;

				if ( results.Count >= maxResults ) break;

				results.Add( new Dictionary<string, object>
				{
					["path"]       = asset.Path ?? "",
					["name"]       = assetName,
					["type"]       = asset.AssetType?.FriendlyName ?? assetType,
					["extension"]  = assetType,
					["relativePath"] = asset.RelativePath ?? asset.Path ?? ""
				} );
			}
		}
		catch ( Exception ex )
		{
			return ToolHandlerBase.TextResult( $"Error enumerating assets: {ex.Message}" );
		}

		var summary = $"Found {results.Count} asset(s)" +
			( !string.IsNullOrEmpty( typeFilter ) ? $" of type '{typeFilter}'" : "" ) +
			( !string.IsNullOrEmpty( nameFilter ) ? $" matching '{nameFilter}'" : "" ) +
			$" (scanned {totalSeen} total).";

		var json = JsonSerializer.Serialize( new { summary, results }, jsonOptions );
		return ToolHandlerBase.TextResult( json );
	}

	// ── get_editor_context ─────────────────────────────────────────────────

	internal static object GetEditorContext( JsonSerializerOptions jsonOptions )
	{
		var ctx = new Dictionary<string, object>();

		// Active game scene (in-play)
		ctx["activeGameScene"] = Game.ActiveScene?.Name;

		// Editor session info
		try
		{
			var sessions = new List<Dictionary<string, object>>();
			foreach ( var session in SceneEditorSession.All )
			{
				if ( session == null ) continue;
				sessions.Add( new Dictionary<string, object>
				{
					["isActive"]    = session == SceneEditorSession.Active,
					["sceneName"]   = session.Scene?.Name,
					["isPrefab"]    = session.Scene?.Name?.EndsWith( ".prefab", StringComparison.OrdinalIgnoreCase ) ?? false,
					["objectCount"] = session.Scene != null
						? session.Scene.Children.Count
						: 0
				} );
			}
			ctx["editorSessions"]       = sessions;
			ctx["activeSessionScene"]   = SceneEditorSession.Active?.Scene?.Name;

			// Selection
			var sel = new List<Dictionary<string, object>>();
			foreach ( var go in GetSelectedGameObjects() )
			{
				sel.Add( new Dictionary<string, object>
				{
					["id"]   = go.Id.ToString(),
					["name"] = go.Name,
					["path"] = SceneQueryHelpers.GetObjectPath( go )
				} );
			}
			ctx["selectedObjects"] = sel;
		}
		catch ( Exception ex )
		{
			ctx["editorApiError"] = ex.Message;
		}

		var json = JsonSerializer.Serialize( ctx, jsonOptions );
		return ToolHandlerBase.TextResult( json );
	}
	private static IEnumerable<GameObject> GetSelectedGameObjects()
	{
		var result = new List<GameObject>();
		try
		{
			var session = SceneEditorSession.Active;
			if ( session == null ) return result;
			var selProp = session.GetType().GetProperty( "Selection",
				System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance );
			var selObj = selProp?.GetValue( session );
			if ( selObj == null ) return result;
			var objsProp = selObj.GetType().GetProperty( "Objects" );
			if ( objsProp?.GetValue( selObj ) is IEnumerable<object> objs )
				foreach ( var o in objs )
					if ( o is GameObject go ) result.Add( go );
		}
		catch { }
		return result;
	}
}