mcp-code-indexer 1.0.2__tar.gz → 1.0.5__tar.gz

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.
Files changed (36) hide show
  1. {mcp_code_indexer-1.0.2/src/mcp_code_indexer.egg-info → mcp_code_indexer-1.0.5}/PKG-INFO +2 -2
  2. {mcp_code_indexer-1.0.2 → mcp_code_indexer-1.0.5}/pyproject.toml +2 -2
  3. {mcp_code_indexer-1.0.2 → mcp_code_indexer-1.0.5}/src/mcp_code_indexer/__init__.py +1 -1
  4. {mcp_code_indexer-1.0.2 → mcp_code_indexer-1.0.5}/src/mcp_code_indexer/file_scanner.py +15 -9
  5. {mcp_code_indexer-1.0.2 → mcp_code_indexer-1.0.5}/src/mcp_code_indexer/main.py +2 -0
  6. {mcp_code_indexer-1.0.2 → mcp_code_indexer-1.0.5}/src/mcp_code_indexer/server/mcp_server.py +139 -10
  7. {mcp_code_indexer-1.0.2 → mcp_code_indexer-1.0.5/src/mcp_code_indexer.egg-info}/PKG-INFO +2 -2
  8. {mcp_code_indexer-1.0.2 → mcp_code_indexer-1.0.5}/src/mcp_code_indexer.egg-info/requires.txt +1 -1
  9. {mcp_code_indexer-1.0.2 → mcp_code_indexer-1.0.5}/LICENSE +0 -0
  10. {mcp_code_indexer-1.0.2 → mcp_code_indexer-1.0.5}/MANIFEST.in +0 -0
  11. {mcp_code_indexer-1.0.2 → mcp_code_indexer-1.0.5}/README.md +0 -0
  12. {mcp_code_indexer-1.0.2 → mcp_code_indexer-1.0.5}/docs/api-reference.md +0 -0
  13. {mcp_code_indexer-1.0.2 → mcp_code_indexer-1.0.5}/docs/architecture.md +0 -0
  14. {mcp_code_indexer-1.0.2 → mcp_code_indexer-1.0.5}/docs/configuration.md +0 -0
  15. {mcp_code_indexer-1.0.2 → mcp_code_indexer-1.0.5}/docs/contributing.md +0 -0
  16. {mcp_code_indexer-1.0.2 → mcp_code_indexer-1.0.5}/migrations/001_initial.sql +0 -0
  17. {mcp_code_indexer-1.0.2 → mcp_code_indexer-1.0.5}/migrations/002_performance_indexes.sql +0 -0
  18. {mcp_code_indexer-1.0.2 → mcp_code_indexer-1.0.5}/requirements.txt +0 -0
  19. {mcp_code_indexer-1.0.2 → mcp_code_indexer-1.0.5}/setup.cfg +0 -0
  20. {mcp_code_indexer-1.0.2 → mcp_code_indexer-1.0.5}/setup.py +0 -0
  21. {mcp_code_indexer-1.0.2 → mcp_code_indexer-1.0.5}/src/mcp_code_indexer/database/__init__.py +0 -0
  22. {mcp_code_indexer-1.0.2 → mcp_code_indexer-1.0.5}/src/mcp_code_indexer/database/database.py +0 -0
  23. {mcp_code_indexer-1.0.2 → mcp_code_indexer-1.0.5}/src/mcp_code_indexer/database/models.py +0 -0
  24. {mcp_code_indexer-1.0.2 → mcp_code_indexer-1.0.5}/src/mcp_code_indexer/error_handler.py +0 -0
  25. {mcp_code_indexer-1.0.2 → mcp_code_indexer-1.0.5}/src/mcp_code_indexer/logging_config.py +0 -0
  26. {mcp_code_indexer-1.0.2 → mcp_code_indexer-1.0.5}/src/mcp_code_indexer/merge_handler.py +0 -0
  27. {mcp_code_indexer-1.0.2 → mcp_code_indexer-1.0.5}/src/mcp_code_indexer/middleware/__init__.py +0 -0
  28. {mcp_code_indexer-1.0.2 → mcp_code_indexer-1.0.5}/src/mcp_code_indexer/middleware/error_middleware.py +0 -0
  29. {mcp_code_indexer-1.0.2 → mcp_code_indexer-1.0.5}/src/mcp_code_indexer/server/__init__.py +0 -0
  30. {mcp_code_indexer-1.0.2 → mcp_code_indexer-1.0.5}/src/mcp_code_indexer/tiktoken_cache/9b5ad71b2ce5302211f9c61530b329a4922fc6a4 +0 -0
  31. {mcp_code_indexer-1.0.2 → mcp_code_indexer-1.0.5}/src/mcp_code_indexer/token_counter.py +0 -0
  32. {mcp_code_indexer-1.0.2 → mcp_code_indexer-1.0.5}/src/mcp_code_indexer/tools/__init__.py +0 -0
  33. {mcp_code_indexer-1.0.2 → mcp_code_indexer-1.0.5}/src/mcp_code_indexer.egg-info/SOURCES.txt +0 -0
  34. {mcp_code_indexer-1.0.2 → mcp_code_indexer-1.0.5}/src/mcp_code_indexer.egg-info/dependency_links.txt +0 -0
  35. {mcp_code_indexer-1.0.2 → mcp_code_indexer-1.0.5}/src/mcp_code_indexer.egg-info/entry_points.txt +0 -0
  36. {mcp_code_indexer-1.0.2 → mcp_code_indexer-1.0.5}/src/mcp_code_indexer.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp-code-indexer
