@@ -148,9 +148,9 @@ func (h *handlers) buildRagIndex(ctx context.Context, request mcp.CallToolReques
148148}
149149
150150// getRagTree returns the documentation tree or a pruned node subtree.
151- // @intent Exposes the documentation RAG index in a tree format to enable exploratory lookups, falling back to DB synthesis when doc-index.json is absent .
151+ // @intent Exposes the documentation RAG index in a tree format from DB graph evidence .
152152// @param request node_id is the starting point for a subtree, community_id is a deprecated alias, and depth limits the return depth.
153- // @ensures Returns the TreeNode JSON for the requested range on success and prefers doc-index.json when present .
153+ // @ensures Returns the TreeNode JSON for the requested range on success.
154154// @see mcp.handlers.buildRagIndex
155155func (h * handlers ) getRagTree (ctx context.Context , request mcp.CallToolRequest ) (* mcp.CallToolResult , error ) {
156156 nodeID := request .GetString ("node_id" , "" )
@@ -160,66 +160,32 @@ func (h *handlers) getRagTree(ctx context.Context, request mcp.CallToolRequest)
160160 depth := int (request .GetFloat ("depth" , 0 ))
161161 namespace := resolveNamespace (ctx , requestNamespace (request ))
162162 ctx = ctxns .WithNamespace (ctx , namespace )
163- indexNamespace := namespace
164- if namespace == ctxns .DefaultNamespace {
165- indexNamespace = ""
166- }
167- indexPath , err := h .resolvedRagIndexPath (indexNamespace )
168- if err != nil {
169- return mcp .NewToolResultError (err .Error ()), nil
163+ if h .deps .DB == nil {
164+ return mcp .NewToolResultError ("DB is not configured" ), nil
170165 }
171-
172- stat , statErr := os . Stat ( indexPath )
173- if statErr != nil {
174- if ! os . IsNotExist ( statErr ) {
175- return mcp . NewToolResultError ( fmt . Sprintf ( "stat doc-index: %v" , statErr )), nil
166+ return finalizeToolResult ( h . cachedExecute ( ctx , "get_rag_tree:db:" , map [ string ] any { "node_id" : nodeID , "depth" : depth , "namespace" : namespace }, func () ( string , error ) {
167+ builder := & ragindex. Builder {
168+ DB : h . deps . DB ,
169+ OutDir : "docs" ,
170+ ProjectDesc : h . deps . RagProjectDesc ,
176171 }
177- return finalizeToolResult (h .cachedExecute (ctx , "get_rag_tree:db:" , map [string ]any {"node_id" : nodeID , "depth" : depth , "namespace" : namespace }, func () (string , error ) {
178- if h .deps .DB == nil {
179- return "" , newToolResultErr ("DB not configured" )
180- }
181- builder := & ragindex.Builder {
182- DB : h .deps .DB ,
183- OutDir : "docs" ,
184- ProjectDesc : h .deps .RagProjectDesc ,
185- }
186- root , _ , _ , err := builder .BuildTree (ctx )
187- if err != nil {
188- return "" , newToolResultErr (fmt .Sprintf ("build rag tree from DB: %v" , err ))
189- }
190- node , err := selectRagTreeNode (root , nodeID )
191- if err != nil {
192- return "" , err
193- }
194- node = ragindex .PruneTree (node , depth )
195-
196- // TreeNode fields are JSON-safe DTO fields; json.Marshal cannot fail for this payload.
197- encoded , _ := json .Marshal (node )
198- return string (encoded ), nil
199- }))
200- }
201-
202- indexMtime := stat .ModTime ().UnixNano ()
203- return finalizeToolResult (h .cachedExecute (ctx , "get_rag_tree:" , map [string ]any {"node_id" : nodeID , "depth" : depth , "namespace" : namespace , "mtime" : indexMtime }, func () (string , error ) {
204- idx , err := ragindex .LoadIndex (indexPath )
172+ root , _ , _ , err := builder .BuildTree (ctx )
205173 if err != nil {
206- return "" , newToolResultErr (fmt .Sprintf ("load doc-index : %v" , err ))
174+ return "" , newToolResultErr (fmt .Sprintf ("build rag tree from DB : %v" , err ))
207175 }
208-
209- node , err := selectRagTreeNode (idx .Root , nodeID )
176+ node , err := selectRagTreeNode (root , nodeID )
210177 if err != nil {
211178 return "" , err
212179 }
213-
214180 node = ragindex .PruneTree (node , depth )
215181
216182 // TreeNode fields are JSON-safe DTO fields; json.Marshal cannot fail for this payload.
217- b , _ := json .Marshal (node )
218- return string (b ), nil
183+ encoded , _ := json .Marshal (node )
184+ return string (encoded ), nil
219185 }))
220186}
221187
222- // @intent select the requested RAG tree node with shared not-found semantics for JSON indexes and DB fallback trees.
188+ // @intent select the requested RAG tree node in DB-built trees.
223189func selectRagTreeNode (root * ragindex.TreeNode , nodeID string ) (* ragindex.TreeNode , error ) {
224190 if nodeID == "" {
225191 return root , nil
@@ -305,45 +271,20 @@ func (h *handlers) searchDocs(ctx context.Context, request mcp.CallToolRequest)
305271 }
306272 namespace := requestNamespace (request )
307273 ctx = h .applyNamespace (ctx , request )
308- indexPath , err := h .resolvedRagIndexPath (namespace )
309- if err != nil {
310- return mcp .NewToolResultError (err .Error ()), nil
311- }
312-
313- stat , statErr := os .Stat (indexPath )
314- if statErr != nil && ! os .IsNotExist (statErr ) {
315- return mcp .NewToolResultError (fmt .Sprintf ("stat doc-index: %v" , statErr )), nil
316- }
317- if statErr != nil && os .IsNotExist (statErr ) {
318- return finalizeToolResult (h .cachedExecute (ctx , "search_docs:db:" , map [string ]any {"query" : query , "limit" : pageReq .Limit , "namespace" : namespace }, func () (string , error ) {
319- results , err := h .searchDocsFromDB (ctx , namespace , query , pageReq .Limit )
320- if err != nil {
321- return "" , newToolResultErr (err .Error ())
322- }
323- b , _ := json .Marshal (results )
324- return string (b ), nil
325- }))
274+ if h .deps .DB == nil {
275+ return mcp .NewToolResultError ("DB is not configured" ), nil
326276 }
327- indexMtime := stat .ModTime ().UnixNano ()
328-
329- return finalizeToolResult (h .cachedExecute (ctx , "search_docs:" , map [string ]any {"query" : query , "limit" : pageReq .Limit , "namespace" : namespace , "mtime" : indexMtime }, func () (string , error ) {
330- idx , err := ragindex .LoadIndex (indexPath )
277+ return finalizeToolResult (h .cachedExecute (ctx , "search_docs:db:" , map [string ]any {"query" : query , "limit" : pageReq .Limit , "namespace" : namespace }, func () (string , error ) {
278+ results , err := h .searchDocsFromDB (ctx , namespace , query , pageReq .Limit )
331279 if err != nil {
332- return "" , newToolResultErr (fmt . Sprintf ( "load doc-index: %v" , err ))
280+ return "" , newToolResultErr (err . Error ( ))
333281 }
334-
335- results := ragindex .Search (idx .Root , query , pageReq .Limit )
336- if results == nil {
337- results = []ragindex.SearchResult {}
338- }
339-
340- // SearchResult fields are all basic types (string, []string); json.Marshal cannot fail.
341282 b , _ := json .Marshal (results )
342283 return string (b ), nil
343284 }))
344285}
345286
346- // @intent search persisted graph nodes when search_docs has no generated doc-index.json to load .
287+ // @intent search persisted graph nodes directly from DB and search backend .
347288// @requires ctx must carry the selected namespace for SearchBackend.Query.
348289// @ensures returns SearchResult-compatible JSON items without requiring generated index files.
349290// @sideEffect queries the search backend and may scan graph annotations as a fallback.
@@ -387,7 +328,7 @@ type retrieveDocsResult = retrieval.Result
387328
388329var _ = (* handlers ).retrieveDocsFromDB
389330
390- // @intent build a retrieve_docs response from persisted graph nodes and annotation tags before consulting compatibility doc-index snapshots .
331+ // @intent build a retrieve_docs response from persisted graph nodes and annotation tags.
391332// @requires SearchBackend and DB must be configured, and ctx must carry the desired namespace.
392333// @ensures returned results are grouped one-per-file in first-seen FTS order and keep retrieve_docs' stable JSON shape.
393334// @sideEffect queries the search backend and may read generated Markdown files for bounded content.
@@ -402,12 +343,12 @@ func (h *handlers) retrieveDocsFromDB(ctx context.Context, namespace, query stri
402343 return retrieved , nil
403344}
404345
405- // retrieveDocs retrieves generated Markdown docs using DB-backed graph evidence first and doc-index snapshots as fallback .
406- // @intent provide DB-primary document retrieval without replacing quick keyword search or removing doc-index compatibility .
346+ // retrieveDocs retrieves generated Markdown docs using DB-backed graph evidence.
347+ // @intent provide DB-primary document retrieval.
407348// @param request query is the natural-language retrieval prompt, limit bounds document count, and content_limit bounds each Markdown payload.
408- // @requires either a configured DB or doc-index.json and generated docs must exist.
349+ // @requires a configured DB and generated docs may exist.
409350// @ensures returns file-level matches with tree evidence and bounded document content.
410- // @sideEffect queries the graph DB and may read doc-index.json and generated Markdown files.
351+ // @sideEffect queries the graph DB and reads generated Markdown files.
411352func (h * handlers ) retrieveDocs (ctx context.Context , request mcp.CallToolRequest ) (* mcp.CallToolResult , error ) {
412353 query , err := request .RequireString ("query" )
413354 if err != nil {
@@ -434,88 +375,28 @@ func (h *handlers) retrieveDocs(ctx context.Context, request mcp.CallToolRequest
434375
435376 namespace := requestNamespace (request )
436377 ctx = h .applyNamespace (ctx , request )
437- indexPath , err := h .resolvedRagIndexPath (namespace )
438- if err != nil {
439- return mcp .NewToolResultError (err .Error ()), nil
440- }
441-
442- var dbErr error
443- if h .deps .DB != nil {
444- cacheKey := map [string ]any {
445- "query" : query ,
446- "limit" : pageReq .Limit ,
447- "content_limit" : contentLimit ,
448- "namespace" : namespace ,
449- "explain" : explain ,
450- }
451- result , err := h .cachedExecute (ctx , "retrieve_docs:db:" , cacheKey , func () (string , error ) {
452- response , err := h .retrieveDocsFromDB (ctx , namespace , query , pageReq .Limit , contentLimit , explain )
453- if err != nil {
454- return "" , newToolResultErr (err .Error ())
455- }
456-
457- b , _ := json .Marshal (response )
458- return string (b ), nil
459- })
460- if err == nil {
461- return finalizeToolResult (result , nil )
462- }
463- dbErr = err
464- }
465-
466- stat , statErr := os .Stat (indexPath )
467- if statErr != nil && ! os .IsNotExist (statErr ) {
468- return mcp .NewToolResultError (fmt .Sprintf ("stat doc-index: %v" , statErr )), nil
469- }
470- if statErr != nil && os .IsNotExist (statErr ) {
471- if dbErr != nil {
472- return finalizeToolResult ("" , dbErr )
473- }
474- return finalizeToolResult ("" , newToolResultErr (fmt .Sprintf ("load doc-index: %v" , statErr )))
378+ if h .deps .DB == nil {
379+ return mcp .NewToolResultError ("DB is not configured" ), nil
475380 }
476- indexMtime := stat .ModTime ().UnixNano ()
477-
478381 cacheKey := map [string ]any {
479382 "query" : query ,
480383 "limit" : pageReq .Limit ,
481384 "content_limit" : contentLimit ,
482385 "namespace" : namespace ,
483- "mtime" : indexMtime ,
484386 "explain" : explain ,
485387 }
486- return finalizeToolResult (h .cachedExecute (ctx , "retrieve_docs:" , cacheKey , func () (string , error ) {
487- return h .retrieveDocsFromIndex (namespace , query , pageReq .Limit , contentLimit , explain , indexPath )
488- }))
489- }
490-
491- // @intent run compatibility retrieve_docs against a generated doc-index.json snapshot when DB retrieval is unavailable.
492- // @sideEffect reads doc-index.json and optionally generated Markdown content.
493- func (h * handlers ) retrieveDocsFromIndex (namespace , query string , limit , contentLimit int , explain bool , indexPath string ) (string , error ) {
494- idx , err := ragindex .LoadIndex (indexPath )
495- if err != nil {
496- return "" , newToolResultErr (fmt .Sprintf ("load doc-index: %v" , err ))
497- }
498-
499- candidates := ragindex .RetrieveWithOptions (idx .Root , query , limit , ragindex.RetrieveOptions {Explain : explain })
500- response := retrieveDocsResponse {Results : make ([]retrieveDocsResult , 0 , len (candidates ))}
501- for _ , candidate := range candidates {
502- result := retrieveDocsResult {RetrieveResult : candidate }
503- if contentLimit > 0 {
504- content , truncated , err := h .readIndexedDocContent (namespace , candidate .DocPath , contentLimit )
505- if err != nil {
506- return "" , newToolResultErr (err .Error ())
507- }
508- result .Content = content
509- result .ContentTruncated = truncated
388+ return finalizeToolResult (h .cachedExecute (ctx , "retrieve_docs:db:" , cacheKey , func () (string , error ) {
389+ response , err := h .retrieveDocsFromDB (ctx , namespace , query , pageReq .Limit , contentLimit , explain )
390+ if err != nil {
391+ return "" , newToolResultErr (err .Error ())
510392 }
511- response .Results = append (response .Results , result )
512- }
513393
514- b , _ := json .Marshal (response )
515- return string (b ), nil
394+ b , _ := json .Marshal (response )
395+ return string (b ), nil
396+ }))
516397}
517398
518- // readIndexedDocContent reads a doc_path stored in doc-index.json under the docs/index root.
399+ // readIndexedDocContent reads a doc_path under the generated docs root.
519400// @intent let retrieve_docs return bounded Markdown content while keeping index-provided paths inside a safe root.
520401// @domainRule namespace doc paths are resolved from that namespace; shared doc paths are resolved from the parent of the RAG index root.
521402// @sideEffect reads a generated Markdown file.
0 commit comments