feat: LehrerApp.McpBridge zu resilientem MCP-Proxy umgebaut

Claude Desktop deaktivierte den ganzen MCP-Server, sobald der Initialize-Handshake beim
Bridge-Start fehlschlug - der Regelfall, wenn LehrerApp.Desktop (z.B. nach Windows-Autostart
von Claude Desktop) noch nicht läuft. Die Bridge ist jetzt selbst ein MCP-Server gegenüber
Claude Desktop und gleichzeitig ein MCP-Client gegenüber dem echten Server in LehrerApp.Desktop:
der Handshake gelingt dadurch immer.

Verbunden spiegelt sie die echte Werkzeugliste 1:1; ohne Verbindung bietet sie nur ein
lehrerapp_status-Werkzeug an, das den Grund erklärt und erneut verbindet. Ein Hintergrund-Loop
versucht unabhängig davon alle 5s zu reconnecten und schaltet per notifications/tools/list_changed
automatisch auf die echten Werkzeuge um, sobald LehrerApp erreichbar ist. Bricht die Verbindung
während eines laufenden Aufrufs ab, kommt ein normales Tool-Fehlerergebnis statt eines
Prozessabsturzes zurück.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-12 21:45:03 +02:00
co-authored by Claude Sonnet 5
parent 3f8813df51
commit d979a84e7b
7 changed files with 378 additions and 73 deletions
+56
View File
@@ -2756,6 +2756,62 @@ folgenden Punkte gehören direkt in `LehrerApp.Desktop`:
schrieb und ein reiner No-Op genügte.
- 16 neue Tests in [McpToolsTests.cs](LehrerApp.Desktop.Tests/McpToolsTests.cs) (jetzt 57).
- [x] **4.5.34** `LehrerApp.McpBridge` zu einem resilienten MCP-Proxy umgebaut, statt bei
fehlendem Kontakt zu LehrerApp den ganzen Prozess fehlschlagen zu lassen (2026-09-12,
Nutzer-Feedback): Claude Desktop startet per Windows-Autostart oft, bevor LehrerApp.Desktop
läuft. Die Bridge führte den MCP-Initialize-Handshake bisher selbst nur einmal beim
Prozessstart durch und ließ ihn fehlschlagen, wenn die Pipe zu diesem Zeitpunkt nicht
erreichbar war — Claude Desktop markiert einen MCP-Server nach einem fehlgeschlagenen
Handshake als tot und deaktiviert ihn für die Sitzung, ohne erneut zu versuchen. Ein erster
Zwischenschritt (nur die Fehlermeldung nach Ursache unterscheiden, `TimeoutException` vs.
echter Fehler) hätte daran nichts geändert — auf Nachfrage entschieden, stattdessen das
eigentliche Problem zu lösen.
- **Neue Architektur:** die Bridge ist jetzt selbst ein vollwertiger MCP-Server (über
`ModelContextProtocol.Server`, dieselbe SDK-Bausteine wie `McpServerHostedService` in
LehrerApp.Desktop) UND gleichzeitig ein MCP-**Client** gegenüber dem echten Server in
LehrerApp.Desktop (`ModelContextProtocol.Client.McpClient` über
`ModelContextProtocol.Protocol.StreamClientTransport` auf derselben Named Pipe). Der
Initialize-Handshake gegenüber Claude Desktop gelingt dadurch **immer**, unabhängig davon,
ob LehrerApp erreichbar ist.
- Neues [RealServerBridge.cs](LehrerApp.McpBridge/RealServerBridge.cs): hält die Client-
Verbindung zu LehrerApp und spiegelt deren aktuelle Werkzeugliste 1:1 (Name, Beschreibung,
Eingabeschema unverändert übernommen) in die Werkzeugsammlung, die die Bridge Claude
Desktop anbietet — kein eigenes Wissen über einzelne LehrerApp-Werkzeuge nötig, ändert sich
eins in LehrerApp.Desktop, übernimmt die Bridge das automatisch beim nächsten
Verbindungsaufbau. Ist LehrerApp nicht erreichbar, bietet die Bridge statt der echten
Werkzeuge genau ein Werkzeug an,
[StatusMcpServerTool.cs](LehrerApp.McpBridge/StatusMcpServerTool.cs)
(`lehrerapp_status`) — es erklärt der KI, dass LehrerApp gestartet werden muss, und
versucht bei jedem Aufruf aktiv erneut zu verbinden. Ein Hintergrund-Loop
(`RunBackgroundReconnectLoopAsync`, alle 5s) versucht das unabhängig davon ebenfalls
laufend, damit die echten Werkzeuge auch ohne aktives Nachfragen zurückkommen, sobald der
Nutzer LehrerApp startet. Jeder Wechsel zwischen Status-Werkzeug und echter Werkzeugliste
löst automatisch `notifications/tools/list_changed` aus
(`McpServerPrimitiveCollection<T>.DeferChangedEvents`), worauf ein MCP-konformer Client
seine Werkzeugliste neu abruft.
- [ProxyMcpServerTool.cs](LehrerApp.McpBridge/ProxyMcpServerTool.cs) reicht einen Tool-Aufruf
unverändert an den echten Client weiter; bricht die Verbindung während eines Aufrufs ab
(LehrerApp wurde beendet), liefert `RealServerBridge.CallRealToolAsync` ein reguläres
`CallToolResult` mit `IsError = true` und einer erklärenden Nachricht zurück — kein
Transport-/Prozessabbruch, die Sitzung mit Claude Desktop bleibt bestehen, nur dieser eine
Aufruf schlägt sichtbar (für den Assistenten erklärbar) fehl.
- [ConnectionDiagnostics.cs](LehrerApp.McpBridge/ConnectionDiagnostics.cs): dieselbe
Unterscheidung wie im verworfenen Zwischenschritt (`TimeoutException` = LehrerApp läuft
nicht/MCP-Server nicht aktiviert, alles andere = echtes Konfigurations-/Rechteproblem, z.B.
ein ACL-Mismatch zwischen den Prozessen) — jetzt als gemeinsam genutzter Baustein an allen
drei Verbindungsversuchsstellen (initialer Connect, Hintergrund-Loop, Status-Werkzeug)
statt nur im alten Einzel-Connect-Pfad.
- End-to-End mit einer echten, laufenden LehrerApp.Desktop-Instanz verifiziert (manuelles
JSON-RPC über die Bridge): erfolgreicher Connect übernimmt alle 35 registrierten Werkzeuge
mit vollem Schema; `get_students` liefert echte Daten; LehrerApp.Desktop während einer
laufenden Sitzung beendet lässt einen in Arbeit befindlichen Aufruf mit lesbarer
Fehlermeldung statt Absturz fehlschlagen und schaltet auf das Status-Werkzeug um;
LehrerApp.Desktop neu gestartet stellt die volle Werkzeugliste innerhalb des
Hintergrund-Loop-Intervalls automatisch wieder her.
- Kein zusätzliches Feature wie Windows-Autostart für LehrerApp.Desktop selbst oder ein
Tray-Icon (auf Nachfrage bewusst nicht Teil dieser Änderung) — die Bridge löst das Problem
rein auf Protokollebene, unabhängig davon, wann der Nutzer LehrerApp tatsächlich startet.
**Wichtige Abweichung von der ursprünglichen Planung (5.2):** Vor der Umsetzung zeigte sich,
dass 5.2 wie ursprünglich beschrieben eine zweite, parallele Fehlzeiten-Erfassung neben dem
bereits bestehenden Anwesenheits-Tracking aus Kapitel 3 (`ParticipationEntry.Attendance`,