update.go

 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}