feat(task): implement task deletion capability

Amolith created

This commit introduces the functionality to delete tasks.

It adds a `DeleteTask` method to the Lunatask client, along with
a `DeleteTaskResponse` struct, enabling interaction with the
Lunatask API's task deletion endpoint.

Additionally, a new `delete_task` tool and its corresponding
`handleDeleteTask` handler are integrated into the MCP server,
exposing this new capability.

Change summary

lunatask/tasks.go | 59 +++++++++++++++++++++++++++++++++++++++++++++++++
main.go           | 41 ++++++++++++++++++++++++++++++++++
2 files changed, 100 insertions(+)

Detailed changes

lunatask/tasks.go 🔗

@@ -85,6 +85,11 @@ type UpdateTaskResponse struct {
 	Task Task `json:"task"`
 }
 
+// DeleteTaskResponse represents the response from Lunatask API when deleting a task
+type DeleteTaskResponse struct {
+	Task Task `json:"task"`
+}
+
 // ValidationError represents errors returned by the validator
 type ValidationError struct {
 	Field   string
@@ -257,3 +262,57 @@ func (c *Client) UpdateTask(ctx context.Context, taskID string, task *CreateTask
 
 	return &response, nil
 }
+
+// DeleteTask deletes a task in Lunatask
+func (c *Client) DeleteTask(ctx context.Context, taskID string) (*DeleteTaskResponse, error) {
+	if taskID == "" {
+		return nil, errors.New("task ID cannot be empty")
+	}
+
+	// Create the request
+	req, err := http.NewRequestWithContext(
+		ctx,
+		"DELETE",
+		fmt.Sprintf("%s/tasks/%s", c.BaseURL, taskID),
+		nil,
+	)
+	if err != nil {
+		return nil, fmt.Errorf("failed to create HTTP request: %w", err)
+	}
+
+	// Set headers
+	req.Header.Set("Authorization", "bearer "+c.AccessToken)
+
+	// Send the request
+	resp, err := c.HTTPClient.Do(req)
+	if err != nil {
+		return nil, fmt.Errorf("failed to send HTTP request: %w", err)
+	}
+	defer func() {
+		if resp.Body != nil {
+			if err := resp.Body.Close(); err != nil {
+				fmt.Printf("Error closing response body: %v\n", err)
+			}
+		}
+	}()
+
+	// Handle error status codes
+	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
+		respBody, _ := io.ReadAll(resp.Body)
+		return nil, fmt.Errorf("API error (status %d): %s", resp.StatusCode, string(respBody))
+	}
+
+	// Read and parse the response
+	respBody, err := io.ReadAll(resp.Body)
+	if err != nil {
+		return nil, fmt.Errorf("failed to read response body: %w", err)
+	}
+
+	var response DeleteTaskResponse
+	err = json.Unmarshal(respBody, &response)
+	if err != nil {
+		return nil, fmt.Errorf("failed to parse response: %w", err)
+	}
+
+	return &response, nil
+}

main.go 🔗

@@ -297,6 +297,16 @@ func NewMCPServer(config *Config) *server.MCPServer {
 		return handleUpdateTask(ctx, request, config)
 	})
 
+	mcpServer.AddTool(mcp.NewTool("delete_task",
+		mcp.WithDescription("Deletes an existing task"),
+		mcp.WithString("task_id",
+			mcp.Description("ID of the task to delete."),
+			mcp.Required(),
+		),
+	), func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+		return handleDeleteTask(ctx, request, config)
+	})
+
 	return mcpServer
 }
 
@@ -619,6 +629,37 @@ func handleUpdateTask(
 	}, nil
 }
 
+func handleDeleteTask(
+	ctx context.Context,
+	request mcp.CallToolRequest,
+	config *Config,
+) (*mcp.CallToolResult, error) {
+	arguments := request.Params.Arguments
+
+	taskID, ok := arguments["task_id"].(string)
+	if !ok || taskID == "" {
+		return reportMCPError("Missing or invalid required argument: task_id")
+	}
+
+	// Create Lunatask client
+	client := lunatask.NewClient(config.AccessToken)
+
+	// Call the client to delete the task
+	_, err := client.DeleteTask(ctx, taskID)
+	if err != nil {
+		return reportMCPError(fmt.Sprintf("Failed to delete task: %v", err))
+	}
+
+	return &mcp.CallToolResult{
+		Content: []mcp.Content{
+			mcp.TextContent{
+				Type: "text",
+				Text: "Task deleted successfully.",
+			},
+		},
+	}, nil
+}
+
 func createDefaultConfigFile(configPath string) {
 	defaultConfig := Config{
 		Server: ServerConfig{