1616 resolve_project_and_path ,
1717)
1818from basic_memory .mcp .server import mcp
19+ from basic_memory .mcp .tools .search import search_notes
1920from basic_memory .schemas .memory import memory_url_path
20- from basic_memory .schemas .search import SearchQuery
2121from basic_memory .utils import validate_project_path
2222
2323
@@ -144,7 +144,7 @@ async def read_note(
144144 "mcp.tool.read_note" ,
145145 entrypoint = "mcp" ,
146146 tool_name = "read_note" ,
147- project_name = project ,
147+ requested_project = project ,
148148 workspace_id = workspace ,
149149 output_format = output_format ,
150150 page = page ,
@@ -199,12 +199,11 @@ async def read_note(
199199 )
200200
201201 # Import here to avoid circular import
202- from basic_memory .mcp .clients import KnowledgeClient , ResourceClient , SearchClient
202+ from basic_memory .mcp .clients import KnowledgeClient , ResourceClient
203203
204204 # Use typed clients for API calls
205205 knowledge_client = KnowledgeClient (client , active_project .external_id )
206206 resource_client = ResourceClient (client , active_project .external_id )
207- search_client = SearchClient (client , active_project .external_id )
208207
209208 async def _read_json_payload (entity_id : str ) -> dict :
210209 with telemetry .scope (
@@ -214,7 +213,9 @@ async def _read_json_payload(entity_id: str) -> dict:
214213 phase = "shape_response" ,
215214 ):
216215 entity = await knowledge_client .get_entity (entity_id )
217- response = await resource_client .read (entity_id , page = page , page_size = page_size )
216+ response = await resource_client .read (
217+ entity_id , page = page , page_size = page_size
218+ )
218219 content_text = response .text
219220 body_content , parsed_frontmatter = _parse_opening_frontmatter (content_text )
220221 return {
@@ -241,15 +242,22 @@ def _search_results(payload: object) -> list[dict]:
241242 return results if isinstance (results , list ) else []
242243
243244 async def _search_candidates (identifier_text : str , * , title_only : bool ) -> dict :
244- query = SearchQuery (title = identifier_text ) if title_only else SearchQuery (
245- text = identifier_text
246- )
247- response = await search_client .search (
248- query .model_dump (mode = "json" , exclude_none = True ),
245+ # Trigger: direct entity resolution failed for the caller's identifier.
246+ # Why: search_notes applies the same memory:// normalization and tool-level
247+ # query handling as the rest of MCP routing, which raw client calls skip.
248+ # Outcome: unresolved memory URLs still fall back through normalized search.
249+ search_type = "title" if title_only else "text"
250+ response = await search_notes (
251+ project = active_project .name ,
252+ workspace = workspace ,
253+ query = identifier_text ,
254+ search_type = search_type ,
249255 page = page ,
250256 page_size = page_size ,
257+ output_format = "json" ,
258+ context = context ,
251259 )
252- return response . model_dump ( mode = "json" )
260+ return response if isinstance ( response , dict ) else {}
253261
254262 def _result_title (item : dict ) -> str :
255263 return str (item .get ("title" ) or "" )
0 commit comments