1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
2//
3// SPDX-License-Identifier: AGPL-3.0-or-later
4
5package person
6
7import (
8 "context"
9 "fmt"
10
11 "git.secluded.site/go-lunatask"
12 "git.secluded.site/lune/internal/mcp/shared"
13 "github.com/modelcontextprotocol/go-sdk/mcp"
14)
15
16// UpdateToolName is the name of the update person tool.
17const UpdateToolName = "update_person"
18
19// UpdateToolDescription describes the update person tool for LLMs.
20const UpdateToolDescription = `Updates an existing person in Lunatask.
21
22Required:
23- id: Person UUID or lunatask://person/... deep link
24
25Optional:
26- first_name: New first name
27- last_name: New last name
28- relationship: New relationship strength (family, intimate-friends,
29 close-friends, casual-friends, acquaintances, business-contacts, almost-strangers)
30
31Only provided fields are modified; other fields remain unchanged.`
32
33// UpdateInput is the input schema for updating a person.
34type UpdateInput struct {
35 ID string `json:"id" jsonschema:"required"`
36 FirstName *string `json:"first_name,omitempty"`
37 LastName *string `json:"last_name,omitempty"`
38 Relationship *string `json:"relationship,omitempty"`
39}
40
41// UpdateOutput is the output schema for updating a person.
42type UpdateOutput struct {
43 DeepLink string `json:"deep_link"`
44}
45
46// HandleUpdate updates an existing person.
47func (h *Handler) HandleUpdate(
48 ctx context.Context,
49 _ *mcp.CallToolRequest,
50 input UpdateInput,
51) (*mcp.CallToolResult, UpdateOutput, error) {
52 id, err := parseReference(input.ID)
53 if err != nil {
54 return shared.ErrorResult(err.Error()), UpdateOutput{}, nil
55 }
56
57 builder := h.client.NewPersonUpdate(id)
58
59 if input.FirstName != nil {
60 builder.FirstName(*input.FirstName)
61 }
62
63 if input.LastName != nil {
64 builder.LastName(*input.LastName)
65 }
66
67 if input.Relationship != nil {
68 rel, err := lunatask.ParseRelationshipStrength(*input.Relationship)
69 if err != nil {
70 return shared.ErrorResult(err.Error()), UpdateOutput{}, nil
71 }
72
73 builder.WithRelationshipStrength(rel)
74 }
75
76 person, err := builder.Update(ctx)
77 if err != nil {
78 return shared.ErrorResult(err.Error()), UpdateOutput{}, nil
79 }
80
81 deepLink, _ := lunatask.BuildDeepLink(lunatask.ResourcePerson, person.ID)
82
83 return &mcp.CallToolResult{
84 Content: []mcp.Content{&mcp.TextContent{
85 Text: "Person updated: " + deepLink,
86 }},
87 }, UpdateOutput{DeepLink: deepLink}, nil
88}
89
90// parseReference extracts UUID from either a raw UUID or a lunatask:// deep link.
91func parseReference(ref string) (string, error) {
92 _, id, err := lunatask.ParseReference(ref)
93 if err != nil {
94 return "", fmt.Errorf("invalid ID: %w", err)
95 }
96
97 return id, nil
98}