22
22
from .project_settings import ProjectSettings
23
23
from .services import (
24
24
ProjectService , IndexService , SearchService ,
25
- FileService , SettingsService
25
+ FileService , SettingsService , FileWatcherService
26
26
)
27
27
from .services .settings_service import manage_temp_directory
28
28
from .utils import (
@@ -37,6 +37,7 @@ class CodeIndexerContext:
37
37
file_count : int = 0
38
38
file_index : dict = field (default_factory = dict )
39
39
index_cache : dict = field (default_factory = dict )
40
+ file_watcher_service : FileWatcherService = None
40
41
41
42
@asynccontextmanager
42
43
async def indexer_lifespan (_server : FastMCP ) -> AsyncIterator [CodeIndexerContext ]:
@@ -49,17 +50,23 @@ async def indexer_lifespan(_server: FastMCP) -> AsyncIterator[CodeIndexerContext
49
50
# Initialize settings manager with skip_load=True to skip loading files
50
51
settings = ProjectSettings (base_path , skip_load = True )
51
52
52
- # Initialize context
53
+ # Initialize context - file watcher will be initialized later when project path is set
53
54
context = CodeIndexerContext (
54
55
base_path = base_path ,
55
- settings = settings
56
+ settings = settings ,
57
+ file_watcher_service = None
56
58
)
57
59
58
60
try :
59
61
print ("Server ready. Waiting for user to set project path..." )
60
62
# Provide context to the server
61
63
yield context
62
64
finally :
65
+ # Stop file watcher if it was started
66
+ if context .file_watcher_service :
67
+ print ("Stopping file watcher service..." )
68
+ await context .file_watcher_service .stop_monitoring ()
69
+
63
70
# Only save index if project path has been set
64
71
if context .base_path and context .index_cache :
65
72
print (f"Saving index for project: { context .base_path } " )
@@ -203,18 +210,18 @@ def refresh_index(ctx: Context) -> str:
203
210
Manually refresh the project index when files have been added/removed/moved.
204
211
205
212
Use when:
206
- - After AI/LLM has created, modified, or deleted files
207
- - After git operations (checkout, merge, pull) that change files
213
+ - File watcher is disabled or unavailable
214
+ - After large-scale operations (git checkout, merge, pull) that change many files
215
+ - When you want immediate index rebuild without waiting for file watcher debounce
208
216
- When find_files results seem incomplete or outdated
209
- - For immediate refresh without waiting for auto-refresh rate limits
217
+ - For troubleshooting suspected index synchronization issues
210
218
211
219
Important notes for LLMs:
212
- - This tool bypasses the 5-second rate limit that applies to auto-refresh
213
- - Always available for immediate use when you know files have changed
220
+ - Always available as backup when file watcher is not working
214
221
- Performs full project re-indexing for complete accuracy
215
222
- Use when you suspect the index is stale after file system changes
216
- - **Always call this after modifying files programmatically **
217
- - Essential for find_files tool to see new/changed files
223
+ - **Call this after programmatic file modifications if file watcher seems unresponsive **
224
+ - Complements the automatic file watcher system
218
225
219
226
Returns:
220
227
Success message with total file count
@@ -254,6 +261,75 @@ def refresh_search_tools(ctx: Context) -> str:
254
261
"""
255
262
return SearchService (ctx ).refresh_search_tools ()
256
263
264
+ @mcp .tool ()
265
+ @handle_mcp_tool_errors (return_type = 'dict' )
266
+ def get_file_watcher_status (ctx : Context ) -> Dict [str , Any ]:
267
+ """Get file watcher service status and statistics."""
268
+ try :
269
+ # Get file watcher service from context
270
+ file_watcher_service = None
271
+ if hasattr (ctx .request_context .lifespan_context , 'file_watcher_service' ):
272
+ file_watcher_service = ctx .request_context .lifespan_context .file_watcher_service
273
+
274
+ if not file_watcher_service :
275
+ return {"status" : "not_initialized" , "message" : "File watcher service not initialized" }
276
+
277
+ # Get status from file watcher service
278
+ status = file_watcher_service .get_status ()
279
+
280
+ # Add index service status
281
+ index_service = IndexService (ctx )
282
+ rebuild_status = index_service .get_rebuild_status ()
283
+ status ["rebuild_status" ] = rebuild_status
284
+
285
+ # Add configuration
286
+ if hasattr (ctx .request_context .lifespan_context , 'settings' ) and ctx .request_context .lifespan_context .settings :
287
+ file_watcher_config = ctx .request_context .lifespan_context .settings .get_file_watcher_config ()
288
+ status ["configuration" ] = file_watcher_config
289
+
290
+ return status
291
+
292
+ except Exception as e :
293
+ return {"status" : "error" , "message" : f"Failed to get file watcher status: { e } " }
294
+
295
+ @mcp .tool ()
296
+ @handle_mcp_tool_errors (return_type = 'str' )
297
+ def configure_file_watcher (
298
+ ctx : Context ,
299
+ enabled : bool = None ,
300
+ debounce_seconds : float = None ,
301
+ additional_exclude_patterns : list = None
302
+ ) -> str :
303
+ """Configure file watcher service settings."""
304
+ try :
305
+ # Get settings from context
306
+ if not hasattr (ctx .request_context .lifespan_context , 'settings' ) or not ctx .request_context .lifespan_context .settings :
307
+ return "Settings not available - project path not set"
308
+
309
+ settings = ctx .request_context .lifespan_context .settings
310
+
311
+ # Build updates dictionary
312
+ updates = {}
313
+ if enabled is not None :
314
+ updates ["enabled" ] = enabled
315
+ if debounce_seconds is not None :
316
+ updates ["debounce_seconds" ] = debounce_seconds
317
+ if additional_exclude_patterns is not None :
318
+ updates ["additional_exclude_patterns" ] = additional_exclude_patterns
319
+
320
+ if not updates :
321
+ return "No configuration changes specified"
322
+
323
+ # Update configuration
324
+ settings .update_file_watcher_config (updates )
325
+
326
+ # If file watcher is running, we would need to restart it for changes to take effect
327
+ # For now, just return success message with note about restart
328
+ return f"File watcher configuration updated: { updates } . Restart may be required for changes to take effect."
329
+
330
+ except Exception as e :
331
+ return f"Failed to update file watcher configuration: { e } "
332
+
257
333
# ----- PROMPTS -----
258
334
259
335
@mcp .prompt ()
0 commit comments