using System.Text.Json; using PolyTrader.Modules.Supervisor.Agent; using PolyTrader.Modules.Supervisor.Mcp; using Xunit; namespace PolyTrader.Tests { /// /// Sicherheitsnetz für MCP-Light (S-4): JSON-RPC-Handling (initialize, tools/list, tools/call, /// Notifications, Fehlerfälle) über die read-only Tool-Registry — pur, ohne HTTP. /// public class McpJsonRpcTests { private static SupervisorToolRegistry Registry() { var reg = new SupervisorToolRegistry(); reg.Register(new SupervisorTool("echo", "Echo-Tool", """{"type":"object","properties":{"text":{"type":"string"}}}""", args => "ECHO:" + (SupervisorToolRegistry.GetString(args, "text") ?? ""))); return reg; } [Fact] public void Initialize_returns_protocol_and_serverinfo() { string? resp = McpJsonRpc.Handle( """{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26"}}""", Registry()); Assert.NotNull(resp); using var doc = JsonDocument.Parse(resp!); var result = doc.RootElement.GetProperty("result"); Assert.Equal(McpJsonRpc.ProtocolVersion, result.GetProperty("protocolVersion").GetString()); Assert.Equal(McpJsonRpc.ServerName, result.GetProperty("serverInfo").GetProperty("name").GetString()); Assert.True(result.GetProperty("capabilities").TryGetProperty("tools", out _)); } [Fact] public void ToolsList_exposes_registry_tools_with_schema() { string? resp = McpJsonRpc.Handle("""{"jsonrpc":"2.0","id":2,"method":"tools/list"}""", Registry()); using var doc = JsonDocument.Parse(resp!); var tools = doc.RootElement.GetProperty("result").GetProperty("tools"); Assert.Equal(1, tools.GetArrayLength()); Assert.Equal("echo", tools[0].GetProperty("name").GetString()); Assert.Equal("object", tools[0].GetProperty("inputSchema").GetProperty("type").GetString()); } [Fact] public void ToolsCall_executes_and_wraps_result_as_text_content() { string? resp = McpJsonRpc.Handle( """{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"echo","arguments":{"text":"hallo"}}}""", Registry()); using var doc = JsonDocument.Parse(resp!); var result = doc.RootElement.GetProperty("result"); Assert.Equal("ECHO:hallo", result.GetProperty("content")[0].GetProperty("text").GetString()); Assert.False(result.GetProperty("isError").GetBoolean()); } [Fact] public void ToolsCall_unknown_tool_sets_isError() { string? resp = McpJsonRpc.Handle( """{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"nix","arguments":{}}}""", Registry()); using var doc = JsonDocument.Parse(resp!); Assert.True(doc.RootElement.GetProperty("result").GetProperty("isError").GetBoolean()); } [Fact] public void Notification_returns_null_and_unknown_method_returns_error() { Assert.Null(McpJsonRpc.Handle("""{"jsonrpc":"2.0","method":"notifications/initialized"}""", Registry())); string? resp = McpJsonRpc.Handle("""{"jsonrpc":"2.0","id":5,"method":"gibtsnicht"}""", Registry()); using var doc = JsonDocument.Parse(resp!); Assert.Equal(-32601, doc.RootElement.GetProperty("error").GetProperty("code").GetInt32()); } [Fact] public void Parse_error_returns_minus32700() { string? resp = McpJsonRpc.Handle("{kaputt", Registry()); using var doc = JsonDocument.Parse(resp!); Assert.Equal(-32700, doc.RootElement.GetProperty("error").GetProperty("code").GetInt32()); } } }