mcp-code-indexer 1.6.1__tar.gz → 1.6.3__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 (41) hide show
  1. {mcp_code_indexer-1.6.1/src/mcp_code_indexer.egg-info → mcp_code_indexer-1.6.3}/PKG-INFO +3 -3
  2. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/README.md +2 -2
  3. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/pyproject.toml +1 -1
  4. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/setup.py +1 -1
  5. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/src/mcp_code_indexer/git_hook_handler.py +55 -7
  6. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/src/mcp_code_indexer/server/mcp_server.py +58 -1
  7. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3/src/mcp_code_indexer.egg-info}/PKG-INFO +3 -3
  8. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/LICENSE +0 -0
  9. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/MANIFEST.in +0 -0
  10. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/docs/api-reference.md +0 -0
  11. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/docs/architecture.md +0 -0
  12. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/docs/configuration.md +0 -0
  13. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/docs/contributing.md +0 -0
  14. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/docs/git-hook-setup.md +0 -0
  15. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/migrations/001_initial.sql +0 -0
  16. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/migrations/002_performance_indexes.sql +0 -0
  17. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/migrations/003_project_overviews.sql +0 -0
  18. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/requirements.txt +0 -0
  19. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/setup.cfg +0 -0
  20. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/src/mcp_code_indexer/__init__.py +0 -0
  21. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/src/mcp_code_indexer/__main__.py +0 -0
  22. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/src/mcp_code_indexer/data/stop_words_english.txt +0 -0
  23. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/src/mcp_code_indexer/database/__init__.py +0 -0
  24. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/src/mcp_code_indexer/database/database.py +0 -0
  25. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/src/mcp_code_indexer/database/models.py +0 -0
  26. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/src/mcp_code_indexer/error_handler.py +0 -0
  27. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/src/mcp_code_indexer/file_scanner.py +0 -0
  28. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/src/mcp_code_indexer/logging_config.py +0 -0
  29. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/src/mcp_code_indexer/main.py +0 -0
  30. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/src/mcp_code_indexer/merge_handler.py +0 -0
  31. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/src/mcp_code_indexer/middleware/__init__.py +0 -0
  32. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/src/mcp_code_indexer/middleware/error_middleware.py +0 -0
  33. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/src/mcp_code_indexer/server/__init__.py +0 -0
  34. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/src/mcp_code_indexer/tiktoken_cache/9b5ad71b2ce5302211f9c61530b329a4922fc6a4 +0 -0
  35. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/src/mcp_code_indexer/token_counter.py +0 -0
  36. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/src/mcp_code_indexer/tools/__init__.py +0 -0
  37. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/src/mcp_code_indexer.egg-info/SOURCES.txt +0 -0
  38. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/src/mcp_code_indexer.egg-info/dependency_links.txt +0 -0
  39. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/src/mcp_code_indexer.egg-info/entry_points.txt +0 -0
  40. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/src/mcp_code_indexer.egg-info/requires.txt +0 -0
  41. {mcp_code_indexer-1.6.1 → mcp_code_indexer-1.6.3}/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.6.1
3
+ Version: 1.6.3
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
@@ -57,8 +57,8 @@ Dynamic: requires-python
57
57
 
58
58
  # MCP Code Indexer 🚀
59
59
 