3
- Version: 1.0.2
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
@@ -30,7 +30,7 @@ Requires-Python: >=3.9
30
30
  Description-Content-Type: text/markdown
31
31
  License-File: LICENSE
32
32
  Requires-Dist: tiktoken>=0.9.0
33
- Requires-Dist: mcp==1.0.0
33
+ Requires-Dist: mcp>=1.9.0
34
34
  Requires-Dist: gitignore_parser==0.1.11
35
35
  Requires-Dist: pydantic>=2.8.0
36
36
  Requires-Dist: aiofiles==23.2.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "mcp-code-indexer"
7
- version = "1.0.2"
7
+ version = "1.0.5"
8
8
  description = "MCP server that tracks file descriptions across codebases, enabling AI agents to efficiently navigate and understand code through searchable summaries and token-aware overviews."
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -44,7 +44,7 @@ classifiers = [
44
44
  requires-python = ">=3.9"
45
45
  dependencies = [
46
46
  "tiktoken>=0.9.0",
47
- "mcp==1.0.0",
47
+ "mcp>=1.9.0",
48
48
  "gitignore_parser==0.1.11",
49
49
  "pydantic>=2.8.0",
50
50
  "aiofiles==23.2.0",
@@ -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.2"
9
+ __version__ = "1.0.5"
10
10
  __author__ = "MCP Code Indexer Contributors"
11
11
  __email__ = ""
12
12
  __license__ = "MIT"
@@ -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
- stats['total_files'] += 1
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
- # Check if trackable
367
- if self.should_ignore_file(file_path):
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}")
@@ -124,7 +124,9 @@ def cli_main():
124
124
  pass
125
125
  except Exception as e:
126
126
  # Log critical errors to stderr, not stdout
127
+ import traceback
127
128
  print(f"Server failed to start: {e}", file=sys.stderr)
129
+ print(f"Traceback: {traceback.format_exc()}", file=sys.stderr)
128
130
  sys.exit(1)
129
131
 
130
132
 
@@ -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
@@ -75,6 +76,9 @@ class MCPCodeIndexServer:
75
76
  # Register handlers
76
77
  self._register_handlers()
77
78
 
79
+ # Add debug logging for server events
80
+ self.logger.debug("MCP server instance created and handlers registered")
81
+
78
82
  self.logger.info(
79
83
  "MCP Code Index Server initialized",
80
84
  extra={"structured_data": {"initialization": {"token_limit": token_limit}}}
@@ -648,21 +652,146 @@ class MCPCodeIndexServer:
648
652
  **result
649
653
  }
650
654
 
651
- async def run(self) -> None:
652
- """Run the MCP server."""
653
- await self.initialize()
654
-
655
- try:
656
- async with stdio_server() as (read_stream, write_stream):
657
- initialization_options = self.server.create_initialization_options()
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})...")
658
663
  await self.server.run(
659
664
  read_stream,
660
665
  write_stream,
661
666
  initialization_options
662
667
  )
663
- finally:
664
- # Clean shutdown
665
- await self.shutdown()
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
+
728
+ async def run(self) -> None:
729
+ """Run the MCP server with robust error handling."""
730
+ logger.info("Starting server initialization...")
731
+ await self.initialize()
732
+ logger.info("Server initialization completed, starting MCP protocol...")
733
+
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
750
+
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={
785
+ "structured_data": {
786
+ "error_type": type(e).__name__,
787
+ "error_message": str(e),
788
+ "traceback": traceback.format_exc()
789
+ }
790
+ })
791
+ raise
792
+
793
+ # Clean shutdown
794
+ await self.shutdown()
666
795
 
667
796
  async def shutdown(self) -> None:
668
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.2
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
@@ -30,7 +30,7 @@ Requires-Python: >=3.9
30
30
  Description-Content-Type: text/markdown
31
31
  License-File: LICENSE
32
32
  Requires-Dist: tiktoken>=0.9.0
33
- Requires-Dist: mcp==1.0.0
33
+ Requires-Dist: mcp>=1.9.0
34
34
  Requires-Dist: gitignore_parser==0.1.11
35
35
  Requires-Dist: pydantic>=2.8.0
36
36
  Requires-Dist: aiofiles==23.2.0
@@ -1,5 +1,5 @@
1
1
  tiktoken>=0.9.0
2
- mcp==1.0.0
2
+ mcp>=1.9.0
3
3
  gitignore_parser==0.1.11
4
4
  pydantic>=2.8.0
5
5
  aiofiles==23.2.0