handler.go

 1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
 2//
 3// SPDX-License-Identifier: AGPL-3.0-or-later
 4
 5// Package notebooks provides the MCP resource handler for Lunatask notebooks.
 6package notebooks
 7
 8import (
 9	"context"
10	"encoding/json"
11	"fmt"
12
13	"git.secluded.site/lune/internal/mcp/shared"
14	"github.com/modelcontextprotocol/go-sdk/mcp"
15)
16
17// ResourceURI is the URI for the notebooks resource.
18const ResourceURI = "lunatask://notebooks"
19
20// ResourceDescription describes the notebooks resource for LLMs.
21const ResourceDescription = `Lists all configured Lunatask notebooks.
22
23Each notebook contains:
24- id: UUID to use when creating notes in this notebook
25- name: Human-readable notebook name
26- key: Short alias for CLI usage
27
28Use this resource to discover valid notebook IDs before creating or updating notes.`
29
30// Handler handles notebook resource requests.
31type Handler struct {
32	notebooks []shared.NotebookProvider
33}
34
35// NewHandler creates a new notebooks resource handler.
36func NewHandler(notebooks []shared.NotebookProvider) *Handler {
37	return &Handler{notebooks: notebooks}
38}
39
40// notebookInfo represents a notebook in the resource response.
41type notebookInfo struct {
42	ID   string `json:"id"`
43	Name string `json:"name"`
44	Key  string `json:"key"`
45}
46
47// HandleRead returns the configured notebooks.
48func (h *Handler) HandleRead(
49	_ context.Context,
50	_ *mcp.ReadResourceRequest,
51) (*mcp.ReadResourceResult, error) {
52	notebooksInfo := make([]notebookInfo, 0, len(h.notebooks))
53
54	for _, nb := range h.notebooks {
55		notebooksInfo = append(notebooksInfo, notebookInfo{
56			ID:   nb.ID,
57			Name: nb.Name,
58			Key:  nb.Key,
59		})
60	}
61
62	data, err := json.MarshalIndent(notebooksInfo, "", "  ")
63	if err != nil {
64		return nil, fmt.Errorf("marshaling notebooks: %w", err)
65	}
66
67	return &mcp.ReadResourceResult{
68		Contents: []*mcp.ResourceContents{{
69			URI:      ResourceURI,
70			MIMEType: "application/json",
71			Text:     string(data),
72		}},
73	}, nil
74}