Editor/Server/McpTypes.cs

MCP (Mod Control Protocol) server types and result builders used by the Editor server. Defines McpToolDescriptor record, helper factories for standardized MCP response payloads (initialize, tools list, text/image content), and protocol version negotiation logic.

using System.Collections.Generic;
using System.Linq;
using System.Text.Json;

namespace SboxMcp.Server;

/// <summary>
/// A tool as advertised to MCP clients via tools/list.
/// </summary>
public record McpToolDescriptor( string Name, string Description, JsonElement InputSchema );

/// <summary>
/// Result payload shapes defined by the MCP specification.
/// </summary>
public static class McpResults
{
	public const string ServerName = "sbox-mcp";
	public const string ServerVersion = "1.0.0";

	public static object Initialize( string negotiatedVersion ) => new
	{
		protocolVersion = negotiatedVersion,
		capabilities = new { tools = new { listChanged = false } },
		serverInfo = new { name = ServerName, version = ServerVersion }
	};

	public static object ToolsList( IEnumerable<McpToolDescriptor> tools ) => new
	{
		tools = tools.ToArray()
	};

	public static object TextContent( string text, bool isError = false ) => new
	{
		content = new object[] { new { type = "text", text } },
		isError
	};

	public static object ImageContent( string base64Png, string text = null )
	{
		var content = new List<object> { new { type = "image", data = base64Png, mimeType = "image/png" } };
		if ( !string.IsNullOrEmpty( text ) )
			content.Add( new { type = "text", text } );

		return new { content = content.ToArray(), isError = false };
	}
}

public static class McpVersion
{
	/// <summary>
	/// Protocol revisions this server understands. 2025-06-18 only: older
	/// revisions REQUIRE JSON-RPC batch support, which this server does not
	/// implement, so advertising them would be a lie.
	/// </summary>
	public static readonly string[] Supported = { "2025-06-18" };

	/// <summary>Exact match wins; anything else gets our newest revision.</summary>
	public static string Negotiate( string clientRequested ) =>
		Supported.Contains( clientRequested ) ? clientRequested : Supported[0];
}