Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,15 +107,15 @@ 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

- 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

Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
```
Expand Down
10 changes: 5 additions & 5 deletions docs/AI Assistant Guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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]
```

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion docs/Technical Information.md
Original file line number Diff line number Diff line change
Expand Up @@ -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))

Expand Down
6 changes: 3 additions & 3 deletions src/basic_memory/cli/commands/tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
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
Expand Down Expand Up @@ -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,
Expand Down
6 changes: 3 additions & 3 deletions src/basic_memory/mcp/prompts/continue_conversation.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
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
Expand Down Expand Up @@ -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])
)

Expand Down Expand Up @@ -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]")`

Expand Down
8 changes: 4 additions & 4 deletions src/basic_memory/mcp/prompts/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
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

Expand Down Expand Up @@ -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
Expand Down
10 changes: 5 additions & 5 deletions src/basic_memory/mcp/resources/ai_assistant_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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]
```

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/basic_memory/mcp/tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
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__ = [
Expand All @@ -22,6 +22,6 @@
"read_content",
"read_note",
"recent_activity",
"search",
"search_notes",
"write_note",
]
6 changes: 3 additions & 3 deletions src/basic_memory/mcp/tools/read_note.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

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
Expand Down Expand Up @@ -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
Expand All @@ -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:
Expand Down
18 changes: 9 additions & 9 deletions src/basic_memory/mcp/tools/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
@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,
Expand All @@ -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-*"
))
"""
Expand Down
10 changes: 5 additions & 5 deletions static/ai_assistant_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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]
```

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions tests/cli/test_cli_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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

Expand Down
2 changes: 1 addition & 1 deletion tests/mcp/test_tool_read_note.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading
Loading