From b27827671dc010be3e261b8b221aca6b7f836661 Mon Sep 17 00:00:00 2001 From: Paul Hernandez <60959+phernandez@users.noreply.github.com> Date: Fri, 28 Mar 2025 19:11:35 -0500 Subject: [PATCH] feat: rename search tool to search_notes (#66) Signed-off-by: phernandez --- CLAUDE.md | 4 ++-- README.md | 5 +++-- docs/AI Assistant Guide.md | 10 +++++----- docs/Technical Information.md | 2 +- src/basic_memory/cli/commands/tool.py | 6 +++--- .../mcp/prompts/continue_conversation.py | 6 +++--- src/basic_memory/mcp/prompts/search.py | 8 ++++---- .../mcp/resources/ai_assistant_guide.md | 10 +++++----- src/basic_memory/mcp/tools/__init__.py | 4 ++-- src/basic_memory/mcp/tools/read_note.py | 6 +++--- src/basic_memory/mcp/tools/search.py | 18 +++++++++--------- static/ai_assistant_guide.md | 10 +++++----- tests/cli/test_cli_tools.py | 4 ++-- tests/mcp/test_tool_read_note.py | 2 +- tests/mcp/test_tool_search.py | 10 +++++----- 15 files changed, 53 insertions(+), 52 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index d6b26f5d..6d65a656 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -107,7 +107,7 @@ See the [README.md](README.md) file for a project overview. 1d", "1 week") **Search & Discovery:** - - `search(query, page, page_size)` - Full-text search across all content with filtering options + - `search_notes(query, page, page_size)` - Full-text search across all content with filtering options **Visualization:** - `canvas(nodes, edges, title, folder)` - Generate Obsidian canvas files for knowledge graph visualization @@ -115,7 +115,7 @@ See the [README.md](README.md) file for a project overview. - MCP Prompts for better AI interaction: - `ai_assistant_guide()` - Guidance on effectively using Basic Memory tools for AI assistants - `continue_conversation(topic, timeframe)` - Continue previous conversations with relevant historical context - - `search(query, after_date)` - Search with detailed, formatted results for better context understanding + - `search_notes(query, after_date)` - Search with detailed, formatted results for better context understanding - `recent_activity(timeframe)` - View recently changed items with formatted output - `json_canvas_spec()` - Full JSON Canvas specification for Obsidian visualization diff --git a/README.md b/README.md index aed8854e..3a0ba816 100644 --- a/README.md +++ b/README.md @@ -285,7 +285,8 @@ for OS X): } ``` -If you want to use a specific project (see [Multiple Projects](docs/User%20Guide.md#multiple-projects)), update your Claude Desktop +If you want to use a specific project (see [Multiple Projects](docs/User%20Guide.md#multiple-projects)), update your +Claude Desktop config: ```json @@ -320,7 +321,7 @@ basic-memory sync --watch write_note(title, content, folder, tags) - Create or update notes read_note(identifier, page, page_size) - Read notes by title or permalink build_context(url, depth, timeframe) - Navigate knowledge graph via memory:// URLs -search(query, page, page_size) - Search across your knowledge base +search_notes(query, page, page_size) - Search across your knowledge base recent_activity(type, depth, timeframe) - Find recently updated information canvas(nodes, edges, title, folder) - Generate knowledge visualizations ``` diff --git a/docs/AI Assistant Guide.md b/docs/AI Assistant Guide.md index 33ed0695..d1eb3368 100644 --- a/docs/AI Assistant Guide.md +++ b/docs/AI Assistant Guide.md @@ -53,7 +53,7 @@ content = await read_note("specs/search-design") # By path content = await read_note("memory://specs/search") # By memory URL # Searching for knowledge -results = await search( +results = await search_notes( query="authentication system", # Text to search for page=1, # Optional: Pagination page_size=10 # Optional: Results per page @@ -154,7 +154,7 @@ Users will interact with Basic Memory in patterns like: Human: "What were our decisions about auth?" You: Let me find that information for you. - [Use search() to find relevant notes] + [Use search_notes() to find relevant notes] [Then build_context() to understand connections] ``` @@ -251,7 +251,7 @@ When creating relations, you can: # Example workflow for creating notes with effective relations async def create_note_with_effective_relations(): # Search for existing entities to reference - search_results = await search("travel") + search_results = await search_notes("travel") existing_entities = [result.title for result in search_results.primary_results] # Check if specific entities exist @@ -323,7 +323,7 @@ Common issues to watch for: content = await read_note("Document") except: # Try search instead - results = await search("Document") + results = await search_notes("Document") if results and results.primary_results: # Found something similar content = await read_note(results.primary_results[0].permalink) @@ -369,7 +369,7 @@ Common issues to watch for: - **Create deliberate relations**: Connect each note to at least 2-3 related entities - **Use existing entities**: Before creating a new relation, search for existing entities - **Verify wikilinks**: When referencing `[[Entity]]`, use exact titles of existing notes - - **Check accuracy**: Use `search()` or `recent_activity()` to confirm entity titles + - **Check accuracy**: Use `search_notes()` or `recent_activity()` to confirm entity titles - **Use precise relation types**: Choose specific relation types that convey meaning (e.g., "implements" instead of "relates_to") - **Consider bidirectional relations**: When appropriate, create inverse relations in both entities diff --git a/docs/Technical Information.md b/docs/Technical Information.md index 801545fc..721c6960 100644 --- a/docs/Technical Information.md +++ b/docs/Technical Information.md @@ -196,7 +196,7 @@ flowchart TD end BMCP <-->|"write_note() read_note()"| KnowledgeFiles - BMCP <-->|"search() build_context()"| KnowledgeIndex + BMCP <-->|"search_notes() build_context()"| KnowledgeIndex KnowledgeFiles <-.->|Sync Process| KnowledgeIndex KnowledgeFiles <-->|Direct Editing| Editors((Text Editors & Git)) diff --git a/src/basic_memory/cli/commands/tool.py b/src/basic_memory/cli/commands/tool.py index 50fda8d6..9df2b2f3 100644 --- a/src/basic_memory/cli/commands/tool.py +++ b/src/basic_memory/cli/commands/tool.py @@ -12,7 +12,7 @@ from basic_memory.cli.app import app from basic_memory.mcp.tools import build_context as mcp_build_context from basic_memory.mcp.tools import read_note as mcp_read_note from basic_memory.mcp.tools import recent_activity as mcp_recent_activity -from basic_memory.mcp.tools import search as mcp_search +from basic_memory.mcp.tools import search_notes as mcp_search from basic_memory.mcp.tools import write_note as mcp_write_note # Import prompts @@ -180,8 +180,8 @@ def recent_activity( raise -@tool_app.command() -def search( +@tool_app.command("search-notes") +def search_notes( query: str, permalink: Annotated[bool, typer.Option("--permalink", help="Search permalink values")] = False, title: Annotated[bool, typer.Option("--title", help="Search title values")] = False, diff --git a/src/basic_memory/mcp/prompts/continue_conversation.py b/src/basic_memory/mcp/prompts/continue_conversation.py index 3c96335d..d2596195 100644 --- a/src/basic_memory/mcp/prompts/continue_conversation.py +++ b/src/basic_memory/mcp/prompts/continue_conversation.py @@ -14,7 +14,7 @@ from basic_memory.mcp.prompts.utils import format_prompt_context, PromptContext, from basic_memory.mcp.server import mcp from basic_memory.mcp.tools.build_context import build_context from basic_memory.mcp.tools.recent_activity import recent_activity -from basic_memory.mcp.tools.search import search +from basic_memory.mcp.tools.search import search_notes from basic_memory.schemas.base import TimeFrame from basic_memory.schemas.memory import GraphContext from basic_memory.schemas.search import SearchQuery, SearchItemType @@ -47,7 +47,7 @@ async def continue_conversation( # If topic provided, search for it if topic: - search_results = await search( + search_results = await search_notes( SearchQuery(text=topic, after_date=timeframe, types=[SearchItemType.ENTITY]) ) @@ -93,7 +93,7 @@ async def continue_conversation( ## Next Steps You can: - - Explore more with: `search({{"text": "{topic}"}})` + - Explore more with: `search_notes({{"text": "{topic}"}})` - See what's changed: `recent_activity(timeframe="{timeframe or "7d"}")` - **Record new learnings or decisions from this conversation:** `write_note(title="[Create a meaningful title]", content="[Content with observations and relations]")` diff --git a/src/basic_memory/mcp/prompts/search.py b/src/basic_memory/mcp/prompts/search.py index c641d229..486d049b 100644 --- a/src/basic_memory/mcp/prompts/search.py +++ b/src/basic_memory/mcp/prompts/search.py @@ -10,7 +10,7 @@ from loguru import logger from pydantic import Field from basic_memory.mcp.server import mcp -from basic_memory.mcp.tools.search import search as search_tool +from basic_memory.mcp.tools.search import search_notes as search_tool from basic_memory.schemas.base import TimeFrame from basic_memory.schemas.search import SearchQuery, SearchResponse @@ -144,9 +144,9 @@ def format_search_results( ## Next Steps You can: - - Refine your search: `search("{query} AND additional_term")` - - Exclude terms: `search("{query} NOT exclude_term")` - - View more results: `search("{query}", after_date=None)` + - Refine your search: `search_notes("{query} AND additional_term")` + - Exclude terms: `search_notes("{query} NOT exclude_term")` + - View more results: `search_notes("{query}", after_date=None)` - Check recent activity: `recent_activity()` ## Synthesize and Capture Knowledge diff --git a/src/basic_memory/mcp/resources/ai_assistant_guide.md b/src/basic_memory/mcp/resources/ai_assistant_guide.md index cd7bc1dd..d66a563a 100644 --- a/src/basic_memory/mcp/resources/ai_assistant_guide.md +++ b/src/basic_memory/mcp/resources/ai_assistant_guide.md @@ -49,7 +49,7 @@ content = await read_note("specs/search-design") # By path content = await read_note("memory://specs/search") # By memory URL # Searching for knowledge -results = await search( +results = await search_notes( query="authentication system", # Text to search for page=1, # Optional: Pagination page_size=10 # Optional: Results per page @@ -154,7 +154,7 @@ Users will interact with Basic Memory in patterns like: Human: "What were our decisions about auth?" You: Let me find that information for you. - [Use search() to find relevant notes] + [Use search_notes() to find relevant notes] [Then build_context() to understand connections] ``` @@ -263,7 +263,7 @@ When creating relations, you can: # Example workflow for creating notes with effective relations async def create_note_with_effective_relations(): # Search for existing entities to reference - search_results = await search("travel") + search_results = await search_notes("travel") existing_entities = [result.title for result in search_results.primary_results] # Check if specific entities exist @@ -335,7 +335,7 @@ Common issues to watch for: content = await read_note("Document") except: # Try search instead - results = await search("Document") + results = await search_notes("Document") if results and results.primary_results: # Found something similar content = await read_note(results.primary_results[0].permalink) @@ -381,7 +381,7 @@ Common issues to watch for: - **Create deliberate relations**: Connect each note to at least 2-3 related entities - **Use existing entities**: Before creating a new relation, search for existing entities - **Verify wikilinks**: When referencing `[[Entity]]`, use exact titles of existing notes - - **Check accuracy**: Use `search()` or `recent_activity()` to confirm entity titles + - **Check accuracy**: Use `search_notes()` or `recent_activity()` to confirm entity titles - **Use precise relation types**: Choose specific relation types that convey meaning (e.g., "implements" instead of "relates_to") - **Consider bidirectional relations**: When appropriate, create inverse relations in both entities diff --git a/src/basic_memory/mcp/tools/__init__.py b/src/basic_memory/mcp/tools/__init__.py index ac27fdb9..5cd8ced3 100644 --- a/src/basic_memory/mcp/tools/__init__.py +++ b/src/basic_memory/mcp/tools/__init__.py @@ -12,7 +12,7 @@ from basic_memory.mcp.tools.build_context import build_context from basic_memory.mcp.tools.recent_activity import recent_activity from basic_memory.mcp.tools.read_note import read_note from basic_memory.mcp.tools.write_note import write_note -from basic_memory.mcp.tools.search import search +from basic_memory.mcp.tools.search import search_notes from basic_memory.mcp.tools.canvas import canvas __all__ = [ @@ -22,6 +22,6 @@ __all__ = [ "read_content", "read_note", "recent_activity", - "search", + "search_notes", "write_note", ] diff --git a/src/basic_memory/mcp/tools/read_note.py b/src/basic_memory/mcp/tools/read_note.py index 3afd7ccd..44c53743 100644 --- a/src/basic_memory/mcp/tools/read_note.py +++ b/src/basic_memory/mcp/tools/read_note.py @@ -6,7 +6,7 @@ from loguru import logger from basic_memory.mcp.async_client import client from basic_memory.mcp.server import mcp -from basic_memory.mcp.tools.search import search +from basic_memory.mcp.tools.search import search_notes from basic_memory.mcp.tools.utils import call_get from basic_memory.schemas.memory import memory_url_path from basic_memory.schemas.search import SearchQuery @@ -63,7 +63,7 @@ async def read_note(identifier: str, page: int = 1, page_size: int = 10) -> str: # Fallback 1: Try title search via API logger.info(f"Search title for: {identifier}") - title_results = await search(SearchQuery(title=identifier)) + title_results = await search_notes(SearchQuery(title=identifier)) if title_results and title_results.results: result = title_results.results[0] # Get the first/best match @@ -87,7 +87,7 @@ async def read_note(identifier: str, page: int = 1, page_size: int = 10) -> str: # Fallback 2: Text search as a last resort logger.info(f"Title search failed, trying text search for: {identifier}") - text_results = await search(SearchQuery(text=identifier)) + text_results = await search_notes(SearchQuery(text=identifier)) # We didn't find a direct match, construct a helpful error message if not text_results or not text_results.results: diff --git a/src/basic_memory/mcp/tools/search.py b/src/basic_memory/mcp/tools/search.py index 14da86d4..9eb4644b 100644 --- a/src/basic_memory/mcp/tools/search.py +++ b/src/basic_memory/mcp/tools/search.py @@ -11,7 +11,7 @@ from basic_memory.mcp.async_client import client @mcp.tool( description="Search across all content in the knowledge base.", ) -async def search(query: SearchQuery, page: int = 1, page_size: int = 10) -> SearchResponse: +async def search_notes(query: SearchQuery, page: int = 1, page_size: int = 10) -> SearchResponse: """Search across all content in the knowledge base. This tool searches the knowledge base using full-text search, pattern matching, @@ -36,34 +36,34 @@ async def search(query: SearchQuery, page: int = 1, page_size: int = 10) -> Sear Examples: # Basic text search - results = await search(SearchQuery(text="project planning")) + results = await search_notes(SearchQuery(text="project planning")) # Boolean AND search (both terms must be present) - results = await search(SearchQuery(text="project AND planning")) + results = await search_notes(SearchQuery(text="project AND planning")) # Boolean OR search (either term can be present) - results = await search(SearchQuery(text="project OR meeting")) + results = await search_notes(SearchQuery(text="project OR meeting")) # Boolean NOT search (exclude terms) - results = await search(SearchQuery(text="project NOT meeting")) + results = await search_notes(SearchQuery(text="project NOT meeting")) # Boolean search with grouping - results = await search(SearchQuery(text="(project OR planning) AND notes")) + results = await search_notes(SearchQuery(text="(project OR planning) AND notes")) # Search with type filter - results = await search(SearchQuery( + results = await search_notes(SearchQuery( text="meeting notes", types=["entity"], )) # Search for recent content - results = await search(SearchQuery( + results = await search_notes(SearchQuery( text="bug report", after_date="1 week" )) # Pattern matching on permalinks - results = await search(SearchQuery( + results = await search_notes(SearchQuery( permalink_match="docs/meeting-*" )) """ diff --git a/static/ai_assistant_guide.md b/static/ai_assistant_guide.md index cd7bc1dd..d66a563a 100644 --- a/static/ai_assistant_guide.md +++ b/static/ai_assistant_guide.md @@ -49,7 +49,7 @@ content = await read_note("specs/search-design") # By path content = await read_note("memory://specs/search") # By memory URL # Searching for knowledge -results = await search( +results = await search_notes( query="authentication system", # Text to search for page=1, # Optional: Pagination page_size=10 # Optional: Results per page @@ -154,7 +154,7 @@ Users will interact with Basic Memory in patterns like: Human: "What were our decisions about auth?" You: Let me find that information for you. - [Use search() to find relevant notes] + [Use search_notes() to find relevant notes] [Then build_context() to understand connections] ``` @@ -263,7 +263,7 @@ When creating relations, you can: # Example workflow for creating notes with effective relations async def create_note_with_effective_relations(): # Search for existing entities to reference - search_results = await search("travel") + search_results = await search_notes("travel") existing_entities = [result.title for result in search_results.primary_results] # Check if specific entities exist @@ -335,7 +335,7 @@ Common issues to watch for: content = await read_note("Document") except: # Try search instead - results = await search("Document") + results = await search_notes("Document") if results and results.primary_results: # Found something similar content = await read_note(results.primary_results[0].permalink) @@ -381,7 +381,7 @@ Common issues to watch for: - **Create deliberate relations**: Connect each note to at least 2-3 related entities - **Use existing entities**: Before creating a new relation, search for existing entities - **Verify wikilinks**: When referencing `[[Entity]]`, use exact titles of existing notes - - **Check accuracy**: Use `search()` or `recent_activity()` to confirm entity titles + - **Check accuracy**: Use `search_notes()` or `recent_activity()` to confirm entity titles - **Use precise relation types**: Choose specific relation types that convey meaning (e.g., "implements" instead of "relates_to") - **Consider bidirectional relations**: When appropriate, create inverse relations in both entities diff --git a/tests/cli/test_cli_tools.py b/tests/cli/test_cli_tools.py index 58870fbf..e57a0334 100644 --- a/tests/cli/test_cli_tools.py +++ b/tests/cli/test_cli_tools.py @@ -211,7 +211,7 @@ def test_search_basic(cli_env, setup_test_note): """Test basic search command.""" result = runner.invoke( tool_app, - ["search", "test observation"], + ["search-notes", "test observation"], ) assert result.exit_code == 0 @@ -235,7 +235,7 @@ def test_search_permalink(cli_env, setup_test_note): result = runner.invoke( tool_app, - ["search", permalink, "--permalink"], + ["search-notes", permalink, "--permalink"], ) assert result.exit_code == 0 diff --git a/tests/mcp/test_tool_read_note.py b/tests/mcp/test_tool_read_note.py index 075c01f3..6e1c5b2a 100644 --- a/tests/mcp/test_tool_read_note.py +++ b/tests/mcp/test_tool_read_note.py @@ -26,7 +26,7 @@ async def mock_call_get(): @pytest_asyncio.fixture async def mock_search(): """Mock for search tool.""" - with patch("basic_memory.mcp.tools.read_note.search") as mock: + with patch("basic_memory.mcp.tools.read_note.search_notes") as mock: # Default to empty results mock.return_value = SearchResponse(results=[], current_page=1, page_size=1) yield mock diff --git a/tests/mcp/test_tool_search.py b/tests/mcp/test_tool_search.py index ad05ffc6..523a10aa 100644 --- a/tests/mcp/test_tool_search.py +++ b/tests/mcp/test_tool_search.py @@ -4,7 +4,7 @@ import pytest from datetime import datetime, timedelta from basic_memory.mcp.tools import write_note -from basic_memory.mcp.tools.search import search +from basic_memory.mcp.tools.search import search_notes from basic_memory.schemas.search import SearchQuery, SearchItemType @@ -22,7 +22,7 @@ async def test_search_basic(client): # Search for it query = SearchQuery(text="searchable") - response = await search(query) + response = await search_notes(query) # Verify results assert len(response.results) > 0 @@ -43,7 +43,7 @@ async def test_search_pagination(client): # Search for it query = SearchQuery(text="searchable") - response = await search(query, page=1, page_size=1) + response = await search_notes(query, page=1, page_size=1) # Verify results assert len(response.results) == 1 @@ -62,7 +62,7 @@ async def test_search_with_type_filter(client): # Search with type filter query = SearchQuery(text="type", types=[SearchItemType.ENTITY]) - response = await search(query) + response = await search_notes(query) # Verify all results are entities assert all(r.type == "entity" for r in response.results) @@ -81,7 +81,7 @@ async def test_search_with_date_filter(client): # Search with date filter one_hour_ago = datetime.now() - timedelta(hours=1) query = SearchQuery(text="recent", after_date=one_hour_ago) - response = await search(query) + response = await search_notes(query) # Verify we get results within timeframe assert len(response.results) > 0