Supervisor S-3/S-4: MCP-Light-Server, Profile, gespeicherte Berichte, Designer-UI
S-4 MCP-Light (Daten-Tuer fuer externe KI-Clients, KEIN Modell-Zugang - Modelle laufen weiter ueber OpenRouter): - McpJsonRpc (pur): JSON-RPC 2.0 fuer initialize/ping/tools/list/tools/call ueber die read-only Tool-Registry; Notifications/Fehlerfaelle spezifikationskonform. - McpLightServer (HostedService): lokaler Streamable-HTTP-Endpoint. OPT-IN via POLYTRADER_MCP_PORT, bindet NUR 127.0.0.1. Claude Code: claude mcp add --transport http polytrader http://127.0.0.1:PORT/mcp - End-to-End-Test ueber echtes HTTP (initialize, tools/call, Notification=202, GET=405). S-3 Profile + Berichte: - SupervisorProfiles: Allgemein/Technik/CopyTrading/ResolutionFarming als System-Prompt- Zusatz + Tool-Subset ueber EINER Agent-Infrastruktur (Technik z.B. ohne Strategie-Tools). Agent filtert Tools je Profil. - sup_reports (SupervisorDbContext, Migration generiert UND angewendet): jede Analyse wird mit Profil/Modell/Frage/Antwort/Tool-Aufrufen/Token gespeichert -> Supervisor auditierbar. UI (Richards Vorgabe: designerfaehig): - SupervisorMainForm auf partial + .Designer.cs umgestellt - alle Controls im Designer (3 Tabs: Analyse mit Profil-Combo+Modellfeld, Dossiers, Berichte mit Split/Grid/Detail). Tests: +16 (MCP-JSON-RPC 6, MCP-HTTP-E2E 1, Profile 2, bestehende erweitert). Build 0 Fehler, 360 Tests gruen, --smoke-ui alle 5 Views gruen. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
5d732277b2
commit
b7b2a141e3
@@ -0,0 +1,89 @@
|
||||
using System.Text.Json;
|
||||
using PolyTrader.Modules.Supervisor.Agent;
|
||||
using PolyTrader.Modules.Supervisor.Mcp;
|
||||
using Xunit;
|
||||
|
||||
namespace PolyTrader.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user