1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
2//
3// SPDX-License-Identifier: AGPL-3.0-or-later
4
5package crud
6
7import (
8 "context"
9
10 "git.secluded.site/go-lunatask"
11 "git.secluded.site/lune/internal/mcp/shared"
12 "github.com/modelcontextprotocol/go-sdk/mcp"
13)
14
15// DeleteToolName is the name of the consolidated delete tool.
16const DeleteToolName = "delete"
17
18// DeleteToolDescription describes the delete tool for LLMs.
19const DeleteToolDescription = `Deletes an entity from Lunatask.
20
21Required:
22- entity: Type to delete (task, note, person)
23- id: Entity UUID or lunatask:// deep link
24
25This action is permanent and cannot be undone.
26
27Returns confirmation of deletion with the entity's deep link.`
28
29// DeleteInput is the input schema for the consolidated delete tool.
30type DeleteInput struct {
31 Entity string `json:"entity" jsonschema:"required"`
32 ID string `json:"id" jsonschema:"required"`
33}
34
35// DeleteOutput is the output schema for the consolidated delete tool.
36type DeleteOutput struct {
37 Entity string `json:"entity"`
38 DeepLink string `json:"deep_link"`
39 Success bool `json:"success"`
40}
41
42// HandleDelete deletes an entity based on the entity type.
43func (h *Handler) HandleDelete(
44 ctx context.Context,
45 _ *mcp.CallToolRequest,
46 input DeleteInput,
47) (*mcp.CallToolResult, DeleteOutput, error) {
48 switch input.Entity {
49 case EntityTask:
50 return h.deleteTask(ctx, input)
51 case EntityNote:
52 return h.deleteNote(ctx, input)
53 case EntityPerson:
54 return h.deletePerson(ctx, input)
55 default:
56 return shared.ErrorResult("invalid entity: must be task, note, or person"),
57 DeleteOutput{Entity: input.Entity}, nil
58 }
59}
60
61func (h *Handler) deleteTask(
62 ctx context.Context,
63 input DeleteInput,
64) (*mcp.CallToolResult, DeleteOutput, error) {
65 _, id, err := lunatask.ParseReference(input.ID)
66 if err != nil {
67 return shared.ErrorResult("invalid ID: expected UUID or lunatask:// deep link"),
68 DeleteOutput{Entity: input.Entity}, nil
69 }
70
71 deepLink, _ := lunatask.BuildDeepLink(lunatask.ResourceTask, id)
72
73 if _, err := h.client.DeleteTask(ctx, id); err != nil {
74 return shared.ErrorResult(err.Error()), DeleteOutput{Entity: input.Entity}, nil
75 }
76
77 return &mcp.CallToolResult{
78 Content: []mcp.Content{&mcp.TextContent{
79 Text: "Task deleted: " + deepLink,
80 }},
81 }, DeleteOutput{Entity: input.Entity, DeepLink: deepLink, Success: true}, nil
82}
83
84func (h *Handler) deleteNote(
85 ctx context.Context,
86 input DeleteInput,
87) (*mcp.CallToolResult, DeleteOutput, error) {
88 _, id, err := lunatask.ParseReference(input.ID)
89 if err != nil {
90 return shared.ErrorResult("invalid ID: expected UUID or lunatask:// deep link"),
91 DeleteOutput{Entity: input.Entity}, nil
92 }
93
94 note, err := h.client.DeleteNote(ctx, id)
95 if err != nil {
96 return shared.ErrorResult(err.Error()), DeleteOutput{Entity: input.Entity}, nil
97 }
98
99 deepLink, _ := lunatask.BuildDeepLink(lunatask.ResourceNote, note.ID)
100
101 return &mcp.CallToolResult{
102 Content: []mcp.Content{&mcp.TextContent{
103 Text: "Note deleted: " + deepLink,
104 }},
105 }, DeleteOutput{Entity: input.Entity, DeepLink: deepLink, Success: true}, nil
106}
107
108func (h *Handler) deletePerson(
109 ctx context.Context,
110 input DeleteInput,
111) (*mcp.CallToolResult, DeleteOutput, error) {
112 _, id, err := lunatask.ParseReference(input.ID)
113 if err != nil {
114 return shared.ErrorResult("invalid ID: expected UUID or lunatask:// deep link"),
115 DeleteOutput{Entity: input.Entity}, nil
116 }
117
118 person, err := h.client.DeletePerson(ctx, id)
119 if err != nil {
120 return shared.ErrorResult(err.Error()), DeleteOutput{Entity: input.Entity}, nil
121 }
122
123 deepLink, _ := lunatask.BuildDeepLink(lunatask.ResourcePerson, person.ID)
124
125 return &mcp.CallToolResult{
126 Content: []mcp.Content{&mcp.TextContent{
127 Text: "Person deleted: " + deepLink,
128 }},
129 }, DeleteOutput{Entity: input.Entity, DeepLink: deepLink, Success: true}, nil
130}