feat: lokaler MCP-Server, Phase 2 (Write-Tools + Bestätigungsdialog + Lesson-Plans)
Neue Write-Tools create_time_entry, create_grade_entry und update_student_group_assignment schreiben nie direkt: jeder Aufruf zeigt zuerst einen menschenlesbaren Bestätigungsdialog (bestehender ConfirmDialog, über Dispatcher.UIThread aus dem Pipe-Session-Thread angezeigt) und schreibt erst nach Bestätigung, mit 2-Minuten-Timeout gegen eine hängende Session. Zusätzliches Read-Tool get_lesson_plans. create_note bewusst nicht umgesetzt (kollidiert mit dem bestehenden Dokumentations-Ausschluss aus Phase 1), create_lesson_plan/ update_lesson_plan wegen der Modellkomplexität von Lesson zurückgestellt (siehe TODO.md 4.5.26). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,12 +1,13 @@
|
||||
using System.ComponentModel;
|
||||
using LehrerApp.Core.Interfaces;
|
||||
using LehrerApp.Core.Models;
|
||||
|
||||
namespace LehrerApp.Desktop.Services.Mcp.Tools;
|
||||
|
||||
/// <summary>MCP-Read-Tool "get_time_entries" (Phase 1, siehe Planungsdokument). Der Zeitraum ist
|
||||
/// Pflicht (nicht optional), damit eine unbedachte Anfrage nicht die gesamte Zeiterfassungshistorie
|
||||
/// zurückgibt.</summary>
|
||||
public class TimeEntryTools(ITimeEntryRepository timeEntries)
|
||||
/// <summary>MCP-Tools "get_time_entries" (Phase 1) und "create_time_entry" (Phase 2), siehe
|
||||
/// Planungsdokument. Der Zeitraum bei "get_time_entries" ist Pflicht (nicht optional), damit eine
|
||||
/// unbedachte Anfrage nicht die gesamte Zeiterfassungshistorie zurückgibt.</summary>
|
||||
public class TimeEntryTools(ITimeEntryRepository timeEntries, IGroupRepository groups, IMcpConfirmationService confirmation)
|
||||
{
|
||||
[Description("Listet eigene Zeiterfassungs-Einträge in einem Datumsbereich.")]
|
||||
public List<TimeEntryDto> GetTimeEntries(
|
||||
@@ -15,4 +16,34 @@ public class TimeEntryTools(ITimeEntryRepository timeEntries)
|
||||
timeEntries.GetByDateRange(from, to).Select(t => new TimeEntryDto(
|
||||
t.Id, t.TaskId, t.Category, t.GroupId, t.Date, t.StartTime, t.EndTime,
|
||||
t.DurationMinutes, t.Description)).ToList();
|
||||
|
||||
[Description("Schlägt einen neuen Zeiterfassungs-Eintrag vor. Muss der Nutzer erst in einem Dialog in LehrerApp bestätigen, bevor er gespeichert wird.")]
|
||||
public async Task<WriteResultDto> CreateTimeEntry(
|
||||
[Description("Kategorie, z.B. \"Unterricht\", \"Korrektur\", \"Vorbereitung\".")] string category,
|
||||
[Description("Datum, Format YYYY-MM-DD.")] DateOnly date,
|
||||
[Description("Dauer in Minuten.")] int durationMinutes,
|
||||
[Description("Optionale Lerngruppen-ID.")] Guid? groupId = null,
|
||||
[Description("Optionale Beschreibung.")] string? description = null,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var groupName = groupId is { } gid ? groups.GetById(gid)?.Name : null;
|
||||
var message =
|
||||
$"Neuer Zeiteintrag: {category}, {durationMinutes} Min. am {date:dd.MM.yyyy}" +
|
||||
(groupName is not null ? $", Gruppe {groupName}" : "") +
|
||||
(string.IsNullOrWhiteSpace(description) ? "" : $"\n„{description}“");
|
||||
|
||||
if (!await confirmation.ConfirmAsync("Zeiteintrag anlegen?", message, ct))
|
||||
return new WriteResultDto(false, null, "Vom Nutzer abgelehnt oder nicht bestätigt.");
|
||||
|
||||
var entry = new TimeEntry
|
||||
{
|
||||
Category = category,
|
||||
Date = date,
|
||||
DurationMinutes = durationMinutes,
|
||||
GroupId = groupId,
|
||||
Description = description,
|
||||
};
|
||||
timeEntries.Save(entry);
|
||||
return new WriteResultDto(true, entry.Id, "Zeiteintrag gespeichert.");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user