Skip to content

Commit c977037

Browse files
groksrcclaude
andcommitted
feat(mcp): add title and tags annotations to all MCP tool decorators (issue #826 phase 1)
Adds human-readable `title` and categorization `tags` to all ~24 @mcp.tool decorators across the MCP tools package. FastMCP 3.3.1 (pinned in pyproject.toml) supports both fields natively. Tags used: notes, search, projects, cloud, schema, navigation, canvas, ui. Extends test_tool_contracts.py with an async test that asserts every registered tool has a non-empty title and at least one tag to prevent future regressions. output_schema is explicitly deferred as a follow-up (phase 2): it requires per-tool design decisions about which tools reliably return structured JSON and how to handle tools that return str|dict depending on output_format. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Drew Cain <groksrc@gmail.com>
1 parent 0811c48 commit c977037

20 files changed

Lines changed: 66 additions & 0 deletions

src/basic_memory/mcp/tools/build_context.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ def _format_context_markdown(graph: GraphContext, project: str) -> str:
112112

113113

114114
@mcp.tool(
115+
title="Build Context",
115116
description="""Build context from a memory:// URI to continue conversations naturally.
116117
117118
Use this to follow up on previous discussions or explore related topics.
@@ -131,6 +132,7 @@ def _format_context_markdown(graph: GraphContext, project: str) -> str:
131132
- "json" (default): Structured JSON with internal fields excluded
132133
- "text": Compact markdown text for LLM consumption
133134
""",
135+
tags={"navigation", "notes"},
134136
annotations={"readOnlyHint": True, "openWorldHint": False},
135137
)
136138
async def build_context(

src/basic_memory/mcp/tools/canvas.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717

1818

1919
@mcp.tool(
20+
title="Create Canvas",
2021
description="Create an Obsidian canvas file to visualize concepts and connections.",
22+
tags={"canvas", "notes"},
2123
annotations={"destructiveHint": False, "idempotentHint": True, "openWorldHint": False},
2224
)
2325
async def canvas(

src/basic_memory/mcp/tools/chatgpt_tools.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,9 @@ def _format_document_for_chatgpt(
105105

106106

107107
@mcp.tool(
108+
title="Search Knowledge Base",
108109
description="Search for content across the knowledge base",
110+
tags={"search"},
109111
annotations={"readOnlyHint": True, "openWorldHint": False},
110112
)
111113
async def search(
@@ -167,7 +169,9 @@ async def search(
167169

168170

169171
@mcp.tool(
172+
title="Fetch Document",
170173
description="Fetch the full contents of a search result document",
174+
tags={"search", "notes"},
171175
annotations={"readOnlyHint": True, "openWorldHint": False},
172176
)
173177
async def fetch(

src/basic_memory/mcp/tools/cloud_info.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
@mcp.tool(
99
"cloud_info",
10+
title="Cloud Info",
11+
tags={"cloud"},
1012
annotations={"readOnlyHint": True, "openWorldHint": False},
1113
)
1214
def cloud_info() -> str:

src/basic_memory/mcp/tools/delete_note.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,9 @@ def _directory_path_for_delete(
182182

183183

184184
@mcp.tool(
185+
title="Delete Note",
185186
description="Delete a note or directory by title, permalink, or path",
187+
tags={"notes"},
186188
annotations={"destructiveHint": True, "openWorldHint": False},
187189
)
188190
async def delete_note(

src/basic_memory/mcp/tools/edit_note.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,9 @@ def _format_error_response(
304304

305305

306306
@mcp.tool(
307+
title="Edit Note",
307308
description="Edit an existing markdown note using various operations like append, prepend, find_replace, replace_section, insert_before_section, or insert_after_section.",
309+
tags={"notes"},
308310
annotations={"destructiveHint": False, "openWorldHint": False},
309311
)
310312
async def edit_note(

src/basic_memory/mcp/tools/list_directory.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111

1212

1313
@mcp.tool(
14+
title="List Directory",
1415
description="List directory contents with filtering and depth control.",
16+
tags={"navigation", "notes"},
1517
annotations={"readOnlyHint": True, "openWorldHint": False},
1618
)
1719
async def list_directory(

src/basic_memory/mcp/tools/move_note.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,9 @@ def _format_move_error_response(error_message: str, identifier: str, destination
357357

358358

359359
@mcp.tool(
360+
title="Move Note",
360361
description="Move a note or directory to a new location, updating database and maintaining links.",
362+
tags={"notes"},
361363
annotations={"destructiveHint": False, "openWorldHint": False},
362364
)
363365
async def move_note(

src/basic_memory/mcp/tools/project_management.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,8 @@ def _format_project_list_json(
357357

358358
@mcp.tool(
359359
"list_memory_projects",
360+
title="List Memory Projects",
361+
tags={"projects"},
360362
annotations={"readOnlyHint": True, "openWorldHint": False},
361363
)
362364
async def list_memory_projects(
@@ -495,6 +497,8 @@ async def _resolve_workspace_routing(
495497

496498
@mcp.tool(
497499
"create_memory_project",
500+
title="Create Memory Project",
501+
tags={"projects"},
498502
annotations={"destructiveHint": False, "openWorldHint": False},
499503
)
500504
async def create_memory_project(
@@ -636,6 +640,8 @@ async def create_memory_project(
636640

637641

638642
@mcp.tool(
643+
title="Delete Project",
644+
tags={"projects"},
639645
annotations={"destructiveHint": True, "openWorldHint": False},
640646
)
641647
async def delete_project(

src/basic_memory/mcp/tools/read_content.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,9 @@ def optimize_image(img, content_length, max_output_bytes=350000):
155155

156156

157157
@mcp.tool(
158+
title="Read Content",
158159
description="Read a file's raw content by path or permalink",
160+
tags={"notes"},
159161
annotations={"readOnlyHint": True, "openWorldHint": False},
160162
)
161163
async def read_content(

0 commit comments

Comments
 (0)