shared.go

 1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
 2//
 3// SPDX-License-Identifier: AGPL-3.0-or-later
 4
 5// Package shared provides common interfaces, types, and helpers for MCP tools.
 6package shared
 7
 8import (
 9	"errors"
10	"fmt"
11	"time"
12
13	"github.com/mark3labs/mcp-go/mcp"
14)
15
16// ErrTimezoneNotConfigured is returned when the timezone config value is empty.
17var ErrTimezoneNotConfigured = errors.New(
18	"timezone is not configured; please set the 'timezone' value in your " +
19		"config file (e.g. 'UTC' or 'America/New_York')",
20)
21
22// AreaProvider defines the interface for accessing area data.
23type AreaProvider interface {
24	GetName() string
25	GetID() string
26	GetGoals() []GoalProvider
27}
28
29// GoalProvider defines the interface for accessing goal data.
30//
31//nolint:iface // semantically distinct from HabitProvider
32type GoalProvider interface {
33	GetName() string
34	GetID() string
35}
36
37// HabitProvider defines the interface for accessing habit data.
38//
39//nolint:iface // semantically distinct from GoalProvider
40type HabitProvider interface {
41	GetName() string
42	GetID() string
43}
44
45// Config holds the necessary configuration for tool handlers.
46type Config struct {
47	AccessToken string
48	Timezone    string
49	Areas       []AreaProvider
50	Habits      []HabitProvider
51}
52
53// ReportError creates an MCP error result.
54func ReportError(msg string) (*mcp.CallToolResult, error) {
55	return &mcp.CallToolResult{
56		IsError: true,
57		Content: []mcp.Content{mcp.TextContent{Type: "text", Text: msg}},
58	}, nil
59}
60
61// LoadLocation loads a timezone location string, returning a *time.Location or error.
62func LoadLocation(timezone string) (*time.Location, error) {
63	if timezone == "" {
64		return nil, ErrTimezoneNotConfigured
65	}
66
67	loc, err := time.LoadLocation(timezone)
68	if err != nil {
69		return nil, fmt.Errorf("could not load timezone '%s': %w", timezone, err)
70	}
71
72	return loc, nil
73}