@@ -415,21 +415,26 @@ async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent]:
415415 """Route tool calls to the appropriate handler."""
416416 try :
417417 # --- Fine-grained tools (no LLM config needed) ---
418+ # Synchronous handlers run via asyncio.to_thread() so they never
419+ # block the event loop (which would hang the MCP stdio server).
418420 if name == "analyze_repo" :
419421 from codewiki .mcp .tools .analysis import handle_analyze_repo
422+ # NOTE: Tree-sitter C extensions are not thread-safe, so this
423+ # must run on the main thread (blocking the event loop is
424+ # acceptable for this one-time heavy operation).
420425 return [_text (handle_analyze_repo (arguments , _store ))]
421426
422427 elif name == "read_code_components" :
423428 from codewiki .mcp .tools .code_reader import handle_read_code_components
424- return [_text (handle_read_code_components ( arguments , _store ))]
429+ return [_text (await asyncio . to_thread ( handle_read_code_components , arguments , _store ))]
425430
426431 elif name == "list_components" :
427432 from codewiki .mcp .tools .analysis import handle_list_components
428- return [_text (handle_list_components ( arguments , _store ))]
433+ return [_text (await asyncio . to_thread ( handle_list_components , arguments , _store ))]
429434
430435 elif name == "view_repo_file" :
431436 from codewiki .mcp .tools .code_reader import handle_view_repo_file
432- return [_text (handle_view_repo_file ( arguments , _store ))]
437+ return [_text (await asyncio . to_thread ( handle_view_repo_file , arguments , _store ))]
433438
434439 elif name == "write_doc_file" :
435440 from codewiki .mcp .tools .doc_writer import handle_write_doc_file
@@ -443,15 +448,15 @@ async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent]:
443448
444449 elif name == "save_module_tree" :
445450 from codewiki .mcp .tools .module_tree import handle_save_module_tree
446- return [_text (handle_save_module_tree ( arguments , _store ))]
451+ return [_text (await asyncio . to_thread ( handle_save_module_tree , arguments , _store ))]
447452
448453 elif name == "get_processing_order" :
449454 from codewiki .mcp .tools .module_tree import handle_get_processing_order
450- return [_text (handle_get_processing_order ( arguments , _store ))]
455+ return [_text (await asyncio . to_thread ( handle_get_processing_order , arguments , _store ))]
451456
452457 elif name == "get_prompt" :
453458 from codewiki .mcp .tools .prompt_server import handle_get_prompt
454- return [_text (handle_get_prompt ( arguments , _store ))]
459+ return [_text (await asyncio . to_thread ( handle_get_prompt , arguments , _store ))]
455460
456461 elif name == "close_session" :
457462 sid = arguments ["session_id" ]
0 commit comments