feat: lokaler MCP-Server, Phase 4 (Elternbrief-Vorlagen + Claude-Desktop-Registrierung)

Schließt die MCP-Server-Spec ab. "Worksheets" aus der Spec entsprechen
im tatsächlichen Datenmodell den .lavorlage-Elternbrief-Vorlagen
(LehrerApp.Templating) - es gibt kein separates Arbeitsblatt-Konzept mit
Fach/Klassenstufe-Metadaten. Neue Tools list_letter_templates (Read) und
render_letter (Read, liefert Base64-PDF, kein DB-Schreibzugriff).

upload_worksheet/update_worksheet bewusst nicht umgesetzt: das
Seitenlayout ist eine eigene positionsbasierte DSL mit eigenem
visuellen Editor (LehrerApp.TemplateDesigner) - ein LLM müsste sie
blind erzeugen, mit hohem Risiko für kaputte Layouts. Platzhalter-Logik
aus CreateLetterDialogViewModel nach LetterPlaceholderBuilder extrahiert,
damit Dialog und MCP-Tool nicht auseinanderdriften.

Neuer McpClientRegistrationService trägt den Bridge-Pfad in Claude
Desktops claude_desktop_config.json ein (Button in den Einstellungen,
nie automatisch), ohne bestehende Fremdeinträge zu verlieren und ohne
eine nicht lesbare Konfigurationsdatei zu überschreiben.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-11 22:53:46 +02:00
co-authored by Claude Sonnet 5
parent dd2e1e7c61
commit 55fba2cadb
17 changed files with 689 additions and 26 deletions
@@ -1,5 +1,7 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using LehrerApp.Desktop.Services;
using LehrerApp.Desktop.Services.Mcp;
namespace LehrerApp.Desktop.ViewModels.Settings;
@@ -14,4 +16,27 @@ public partial class SettingsViewModel
// Kein Live-Reload (siehe McpServerHostedService/Planungsdokument) - der Pipe-Listener wird
// nur beim App-Start eingerichtet, deshalb wirkt eine Änderung hier erst nach Neustart.
partial void OnMcpEnabledChanged(bool value) => _mcpSettings.SetEnabled(value);
// ── MCP: Registrierung bei Claude Desktop ─────────────────────────────────
[ObservableProperty] private string _mcpRegistrationStatus = "";
[ObservableProperty] private bool _mcpIsRegisteredWithClaude;
private void LoadMcpRegistrationStatus() => McpIsRegisteredWithClaude = _mcpRegistration.IsRegistered();
[RelayCommand]
private void RegisterWithClaudeDesktop()
{
var result = _mcpRegistration.Register();
McpRegistrationStatus = result.Message;
McpIsRegisteredWithClaude = _mcpRegistration.IsRegistered();
}
[RelayCommand]
private void UnregisterFromClaudeDesktop()
{
var result = _mcpRegistration.Unregister();
McpRegistrationStatus = result.Message;
McpIsRegisteredWithClaude = _mcpRegistration.IsRegistered();
}
}
@@ -75,6 +75,7 @@ public partial class SettingsViewModel : ObservableObject
private readonly AiSettingsService _aiSettings;
private readonly AiPlanningService _aiPlanning;
private readonly McpSettingsService _mcpSettings;
private readonly Services.Mcp.McpClientRegistrationService _mcpRegistration;
private readonly WebUntisSettingsService _untisSettings;
private readonly WebUntisIntegrationService? _untisIntegration;
private readonly UntisSyncService? _untisSync;
@@ -102,6 +103,7 @@ public partial class SettingsViewModel : ObservableObject
SchoolCalendarSettingsService calendarSettings, PeriodScheduleService periodSchedule,
ISupervisionDutyRepository supervisionDuties, TemplateStore letterTemplates,
AiSettingsService aiSettings, AiPlanningService aiPlanning, McpSettingsService mcpSettings,
Services.Mcp.McpClientRegistrationService mcpRegistration,
WebUntisSettingsService untisSettings,
AnnualPlanSettingsService annualPlanSettings,
SyncSettingsService syncSettings, SyncAuthService syncAuth, EventQueue eventQueue,
@@ -140,6 +142,7 @@ public partial class SettingsViewModel : ObservableObject
_aiSettings = aiSettings;
_aiPlanning = aiPlanning;
_mcpSettings = mcpSettings;
_mcpRegistration = mcpRegistration;
_untisSettings = untisSettings;
_untisIntegration = untisIntegration;
_untisSync = untisSync;
@@ -168,6 +171,7 @@ public partial class SettingsViewModel : ObservableObject
LoadLetterTemplates();
LoadAiSettings();
LoadMcpSettings();
LoadMcpRegistrationStatus();
LoadUntisSettings();
LoadAnnualPlanSettings();
LoadSyncSettings();