Detailed changes
@@ -12,6 +12,7 @@ import (
"git.secluded.site/lune/internal/client"
"git.secluded.site/lune/internal/config"
"git.secluded.site/lune/internal/mcp/tools/area"
+ "git.secluded.site/lune/internal/mcp/tools/crud"
"git.secluded.site/lune/internal/mcp/tools/goal"
"git.secluded.site/lune/internal/mcp/tools/habit"
"git.secluded.site/lune/internal/mcp/tools/journal"
@@ -183,6 +184,10 @@ var validToolNames = map[string]func(*config.ToolsConfig, bool){
area.ListToolName: func(t *config.ToolsConfig, v bool) { t.ListAreas = v },
goal.ListToolName: func(t *config.ToolsConfig, v bool) { t.ListGoals = v },
journal.CreateToolName: func(t *config.ToolsConfig, v bool) { t.CreateJournal = v },
+ crud.CreateToolName: func(t *config.ToolsConfig, v bool) { t.Create = v },
+ crud.UpdateToolName: func(t *config.ToolsConfig, v bool) { t.Update = v },
+ crud.DeleteToolName: func(t *config.ToolsConfig, v bool) { t.Delete = v },
+ crud.QueryToolName: func(t *config.ToolsConfig, v bool) { t.Query = v },
}
// resolveTools modifies cfg.MCP.Tools based on CLI flags.
@@ -23,6 +23,7 @@ import (
"git.secluded.site/lune/internal/mcp/resources/tasks"
"git.secluded.site/lune/internal/mcp/shared"
areatool "git.secluded.site/lune/internal/mcp/tools/area"
+ "git.secluded.site/lune/internal/mcp/tools/crud"
goaltool "git.secluded.site/lune/internal/mcp/tools/goal"
"git.secluded.site/lune/internal/mcp/tools/habit"
"git.secluded.site/lune/internal/mcp/tools/journal"
@@ -367,6 +368,8 @@ func registerTools(
Description: journal.CreateToolDescription,
}, journalHandler.HandleCreate)
}
+
+ registerCRUDTools(mcpServer, cfg, tools, accessToken, areaProviders, habitProviders, notebookProviders)
}
func registerHabitTools(
@@ -427,6 +430,50 @@ func registerConfigListTools(
}
}
+func registerCRUDTools(
+ mcpServer *mcp.Server,
+ cfg *config.Config,
+ tools *config.ToolsConfig,
+ accessToken string,
+ areaProviders []shared.AreaProvider,
+ habitProviders []shared.HabitProvider,
+ notebookProviders []shared.NotebookProvider,
+) {
+ if !tools.Create && !tools.Update && !tools.Delete && !tools.Query {
+ return
+ }
+
+ crudHandler := crud.NewHandler(accessToken, cfg, areaProviders, habitProviders, notebookProviders)
+
+ if tools.Create {
+ mcp.AddTool(mcpServer, &mcp.Tool{
+ Name: crud.CreateToolName,
+ Description: crud.CreateToolDescription,
+ }, crudHandler.HandleCreate)
+ }
+
+ if tools.Update {
+ mcp.AddTool(mcpServer, &mcp.Tool{
+ Name: crud.UpdateToolName,
+ Description: crud.UpdateToolDescription,
+ }, crudHandler.HandleUpdate)
+ }
+
+ if tools.Delete {
+ mcp.AddTool(mcpServer, &mcp.Tool{
+ Name: crud.DeleteToolName,
+ Description: crud.DeleteToolDescription,
+ }, crudHandler.HandleDelete)
+ }
+
+ if tools.Query {
+ mcp.AddTool(mcpServer, &mcp.Tool{
+ Name: crud.QueryToolName,
+ Description: crud.QueryToolDescription,
+ }, crudHandler.HandleQuery)
+ }
+}
+
func registerTaskTools(
mcpServer *mcp.Server,
cfg *config.Config,
@@ -67,6 +67,11 @@ type ToolsConfig struct {
ListGoals bool `toml:"list_goals"`
CreateJournal bool `toml:"create_journal"`
+
+ Create bool `toml:"create"` // task, note, person, journal
+ Update bool `toml:"update"` // task, note, person
+ Delete bool `toml:"delete"` // task, note, person
+ Query bool `toml:"query"` // all entities; default-disabled fallback for resources
}
// MCPDefaults applies default values to MCP config.
@@ -98,7 +103,8 @@ func (t *ToolsConfig) ApplyDefaults() {
!t.DeleteNote && !t.ListNotes && !t.ShowNote && !t.CreatePerson &&
!t.UpdatePerson && !t.DeletePerson && !t.ListPeople && !t.ShowPerson &&
!t.PersonTimeline && !t.TrackHabit && !t.ListHabits && !t.ListNotebooks &&
- !t.ListAreas && !t.ListGoals && !t.CreateJournal {
+ !t.ListAreas && !t.ListGoals && !t.CreateJournal &&
+ !t.Create && !t.Update && !t.Delete && !t.Query {
t.GetTimestamp = true
t.CreateTask = true
t.UpdateTask = true
@@ -119,6 +125,9 @@ func (t *ToolsConfig) ApplyDefaults() {
t.TrackHabit = true
// ListHabits: default-disabled (fallback for lunatask://habits resource)
t.CreateJournal = true
+ t.Create = true
+ t.Update = true
+ t.Delete = true
}
}
@@ -0,0 +1,193 @@
+// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+// Package crud provides consolidated MCP tools for Lunatask CRUD operations.
+package crud
+
+import (
+ "context"
+
+ "git.secluded.site/go-lunatask"
+ "git.secluded.site/lune/internal/config"
+ "git.secluded.site/lune/internal/mcp/shared"
+ "github.com/modelcontextprotocol/go-sdk/mcp"
+)
+
+// Entity type constants.
+const (
+ EntityTask = "task"
+ EntityNote = "note"
+ EntityPerson = "person"
+ EntityJournal = "journal"
+ EntityArea = "area"
+ EntityGoal = "goal"
+ EntityNotebook = "notebook"
+ EntityHabit = "habit"
+)
+
+// CreateToolName is the name of the consolidated create tool.
+const CreateToolName = "create"
+
+// CreateToolDescription describes the create tool for LLMs.
+const CreateToolDescription = `Creates a new entity in Lunatask.
+
+Required:
+- entity: Type to create (task, note, person, journal)
+
+Entity-specific fields:
+
+**task** (requires name, area_id):
+- name: Task title
+- area_id: Area UUID, lunatask:// deep link, or config key
+- goal_id: Goal UUID, deep link, or config key (optional)
+- status: later, next, started, waiting (default: later)
+- note: Markdown note/description
+- priority: lowest, low, normal, high, highest
+- estimate: Time estimate in minutes (0-720)
+- motivation: must, should, want
+- important: true/false for Eisenhower matrix
+- urgent: true/false for Eisenhower matrix
+- scheduled_on: Date to schedule (YYYY-MM-DD or natural language)
+
+**note** (all fields optional):
+- name: Note title
+- notebook_id: Notebook UUID
+- content: Markdown content
+- source: Origin identifier for integrations
+- source_id: Source-specific ID (requires source)
+
+**person** (requires first_name):
+- first_name: First name
+- last_name: Last name
+- relationship: Relationship strength (family, intimate-friends, close-friends,
+ casual-friends, acquaintances, business-contacts, almost-strangers)
+- source: Origin identifier
+- source_id: Source-specific ID (requires source)
+
+**journal** (all fields optional):
+- name: Entry title (defaults to weekday name)
+- content: Markdown content
+- date: Entry date (YYYY-MM-DD or natural language, default: today)
+
+Returns the created entity's ID and deep link.`
+
+// CreateInput is the input schema for the consolidated create tool.
+type CreateInput struct {
+ Entity string `json:"entity" jsonschema:"required,enum=task,enum=note,enum=person,enum=journal"`
+
+ // Common fields
+ Name *string `json:"name,omitempty"`
+ Content *string `json:"content,omitempty"`
+ Source *string `json:"source,omitempty"`
+ SourceID *string `json:"source_id,omitempty"`
+
+ // Task-specific fields
+ AreaID *string `json:"area_id,omitempty"`
+ GoalID *string `json:"goal_id,omitempty"`
+ Status *string `json:"status,omitempty"`
+ Note *string `json:"note,omitempty"`
+ Priority *string `json:"priority,omitempty"`
+ Estimate *int `json:"estimate,omitempty"`
+ Motivation *string `json:"motivation,omitempty"`
+ Important *bool `json:"important,omitempty"`
+ Urgent *bool `json:"urgent,omitempty"`
+ ScheduledOn *string `json:"scheduled_on,omitempty"`
+
+ // Note-specific fields
+ NotebookID *string `json:"notebook_id,omitempty"`
+
+ // Person-specific fields
+ FirstName *string `json:"first_name,omitempty"`
+ LastName *string `json:"last_name,omitempty"`
+ Relationship *string `json:"relationship,omitempty"`
+
+ // Journal-specific fields
+ Date *string `json:"date,omitempty"`
+}
+
+// CreateOutput is the output schema for the consolidated create tool.
+type CreateOutput struct {
+ Entity string `json:"entity"`
+ DeepLink string `json:"deep_link,omitempty"`
+ ID string `json:"id,omitempty"`
+}
+
+// Handler handles consolidated CRUD tool requests.
+type Handler struct {
+ client *lunatask.Client
+ cfg *config.Config
+ areas []shared.AreaProvider
+ habits []shared.HabitProvider
+ notebooks []shared.NotebookProvider
+}
+
+// NewHandler creates a new consolidated CRUD handler.
+func NewHandler(
+ accessToken string,
+ cfg *config.Config,
+ areas []shared.AreaProvider,
+ habits []shared.HabitProvider,
+ notebooks []shared.NotebookProvider,
+) *Handler {
+ return &Handler{
+ client: lunatask.NewClient(accessToken, lunatask.UserAgent("lune-mcp/1.0")),
+ cfg: cfg,
+ areas: areas,
+ habits: habits,
+ notebooks: notebooks,
+ }
+}
+
+// HandleCreate creates a new entity based on the entity type.
+func (h *Handler) HandleCreate(
+ ctx context.Context,
+ _ *mcp.CallToolRequest,
+ input CreateInput,
+) (*mcp.CallToolResult, CreateOutput, error) {
+ switch input.Entity {
+ case EntityTask:
+ return h.createTask(ctx, input)
+ case EntityNote:
+ return h.createNote(ctx, input)
+ case EntityPerson:
+ return h.createPerson(ctx, input)
+ case EntityJournal:
+ return h.createJournal(ctx, input)
+ default:
+ return shared.ErrorResult("invalid entity: must be task, note, person, or journal"),
+ CreateOutput{Entity: input.Entity}, nil
+ }
+}
+
+func (h *Handler) createTask(
+ _ context.Context,
+ input CreateInput,
+) (*mcp.CallToolResult, CreateOutput, error) {
+ return shared.ErrorResult("task creation not yet implemented"),
+ CreateOutput{Entity: input.Entity}, nil
+}
+
+func (h *Handler) createNote(
+ _ context.Context,
+ input CreateInput,
+) (*mcp.CallToolResult, CreateOutput, error) {
+ return shared.ErrorResult("note creation not yet implemented"),
+ CreateOutput{Entity: input.Entity}, nil
+}
+
+func (h *Handler) createPerson(
+ _ context.Context,
+ input CreateInput,
+) (*mcp.CallToolResult, CreateOutput, error) {
+ return shared.ErrorResult("person creation not yet implemented"),
+ CreateOutput{Entity: input.Entity}, nil
+}
+
+func (h *Handler) createJournal(
+ _ context.Context,
+ input CreateInput,
+) (*mcp.CallToolResult, CreateOutput, error) {
+ return shared.ErrorResult("journal creation not yet implemented"),
+ CreateOutput{Entity: input.Entity}, nil
+}
@@ -0,0 +1,82 @@
+// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+package crud
+
+import (
+ "context"
+
+ "git.secluded.site/lune/internal/mcp/shared"
+ "github.com/modelcontextprotocol/go-sdk/mcp"
+)
+
+// DeleteToolName is the name of the consolidated delete tool.
+const DeleteToolName = "delete"
+
+// DeleteToolDescription describes the delete tool for LLMs.
+const DeleteToolDescription = `Deletes an entity from Lunatask.
+
+Required:
+- entity: Type to delete (task, note, person)
+- id: Entity UUID or lunatask:// deep link
+
+This action is permanent and cannot be undone.
+
+Returns confirmation of deletion with the entity's deep link.`
+
+// DeleteInput is the input schema for the consolidated delete tool.
+type DeleteInput struct {
+ Entity string `json:"entity" jsonschema:"required,enum=task,enum=note,enum=person"`
+ ID string `json:"id" jsonschema:"required"`
+}
+
+// DeleteOutput is the output schema for the consolidated delete tool.
+type DeleteOutput struct {
+ Entity string `json:"entity"`
+ DeepLink string `json:"deep_link"`
+ Success bool `json:"success"`
+}
+
+// HandleDelete deletes an entity based on the entity type.
+func (h *Handler) HandleDelete(
+ ctx context.Context,
+ _ *mcp.CallToolRequest,
+ input DeleteInput,
+) (*mcp.CallToolResult, DeleteOutput, error) {
+ switch input.Entity {
+ case EntityTask:
+ return h.deleteTask(ctx, input)
+ case EntityNote:
+ return h.deleteNote(ctx, input)
+ case EntityPerson:
+ return h.deletePerson(ctx, input)
+ default:
+ return shared.ErrorResult("invalid entity: must be task, note, or person"),
+ DeleteOutput{Entity: input.Entity}, nil
+ }
+}
+
+func (h *Handler) deleteTask(
+ _ context.Context,
+ input DeleteInput,
+) (*mcp.CallToolResult, DeleteOutput, error) {
+ return shared.ErrorResult("task deletion not yet implemented"),
+ DeleteOutput{Entity: input.Entity}, nil
+}
+
+func (h *Handler) deleteNote(
+ _ context.Context,
+ input DeleteInput,
+) (*mcp.CallToolResult, DeleteOutput, error) {
+ return shared.ErrorResult("note deletion not yet implemented"),
+ DeleteOutput{Entity: input.Entity}, nil
+}
+
+func (h *Handler) deletePerson(
+ _ context.Context,
+ input DeleteInput,
+) (*mcp.CallToolResult, DeleteOutput, error) {
+ return shared.ErrorResult("person deletion not yet implemented"),
+ DeleteOutput{Entity: input.Entity}, nil
+}
@@ -0,0 +1,181 @@
+// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+package crud
+
+import (
+ "context"
+
+ "git.secluded.site/lune/internal/mcp/shared"
+ "github.com/modelcontextprotocol/go-sdk/mcp"
+)
+
+// QueryToolName is the name of the consolidated query tool.
+const QueryToolName = "query"
+
+// QueryToolDescription describes the query tool for LLMs.
+const QueryToolDescription = `Queries entities from Lunatask. Fallback for agents without MCP resource support.
+
+Required:
+- entity: Type to query (task, note, person, area, goal, notebook, habit)
+
+Optional:
+- id: Entity UUID or lunatask:// deep link (if provided, returns single entity details)
+
+When id is omitted, returns a list with optional filters:
+
+**task** filters:
+- area_id: Filter by area UUID
+- status: Filter by status (later, next, started, waiting, completed)
+- include_completed: Include completed tasks (default: false)
+
+**note** filters:
+- notebook_id: Filter by notebook UUID
+- source: Filter by source identifier
+- source_id: Filter by source-specific ID
+
+**person** filters:
+- source: Filter by source identifier
+- source_id: Filter by source-specific ID
+
+**goal** filters:
+- area_id: Required - area UUID, deep link, or config key
+
+**area, notebook, habit**: No filters (returns all from config)
+
+Note: Due to end-to-end encryption, names and content are not available
+for list operations — only metadata is returned. Use id parameter for details.`
+
+// QueryInput is the input schema for the consolidated query tool.
+type QueryInput struct {
+ Entity string `json:"entity" jsonschema:"required,enum=task,enum=note,enum=person,enum=area,enum=goal,enum=notebook,enum=habit"` //nolint:lll // JSON schema enum list
+ ID *string `json:"id,omitempty"`
+
+ // Task/Goal filters
+ AreaID *string `json:"area_id,omitempty"`
+
+ // Task filters
+ Status *string `json:"status,omitempty"`
+ IncludeCompleted *bool `json:"include_completed,omitempty"`
+
+ // Note filters
+ NotebookID *string `json:"notebook_id,omitempty"`
+
+ // Note/Person filters
+ Source *string `json:"source,omitempty"`
+ SourceID *string `json:"source_id,omitempty"`
+}
+
+// QueryOutput is the output schema for the consolidated query tool.
+type QueryOutput struct {
+ Entity string `json:"entity"`
+ Count int `json:"count,omitempty"`
+ // Results will be entity-specific; kept as any for flexibility
+ Items any `json:"items,omitempty"`
+ // Single item fields (when ID is provided)
+ DeepLink string `json:"deep_link,omitempty"`
+}
+
+// HandleQuery queries entities based on the entity type.
+func (h *Handler) HandleQuery(
+ ctx context.Context,
+ _ *mcp.CallToolRequest,
+ input QueryInput,
+) (*mcp.CallToolResult, QueryOutput, error) {
+ switch input.Entity {
+ case EntityTask:
+ return h.queryTask(ctx, input)
+ case EntityNote:
+ return h.queryNote(ctx, input)
+ case EntityPerson:
+ return h.queryPerson(ctx, input)
+ case EntityArea:
+ return h.queryArea(ctx, input)
+ case EntityGoal:
+ return h.queryGoal(ctx, input)
+ case EntityNotebook:
+ return h.queryNotebook(ctx, input)
+ case EntityHabit:
+ return h.queryHabit(ctx, input)
+ default:
+ return shared.ErrorResult("invalid entity: must be task, note, person, area, goal, notebook, or habit"),
+ QueryOutput{Entity: input.Entity}, nil
+ }
+}
+
+func (h *Handler) queryTask(
+ _ context.Context,
+ input QueryInput,
+) (*mcp.CallToolResult, QueryOutput, error) {
+ return shared.ErrorResult("task query not yet implemented"),
+ QueryOutput{Entity: input.Entity}, nil
+}
+
+func (h *Handler) queryNote(
+ _ context.Context,
+ input QueryInput,
+) (*mcp.CallToolResult, QueryOutput, error) {
+ return shared.ErrorResult("note query not yet implemented"),
+ QueryOutput{Entity: input.Entity}, nil
+}
+
+func (h *Handler) queryPerson(
+ _ context.Context,
+ input QueryInput,
+) (*mcp.CallToolResult, QueryOutput, error) {
+ return shared.ErrorResult("person query not yet implemented"),
+ QueryOutput{Entity: input.Entity}, nil
+}
+
+func (h *Handler) queryArea(
+ _ context.Context,
+ input QueryInput,
+) (*mcp.CallToolResult, QueryOutput, error) {
+ if input.ID != nil {
+ return shared.ErrorResult("area entities are config-based and do not support ID lookup"),
+ QueryOutput{Entity: input.Entity}, nil
+ }
+
+ return shared.ErrorResult("area query not yet implemented"),
+ QueryOutput{Entity: input.Entity}, nil
+}
+
+func (h *Handler) queryGoal(
+ _ context.Context,
+ input QueryInput,
+) (*mcp.CallToolResult, QueryOutput, error) {
+ if input.ID != nil {
+ return shared.ErrorResult("goal entities are config-based and do not support ID lookup"),
+ QueryOutput{Entity: input.Entity}, nil
+ }
+
+ return shared.ErrorResult("goal query not yet implemented"),
+ QueryOutput{Entity: input.Entity}, nil
+}
+
+func (h *Handler) queryNotebook(
+ _ context.Context,
+ input QueryInput,
+) (*mcp.CallToolResult, QueryOutput, error) {
+ if input.ID != nil {
+ return shared.ErrorResult("notebook entities are config-based and do not support ID lookup"),
+ QueryOutput{Entity: input.Entity}, nil
+ }
+
+ return shared.ErrorResult("notebook query not yet implemented"),
+ QueryOutput{Entity: input.Entity}, nil
+}
+
+func (h *Handler) queryHabit(
+ _ context.Context,
+ input QueryInput,
+) (*mcp.CallToolResult, QueryOutput, error) {
+ if input.ID != nil {
+ return shared.ErrorResult("habit entities are config-based and do not support ID lookup"),
+ QueryOutput{Entity: input.Entity}, nil
+ }
+
+ return shared.ErrorResult("habit query not yet implemented"),
+ QueryOutput{Entity: input.Entity}, nil
+}
@@ -0,0 +1,131 @@
+// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+package crud
+
+import (
+ "context"
+
+ "git.secluded.site/lune/internal/mcp/shared"
+ "github.com/modelcontextprotocol/go-sdk/mcp"
+)
+
+// UpdateToolName is the name of the consolidated update tool.
+const UpdateToolName = "update"
+
+// UpdateToolDescription describes the update tool for LLMs.
+const UpdateToolDescription = `Updates an existing entity in Lunatask.
+
+Required:
+- entity: Type to update (task, note, person)
+- id: Entity UUID or lunatask:// deep link
+
+Entity-specific fields (only provided fields are modified):
+
+**task**:
+- name: New task title
+- area_id: Move to area (UUID, deep link, or config key)
+- goal_id: Move to goal (UUID, deep link, or config key; requires area_id)
+- status: later, next, started, waiting, completed
+- note: New markdown note (replaces existing)
+- priority: lowest, low, normal, high, highest
+- estimate: Time estimate in minutes (0-720)
+- motivation: must, should, want
+- important: true/false for Eisenhower matrix
+- urgent: true/false for Eisenhower matrix
+- scheduled_on: Date to schedule (YYYY-MM-DD or natural language)
+
+**note**:
+- name: New note title
+- notebook_id: Move to notebook (UUID)
+- content: Replace content (Markdown)
+- date: Note date (YYYY-MM-DD or natural language)
+
+**person**:
+- first_name: New first name
+- last_name: New last name
+- relationship: New relationship strength (family, intimate-friends, close-friends,
+ casual-friends, acquaintances, business-contacts, almost-strangers)
+
+Returns the updated entity's deep link.`
+
+// UpdateInput is the input schema for the consolidated update tool.
+type UpdateInput struct {
+ Entity string `json:"entity" jsonschema:"required,enum=task,enum=note,enum=person"`
+ ID string `json:"id" jsonschema:"required"`
+
+ // Common fields
+ Name *string `json:"name,omitempty"`
+ Content *string `json:"content,omitempty"`
+
+ // Task-specific fields
+ AreaID *string `json:"area_id,omitempty"`
+ GoalID *string `json:"goal_id,omitempty"`
+ Status *string `json:"status,omitempty"`
+ Note *string `json:"note,omitempty"`
+ Priority *string `json:"priority,omitempty"`
+ Estimate *int `json:"estimate,omitempty"`
+ Motivation *string `json:"motivation,omitempty"`
+ Important *bool `json:"important,omitempty"`
+ Urgent *bool `json:"urgent,omitempty"`
+ ScheduledOn *string `json:"scheduled_on,omitempty"`
+
+ // Note-specific fields
+ NotebookID *string `json:"notebook_id,omitempty"`
+ Date *string `json:"date,omitempty"`
+
+ // Person-specific fields
+ FirstName *string `json:"first_name,omitempty"`
+ LastName *string `json:"last_name,omitempty"`
+ Relationship *string `json:"relationship,omitempty"`
+}
+
+// UpdateOutput is the output schema for the consolidated update tool.
+type UpdateOutput struct {
+ Entity string `json:"entity"`
+ DeepLink string `json:"deep_link"`
+}
+
+// HandleUpdate updates an existing entity based on the entity type.
+func (h *Handler) HandleUpdate(
+ ctx context.Context,
+ _ *mcp.CallToolRequest,
+ input UpdateInput,
+) (*mcp.CallToolResult, UpdateOutput, error) {
+ switch input.Entity {
+ case EntityTask:
+ return h.updateTask(ctx, input)
+ case EntityNote:
+ return h.updateNote(ctx, input)
+ case EntityPerson:
+ return h.updatePerson(ctx, input)
+ default:
+ return shared.ErrorResult("invalid entity: must be task, note, or person"),
+ UpdateOutput{Entity: input.Entity}, nil
+ }
+}
+
+func (h *Handler) updateTask(
+ _ context.Context,
+ input UpdateInput,
+) (*mcp.CallToolResult, UpdateOutput, error) {
+ return shared.ErrorResult("task update not yet implemented"),
+ UpdateOutput{Entity: input.Entity}, nil
+}
+
+func (h *Handler) updateNote(
+ _ context.Context,
+ input UpdateInput,
+) (*mcp.CallToolResult, UpdateOutput, error) {
+ return shared.ErrorResult("note update not yet implemented"),
+ UpdateOutput{Entity: input.Entity}, nil
+}
+
+func (h *Handler) updatePerson(
+ _ context.Context,
+ input UpdateInput,
+) (*mcp.CallToolResult, UpdateOutput, error) {
+ return shared.ErrorResult("person update not yet implemented"),
+ UpdateOutput{Entity: input.Entity}, nil
+}