feat(mcp): add OpenWorldHint and Title to tools

Amolith created

Add OpenWorldHint (true) and human-readable Title to all 7 MCP tools:

- get_timestamp: Parse date
- add_timeline_note: Add timeline note
- track_habit: Track habit
- create: Create entity
- update: Update entity
- delete: Delete entity
- query: Query entities

The OpenWorldHint indicates these tools interact with external APIs
(Lunatask), and titles provide nicer UI labels for MCP clients.

Change summary

internal/mcp/tools/crud/create.go       | 2 ++
internal/mcp/tools/crud/delete.go       | 2 ++
internal/mcp/tools/crud/query.go        | 4 +++-
internal/mcp/tools/crud/update.go       | 2 ++
internal/mcp/tools/habit/track.go       | 4 ++++
internal/mcp/tools/timeline/handler.go  | 2 ++
internal/mcp/tools/timestamp/handler.go | 6 +++++-
7 files changed, 20 insertions(+), 2 deletions(-)

Detailed changes

internal/mcp/tools/crud/create.go πŸ”—

@@ -44,6 +44,8 @@ Returns the new entity's ID and deep link.`
 func CreateToolAnnotations() *mcp.ToolAnnotations {
 	return &mcp.ToolAnnotations{
 		DestructiveHint: ptr(false),
+		OpenWorldHint:   ptr(true),
+		Title:           "Create entity",
 	}
 }
 

internal/mcp/tools/crud/delete.go πŸ”—

@@ -24,6 +24,8 @@ This action cannot be undone. The entity and its associations are removed.`
 func DeleteToolAnnotations() *mcp.ToolAnnotations {
 	return &mcp.ToolAnnotations{
 		DestructiveHint: ptr(true),
+		OpenWorldHint:   ptr(true),
+		Title:           "Delete entity",
 	}
 }
 

internal/mcp/tools/crud/query.go πŸ”—

@@ -29,7 +29,9 @@ Prefer MCP resources (lunatask://tasks/today, lunatask://areas) when available.`
 // QueryToolAnnotations returns hints about tool behavior.
 func QueryToolAnnotations() *mcp.ToolAnnotations {
 	return &mcp.ToolAnnotations{
-		ReadOnlyHint: true,
+		ReadOnlyHint:  true,
+		OpenWorldHint: ptr(true),
+		Title:         "Query entities",
 	}
 }
 

internal/mcp/tools/crud/update.go πŸ”—

@@ -28,6 +28,8 @@ Task note/content replaces existing (not appended). Idempotent.`
 func UpdateToolAnnotations() *mcp.ToolAnnotations {
 	return &mcp.ToolAnnotations{
 		IdempotentHint: true,
+		OpenWorldHint:  ptr(true),
+		Title:          "Update entity",
 	}
 }
 

internal/mcp/tools/habit/track.go πŸ”—

@@ -27,9 +27,13 @@ Tracks for today by default. Idempotentβ€”re-tracking the same date has no effec
 func TrackToolAnnotations() *mcp.ToolAnnotations {
 	return &mcp.ToolAnnotations{
 		IdempotentHint: true,
+		OpenWorldHint:  ptr(true),
+		Title:          "Track habit",
 	}
 }
 
+func ptr[T any](v T) *T { return &v }
+
 // TrackInput is the input schema for tracking a habit.
 type TrackInput struct {
 	HabitID     string  `json:"habit_id"               jsonschema:"Habit UUID, lunatask:// deep link, or config key"`

internal/mcp/tools/timeline/handler.go πŸ”—

@@ -27,6 +27,8 @@ interactions, meetings, or memorable moments with someone.`
 func ToolAnnotations() *mcp.ToolAnnotations {
 	return &mcp.ToolAnnotations{
 		DestructiveHint: ptr(false),
+		OpenWorldHint:   ptr(true),
+		Title:           "Add timeline note",
 	}
 }
 

internal/mcp/tools/timestamp/handler.go πŸ”—

@@ -27,10 +27,14 @@ Empty input returns today.`
 // ToolAnnotations returns hints about tool behavior.
 func ToolAnnotations() *mcp.ToolAnnotations {
 	return &mcp.ToolAnnotations{
-		ReadOnlyHint: true,
+		ReadOnlyHint:    true,
+		OpenWorldHint:   ptr(true),
+		Title:           "Parse date",
 	}
 }
 
+func ptr[T any](v T) *T { return &v }
+
 // Input is the input schema for the timestamp tool.
 type Input struct {
 	Date string `json:"date" jsonschema:"Date/time expression to parse (empty = today)"`