feat(mcp/external): add DeleteTask

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
mika kuns
2026-04-25 11:28:47 +02:00
parent d99cb68afb
commit 31a394e694
2 changed files with 48 additions and 0 deletions

View File

@@ -219,6 +219,18 @@ public sealed class ExternalMcpService
return cancelled; return cancelled;
} }
[McpServerTool, Description("Delete a task. Refuses if the task is currently Running — cancel it first.")]
public async Task DeleteTask(string taskId, CancellationToken cancellationToken)
{
var task = await _tasks.GetByIdAsync(taskId, cancellationToken)
?? throw new InvalidOperationException($"Task {taskId} not found.");
if (task.Status == TaskStatus.Running)
throw new InvalidOperationException("Cannot delete a running task. Cancel it first.");
await _tasks.DeleteAsync(taskId, cancellationToken);
await _broadcaster.TaskUpdated(taskId);
}
[McpServerTool, Description("List all known tags. Useful for discovering existing tag names (including 'agent' which marks tasks for auto-execution) before tagging.")] [McpServerTool, Description("List all known tags. Useful for discovering existing tag names (including 'agent' which marks tasks for auto-execution) before tagging.")]
public async Task<IReadOnlyList<TagDto>> ListTags(CancellationToken cancellationToken) public async Task<IReadOnlyList<TagDto>> ListTags(CancellationToken cancellationToken)
{ {

View File

@@ -225,4 +225,40 @@ public sealed class ExternalMcpServiceTests : IDisposable
await Assert.ThrowsAsync<InvalidOperationException>(() => await Assert.ThrowsAsync<InvalidOperationException>(() =>
sut.UpdateTask("does-not-exist", "x", null, null, null, CancellationToken.None)); sut.UpdateTask("does-not-exist", "x", null, null, null, CancellationToken.None));
} }
[Fact]
public async Task DeleteTask_RemovesTaskAndTagJoins()
{
var listId = await SeedListAsync();
var task = await SeedTaskAsync(listId);
await _tasks.SetTagsAsync(task.Id, new[] { "agent" });
var queue = CreateQueue();
var sut = BuildSut(queue);
await sut.DeleteTask(task.Id, CancellationToken.None);
Assert.Null(await _tasks.GetByIdAsync(task.Id));
}
[Fact]
public async Task DeleteTask_OnRunning_Throws()
{
var listId = await SeedListAsync();
var task = await SeedTaskAsync(listId, status: TaskStatus.Running);
var queue = CreateQueue();
var sut = BuildSut(queue);
await Assert.ThrowsAsync<InvalidOperationException>(() =>
sut.DeleteTask(task.Id, CancellationToken.None));
}
[Fact]
public async Task DeleteTask_NotFound_Throws()
{
var queue = CreateQueue();
var sut = BuildSut(queue);
await Assert.ThrowsAsync<InvalidOperationException>(() =>
sut.DeleteTask("does-not-exist", CancellationToken.None));
}
} }