60
- [![PyPI version](https://badge.fury.io/py/mcp-code-indexer.svg?5)](https://badge.fury.io/py/mcp-code-indexer)
61
- [![Python](https://img.shields.io/pypi/pyversions/mcp-code-indexer.svg?5)](https://pypi.org/project/mcp-code-indexer/)
60
+ [![PyPI version](https://badge.fury.io/py/mcp-code-indexer.svg?7)](https://badge.fury.io/py/mcp-code-indexer)
61
+ [![Python](https://img.shields.io/pypi/pyversions/mcp-code-indexer.svg?7)](https://pypi.org/project/mcp-code-indexer/)
62
62
  [![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
63
63
 
64
64
  A production-ready **Model Context Protocol (MCP) server** that revolutionizes how AI agents navigate and understand codebases. Instead of repeatedly scanning files, agents get instant access to intelligent descriptions, semantic search, and context-aware recommendations.
@@ -1,7 +1,7 @@
1
1
  # MCP Code Indexer 🚀
2
2
 
3
- [![PyPI version](https://badge.fury.io/py/mcp-code-indexer.svg?5)](https://badge.fury.io/py/mcp-code-indexer)
4
- [![Python](https://img.shields.io/pypi/pyversions/mcp-code-indexer.svg?5)](https://pypi.org/project/mcp-code-indexer/)
3
+ [![PyPI version](https://badge.fury.io/py/mcp-code-indexer.svg?7)](https://badge.fury.io/py/mcp-code-indexer)
4
+ [![Python](https://img.shields.io/pypi/pyversions/mcp-code-indexer.svg?7)](https://pypi.org/project/mcp-code-indexer/)
5
5
  [![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
6
6
 
7
7
  A production-ready **Model Context Protocol (MCP) server** that revolutionizes how AI agents navigate and understand codebases. Instead of repeatedly scanning files, agents get instant access to intelligent descriptions, semantic search, and context-aware recommendations.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "mcp-code-indexer"
7
- version = "1.6.1"
7
+ version = "1.6.3"
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"}
@@ -21,7 +21,7 @@ def get_version():
21
21
  return data["project"]["version"]
22
22
  except Exception:
23
23
  # Fallback version if reading fails
24
- return "1.6.1"
24
+ return "1.6.3"
25
25
 
26
26
  setup(
27
27
  name="mcp-code-indexer",
@@ -90,8 +90,17 @@ class GitHookHandler:
90
90
  This is the main entry point for git hook functionality.
91
91
  """
92
92
  try:
93
+ self.logger.info(f"=== Git Hook Analysis Started ===")
94
+ if commit_hash:
95
+ self.logger.info(f"Mode: Single commit ({commit_hash})")
96
+ elif commit_range:
97
+ self.logger.info(f"Mode: Commit range ({commit_range[0]}..{commit_range[1]})")
98
+ else:
99
+ self.logger.info(f"Mode: Staged changes")
100
+
93
101
  # Get git info from current directory
94
102
  project_info = await self._identify_project_from_git()
103
+ self.logger.info(f"Project identified: {project_info.get('name', 'Unknown')} at {project_info.get('folderPath', 'Unknown')}")
95
104
 
96
105
  # Get git diff and commit message based on mode
97
106
  if commit_hash:
@@ -104,17 +113,17 @@ class GitHookHandler:
104
113
  git_diff = await self._get_git_diff()
105
114
  commit_message = await self._get_commit_message()
106
115
 
116
+ # Log diff details
107
117
  if not git_diff:
108
118
  self.logger.info(f"Skipping git hook update - no git diff")
109
119
  return
110
-
111
- # Check token count instead of character count
120
+
121
+ diff_chars = len(git_diff)
112
122
  diff_tokens = self.token_counter.count_tokens(git_diff)
113
- if diff_tokens > self.config["max_diff_tokens"]:
114
- self.logger.info(f"Skipping git hook update - diff too large ({diff_tokens} tokens > {self.config['max_diff_tokens']} limit)")
115
- return
123
+ self.logger.info(f"Git diff: {diff_chars} characters, {diff_tokens} tokens")
116
124
 
117
125
  # Fetch current state
126
+ self.logger.info("Fetching current project state...")
118
127
  current_overview = await self._get_project_overview(project_info)
119
128
  current_descriptions = await self._get_all_descriptions(project_info)
120
129
  changed_files = self._extract_changed_files(git_diff)
@@ -123,7 +132,12 @@ class GitHookHandler:
123
132
  self.logger.info("No changed files detected in git diff")
124
133
  return
125
134
 
135
+ self.logger.info(f"Found {len(changed_files)} changed files: {', '.join(changed_files)}")
136
+ self.logger.info(f"Current overview length: {len(current_overview) if current_overview else 0} characters")
137
+ self.logger.info(f"Current descriptions count: {len(current_descriptions)}")
138
+
126
139
  # Build prompt for OpenRouter
140
+ self.logger.info("Building analysis prompt...")
127
141
  prompt = self._build_githook_prompt(
128
142
  git_diff,
129
143
  commit_message,
@@ -132,16 +146,33 @@ class GitHookHandler:
132
146
  changed_files
133
147
  )
134
148
 
149
+ # Log prompt details
150
+ prompt_chars = len(prompt)
151
+ prompt_tokens = self.token_counter.count_tokens(prompt)
152
+ self.logger.info(f"Analysis prompt: {prompt_chars} characters, {prompt_tokens} tokens")
153
+
154
+ # Check total prompt token count
155
+ if prompt_tokens > self.config["max_diff_tokens"]:
156
+ self.logger.info(f"Skipping git hook update - prompt too large ({prompt_tokens} tokens > {self.config['max_diff_tokens']} limit)")
157
+ return
158
+
159
+ self.logger.info(f"Prompt size OK ({prompt_tokens} <= {self.config['max_diff_tokens']} tokens), calling OpenRouter...")
160
+
135
161
  # Call OpenRouter API
136
162
  updates = await self._call_openrouter(prompt)
137
163
 
164
+ self.logger.info(f"OpenRouter response received, processing updates...")
165
+
138
166
  # Apply updates to database
139
167
  await self._apply_updates(project_info, updates)
140
168
 
141
- self.logger.info(f"Git hook update completed for {len(changed_files)} files")
169
+ self.logger.info(f"Git hook update completed successfully for {len(changed_files)} files")
142
170
 
143
171
  except Exception as e:
144
172
  self.logger.error(f"Git hook mode failed: {e}")
173
+ self.logger.error(f"Exception details: {type(e).__name__}: {str(e)}")
174
+ import traceback
175
+ self.logger.error(f"Full traceback:\n{traceback.format_exc()}")
145
176
  # Don't fail the git operation - just log the error
146
177
  raise GitHookError(f"Git hook processing failed: {e}")
147
178
 
@@ -543,8 +574,16 @@ Return ONLY the JSON, no other text."""
543
574
  "max_tokens": 24000,
544
575
  }
545
576
 
577
+
578
+
546
579
  timeout = aiohttp.ClientTimeout(total=self.config["timeout"])
547
580
 
581
+ self.logger.info(f"Sending request to OpenRouter API...")
582
+ self.logger.info(f" Model: {self.config['model']}")
583
+ self.logger.info(f" Temperature: {self.config['temperature']}")
584
+ self.logger.info(f" Max tokens: 24000")
585
+ self.logger.info(f" Timeout: {self.config['timeout']}s")
586
+
548
587
  try:
549
588
  async with aiohttp.ClientSession(timeout=timeout) as session:
550
589
  async with session.post(
@@ -553,8 +592,11 @@ Return ONLY the JSON, no other text."""
553
592
  json=payload
554
593
  ) as response:
555
594
 
595
+ self.logger.info(f"OpenRouter API response status: {response.status}")
596
+
556
597
  if response.status == 429:
557
598
  retry_after = int(response.headers.get("Retry-After", 60))
599
+ self.logger.warning(f"Rate limited by OpenRouter, retry after {retry_after}s")
558
600
  raise ThrottlingError(f"Rate limited. Retry after {retry_after}s")
559
601
 
560
602
  response.raise_for_status()
@@ -562,14 +604,20 @@ Return ONLY the JSON, no other text."""
562
604
  response_data = await response.json()
563
605
 
564
606
  if "choices" not in response_data:
607
+ self.logger.error(f"Invalid API response format: {response_data}")
565
608
  raise GitHookError(f"Invalid API response format: {response_data}")
566
609
 
567
610
  content = response_data["choices"][0]["message"]["content"]
611
+ self.logger.info(f"OpenRouter response content length: {len(content)} characters")
612
+
568
613
  return self._validate_githook_response(content)
569
614
 
570
615
  except aiohttp.ClientError as e:
616
+ self.logger.error(f"OpenRouter API request failed: {e}")
617
+ self.logger.error(f"ClientError details: {type(e).__name__}: {str(e)}")
571
618
  raise GitHookError(f"OpenRouter API request failed: {e}")
572
- except asyncio.TimeoutError:
619
+ except asyncio.TimeoutError as e:
620
+ self.logger.error(f"OpenRouter API request timed out after {self.config['timeout']}s")
573
621
  raise GitHookError("OpenRouter API request timed out")
574
622
 
575
623
  def _validate_githook_response(self, response_text: str) -> Dict[str, Any]:
@@ -437,6 +437,12 @@ src/
437
437
  @self.server.call_tool()
438
438
  async def call_tool(name: str, arguments: Dict[str, Any]) -> List[types.TextContent]:
439
439
  """Handle tool calls with middleware."""
440
+ import time
441
+ start_time = time.time()
442
+
443
+ logger.info(f"=== MCP Tool Call: {name} ===")
444
+ logger.info(f"Arguments: {', '.join(arguments.keys())}")
445
+
440
446
  # Map tool names to handler methods
441
447
  tool_handlers = {
442
448
  "get_file_description": self._handle_get_file_description,
@@ -452,6 +458,7 @@ src/
452
458
  }
453
459
 
454
460
  if name not in tool_handlers:
461
+ logger.error(f"Unknown tool requested: {name}")
455
462
  from ..error_handler import ValidationError
456
463
  raise ValidationError(f"Unknown tool: {name}")
457
464
 
@@ -460,7 +467,18 @@ src/
460
467
  lambda args: self._execute_tool_handler(tool_handlers[name], args)
461
468
  )
462
469
 
463
- return await wrapped_handler(arguments)
470
+ try:
471
+ result = await wrapped_handler(arguments)
472
+
473
+ elapsed_time = time.time() - start_time
474
+ logger.info(f"MCP Tool '{name}' completed successfully in {elapsed_time:.2f}s")
475
+
476
+ return result
477
+ except Exception as e:
478
+ elapsed_time = time.time() - start_time
479
+ logger.error(f"MCP Tool '{name}' failed after {elapsed_time:.2f}s: {e}")
480
+ logger.error(f"Exception details: {type(e).__name__}: {str(e)}")
481
+ raise
464
482
 
465
483
  async def _execute_tool_handler(self, handler, arguments: Dict[str, Any]) -> List[types.TextContent]:
466
484
  """Execute a tool handler and format the result."""
@@ -758,9 +776,18 @@ src/
758
776
 
759
777
  async def _handle_update_file_description(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
760
778
  """Handle update_file_description tool calls."""
779
+ logger.info(f"Updating file description for: {arguments['filePath']}")
780
+ logger.info(f"Project: {arguments.get('projectName', 'Unknown')}")
781
+ logger.info(f"Branch: {arguments.get('branch', 'Unknown')}")
782
+
783
+ description_length = len(arguments.get("description", ""))
784
+ logger.info(f"Description length: {description_length} characters")
785
+
761
786
  project_id = await self._get_or_create_project_id(arguments)
762
787
  resolved_branch = await self._resolve_branch(project_id, arguments["branch"])
763
788
 
789
+ logger.info(f"Resolved project_id: {project_id}, branch: {resolved_branch}")
790
+
764
791
  file_desc = FileDescription(
765
792
  project_id=project_id,
766
793
  branch=resolved_branch,
@@ -773,6 +800,8 @@ src/
773
800
 
774
801
  await self.db_manager.create_file_description(file_desc)
775
802
 
803
+ logger.info(f"Successfully updated description for: {arguments['filePath']}")
804
+
776
805
  return {
777
806
  "success": True,
778
807
  "message": f"Description updated for {arguments['filePath']}",
@@ -782,31 +811,46 @@ src/
782
811
 
783
812
  async def _handle_check_codebase_size(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
784
813
  """Handle check_codebase_size tool calls."""
814
+ logger.info(f"Checking codebase size for: {arguments.get('projectName', 'Unknown')}")
815
+ logger.info(f"Folder path: {arguments.get('folderPath', 'Unknown')}")
816
+ logger.info(f"Branch: {arguments.get('branch', 'Unknown')}")
817
+
785
818
  project_id = await self._get_or_create_project_id(arguments)
786
819
  resolved_branch = await self._resolve_branch(project_id, arguments["branch"])
787
820
  folder_path = Path(arguments["folderPath"])
788
821
 
822
+ logger.info(f"Resolved project_id: {project_id}, branch: {resolved_branch}")
823
+
789
824
  # Clean up descriptions for files that no longer exist
825
+ logger.info("Cleaning up descriptions for missing files...")
790
826
  cleaned_up_files = await self.db_manager.cleanup_missing_files(
791
827
  project_id=project_id,
792
828
  branch=resolved_branch,
793
829
  project_root=folder_path
794
830
  )
831
+ logger.info(f"Cleaned up {len(cleaned_up_files)} missing files")
795
832
 
796
833
  # Get file descriptions for this project/branch (after cleanup)
834
+ logger.info("Retrieving file descriptions...")
797
835
  file_descriptions = await self.db_manager.get_all_file_descriptions(
798
836
  project_id=project_id,
799
837
  branch=resolved_branch
800
838
  )
839
+ logger.info(f"Found {len(file_descriptions)} file descriptions")
801
840
 
802
841
  # Use provided token limit or fall back to server default
803
842
  token_limit = arguments.get("tokenLimit", self.token_limit)
804
843
 
805
844
  # Calculate total tokens
845
+ logger.info("Calculating total token count...")
806
846
  total_tokens = self.token_counter.calculate_codebase_tokens(file_descriptions)
807
847
  is_large = total_tokens > token_limit
808
848
  recommendation = "use_search" if is_large else "use_overview"
809
849
 
850
+ logger.info(f"Codebase analysis complete: {total_tokens} tokens, {len(file_descriptions)} files")
851
+ logger.info(f"Size assessment: {'LARGE' if is_large else 'SMALL'} (limit: {token_limit})")
852
+ logger.info(f"Recommendation: {recommendation}")
853
+
810
854
  return {
811
855
  "totalTokens": total_tokens,
812
856
  "isLarge": is_large,
@@ -819,20 +863,29 @@ src/
819
863
 
820
864
  async def _handle_find_missing_descriptions(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
821
865
  """Handle find_missing_descriptions tool calls."""
866
+ logger.info(f"Finding missing descriptions for: {arguments.get('projectName', 'Unknown')}")
867
+ logger.info(f"Folder path: {arguments.get('folderPath', 'Unknown')}")
868
+
822
869
  project_id = await self._get_or_create_project_id(arguments)
823
870
  resolved_branch = await self._resolve_branch(project_id, arguments["branch"])
824
871
  folder_path = Path(arguments["folderPath"])
825
872
 
873
+ logger.info(f"Resolved project_id: {project_id}, branch: {resolved_branch}")
874
+
826
875
  # Get existing file descriptions
876
+ logger.info("Retrieving existing file descriptions...")
827
877
  existing_descriptions = await self.db_manager.get_all_file_descriptions(
828
878
  project_id=project_id,
829
879
  branch=resolved_branch
830
880
  )
831
881
  existing_paths = {desc.file_path for desc in existing_descriptions}
882
+ logger.info(f"Found {len(existing_paths)} existing descriptions")
832
883
 
833
884
  # Scan directory for files
885
+ logger.info(f"Scanning project directory: {folder_path}")
834
886
  scanner = FileScanner(folder_path)
835
887
  if not scanner.is_valid_project_directory():
888
+ logger.error(f"Invalid or inaccessible project directory: {folder_path}")
836
889
  return {
837
890
  "error": f"Invalid or inaccessible project directory: {folder_path}"
838
891
  }
@@ -840,14 +893,18 @@ src/
840
893
  missing_files = scanner.find_missing_files(existing_paths)
841
894
  missing_paths = [scanner.get_relative_path(f) for f in missing_files]
842
895
 
896
+ logger.info(f"Found {len(missing_paths)} files without descriptions")
897
+
843
898
  # Apply limit if specified
844
899
  limit = arguments.get("limit")
845
900
  total_missing = len(missing_paths)
846
901
  if limit is not None and isinstance(limit, int) and limit > 0:
847
902
  missing_paths = missing_paths[:limit]
903
+ logger.info(f"Applied limit {limit}, returning {len(missing_paths)} files")
848
904
 
849
905
  # Get project stats
850
906
  stats = scanner.get_project_stats()
907
+ logger.info(f"Project stats: {stats.get('total_files', 0)} total files")
851
908
 
852
909
  return {
853
910
  "missingFiles": missing_paths,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp-code-indexer
3
- Version: 1.6.1
3
+ Version: 1.6.3
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
@@ -57,8 +57,8 @@ Dynamic: requires-python
57
57
 
58
58
  # MCP Code Indexer 🚀
59
59
 
60
- [![PyPI version](https://badge.fury.io/py/mcp-code-indexer.svg?5)](https://badge.fury.io/py/mcp-code-indexer)
61
- [![Python](https://img.shields.io/pypi/pyversions/mcp-code-indexer.svg?5)](https://pypi.org/project/mcp-code-indexer/)
60
+ [![PyPI version](https://badge.fury.io/py/mcp-code-indexer.svg?7)](https://badge.fury.io/py/mcp-code-indexer)
61
+ [![Python](https://img.shields.io/pypi/pyversions/mcp-code-indexer.svg?7)](https://pypi.org/project/mcp-code-indexer/)
62
62
  [![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
63
63
 
64
64
  A production-ready **Model Context Protocol (MCP) server** that revolutionizes how AI agents navigate and understand codebases. Instead of repeatedly scanning files, agents get instant access to intelligent descriptions, semantic search, and context-aware recommendations.