using System.ComponentModel; using ClaudeDo.Data.Models; using ClaudeDo.Data.Repositories; using ClaudeDo.Worker.Hub; using ModelContextProtocol.Server; namespace ClaudeDo.Worker.External; public sealed record ListSummaryDto(string Id, string Name, string? WorkingDir, string DefaultCommitType); [McpServerToolType] public sealed class ListMcpTools { private readonly ListRepository _lists; private readonly HubBroadcaster _broadcaster; public ListMcpTools(ListRepository lists, HubBroadcaster broadcaster) { _lists = lists; _broadcaster = broadcaster; } [McpServerTool, Description("Create a new task list. workingDir sets the git repo tasks run against; commitType defaults to 'chore'.")] public async Task CreateList( string name, string? workingDir, string? commitType, CancellationToken cancellationToken) { if (string.IsNullOrWhiteSpace(name)) throw new InvalidOperationException("name is required."); var entity = new ListEntity { Id = Guid.NewGuid().ToString(), Name = name, WorkingDir = string.IsNullOrWhiteSpace(workingDir) ? null : workingDir, DefaultCommitType = string.IsNullOrWhiteSpace(commitType) ? CommitTypeRegistry.DefaultType : commitType, CreatedAt = DateTime.UtcNow, }; await _lists.AddAsync(entity, cancellationToken); await _broadcaster.ListUpdated(entity.Id); return ToDto(entity); } [McpServerTool, Description("Rename a list and/or change its working dir and default commit type. Pass null to leave a field unchanged.")] public async Task UpdateList( string listId, string? name, string? workingDir, string? commitType, CancellationToken cancellationToken) { var entity = await _lists.GetByIdAsync(listId, cancellationToken) ?? throw new InvalidOperationException($"List {listId} not found."); if (name is not null) entity.Name = name; if (workingDir is not null) entity.WorkingDir = string.IsNullOrWhiteSpace(workingDir) ? null : workingDir; if (commitType is not null) entity.DefaultCommitType = string.IsNullOrWhiteSpace(commitType) ? CommitTypeRegistry.DefaultType : commitType; await _lists.UpdateAsync(entity, cancellationToken); await _broadcaster.ListUpdated(listId); return ToDto(entity); } [McpServerTool, Description("Delete a list and its tasks. Irreversible.")] public async Task DeleteList(string listId, CancellationToken cancellationToken) { _ = await _lists.GetByIdAsync(listId, cancellationToken) ?? throw new InvalidOperationException($"List {listId} not found."); await _lists.DeleteAsync(listId, cancellationToken); await _broadcaster.ListUpdated(listId); } private static ListSummaryDto ToDto(ListEntity l) => new(l.Id, l.Name, l.WorkingDir, l.DefaultCommitType); }