From 26a88eca66f2cae7b5d8a5e5c6911ffcdac262e2 Mon Sep 17 00:00:00 2001 From: Amolith Date: Tue, 23 Dec 2025 22:25:15 -0700 Subject: [PATCH] feat(mcp): add add_journal_entry tool Assisted-by: Claude Sonnet 4 via Crush --- cmd/mcp/server.go | 34 ++++++--- internal/mcp/tools/journal/create.go | 102 +++++++++++++++++++++++++++ 2 files changed, 128 insertions(+), 8 deletions(-) create mode 100644 internal/mcp/tools/journal/create.go diff --git a/cmd/mcp/server.go b/cmd/mcp/server.go index 993af4d4136e737ad8421279c5dfa88dfbf359ae..decc11c610a452fd4bb8efdcd90fa3f203f3cc4c 100644 --- a/cmd/mcp/server.go +++ b/cmd/mcp/server.go @@ -20,6 +20,7 @@ import ( taskrs "git.secluded.site/lune/internal/mcp/resources/task" "git.secluded.site/lune/internal/mcp/shared" "git.secluded.site/lune/internal/mcp/tools/habit" + "git.secluded.site/lune/internal/mcp/tools/journal" "git.secluded.site/lune/internal/mcp/tools/task" "git.secluded.site/lune/internal/mcp/tools/timestamp" "github.com/modelcontextprotocol/go-sdk/mcp" @@ -137,6 +138,31 @@ func registerTools( }, tsHandler.Handle) } + registerTaskTools(mcpServer, tools, accessToken, areaProviders) + + if tools.TrackHabit { + habitHandler := habit.NewHandler(accessToken, habitProviders) + mcp.AddTool(mcpServer, &mcp.Tool{ + Name: habit.TrackToolName, + Description: habit.TrackToolDescription, + }, habitHandler.HandleTrack) + } + + if tools.CreateJournal { + journalHandler := journal.NewHandler(accessToken) + mcp.AddTool(mcpServer, &mcp.Tool{ + Name: journal.CreateToolName, + Description: journal.CreateToolDescription, + }, journalHandler.HandleCreate) + } +} + +func registerTaskTools( + mcpServer *mcp.Server, + tools *config.ToolsConfig, + accessToken string, + areaProviders []shared.AreaProvider, +) { taskHandler := task.NewHandler(accessToken, areaProviders) if tools.CreateTask { @@ -173,14 +199,6 @@ func registerTools( Description: task.ShowToolDescription, }, taskHandler.HandleShow) } - - if tools.TrackHabit { - habitHandler := habit.NewHandler(accessToken, habitProviders) - mcp.AddTool(mcpServer, &mcp.Tool{ - Name: habit.TrackToolName, - Description: habit.TrackToolDescription, - }, habitHandler.HandleTrack) - } } func runStdio(mcpServer *mcp.Server) error { diff --git a/internal/mcp/tools/journal/create.go b/internal/mcp/tools/journal/create.go new file mode 100644 index 0000000000000000000000000000000000000000..60c8ab359cca32a4078a17b154e5408fa70248c1 --- /dev/null +++ b/internal/mcp/tools/journal/create.go @@ -0,0 +1,102 @@ +// SPDX-FileCopyrightText: Amolith +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +// Package journal provides MCP tools for Lunatask journal operations. +package journal + +import ( + "context" + + "git.secluded.site/go-lunatask" + "git.secluded.site/lune/internal/dateutil" + "git.secluded.site/lune/internal/mcp/shared" + "github.com/modelcontextprotocol/go-sdk/mcp" +) + +// CreateToolName is the name of the create journal entry tool. +const CreateToolName = "add_journal_entry" + +// CreateToolDescription describes the create journal entry tool for LLMs. +const CreateToolDescription = `Creates a journal entry in Lunatask for daily reflection. + +Optional: +- content: Markdown content for the entry +- name: Entry title (defaults to weekday name if omitted) +- date: Entry date (YYYY-MM-DD or natural language, default: today) + +Entries are date-keyed. If no date is provided, uses today. +Content supports Markdown formatting. + +Common uses: +- End-of-day summaries +- Reflection on completed work +- Recording thoughts or learnings` + +// CreateInput is the input schema for creating a journal entry. +type CreateInput struct { + Content *string `json:"content,omitempty"` + Name *string `json:"name,omitempty"` + Date *string `json:"date,omitempty"` +} + +// CreateOutput is the output schema for creating a journal entry. +type CreateOutput struct { + ID string `json:"id"` + Date string `json:"date"` +} + +// Handler handles journal-related MCP tool requests. +type Handler struct { + client *lunatask.Client +} + +// NewHandler creates a new journal handler. +func NewHandler(accessToken string) *Handler { + return &Handler{ + client: lunatask.NewClient(accessToken, lunatask.UserAgent("lune-mcp/1.0")), + } +} + +// HandleCreate creates a new journal entry. +func (h *Handler) HandleCreate( + ctx context.Context, + _ *mcp.CallToolRequest, + input CreateInput, +) (*mcp.CallToolResult, CreateOutput, error) { + dateStr := "" + if input.Date != nil { + dateStr = *input.Date + } + + date, err := dateutil.Parse(dateStr) + if err != nil { + return shared.ErrorResult(err.Error()), CreateOutput{}, nil + } + + builder := h.client.NewJournalEntry(date) + + if input.Content != nil { + builder.WithContent(*input.Content) + } + + if input.Name != nil { + builder.WithName(*input.Name) + } + + entry, err := builder.Create(ctx) + if err != nil { + return shared.ErrorResult(err.Error()), CreateOutput{}, nil + } + + formattedDate := date.Format("2006-01-02") + + return &mcp.CallToolResult{ + Content: []mcp.Content{&mcp.TextContent{ + Text: "Journal entry created for " + formattedDate, + }}, + }, CreateOutput{ + ID: entry.ID, + Date: formattedDate, + }, nil +}