Editor/Registry/SchemaGenerator.cs

Utility that generates a JSON Schema JsonElement for a reflected MethodInfo. It iterates method parameters, builds property schemas from their types and attributes, marks required parameters, and returns a top-level object schema.

Reflection
using System;
using System.Collections;
using System.Linq;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Nodes;

namespace SboxMcp.Registry;

/// <summary>
/// Reflects a tool method's parameters into a JSON Schema object.
/// </summary>
public static class SchemaGenerator
{
	public static JsonElement ForMethod( MethodInfo method )
	{
		var properties = new JsonObject();
		var required = new JsonArray();

		foreach ( var p in method.GetParameters() )
		{
			var prop = ForType( p.ParameterType );

			var desc = p.GetCustomAttribute<DescAttribute>()?.Text;
			if ( desc is not null )
				prop["description"] = desc;

			if ( p.HasDefaultValue )
			{
				if ( p.DefaultValue is not null )
					prop["default"] = JsonValue.Create( p.DefaultValue is Enum e ? e.ToString() : p.DefaultValue );
			}
			else
			{
				required.Add( p.Name );
			}

			properties[p.Name] = prop;
		}

		var schema = new JsonObject
		{
			["type"] = "object",
			["properties"] = properties
		};

		if ( required.Count > 0 )
			schema["required"] = required;

		return JsonSerializer.SerializeToElement( schema );
	}

	static JsonObject ForType( Type t )
	{
		t = Nullable.GetUnderlyingType( t ) ?? t;

		if ( t == typeof( string ) )
			return new JsonObject { ["type"] = "string" };

		if ( t == typeof( bool ) )
			return new JsonObject { ["type"] = "boolean" };

		if ( t == typeof( int ) || t == typeof( long ) || t == typeof( short ) || t == typeof( byte ) )
			return new JsonObject { ["type"] = "integer" };

		if ( t == typeof( float ) || t == typeof( double ) || t == typeof( decimal ) )
			return new JsonObject { ["type"] = "number" };

		if ( t.IsEnum )
		{
			var values = new JsonArray();
			foreach ( var name in Enum.GetNames( t ) )
				values.Add( name );

			return new JsonObject { ["type"] = "string", ["enum"] = values };
		}

		if ( t.IsArray )
			return new JsonObject { ["type"] = "array", ["items"] = ForType( t.GetElementType() ) };

		if ( t.IsGenericType && typeof( IEnumerable ).IsAssignableFrom( t ) )
			return new JsonObject { ["type"] = "array", ["items"] = ForType( t.GetGenericArguments()[0] ) };

		if ( t == typeof( JsonElement ) )
			return new JsonObject(); // accepts anything

		// fall back to a JSON-deserializable object
		return new JsonObject { ["type"] = "object" };
	}
}