mcp-code-indexer 1.0.3__py3-none-any.whl → 1.0.4__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/server/mcp_server.py +128 -35
- {mcp_code_indexer-1.0.3.dist-info → mcp_code_indexer-1.0.4.dist-info}/METADATA +1 -1
- {mcp_code_indexer-1.0.3.dist-info → mcp_code_indexer-1.0.4.dist-info}/RECORD +8 -8
- {mcp_code_indexer-1.0.3.dist-info → mcp_code_indexer-1.0.4.dist-info}/WHEEL +0 -0
- {mcp_code_indexer-1.0.3.dist-info → mcp_code_indexer-1.0.4.dist-info}/entry_points.txt +0 -0
- {mcp_code_indexer-1.0.3.dist-info → mcp_code_indexer-1.0.4.dist-info}/licenses/LICENSE +0 -0
- {mcp_code_indexer-1.0.3.dist-info → mcp_code_indexer-1.0.4.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.4"
|
10
10
|
__author__ = "MCP Code Indexer Contributors"
|
11
11
|
__email__ = ""
|
12
12
|
__license__ = "MIT"
|
@@ -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.4
|
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,4 +1,4 @@
|
|
1
|
-
mcp_code_indexer/__init__.py,sha256=
|
1
|
+
mcp_code_indexer/__init__.py,sha256=tFNpOTeNAG23YobUnL1oXAdr6CfSQXPtzvGswDz8uPg,473
|
2
2
|
mcp_code_indexer/error_handler.py,sha256=cNSUFFrGBMLDv4qa78c7495L1wSl_dXCRbzCJOidx-Q,11590
|
3
3
|
mcp_code_indexer/file_scanner.py,sha256=1Z6wq7H14V1OMAHIF4v9G7SY8hC1puDmU5IXsCKH4kU,11442
|
4
4
|
mcp_code_indexer/logging_config.py,sha256=5L1cYIG8IAX91yCjc5pzkbO_KPt0bvm_ABHB53LBZjI,5184
|
@@ -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.4.dist-info/licenses/LICENSE,sha256=JN9dyPPgYwH9C-UjYM7FLNZjQ6BF7kAzpF3_4PwY4rY,1086
|
18
|
+
mcp_code_indexer-1.0.4.dist-info/METADATA,sha256=ZOl-psh9Y4q9OtUFzuevW53lpm0SsczC7Nr3aSzIso4,11930
|
19
|
+
mcp_code_indexer-1.0.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
20
|
+
mcp_code_indexer-1.0.4.dist-info/entry_points.txt,sha256=8HqWOw1Is7jOP1bvIgaSwouvT9z_Boe-9hd4NzyJOhY,68
|
21
|
+
mcp_code_indexer-1.0.4.dist-info/top_level.txt,sha256=yKYCM-gMGt-cnupGfAhnZaoEsROLB6DQ1KFUuyKx4rw,17
|
22
|
+
mcp_code_indexer-1.0.4.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|