1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
2//
3// SPDX-License-Identifier: AGPL-3.0-or-later
4
5// Package person provides MCP tools for Lunatask person/relationship operations.
6package person
7
8import (
9 "context"
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// CreateToolName is the name of the create person tool.
17const CreateToolName = "create_person"
18
19// CreateToolDescription describes the create person tool for LLMs.
20const CreateToolDescription = `Creates a new person in Lunatask's relationship tracker.
21
22Required:
23- first_name: First name
24
25Optional:
26- last_name: Last name
27- relationship: Relationship strength (family, intimate-friends, close-friends,
28 casual-friends, acquaintances, business-contacts, almost-strangers)
29 Default: casual-friends
30- source: Origin identifier for integrations
31- source_id: Source-specific ID (requires source)
32
33Returns the deep link to the created person.`
34
35// CreateInput is the input schema for creating a person.
36type CreateInput struct {
37 FirstName string `json:"first_name" jsonschema:"required"`
38 LastName *string `json:"last_name,omitempty"`
39 Relationship *string `json:"relationship,omitempty"`
40 Source *string `json:"source,omitempty"`
41 SourceID *string `json:"source_id,omitempty"`
42}
43
44// CreateOutput is the output schema for creating a person.
45type CreateOutput struct {
46 DeepLink string `json:"deep_link"`
47}
48
49// Handler handles person-related MCP tool requests.
50type Handler struct {
51 client *lunatask.Client
52}
53
54// NewHandler creates a new person handler.
55func NewHandler(accessToken string) *Handler {
56 return &Handler{
57 client: lunatask.NewClient(accessToken, lunatask.UserAgent("lune-mcp/1.0")),
58 }
59}
60
61// HandleCreate creates a new person.
62func (h *Handler) HandleCreate(
63 ctx context.Context,
64 _ *mcp.CallToolRequest,
65 input CreateInput,
66) (*mcp.CallToolResult, CreateOutput, error) {
67 lastName := ""
68 if input.LastName != nil {
69 lastName = *input.LastName
70 }
71
72 builder := h.client.NewPerson(input.FirstName, lastName)
73
74 if input.Relationship != nil {
75 rel, err := lunatask.ParseRelationshipStrength(*input.Relationship)
76 if err != nil {
77 return shared.ErrorResult(err.Error()), CreateOutput{}, nil
78 }
79
80 builder.WithRelationshipStrength(rel)
81 }
82
83 if input.Source != nil && input.SourceID != nil {
84 builder.FromSource(*input.Source, *input.SourceID)
85 }
86
87 person, err := builder.Create(ctx)
88 if err != nil {
89 return shared.ErrorResult(err.Error()), CreateOutput{}, nil
90 }
91
92 deepLink, _ := lunatask.BuildDeepLink(lunatask.ResourcePerson, person.ID)
93
94 return &mcp.CallToolResult{
95 Content: []mcp.Content{&mcp.TextContent{
96 Text: "Person created: " + deepLink,
97 }},
98 }, CreateOutput{DeepLink: deepLink}, nil
99}