basic-memory 0.7.0__py3-none-any.whl → 0.16.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of basic-memory might be problematic. Click here for more details.
- basic_memory/__init__.py +5 -1
- basic_memory/alembic/alembic.ini +119 -0
- basic_memory/alembic/env.py +27 -3
- basic_memory/alembic/migrations.py +4 -9
- basic_memory/alembic/versions/502b60eaa905_remove_required_from_entity_permalink.py +51 -0
- basic_memory/alembic/versions/5fe1ab1ccebe_add_projects_table.py +108 -0
- basic_memory/alembic/versions/647e7a75e2cd_project_constraint_fix.py +104 -0
- basic_memory/alembic/versions/9d9c1cb7d8f5_add_mtime_and_size_columns_to_entity_.py +49 -0
- basic_memory/alembic/versions/a1b2c3d4e5f6_fix_project_foreign_keys.py +49 -0
- basic_memory/alembic/versions/b3c3938bacdb_relation_to_name_unique_index.py +44 -0
- basic_memory/alembic/versions/cc7172b46608_update_search_index_schema.py +100 -0
- basic_memory/alembic/versions/e7e1f4367280_add_scan_watermark_tracking_to_project.py +37 -0
- basic_memory/api/app.py +64 -18
- basic_memory/api/routers/__init__.py +4 -1
- basic_memory/api/routers/directory_router.py +84 -0
- basic_memory/api/routers/importer_router.py +152 -0
- basic_memory/api/routers/knowledge_router.py +166 -21
- basic_memory/api/routers/management_router.py +80 -0
- basic_memory/api/routers/memory_router.py +9 -64
- basic_memory/api/routers/project_router.py +406 -0
- basic_memory/api/routers/prompt_router.py +260 -0
- basic_memory/api/routers/resource_router.py +119 -4
- basic_memory/api/routers/search_router.py +5 -5
- basic_memory/api/routers/utils.py +130 -0
- basic_memory/api/template_loader.py +292 -0
- basic_memory/cli/app.py +43 -9
- basic_memory/cli/auth.py +277 -0
- basic_memory/cli/commands/__init__.py +13 -2
- basic_memory/cli/commands/cloud/__init__.py +6 -0
- basic_memory/cli/commands/cloud/api_client.py +112 -0
- basic_memory/cli/commands/cloud/bisync_commands.py +110 -0
- basic_memory/cli/commands/cloud/cloud_utils.py +101 -0
- basic_memory/cli/commands/cloud/core_commands.py +195 -0
- basic_memory/cli/commands/cloud/rclone_commands.py +301 -0
- basic_memory/cli/commands/cloud/rclone_config.py +110 -0
- basic_memory/cli/commands/cloud/rclone_installer.py +249 -0
- basic_memory/cli/commands/cloud/upload.py +233 -0
- basic_memory/cli/commands/cloud/upload_command.py +124 -0
- basic_memory/cli/commands/command_utils.py +51 -0
- basic_memory/cli/commands/db.py +28 -12
- basic_memory/cli/commands/import_chatgpt.py +40 -220
- basic_memory/cli/commands/import_claude_conversations.py +41 -168
- basic_memory/cli/commands/import_claude_projects.py +46 -157
- basic_memory/cli/commands/import_memory_json.py +48 -108
- basic_memory/cli/commands/mcp.py +84 -10
- basic_memory/cli/commands/project.py +876 -0
- basic_memory/cli/commands/status.py +50 -33
- basic_memory/cli/commands/tool.py +341 -0
- basic_memory/cli/main.py +8 -7
- basic_memory/config.py +477 -23
- basic_memory/db.py +168 -17
- basic_memory/deps.py +251 -25
- basic_memory/file_utils.py +113 -58
- basic_memory/ignore_utils.py +297 -0
- basic_memory/importers/__init__.py +27 -0
- basic_memory/importers/base.py +79 -0
- basic_memory/importers/chatgpt_importer.py +232 -0
- basic_memory/importers/claude_conversations_importer.py +177 -0
- basic_memory/importers/claude_projects_importer.py +148 -0
- basic_memory/importers/memory_json_importer.py +108 -0
- basic_memory/importers/utils.py +58 -0
- basic_memory/markdown/entity_parser.py +143 -23
- basic_memory/markdown/markdown_processor.py +3 -3
- basic_memory/markdown/plugins.py +39 -21
- basic_memory/markdown/schemas.py +1 -1
- basic_memory/markdown/utils.py +28 -13
- basic_memory/mcp/async_client.py +134 -4
- basic_memory/mcp/project_context.py +141 -0
- basic_memory/mcp/prompts/__init__.py +19 -0
- basic_memory/mcp/prompts/ai_assistant_guide.py +70 -0
- basic_memory/mcp/prompts/continue_conversation.py +62 -0
- basic_memory/mcp/prompts/recent_activity.py +188 -0
- basic_memory/mcp/prompts/search.py +57 -0
- basic_memory/mcp/prompts/utils.py +162 -0
- basic_memory/mcp/resources/ai_assistant_guide.md +283 -0
- basic_memory/mcp/resources/project_info.py +71 -0
- basic_memory/mcp/server.py +7 -13
- basic_memory/mcp/tools/__init__.py +33 -21
- basic_memory/mcp/tools/build_context.py +120 -0
- basic_memory/mcp/tools/canvas.py +130 -0
- basic_memory/mcp/tools/chatgpt_tools.py +187 -0
- basic_memory/mcp/tools/delete_note.py +225 -0
- basic_memory/mcp/tools/edit_note.py +320 -0
- basic_memory/mcp/tools/list_directory.py +167 -0
- basic_memory/mcp/tools/move_note.py +545 -0
- basic_memory/mcp/tools/project_management.py +200 -0
- basic_memory/mcp/tools/read_content.py +271 -0
- basic_memory/mcp/tools/read_note.py +255 -0
- basic_memory/mcp/tools/recent_activity.py +534 -0
- basic_memory/mcp/tools/search.py +369 -23
- basic_memory/mcp/tools/utils.py +374 -16
- basic_memory/mcp/tools/view_note.py +77 -0
- basic_memory/mcp/tools/write_note.py +207 -0
- basic_memory/models/__init__.py +3 -2
- basic_memory/models/knowledge.py +67 -15
- basic_memory/models/project.py +87 -0
- basic_memory/models/search.py +10 -6
- basic_memory/repository/__init__.py +2 -0
- basic_memory/repository/entity_repository.py +229 -7
- basic_memory/repository/observation_repository.py +35 -3
- basic_memory/repository/project_info_repository.py +10 -0
- basic_memory/repository/project_repository.py +103 -0
- basic_memory/repository/relation_repository.py +21 -2
- basic_memory/repository/repository.py +147 -29
- basic_memory/repository/search_repository.py +411 -62
- basic_memory/schemas/__init__.py +22 -9
- basic_memory/schemas/base.py +97 -8
- basic_memory/schemas/cloud.py +50 -0
- basic_memory/schemas/directory.py +30 -0
- basic_memory/schemas/importer.py +35 -0
- basic_memory/schemas/memory.py +187 -25
- basic_memory/schemas/project_info.py +211 -0
- basic_memory/schemas/prompt.py +90 -0
- basic_memory/schemas/request.py +56 -2
- basic_memory/schemas/response.py +1 -1
- basic_memory/schemas/search.py +31 -35
- basic_memory/schemas/sync_report.py +72 -0
- basic_memory/services/__init__.py +2 -1
- basic_memory/services/context_service.py +241 -104
- basic_memory/services/directory_service.py +295 -0
- basic_memory/services/entity_service.py +590 -60
- basic_memory/services/exceptions.py +21 -0
- basic_memory/services/file_service.py +284 -30
- basic_memory/services/initialization.py +191 -0
- basic_memory/services/link_resolver.py +49 -56
- basic_memory/services/project_service.py +863 -0
- basic_memory/services/search_service.py +168 -32
- basic_memory/sync/__init__.py +3 -2
- basic_memory/sync/background_sync.py +26 -0
- basic_memory/sync/sync_service.py +1180 -109
- basic_memory/sync/watch_service.py +412 -135
- basic_memory/templates/prompts/continue_conversation.hbs +110 -0
- basic_memory/templates/prompts/search.hbs +101 -0
- basic_memory/utils.py +383 -51
- basic_memory-0.16.1.dist-info/METADATA +493 -0
- basic_memory-0.16.1.dist-info/RECORD +148 -0
- {basic_memory-0.7.0.dist-info → basic_memory-0.16.1.dist-info}/entry_points.txt +1 -0
- basic_memory/alembic/README +0 -1
- basic_memory/cli/commands/sync.py +0 -206
- basic_memory/cli/commands/tools.py +0 -157
- basic_memory/mcp/tools/knowledge.py +0 -68
- basic_memory/mcp/tools/memory.py +0 -170
- basic_memory/mcp/tools/notes.py +0 -202
- basic_memory/schemas/discovery.py +0 -28
- basic_memory/sync/file_change_scanner.py +0 -158
- basic_memory/sync/utils.py +0 -31
- basic_memory-0.7.0.dist-info/METADATA +0 -378
- basic_memory-0.7.0.dist-info/RECORD +0 -82
- {basic_memory-0.7.0.dist-info → basic_memory-0.16.1.dist-info}/WHEEL +0 -0
- {basic_memory-0.7.0.dist-info → basic_memory-0.16.1.dist-info}/licenses/LICENSE +0 -0
basic_memory/mcp/tools/search.py
CHANGED
|
@@ -1,38 +1,384 @@
|
|
|
1
1
|
"""Search tools for Basic Memory MCP server."""
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
from textwrap import dedent
|
|
4
|
+
from typing import List, Optional
|
|
5
|
+
|
|
4
6
|
from loguru import logger
|
|
7
|
+
from fastmcp import Context
|
|
5
8
|
|
|
9
|
+
from basic_memory.mcp.async_client import get_client
|
|
10
|
+
from basic_memory.mcp.project_context import get_active_project
|
|
6
11
|
from basic_memory.mcp.server import mcp
|
|
7
12
|
from basic_memory.mcp.tools.utils import call_post
|
|
8
|
-
from basic_memory.schemas.search import SearchQuery, SearchResponse
|
|
9
|
-
|
|
13
|
+
from basic_memory.schemas.search import SearchItemType, SearchQuery, SearchResponse
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _format_search_error_response(
|
|
17
|
+
project: str, error_message: str, query: str, search_type: str = "text"
|
|
18
|
+
) -> str:
|
|
19
|
+
"""Format helpful error responses for search failures that guide users to successful searches."""
|
|
20
|
+
|
|
21
|
+
# FTS5 syntax errors
|
|
22
|
+
if "syntax error" in error_message.lower() or "fts5" in error_message.lower():
|
|
23
|
+
clean_query = (
|
|
24
|
+
query.replace('"', "")
|
|
25
|
+
.replace("(", "")
|
|
26
|
+
.replace(")", "")
|
|
27
|
+
.replace("+", "")
|
|
28
|
+
.replace("*", "")
|
|
29
|
+
)
|
|
30
|
+
return dedent(f"""
|
|
31
|
+
# Search Failed - Invalid Syntax
|
|
32
|
+
|
|
33
|
+
The search query '{query}' contains invalid syntax that the search engine cannot process.
|
|
34
|
+
|
|
35
|
+
## Common syntax issues:
|
|
36
|
+
1. **Special characters**: Characters like `+`, `*`, `"`, `(`, `)` have special meaning in search
|
|
37
|
+
2. **Unmatched quotes**: Make sure quotes are properly paired
|
|
38
|
+
3. **Invalid operators**: Check AND, OR, NOT operators are used correctly
|
|
39
|
+
|
|
40
|
+
## How to fix:
|
|
41
|
+
1. **Simplify your search**: Try using simple words instead: `{clean_query}`
|
|
42
|
+
2. **Remove special characters**: Use alphanumeric characters and spaces
|
|
43
|
+
3. **Use basic boolean operators**: `word1 AND word2`, `word1 OR word2`, `word1 NOT word2`
|
|
44
|
+
|
|
45
|
+
## Examples of valid searches:
|
|
46
|
+
- Simple text: `project planning`
|
|
47
|
+
- Boolean AND: `project AND planning`
|
|
48
|
+
- Boolean OR: `meeting OR discussion`
|
|
49
|
+
- Boolean NOT: `project NOT archived`
|
|
50
|
+
- Grouped: `(project OR planning) AND notes`
|
|
51
|
+
- Exact phrases: `"weekly standup meeting"`
|
|
52
|
+
- Content-specific: `tag:example` or `category:observation`
|
|
53
|
+
|
|
54
|
+
## Try again with:
|
|
55
|
+
```
|
|
56
|
+
search_notes("{project}","{clean_query}")
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Alternative search strategies:
|
|
60
|
+
- Break into simpler terms: `search_notes("{project}", "{" ".join(clean_query.split()[:2])}")`
|
|
61
|
+
- Try different search types: `search_notes("{project}","{clean_query}", search_type="title")`
|
|
62
|
+
- Use filtering: `search_notes("{project}","{clean_query}", types=["entity"])`
|
|
63
|
+
""").strip()
|
|
64
|
+
|
|
65
|
+
# Project not found errors (check before general "not found")
|
|
66
|
+
if "project not found" in error_message.lower():
|
|
67
|
+
return dedent(f"""
|
|
68
|
+
# Search Failed - Project Not Found
|
|
69
|
+
|
|
70
|
+
The current project is not accessible or doesn't exist: {error_message}
|
|
71
|
+
|
|
72
|
+
## How to resolve:
|
|
73
|
+
1. **Check available projects**: `list_projects()`
|
|
74
|
+
3. **Verify project setup**: Ensure your project is properly configured
|
|
75
|
+
|
|
76
|
+
## Current session info:
|
|
77
|
+
- See available projects: `list_projects()`
|
|
78
|
+
""").strip()
|
|
79
|
+
|
|
80
|
+
# No results found
|
|
81
|
+
if "no results" in error_message.lower() or "not found" in error_message.lower():
|
|
82
|
+
simplified_query = (
|
|
83
|
+
" ".join(query.split()[:2])
|
|
84
|
+
if len(query.split()) > 2
|
|
85
|
+
else query.split()[0]
|
|
86
|
+
if query.split()
|
|
87
|
+
else "notes"
|
|
88
|
+
)
|
|
89
|
+
return dedent(f"""
|
|
90
|
+
# Search Complete - No Results Found
|
|
91
|
+
|
|
92
|
+
No content found matching '{query}' in the current project.
|
|
93
|
+
|
|
94
|
+
## Search strategy suggestions:
|
|
95
|
+
1. **Broaden your search**: Try fewer or more general terms
|
|
96
|
+
- Instead of: `{query}`
|
|
97
|
+
- Try: `{simplified_query}`
|
|
98
|
+
|
|
99
|
+
2. **Check spelling and try variations**:
|
|
100
|
+
- Verify terms are spelled correctly
|
|
101
|
+
- Try synonyms or related terms
|
|
102
|
+
|
|
103
|
+
3. **Use different search approaches**:
|
|
104
|
+
- **Text search**: `search_notes("{project}","{query}", search_type="text")` (searches full content)
|
|
105
|
+
- **Title search**: `search_notes("{project}","{query}", search_type="title")` (searches only titles)
|
|
106
|
+
- **Permalink search**: `search_notes("{project}","{query}", search_type="permalink")` (searches file paths)
|
|
107
|
+
|
|
108
|
+
4. **Try boolean operators for broader results**:
|
|
109
|
+
- OR search: `search_notes("{project}","{" OR ".join(query.split()[:3])}")`
|
|
110
|
+
- Remove restrictive terms: Focus on the most important keywords
|
|
111
|
+
|
|
112
|
+
5. **Use filtering to narrow scope**:
|
|
113
|
+
- By content type: `search_notes("{project}","{query}", types=["entity"])`
|
|
114
|
+
- By recent content: `search_notes("{project}","{query}", after_date="1 week")`
|
|
115
|
+
- By entity type: `search_notes("{project}","{query}", entity_types=["observation"])`
|
|
116
|
+
|
|
117
|
+
6. **Try advanced search patterns**:
|
|
118
|
+
- Tag search: `search_notes("{project}","tag:your-tag")`
|
|
119
|
+
- Category search: `search_notes("{project}","category:observation")`
|
|
120
|
+
- Pattern matching: `search_notes("{project}","*{query}*", search_type="permalink")`
|
|
121
|
+
|
|
122
|
+
## Explore what content exists:
|
|
123
|
+
- **Recent activity**: `recent_activity(timeframe="7d")` - See what's been updated recently
|
|
124
|
+
- **List directories**: `list_directory("{project}","/")` - Browse all content
|
|
125
|
+
- **Browse by folder**: `list_directory("{project}","/notes")` or `list_directory("/docs")`
|
|
126
|
+
""").strip()
|
|
127
|
+
|
|
128
|
+
# Server/API errors
|
|
129
|
+
if "server error" in error_message.lower() or "internal" in error_message.lower():
|
|
130
|
+
return dedent(f"""
|
|
131
|
+
# Search Failed - Server Error
|
|
132
|
+
|
|
133
|
+
The search service encountered an error while processing '{query}': {error_message}
|
|
134
|
+
|
|
135
|
+
## Immediate steps:
|
|
136
|
+
1. **Try again**: The error might be temporary
|
|
137
|
+
2. **Simplify the query**: Use simpler search terms
|
|
138
|
+
3. **Check project status**: Ensure your project is properly synced
|
|
139
|
+
|
|
140
|
+
## Alternative approaches:
|
|
141
|
+
- Browse files directly: `list_directory("{project}","/")`
|
|
142
|
+
- Check recent activity: `recent_activity(timeframe="7d")`
|
|
143
|
+
- Try a different search type: `search_notes("{project}","{query}", search_type="title")`
|
|
144
|
+
|
|
145
|
+
## If the problem persists:
|
|
146
|
+
The search index might need to be rebuilt. Send a message to support@basicmachines.co or check the project sync status.
|
|
147
|
+
""").strip()
|
|
148
|
+
|
|
149
|
+
# Permission/access errors
|
|
150
|
+
if (
|
|
151
|
+
"permission" in error_message.lower()
|
|
152
|
+
or "access" in error_message.lower()
|
|
153
|
+
or "forbidden" in error_message.lower()
|
|
154
|
+
):
|
|
155
|
+
return f"""# Search Failed - Access Error
|
|
156
|
+
|
|
157
|
+
You don't have permission to search in the current project: {error_message}
|
|
158
|
+
|
|
159
|
+
## How to resolve:
|
|
160
|
+
1. **Check your project access**: Verify you have read permissions for this project
|
|
161
|
+
2. **Switch projects**: Try searching in a different project you have access to
|
|
162
|
+
3. **Check authentication**: You might need to re-authenticate
|
|
163
|
+
|
|
164
|
+
## Alternative actions:
|
|
165
|
+
- List available projects: `list_projects()`"""
|
|
166
|
+
|
|
167
|
+
# Generic fallback
|
|
168
|
+
return f"""# Search Failed
|
|
169
|
+
|
|
170
|
+
Error searching for '{query}': {error_message}
|
|
171
|
+
|
|
172
|
+
## Troubleshooting steps:
|
|
173
|
+
1. **Simplify your query**: Try basic words without special characters
|
|
174
|
+
2. **Check search syntax**: Ensure boolean operators are correctly formatted
|
|
175
|
+
3. **Verify project access**: Make sure you can access the current project
|
|
176
|
+
4. **Test with simple search**: Try `search_notes("test")` to verify search is working
|
|
177
|
+
|
|
178
|
+
## Alternative search approaches:
|
|
179
|
+
- **Different search types**:
|
|
180
|
+
- Title only: `search_notes("{project}","{query}", search_type="title")`
|
|
181
|
+
- Permalink patterns: `search_notes("{project}","{query}*", search_type="permalink")`
|
|
182
|
+
- **With filters**: `search_notes("{project}","{query}", types=["entity"])`
|
|
183
|
+
- **Recent content**: `search_notes("{project}","{query}", after_date="1 week")`
|
|
184
|
+
- **Boolean variations**: `search_notes("{project}","{" OR ".join(query.split()[:2])}")`
|
|
185
|
+
|
|
186
|
+
## Explore your content:
|
|
187
|
+
- **Browse files**: `list_directory("{project}","/")` - See all available content
|
|
188
|
+
- **Recent activity**: `recent_activity(timeframe="7d")` - Check what's been updated
|
|
189
|
+
- **All projects**: `list_projects()`
|
|
190
|
+
|
|
191
|
+
## Search syntax reference:
|
|
192
|
+
- **Basic**: `keyword` or `multiple words`
|
|
193
|
+
- **Boolean**: `term1 AND term2`, `term1 OR term2`, `term1 NOT term2`
|
|
194
|
+
- **Phrases**: `"exact phrase"`
|
|
195
|
+
- **Grouping**: `(term1 OR term2) AND term3`
|
|
196
|
+
- **Patterns**: `tag:example`, `category:observation`"""
|
|
10
197
|
|
|
11
198
|
|
|
12
199
|
@mcp.tool(
|
|
13
|
-
description="Search across all content in
|
|
200
|
+
description="Search across all content in the knowledge base with advanced syntax support.",
|
|
14
201
|
)
|
|
15
|
-
async def
|
|
16
|
-
|
|
202
|
+
async def search_notes(
|
|
203
|
+
query: str,
|
|
204
|
+
project: Optional[str] = None,
|
|
205
|
+
page: int = 1,
|
|
206
|
+
page_size: int = 10,
|
|
207
|
+
search_type: str = "text",
|
|
208
|
+
types: List[str] = [],
|
|
209
|
+
entity_types: List[str] = [],
|
|
210
|
+
after_date: Optional[str] = None,
|
|
211
|
+
context: Context | None = None,
|
|
212
|
+
) -> SearchResponse | str:
|
|
213
|
+
"""Search across all content in the knowledge base with comprehensive syntax support.
|
|
214
|
+
|
|
215
|
+
This tool searches the knowledge base using full-text search, pattern matching,
|
|
216
|
+
or exact permalink lookup. It supports filtering by content type, entity type,
|
|
217
|
+
and date, with advanced boolean and phrase search capabilities.
|
|
218
|
+
|
|
219
|
+
Project Resolution:
|
|
220
|
+
Server resolves projects in this order: Single Project Mode → project parameter → default project.
|
|
221
|
+
If project unknown, use list_memory_projects() or recent_activity() first.
|
|
222
|
+
|
|
223
|
+
## Search Syntax Examples
|
|
224
|
+
|
|
225
|
+
### Basic Searches
|
|
226
|
+
- `search_notes("my-project", "keyword")` - Find any content containing "keyword"
|
|
227
|
+
- `search_notes("work-docs", "'exact phrase'")` - Search for exact phrase match
|
|
228
|
+
|
|
229
|
+
### Advanced Boolean Searches
|
|
230
|
+
- `search_notes("my-project", "term1 term2")` - Find content with both terms (implicit AND)
|
|
231
|
+
- `search_notes("my-project", "term1 AND term2")` - Explicit AND search (both terms required)
|
|
232
|
+
- `search_notes("my-project", "term1 OR term2")` - Either term can be present
|
|
233
|
+
- `search_notes("my-project", "term1 NOT term2")` - Include term1 but exclude term2
|
|
234
|
+
- `search_notes("my-project", "(project OR planning) AND notes")` - Grouped boolean logic
|
|
235
|
+
|
|
236
|
+
### Content-Specific Searches
|
|
237
|
+
- `search_notes("research", "tag:example")` - Search within specific tags (if supported by content)
|
|
238
|
+
- `search_notes("work-project", "category:observation")` - Filter by observation categories
|
|
239
|
+
- `search_notes("team-docs", "author:username")` - Find content by author (if metadata available)
|
|
240
|
+
|
|
241
|
+
### Search Type Examples
|
|
242
|
+
- `search_notes("my-project", "Meeting", search_type="title")` - Search only in titles
|
|
243
|
+
- `search_notes("work-docs", "docs/meeting-*", search_type="permalink")` - Pattern match permalinks
|
|
244
|
+
- `search_notes("research", "keyword", search_type="text")` - Full-text search (default)
|
|
245
|
+
|
|
246
|
+
### Filtering Options
|
|
247
|
+
- `search_notes("my-project", "query", types=["entity"])` - Search only entities
|
|
248
|
+
- `search_notes("work-docs", "query", types=["note", "person"])` - Multiple content types
|
|
249
|
+
- `search_notes("research", "query", entity_types=["observation"])` - Filter by entity type
|
|
250
|
+
- `search_notes("team-docs", "query", after_date="2024-01-01")` - Recent content only
|
|
251
|
+
- `search_notes("my-project", "query", after_date="1 week")` - Relative date filtering
|
|
252
|
+
|
|
253
|
+
### Advanced Pattern Examples
|
|
254
|
+
- `search_notes("work-project", "project AND (meeting OR discussion)")` - Complex boolean logic
|
|
255
|
+
- `search_notes("research", "\"exact phrase\" AND keyword")` - Combine phrase and keyword search
|
|
256
|
+
- `search_notes("dev-notes", "bug NOT fixed")` - Exclude resolved issues
|
|
257
|
+
- `search_notes("archive", "docs/2024-*", search_type="permalink")` - Year-based permalink search
|
|
17
258
|
|
|
18
259
|
Args:
|
|
19
|
-
query:
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
260
|
+
query: The search query string (supports boolean operators, phrases, patterns)
|
|
261
|
+
project: Project name to search in. Optional - server will resolve using hierarchy.
|
|
262
|
+
If unknown, use list_memory_projects() to discover available projects.
|
|
263
|
+
page: The page number of results to return (default 1)
|
|
264
|
+
page_size: The number of results to return per page (default 10)
|
|
265
|
+
search_type: Type of search to perform, one of: "text", "title", "permalink" (default: "text")
|
|
266
|
+
types: Optional list of note types to search (e.g., ["note", "person"])
|
|
267
|
+
entity_types: Optional list of entity types to filter by (e.g., ["entity", "observation"])
|
|
268
|
+
after_date: Optional date filter for recent content (e.g., "1 week", "2d", "2024-01-01")
|
|
269
|
+
context: Optional FastMCP context for performance caching.
|
|
26
270
|
|
|
27
271
|
Returns:
|
|
28
|
-
SearchResponse with
|
|
272
|
+
SearchResponse with results and pagination info, or helpful error guidance if search fails
|
|
273
|
+
|
|
274
|
+
Examples:
|
|
275
|
+
# Basic text search
|
|
276
|
+
results = await search_notes("project planning")
|
|
277
|
+
|
|
278
|
+
# Boolean AND search (both terms must be present)
|
|
279
|
+
results = await search_notes("project AND planning")
|
|
280
|
+
|
|
281
|
+
# Boolean OR search (either term can be present)
|
|
282
|
+
results = await search_notes("project OR meeting")
|
|
283
|
+
|
|
284
|
+
# Boolean NOT search (exclude terms)
|
|
285
|
+
results = await search_notes("project NOT meeting")
|
|
286
|
+
|
|
287
|
+
# Boolean search with grouping
|
|
288
|
+
results = await search_notes("(project OR planning) AND notes")
|
|
289
|
+
|
|
290
|
+
# Exact phrase search
|
|
291
|
+
results = await search_notes("\"weekly standup meeting\"")
|
|
292
|
+
|
|
293
|
+
# Search with type filter
|
|
294
|
+
results = await search_notes(
|
|
295
|
+
"meeting notes",
|
|
296
|
+
types=["entity"],
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
# Search with entity type filter
|
|
300
|
+
results = await search_notes(
|
|
301
|
+
"meeting notes",
|
|
302
|
+
entity_types=["observation"],
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
# Search for recent content
|
|
306
|
+
results = await search_notes(
|
|
307
|
+
"bug report",
|
|
308
|
+
after_date="1 week"
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
# Pattern matching on permalinks
|
|
312
|
+
results = await search_notes(
|
|
313
|
+
"docs/meeting-*",
|
|
314
|
+
search_type="permalink"
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
# Title-only search
|
|
318
|
+
results = await search_notes(
|
|
319
|
+
"Machine Learning",
|
|
320
|
+
search_type="title"
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
# Complex search with multiple filters
|
|
324
|
+
results = await search_notes(
|
|
325
|
+
"(bug OR issue) AND NOT resolved",
|
|
326
|
+
types=["entity"],
|
|
327
|
+
after_date="2024-01-01"
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
# Explicit project specification
|
|
331
|
+
results = await search_notes("project planning", project="my-project")
|
|
29
332
|
"""
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
333
|
+
# Create a SearchQuery object based on the parameters
|
|
334
|
+
search_query = SearchQuery()
|
|
335
|
+
|
|
336
|
+
# Set the appropriate search field based on search_type
|
|
337
|
+
if search_type == "text":
|
|
338
|
+
search_query.text = query
|
|
339
|
+
elif search_type == "title":
|
|
340
|
+
search_query.title = query
|
|
341
|
+
elif search_type == "permalink" and "*" in query:
|
|
342
|
+
search_query.permalink_match = query
|
|
343
|
+
elif search_type == "permalink":
|
|
344
|
+
search_query.permalink = query
|
|
345
|
+
else:
|
|
346
|
+
search_query.text = query # Default to text search
|
|
347
|
+
|
|
348
|
+
# Add optional filters if provided (empty lists are treated as no filter)
|
|
349
|
+
if entity_types:
|
|
350
|
+
search_query.entity_types = [SearchItemType(t) for t in entity_types]
|
|
351
|
+
if types:
|
|
352
|
+
search_query.types = types
|
|
353
|
+
if after_date:
|
|
354
|
+
search_query.after_date = after_date
|
|
355
|
+
|
|
356
|
+
async with get_client() as client:
|
|
357
|
+
active_project = await get_active_project(client, project, context)
|
|
358
|
+
project_url = active_project.project_url
|
|
359
|
+
|
|
360
|
+
logger.info(f"Searching for {search_query} in project {active_project.name}")
|
|
361
|
+
|
|
362
|
+
try:
|
|
363
|
+
response = await call_post(
|
|
364
|
+
client,
|
|
365
|
+
f"{project_url}/search/",
|
|
366
|
+
json=search_query.model_dump(),
|
|
367
|
+
params={"page": page, "page_size": page_size},
|
|
368
|
+
)
|
|
369
|
+
result = SearchResponse.model_validate(response.json())
|
|
370
|
+
|
|
371
|
+
# Check if we got no results and provide helpful guidance
|
|
372
|
+
if not result.results:
|
|
373
|
+
logger.info(
|
|
374
|
+
f"Search returned no results for query: {query} in project {active_project.name}"
|
|
375
|
+
)
|
|
376
|
+
# Don't treat this as an error, but the user might want guidance
|
|
377
|
+
# We return the empty result as normal - the user can decide if they need help
|
|
378
|
+
|
|
379
|
+
return result
|
|
380
|
+
|
|
381
|
+
except Exception as e:
|
|
382
|
+
logger.error(f"Search failed for query '{query}': {e}, project: {active_project.name}")
|
|
383
|
+
# Return formatted error message as string for better user experience
|
|
384
|
+
return _format_search_error_response(active_project.name, str(e), query, search_type)
|