Editor/Mcp/McpProtocol.cs
using Sandbox;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace SboxMcp.Mcp;

// ----------------------------------------------------------------------------
// JSON-RPC 2.0 envelopes (MCP wire format)
// ----------------------------------------------------------------------------

public class JsonRpcRequest
{
	[JsonPropertyName( "jsonrpc" )] public string Version { get; set; } = "2.0";
	[JsonPropertyName( "id" )] public JsonElement? Id { get; set; }
	[JsonPropertyName( "method" )] public string Method { get; set; } = "";
	[JsonPropertyName( "params" )] public JsonElement? Params { get; set; }

	[JsonIgnore] public bool IsNotification => !Id.HasValue || Id.Value.ValueKind == JsonValueKind.Null || Id.Value.ValueKind == JsonValueKind.Undefined;
}

public class JsonRpcResponse
{
	[JsonPropertyName( "jsonrpc" )] public string Version { get; set; } = "2.0";
	[JsonPropertyName( "id" )] public JsonElement Id { get; set; }

	[JsonPropertyName( "result" )]
	[JsonIgnore( Condition = JsonIgnoreCondition.WhenWritingNull )]
	public object Result { get; set; }

	[JsonPropertyName( "error" )]
	[JsonIgnore( Condition = JsonIgnoreCondition.WhenWritingNull )]
	public JsonRpcError Error { get; set; }

	public static JsonRpcResponse Ok( JsonElement id, object result ) =>
		new() { Id = id, Result = result };

	public static JsonRpcResponse Fail( JsonElement id, int code, string message ) =>
		new() { Id = id, Error = new JsonRpcError { Code = code, Message = message } };
}

public class JsonRpcError
{
	[JsonPropertyName( "code" )] public int Code { get; set; }
	[JsonPropertyName( "message" )] public string Message { get; set; } = "";

	[JsonPropertyName( "data" )]
	[JsonIgnore( Condition = JsonIgnoreCondition.WhenWritingNull )]
	public object Data { get; set; }
}

// ----------------------------------------------------------------------------
// MCP-specific message shapes
// ----------------------------------------------------------------------------

public static class McpErrorCodes
{
	public const int ParseError = -32700;
	public const int InvalidRequest = -32600;
	public const int MethodNotFound = -32601;
	public const int InvalidParams = -32602;
	public const int InternalError = -32603;
}

public class InitializeResult
{
	[JsonPropertyName( "protocolVersion" )] public string ProtocolVersion { get; set; } = "2024-11-05";
	[JsonPropertyName( "capabilities" )] public ServerCapabilities Capabilities { get; set; } = new();
	[JsonPropertyName( "serverInfo" )] public ServerInfo ServerInfo { get; set; } = new();
}

public class ServerCapabilities
{
	[JsonPropertyName( "tools" )] public ToolsCapability Tools { get; set; } = new();
}

public class ToolsCapability
{
	[JsonPropertyName( "listChanged" )] public bool ListChanged { get; set; } = false;
}

public class ServerInfo
{
	[JsonPropertyName( "name" )] public string Name { get; set; } = "sbox-mcp";
	[JsonPropertyName( "version" )] public string Version { get; set; } = "2.0.0";
}

public class ToolListResult
{
	[JsonPropertyName( "tools" )] public List<ToolDescriptor> Tools { get; set; } = new();
}

public class ToolDescriptor
{
	[JsonPropertyName( "name" )] public string Name { get; set; } = "";
	[JsonPropertyName( "description" )] public string Description { get; set; } = "";
	[JsonPropertyName( "inputSchema" )] public object InputSchema { get; set; }
}

public class ToolCallParams
{
	[JsonPropertyName( "name" )] public string Name { get; set; } = "";
	[JsonPropertyName( "arguments" )] public JsonElement? Arguments { get; set; }
}

public class ToolCallResult
{
	[JsonPropertyName( "content" )] public List<ToolContent> Content { get; set; } = new();
	[JsonPropertyName( "isError" )]
	[JsonIgnore( Condition = JsonIgnoreCondition.WhenWritingDefault )]
	public bool IsError { get; set; }
}

public class ToolContent
{
	[JsonPropertyName( "type" )] public string Type { get; set; } = "text";
	[JsonPropertyName( "text" )] public string Text { get; set; } = "";

	public static ToolContent FromText( string text ) => new() { Text = text ?? "" };
}