1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
2//
3// SPDX-License-Identifier: AGPL-3.0-or-later
4
5package habit
6
7import (
8 "context"
9 "fmt"
10 "strings"
11
12 "github.com/modelcontextprotocol/go-sdk/mcp"
13)
14
15// ListToolName is the name of the list habits tool.
16const ListToolName = "list_habits"
17
18// ListToolDescription describes the list habits tool for LLMs.
19const ListToolDescription = `Lists configured habits. Fallback for lunatask://habits resource.
20
21Returns habit metadata (id, name, key) from config.
22Use the lunatask://habits resource if your client supports MCP resources.`
23
24// ListInput is the input schema for listing habits.
25type ListInput struct{}
26
27// Summary represents a habit in the list output.
28type Summary struct {
29 ID string `json:"id"`
30 Name string `json:"name"`
31 Key string `json:"key"`
32}
33
34// ListOutput is the output schema for listing habits.
35type ListOutput struct {
36 Habits []Summary `json:"habits"`
37 Count int `json:"count"`
38}
39
40// HandleList lists configured habits.
41func (h *Handler) HandleList(
42 _ context.Context,
43 _ *mcp.CallToolRequest,
44 _ ListInput,
45) (*mcp.CallToolResult, ListOutput, error) {
46 summaries := make([]Summary, 0, len(h.habits))
47
48 for _, habit := range h.habits {
49 summaries = append(summaries, Summary{
50 ID: habit.ID,
51 Name: habit.Name,
52 Key: habit.Key,
53 })
54 }
55
56 output := ListOutput{
57 Habits: summaries,
58 Count: len(summaries),
59 }
60
61 text := formatListText(summaries)
62
63 return &mcp.CallToolResult{
64 Content: []mcp.Content{&mcp.TextContent{Text: text}},
65 }, output, nil
66}
67
68func formatListText(habits []Summary) string {
69 if len(habits) == 0 {
70 return "No habits configured"
71 }
72
73 var text strings.Builder
74
75 text.WriteString(fmt.Sprintf("Found %d habit(s):\n", len(habits)))
76
77 for _, h := range habits {
78 text.WriteString(fmt.Sprintf("- %s: %s (%s)\n", h.Key, h.Name, h.ID))
79 }
80
81 return text.String()
82}