mcp-code-indexer 1.0.3__py3-none-any.whl → 1.0.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mcp_code_indexer/__init__.py +1 -1
- mcp_code_indexer/file_scanner.py +15 -9
- mcp_code_indexer/server/mcp_server.py +128 -35
- {mcp_code_indexer-1.0.3.dist-info → mcp_code_indexer-1.0.5.dist-info}/METADATA +1 -1
- {mcp_code_indexer-1.0.3.dist-info → mcp_code_indexer-1.0.5.dist-info}/RECORD +9 -9
- {mcp_code_indexer-1.0.3.dist-info → mcp_code_indexer-1.0.5.dist-info}/WHEEL +0 -0
- {mcp_code_indexer-1.0.3.dist-info → mcp_code_indexer-1.0.5.dist-info}/entry_points.txt +0 -0
- {mcp_code_indexer-1.0.3.dist-info → mcp_code_indexer-1.0.5.dist-info}/licenses/LICENSE +0 -0
- {mcp_code_indexer-1.0.3.dist-info → mcp_code_indexer-1.0.5.dist-info}/top_level.txt +0 -0
mcp_code_indexer/__init__.py
CHANGED
@@ -6,7 +6,7 @@ intelligent codebase navigation through searchable file descriptions,
|
|
6
6
|
token-aware overviews, and advanced merge capabilities.
|
7
7
|
"""
|
8
8
|
|
9
|
-
__version__ = "1.0.
|
9
|
+
__version__ = "1.0.5"
|
10
10
|
__author__ = "MCP Code Indexer Contributors"
|
11
11
|
__email__ = ""
|
12
12
|
__license__ = "MIT"
|
mcp_code_indexer/file_scanner.py
CHANGED
@@ -338,7 +338,7 @@ class FileScanner:
|
|
338
338
|
Get statistics about the project directory.
|
339
339
|
|
340
340
|
Returns:
|
341
|
-
Dictionary with project statistics
|
341
|
+
Dictionary with project statistics for trackable files only
|
342
342
|
"""
|
343
343
|
stats = {
|
344
344
|
'total_files': 0,
|
@@ -349,8 +349,17 @@ class FileScanner:
|
|
349
349
|
}
|
350
350
|
|
351
351
|
try:
|
352
|
+
all_files_count = 0
|
352
353
|
for file_path in self._walk_directory():
|
353
|
-
|
354
|
+
all_files_count += 1
|
355
|
+
|
356
|
+
# Check if trackable first
|
357
|
+
if self.should_ignore_file(file_path):
|
358
|
+
stats['ignored_files'] += 1
|
359
|
+
continue
|
360
|
+
|
361
|
+
# Only process trackable files for detailed stats
|
362
|
+
stats['trackable_files'] += 1
|
354
363
|
|
355
364
|
# Track file size
|
356
365
|
try:
|
@@ -359,15 +368,12 @@ class FileScanner:
|
|
359
368
|
except OSError:
|
360
369
|
pass
|
361
370
|
|
362
|
-
# Track extensions
|
371
|
+
# Track extensions for trackable files only
|
363
372
|
ext = file_path.suffix.lower()
|
364
373
|
stats['file_extensions'][ext] = stats['file_extensions'].get(ext, 0) + 1
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
stats['ignored_files'] += 1
|
369
|
-
else:
|
370
|
-
stats['trackable_files'] += 1
|
374
|
+
|
375
|
+
# Total files is just trackable files
|
376
|
+
stats['total_files'] = stats['trackable_files']
|
371
377
|
|
372
378
|
except Exception as e:
|
373
379
|
logger.error(f"Error getting project stats: {e}")
|
@@ -16,6 +16,7 @@ from typing import Any, Dict, List, Optional
|
|
16
16
|
from mcp import types
|
17
17
|
from mcp.server import Server
|
18
18
|
from mcp.server.stdio import stdio_server
|
19
|
+
from pydantic import ValidationError
|
19
20
|
|
20
21
|
from mcp_code_indexer.database.database import DatabaseManager
|
21
22
|
from mcp_code_indexer.file_scanner import FileScanner
|
@@ -651,54 +652,146 @@ class MCPCodeIndexServer:
|
|
651
652
|
**result
|
652
653
|
}
|
653
654
|
|
655
|
+
async def _run_session_with_retry(self, read_stream, write_stream, initialization_options) -> None:
|
656
|
+
"""Run a single MCP session with error handling and retry logic."""
|
657
|
+
max_retries = 3
|
658
|
+
base_delay = 1.0 # seconds
|
659
|
+
|
660
|
+
for attempt in range(max_retries + 1):
|
661
|
+
try:
|
662
|
+
logger.info(f"Starting MCP server protocol session (attempt {attempt + 1})...")
|
663
|
+
await self.server.run(
|
664
|
+
read_stream,
|
665
|
+
write_stream,
|
666
|
+
initialization_options
|
667
|
+
)
|
668
|
+
logger.info("MCP server session completed normally")
|
669
|
+
return # Success, exit retry loop
|
670
|
+
|
671
|
+
except ValidationError as e:
|
672
|
+
# Handle malformed requests gracefully
|
673
|
+
logger.warning(f"Received malformed request (attempt {attempt + 1}): {e}", extra={
|
674
|
+
"structured_data": {
|
675
|
+
"error_type": "ValidationError",
|
676
|
+
"validation_errors": e.errors() if hasattr(e, 'errors') else str(e),
|
677
|
+
"attempt": attempt + 1,
|
678
|
+
"max_retries": max_retries
|
679
|
+
}
|
680
|
+
})
|
681
|
+
|
682
|
+
if attempt < max_retries:
|
683
|
+
delay = base_delay * (2 ** attempt) # Exponential backoff
|
684
|
+
logger.info(f"Retrying in {delay} seconds...")
|
685
|
+
await asyncio.sleep(delay)
|
686
|
+
else:
|
687
|
+
logger.error("Max retries exceeded for validation errors. Server will continue but this session failed.")
|
688
|
+
return
|
689
|
+
|
690
|
+
except (ConnectionError, BrokenPipeError, EOFError) as e:
|
691
|
+
# Handle client disconnection gracefully
|
692
|
+
logger.info(f"Client disconnected: {e}")
|
693
|
+
return
|
694
|
+
|
695
|
+
except Exception as e:
|
696
|
+
# Handle other exceptions with full logging
|
697
|
+
import traceback
|
698
|
+
if "unhandled errors in a TaskGroup" in str(e) and "ValidationError" in str(e):
|
699
|
+
# This is likely a ValidationError wrapped in a TaskGroup exception
|
700
|
+
logger.warning(f"Detected wrapped validation error (attempt {attempt + 1}): {e}", extra={
|
701
|
+
"structured_data": {
|
702
|
+
"error_type": type(e).__name__,
|
703
|
+
"error_message": str(e),
|
704
|
+
"attempt": attempt + 1,
|
705
|
+
"max_retries": max_retries,
|
706
|
+
"likely_validation_error": True
|
707
|
+
}
|
708
|
+
})
|
709
|
+
|
710
|
+
if attempt < max_retries:
|
711
|
+
delay = base_delay * (2 ** attempt)
|
712
|
+
logger.info(f"Retrying in {delay} seconds...")
|
713
|
+
await asyncio.sleep(delay)
|
714
|
+
else:
|
715
|
+
logger.error("Max retries exceeded for validation errors. Server will continue but this session failed.")
|
716
|
+
return
|
717
|
+
else:
|
718
|
+
# This is a genuine error, log and re-raise
|
719
|
+
logger.error(f"MCP server session error: {e}", extra={
|
720
|
+
"structured_data": {
|
721
|
+
"error_type": type(e).__name__,
|
722
|
+
"error_message": str(e),
|
723
|
+
"traceback": traceback.format_exc()
|
724
|
+
}
|
725
|
+
})
|
726
|
+
raise
|
727
|
+
|
654
728
|
async def run(self) -> None:
|
655
|
-
"""Run the MCP server."""
|
729
|
+
"""Run the MCP server with robust error handling."""
|
656
730
|
logger.info("Starting server initialization...")
|
657
731
|
await self.initialize()
|
658
732
|
logger.info("Server initialization completed, starting MCP protocol...")
|
659
733
|
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
734
|
+
max_retries = 5
|
735
|
+
base_delay = 2.0 # seconds
|
736
|
+
|
737
|
+
for attempt in range(max_retries + 1):
|
738
|
+
try:
|
739
|
+
async with stdio_server() as (read_stream, write_stream):
|
740
|
+
logger.info(f"stdio_server context established (attempt {attempt + 1})")
|
741
|
+
initialization_options = self.server.create_initialization_options()
|
742
|
+
logger.debug(f"Initialization options: {initialization_options}")
|
743
|
+
|
744
|
+
await self._run_session_with_retry(read_stream, write_stream, initialization_options)
|
745
|
+
return # Success, exit retry loop
|
746
|
+
|
747
|
+
except KeyboardInterrupt:
|
748
|
+
logger.info("Server stopped by user interrupt")
|
749
|
+
return
|
665
750
|
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
751
|
+
except Exception as e:
|
752
|
+
import traceback
|
753
|
+
|
754
|
+
# Check if this is a wrapped validation error
|
755
|
+
error_str = str(e)
|
756
|
+
is_validation_error = (
|
757
|
+
"ValidationError" in error_str or
|
758
|
+
"Field required" in error_str or
|
759
|
+
"Input should be" in error_str or
|
760
|
+
"pydantic_core._pydantic_core.ValidationError" in error_str
|
761
|
+
)
|
762
|
+
|
763
|
+
if is_validation_error:
|
764
|
+
logger.warning(f"Detected validation error in session (attempt {attempt + 1}): Malformed client request", extra={
|
765
|
+
"structured_data": {
|
766
|
+
"error_type": "ValidationError",
|
767
|
+
"error_message": "Client sent malformed request (likely missing clientInfo)",
|
768
|
+
"attempt": attempt + 1,
|
769
|
+
"max_retries": max_retries,
|
770
|
+
"will_retry": attempt < max_retries
|
771
|
+
}
|
772
|
+
})
|
773
|
+
|
774
|
+
if attempt < max_retries:
|
775
|
+
delay = base_delay * (2 ** min(attempt, 3)) # Cap exponential growth
|
776
|
+
logger.info(f"Retrying server in {delay} seconds...")
|
777
|
+
await asyncio.sleep(delay)
|
778
|
+
continue
|
779
|
+
else:
|
780
|
+
logger.warning("Max retries exceeded for validation errors. Server is robust against malformed requests.")
|
781
|
+
return
|
782
|
+
else:
|
783
|
+
# This is a genuine fatal error
|
784
|
+
logger.error(f"Fatal server error: {e}", extra={
|
678
785
|
"structured_data": {
|
679
786
|
"error_type": type(e).__name__,
|
680
787
|
"error_message": str(e),
|
681
788
|
"traceback": traceback.format_exc()
|
682
789
|
}
|
683
790
|
})
|
684
|
-
# Re-raise to let the MCP framework handle protocol-level errors
|
685
791
|
raise
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
except Exception as e:
|
690
|
-
import traceback
|
691
|
-
logger.error(f"Fatal server error: {e}", extra={
|
692
|
-
"structured_data": {
|
693
|
-
"error_type": type(e).__name__,
|
694
|
-
"error_message": str(e),
|
695
|
-
"traceback": traceback.format_exc()
|
696
|
-
}
|
697
|
-
})
|
698
|
-
raise
|
699
|
-
finally:
|
700
|
-
# Clean shutdown
|
701
|
-
await self.shutdown()
|
792
|
+
|
793
|
+
# Clean shutdown
|
794
|
+
await self.shutdown()
|
702
795
|
|
703
796
|
async def shutdown(self) -> None:
|
704
797
|
"""Clean shutdown of server resources."""
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: mcp-code-indexer
|
3
|
-
Version: 1.0.
|
3
|
+
Version: 1.0.5
|
4
4
|
Summary: MCP server that tracks file descriptions across codebases, enabling AI agents to efficiently navigate and understand code through searchable summaries and token-aware overviews.
|
5
5
|
Author: MCP Code Indexer Contributors
|
6
6
|
Maintainer: MCP Code Indexer Contributors
|
@@ -1,6 +1,6 @@
|
|
1
|
-
mcp_code_indexer/__init__.py,sha256=
|
1
|
+
mcp_code_indexer/__init__.py,sha256=PUkiM7VGRk7n2B_Ma0fzZWC0wmHCjyE15wxsvU9I54E,473
|
2
2
|
mcp_code_indexer/error_handler.py,sha256=cNSUFFrGBMLDv4qa78c7495L1wSl_dXCRbzCJOidx-Q,11590
|
3
|
-
mcp_code_indexer/file_scanner.py,sha256=
|
3
|
+
mcp_code_indexer/file_scanner.py,sha256=ctXeZMROgDThEtjzsANTK9TbK-fhTScMBd4iyuleBT4,11734
|
4
4
|
mcp_code_indexer/logging_config.py,sha256=5L1cYIG8IAX91yCjc5pzkbO_KPt0bvm_ABHB53LBZjI,5184
|
5
5
|
mcp_code_indexer/main.py,sha256=Rou-mAN9-12PPP8jC7dIs2_UNambJuC2F8BF--j-0m8,3715
|
6
6
|
mcp_code_indexer/merge_handler.py,sha256=lJR8eVq2qSrF6MW9mR3Fy8UzrNAaQ7RsI2FMNXne3vQ,14692
|
@@ -11,12 +11,12 @@ mcp_code_indexer/database/models.py,sha256=3wOxHKb6j3zKPWFSwB5g1TLpI507vLNZcqsxZ
|
|
11
11
|
mcp_code_indexer/middleware/__init__.py,sha256=p-mP0pMsfiU2yajCPvokCUxUEkh_lu4XJP1LyyMW2ug,220
|
12
12
|
mcp_code_indexer/middleware/error_middleware.py,sha256=v6jaHmPxf3qerYdb85X1tHIXLxgcbybpitKVakFLQTA,10109
|
13
13
|
mcp_code_indexer/server/__init__.py,sha256=16xMcuriUOBlawRqWNBk6niwrvtv_JD5xvI36X1Vsmk,41
|
14
|
-
mcp_code_indexer/server/mcp_server.py,sha256=
|
14
|
+
mcp_code_indexer/server/mcp_server.py,sha256=Ok4nrwkRNLGmHTEeW-Ij00gVMMRtS5aX-Hi-lUot5bg,39900
|
15
15
|
mcp_code_indexer/tiktoken_cache/9b5ad71b2ce5302211f9c61530b329a4922fc6a4,sha256=Ijkht27pm96ZW3_3OFE-7xAPtR0YyTWXoRO8_-hlsqc,1681126
|
16
16
|
mcp_code_indexer/tools/__init__.py,sha256=m01mxML2UdD7y5rih_XNhNSCMzQTz7WQ_T1TeOcYlnE,49
|
17
|
-
mcp_code_indexer-1.0.
|
18
|
-
mcp_code_indexer-1.0.
|
19
|
-
mcp_code_indexer-1.0.
|
20
|
-
mcp_code_indexer-1.0.
|
21
|
-
mcp_code_indexer-1.0.
|
22
|
-
mcp_code_indexer-1.0.
|
17
|
+
mcp_code_indexer-1.0.5.dist-info/licenses/LICENSE,sha256=JN9dyPPgYwH9C-UjYM7FLNZjQ6BF7kAzpF3_4PwY4rY,1086
|
18
|
+
mcp_code_indexer-1.0.5.dist-info/METADATA,sha256=JnsvZ1a4k6FP_wulSZAwm6kgB3FNuNRPWaG0HMVHd4o,11930
|
19
|
+
mcp_code_indexer-1.0.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
20
|
+
mcp_code_indexer-1.0.5.dist-info/entry_points.txt,sha256=8HqWOw1Is7jOP1bvIgaSwouvT9z_Boe-9hd4NzyJOhY,68
|
21
|
+
mcp_code_indexer-1.0.5.dist-info/top_level.txt,sha256=yKYCM-gMGt-cnupGfAhnZaoEsROLB6DQ1KFUuyKx4rw,17
|
22
|
+
mcp_code_indexer-1.0.5.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|