66import os
77import sys
88from pathlib import Path
9- from typing import List , Optional
9+ from typing import Any , List , Optional
1010
1111from mcp .server .fastmcp import FastMCP
1212
@@ -76,17 +76,17 @@ def _response_meta(docstore: DocStore, baked_manifest: Optional[dict] = None) ->
7676 return meta
7777
7878
79- @app .tool ()
80- def search (
81- query : str ,
82- k : int = 6 ,
83- source_types : Optional [List [str ] | str ] = None ,
84- ) -> List [dict ]:
85- docstore = _get_docstore ()
86- meta = _response_meta (docstore )
79+ def _parse_source_types (source_types : Optional [List [str ] | str ]) -> Optional [List [str ]]:
80+ if source_types is None :
81+ return None
8782 if isinstance (source_types , str ):
88- source_types = [s .strip () for s in source_types .split ("," ) if s .strip ()]
89- results = docstore .search (query = query , k = k , source_types = source_types )
83+ values = [s .strip ().lower () for s in source_types .split ("," ) if s .strip ()]
84+ else :
85+ values = [str (s ).strip ().lower () for s in source_types if str (s ).strip ()]
86+ return values or None
87+
88+
89+ def _serialize_search_results (results : list [Any ], meta : dict ) -> List [dict ]:
9090 return [
9191 {
9292 "chunk_id" : r .chunk_id ,
@@ -104,6 +104,65 @@ def search(
104104 ]
105105
106106
107+ @app .tool ()
108+ def search (
109+ query : str ,
110+ k : int = 6 ,
111+ source_types : Optional [List [str ] | str ] = None ,
112+ debug : bool = False ,
113+ ) -> List [dict ] | dict :
114+ docstore = _get_docstore ()
115+ meta = _response_meta (docstore )
116+ parsed_source_types = _parse_source_types (source_types )
117+ available_source_types = docstore .available_source_types ()
118+ known_source_types = docstore .known_source_types ()
119+
120+ invalid_source_types = []
121+ unavailable_source_types = []
122+ if parsed_source_types :
123+ requested_set = set (parsed_source_types )
124+ known_set = set (known_source_types )
125+ available_set = set (available_source_types )
126+ invalid_source_types = sorted (requested_set - known_set )
127+ unavailable_source_types = sorted ((requested_set & known_set ) - available_set )
128+
129+ if invalid_source_types or unavailable_source_types :
130+ message_parts = []
131+ if invalid_source_types :
132+ message_parts .append (f"Unsupported source_types: { ', ' .join (invalid_source_types )} " )
133+ if unavailable_source_types :
134+ message_parts .append (f"Requested source_types not present in this index: { ', ' .join (unavailable_source_types )} " )
135+ return {
136+ "error" : "invalid_source_types" ,
137+ "message" : ". " .join (message_parts ),
138+ "requested_source_types" : parsed_source_types or [],
139+ "invalid_source_types" : invalid_source_types ,
140+ "unavailable_source_types" : unavailable_source_types ,
141+ "known_source_types" : known_source_types ,
142+ "available_source_types" : available_source_types ,
143+ "results" : [],
144+ "meta" : meta ,
145+ }
146+
147+ results = docstore .search (query = query , k = k , source_types = parsed_source_types )
148+ serialized = _serialize_search_results (results , meta )
149+ if not debug :
150+ return serialized
151+ return {
152+ "results" : serialized ,
153+ "meta" : meta ,
154+ "debug" : {
155+ "query" : query ,
156+ "k" : k ,
157+ "requested_source_types" : parsed_source_types or [],
158+ "available_source_types" : available_source_types ,
159+ "known_source_types" : known_source_types ,
160+ "retrieval_mode" : meta ["retrieval_mode" ],
161+ "result_count" : len (serialized ),
162+ },
163+ }
164+
165+
107166@app .tool ()
108167def open (
109168 doc_id : Optional [str ] = None ,
@@ -177,13 +236,23 @@ def status() -> dict:
177236 paths = make_paths (config )
178237 baked_manifest = _read_manifest (paths .baked_dir / "manifest.json" )
179238 index_manifest = _read_manifest (paths .index_dir / "manifest.json" )
239+ available_source_types = docstore .available_source_types ()
240+ source_type_counts = docstore .source_type_counts ()
241+ coverage_warnings : list [str ] = []
242+ if "scriptref" not in set (available_source_types ):
243+ coverage_warnings .append (
244+ "scriptref source type is not present in the loaded corpus/index; API symbol lookup coverage may be incomplete"
245+ )
180246 return {
181247 "meta" : _response_meta (docstore , baked_manifest = baked_manifest ),
182248 "paths" : vars (config .paths ),
183249 "unity_version" : config .unity_version ,
184250 "embedder" : vars (config .index .embedder ),
185251 "baked_manifest" : baked_manifest ,
186252 "index_manifest" : index_manifest ,
253+ "available_source_types" : available_source_types ,
254+ "source_type_counts" : source_type_counts ,
255+ "coverage_warnings" : coverage_warnings ,
187256 }
188257
189258
0 commit comments