@@ -121,6 +121,19 @@ func (s *Server) registerTools(mcpServer *server.MCPServer) {
),
)
mcpServer.AddTool(updateTasksTool, s.handleUpdateTasks)
+
+ // Register delete_tasks tool
+ deleteTasksTool := mcp.NewTool("delete_tasks",
+ mcp.WithDescription("Delete one or more tasks by their IDs. After deletion, respond with the resulting task list."),
+ mcp.WithArray("task_ids",
+ mcp.Required(),
+ mcp.Description("Array of task IDs to delete"),
+ mcp.Items(map[string]any{
+ "type": "string",
+ }),
+ ),
+ )
+ mcpServer.AddTool(deleteTasksTool, s.handleDeleteTasks)
}
// handleUpdateGoal handles the update_goal tool call
@@ -416,6 +429,95 @@ func (s *Server) handleUpdateTasks(ctx context.Context, request mcp.CallToolRequ
}, nil
}
+// handleDeleteTasks handles the delete_tasks tool call
+func (s *Server) handleDeleteTasks(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+ s.logger.Info("Received delete_tasks tool call")
+
+ // Extract parameters
+ arguments := request.GetArguments()
+ taskIDsRaw, ok := arguments["task_ids"]
+ if !ok {
+ return &mcp.CallToolResult{
+ Content: []mcp.Content{
+ mcp.TextContent{
+ Type: "text",
+ Text: "Error: task_ids parameter is required",
+ },
+ },
+ IsError: true,
+ }, nil
+ }
+
+ // Convert to slice of interfaces
+ taskIDsSlice, ok := taskIDsRaw.([]any)
+ if !ok {
+ return &mcp.CallToolResult{
+ Content: []mcp.Content{
+ mcp.TextContent{
+ Type: "text",
+ Text: "Error: task_ids parameter must be an array",
+ },
+ },
+ IsError: true,
+ }, nil
+ }
+
+ if len(taskIDsSlice) == 0 {
+ return &mcp.CallToolResult{
+ Content: []mcp.Content{
+ mcp.TextContent{
+ Type: "text",
+ Text: "Error: at least one task ID is required",
+ },
+ },
+ IsError: true,
+ }, nil
+ }
+
+ // Parse task IDs
+ taskIDs := make([]string, 0, len(taskIDsSlice))
+ for _, taskIDRaw := range taskIDsSlice {
+ taskID, ok := taskIDRaw.(string)
+ if !ok || taskID == "" {
+ return &mcp.CallToolResult{
+ Content: []mcp.Content{
+ mcp.TextContent{
+ Type: "text",
+ Text: "Error: each task ID must be a non-empty string",
+ },
+ },
+ IsError: true,
+ }, nil
+ }
+ taskIDs = append(taskIDs, taskID)
+ }
+
+ // Delete tasks
+ if err := s.planner.DeleteTasks(taskIDs); err != nil {
+ s.logger.Error("Failed to delete tasks", "error", err)
+ return &mcp.CallToolResult{
+ Content: []mcp.Content{
+ mcp.TextContent{
+ Type: "text",
+ Text: fmt.Sprintf("Error deleting tasks: %v", err),
+ },
+ },
+ IsError: true,
+ }, nil
+ }
+
+ // Return full task list
+ taskList := s.planner.GetTasks()
+ return &mcp.CallToolResult{
+ Content: []mcp.Content{
+ mcp.TextContent{
+ Type: "text",
+ Text: taskList,
+ },
+ },
+ }, nil
+}
+
// GetServer returns the underlying MCP server
func (s *Server) GetServer() *server.MCPServer {
return s.server
@@ -239,3 +239,34 @@ type TaskUpdate struct {
TaskID string `json:"task_id"`
Status TaskStatus `json:"status"`
}
+
+// DeleteTasks deletes one or more tasks by their IDs
+func (m *Manager) DeleteTasks(taskIDs []string) error {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+
+ if len(taskIDs) == 0 {
+ return fmt.Errorf("at least one task ID is required")
+ }
+
+ // First validate all task IDs exist
+ notFound := make([]string, 0)
+ for _, taskID := range taskIDs {
+ if _, exists := m.tasks[taskID]; !exists {
+ notFound = append(notFound, taskID)
+ }
+ }
+
+ if len(notFound) > 0 {
+ return fmt.Errorf("task(s) not found: %s", strings.Join(notFound, ", "))
+ }
+
+ // If all validations pass, delete all tasks
+ for _, taskID := range taskIDs {
+ delete(m.tasks, taskID)
+ m.logger.Info("Task deleted", "id", taskID)
+ }
+
+ m.logger.Info("Tasks deleted", "count", len(taskIDs))
+ return nil
+}