1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
2//
3// SPDX-License-Identifier: AGPL-3.0-or-later
4
5// Package areas provides the areas MCP resource for listing areas and goals.
6package areas
7
8import (
9 "context"
10 "encoding/json"
11
12 "github.com/modelcontextprotocol/go-sdk/mcp"
13
14 "git.secluded.site/lunatask-mcp-server/tools/shared"
15)
16
17// ResourceURI is the URI for the areas resource.
18const ResourceURI = "lunatask://areas"
19
20// Handler handles area-related MCP resource requests.
21type Handler struct {
22 areas []shared.AreaProvider
23}
24
25// NewHandler creates a new areas Handler.
26func NewHandler(areas []shared.AreaProvider) *Handler {
27 return &Handler{areas: areas}
28}
29
30// AreaInfo represents an area with its goals for JSON serialization.
31type AreaInfo struct {
32 Key string `json:"key"`
33 Name string `json:"name"`
34 ID string `json:"id"`
35 Goals []GoalInfo `json:"goals,omitempty"`
36}
37
38// GoalInfo represents a goal for JSON serialization.
39type GoalInfo struct {
40 Key string `json:"key"`
41 Name string `json:"name"`
42 ID string `json:"id"`
43}
44
45// HandleRead handles the areas resource read request.
46func (h *Handler) HandleRead(
47 _ context.Context,
48 _ *mcp.ReadResourceRequest,
49) (*mcp.ReadResourceResult, error) {
50 areasInfo := make([]AreaInfo, 0, len(h.areas))
51
52 for _, area := range h.areas {
53 areaInfo := AreaInfo{
54 Key: area.GetKey(),
55 Name: area.GetName(),
56 ID: area.GetID(),
57 Goals: make([]GoalInfo, 0, len(area.GetGoals())),
58 }
59
60 for _, goal := range area.GetGoals() {
61 areaInfo.Goals = append(areaInfo.Goals, GoalInfo{
62 Key: goal.GetKey(),
63 Name: goal.GetName(),
64 ID: goal.GetID(),
65 })
66 }
67
68 areasInfo = append(areasInfo, areaInfo)
69 }
70
71 data, err := json.MarshalIndent(areasInfo, "", " ")
72 if err != nil {
73 return nil, err
74 }
75
76 return &mcp.ReadResourceResult{
77 Contents: []*mcp.ResourceContents{{
78 URI: ResourceURI,
79 MIMEType: "application/json",
80 Text: string(data),
81 }},
82 }, nil
83}