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