mcp-code-indexer 3.4.0__tar.gz → 3.4.2__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 (35) hide show
  1. {mcp_code_indexer-3.4.0 → mcp_code_indexer-3.4.2}/PKG-INFO +9 -3
  2. {mcp_code_indexer-3.4.0 → mcp_code_indexer-3.4.2}/README.md +8 -2
  3. {mcp_code_indexer-3.4.0 → mcp_code_indexer-3.4.2}/pyproject.toml +1 -1
  4. {mcp_code_indexer-3.4.0 → mcp_code_indexer-3.4.2}/src/mcp_code_indexer/git_hook_handler.py +48 -31
  5. {mcp_code_indexer-3.4.0 → mcp_code_indexer-3.4.2}/src/mcp_code_indexer/main.py +15 -40
  6. {mcp_code_indexer-3.4.0 → mcp_code_indexer-3.4.2}/LICENSE +0 -0
  7. {mcp_code_indexer-3.4.0 → mcp_code_indexer-3.4.2}/src/mcp_code_indexer/__init__.py +0 -0
  8. {mcp_code_indexer-3.4.0 → mcp_code_indexer-3.4.2}/src/mcp_code_indexer/__main__.py +0 -0
  9. {mcp_code_indexer-3.4.0 → mcp_code_indexer-3.4.2}/src/mcp_code_indexer/ask_handler.py +0 -0
  10. {mcp_code_indexer-3.4.0 → mcp_code_indexer-3.4.2}/src/mcp_code_indexer/claude_api_handler.py +0 -0
  11. {mcp_code_indexer-3.4.0 → mcp_code_indexer-3.4.2}/src/mcp_code_indexer/cleanup_manager.py +0 -0
  12. {mcp_code_indexer-3.4.0 → mcp_code_indexer-3.4.2}/src/mcp_code_indexer/data/stop_words_english.txt +0 -0
  13. {mcp_code_indexer-3.4.0 → mcp_code_indexer-3.4.2}/src/mcp_code_indexer/database/__init__.py +0 -0
  14. {mcp_code_indexer-3.4.0 → mcp_code_indexer-3.4.2}/src/mcp_code_indexer/database/connection_health.py +0 -0
  15. {mcp_code_indexer-3.4.0 → mcp_code_indexer-3.4.2}/src/mcp_code_indexer/database/database.py +0 -0
  16. {mcp_code_indexer-3.4.0 → mcp_code_indexer-3.4.2}/src/mcp_code_indexer/database/exceptions.py +0 -0
  17. {mcp_code_indexer-3.4.0 → mcp_code_indexer-3.4.2}/src/mcp_code_indexer/database/models.py +0 -0
  18. {mcp_code_indexer-3.4.0 → mcp_code_indexer-3.4.2}/src/mcp_code_indexer/database/retry_executor.py +0 -0
  19. {mcp_code_indexer-3.4.0 → mcp_code_indexer-3.4.2}/src/mcp_code_indexer/deepask_handler.py +0 -0
  20. {mcp_code_indexer-3.4.0 → mcp_code_indexer-3.4.2}/src/mcp_code_indexer/error_handler.py +0 -0
  21. {mcp_code_indexer-3.4.0 → mcp_code_indexer-3.4.2}/src/mcp_code_indexer/file_scanner.py +0 -0
  22. {mcp_code_indexer-3.4.0 → mcp_code_indexer-3.4.2}/src/mcp_code_indexer/logging_config.py +0 -0
  23. {mcp_code_indexer-3.4.0 → mcp_code_indexer-3.4.2}/src/mcp_code_indexer/middleware/__init__.py +0 -0
  24. {mcp_code_indexer-3.4.0 → mcp_code_indexer-3.4.2}/src/mcp_code_indexer/middleware/error_middleware.py +0 -0
  25. {mcp_code_indexer-3.4.0 → mcp_code_indexer-3.4.2}/src/mcp_code_indexer/migrations/001_initial.sql +0 -0
  26. {mcp_code_indexer-3.4.0 → mcp_code_indexer-3.4.2}/src/mcp_code_indexer/migrations/002_performance_indexes.sql +0 -0
  27. {mcp_code_indexer-3.4.0 → mcp_code_indexer-3.4.2}/src/mcp_code_indexer/migrations/003_project_overviews.sql +0 -0
  28. {mcp_code_indexer-3.4.0 → mcp_code_indexer-3.4.2}/src/mcp_code_indexer/migrations/004_remove_branch_dependency.sql +0 -0
  29. {mcp_code_indexer-3.4.0 → mcp_code_indexer-3.4.2}/src/mcp_code_indexer/migrations/005_remove_git_remotes.sql +0 -0
  30. {mcp_code_indexer-3.4.0 → mcp_code_indexer-3.4.2}/src/mcp_code_indexer/query_preprocessor.py +0 -0
  31. {mcp_code_indexer-3.4.0 → mcp_code_indexer-3.4.2}/src/mcp_code_indexer/server/__init__.py +0 -0
  32. {mcp_code_indexer-3.4.0 → mcp_code_indexer-3.4.2}/src/mcp_code_indexer/server/mcp_server.py +0 -0
  33. {mcp_code_indexer-3.4.0 → mcp_code_indexer-3.4.2}/src/mcp_code_indexer/tiktoken_cache/9b5ad71b2ce5302211f9c61530b329a4922fc6a4 +0 -0
  34. {mcp_code_indexer-3.4.0 → mcp_code_indexer-3.4.2}/src/mcp_code_indexer/token_counter.py +0 -0
  35. {mcp_code_indexer-3.4.0 → mcp_code_indexer-3.4.2}/src/mcp_code_indexer/tools/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: mcp-code-indexer
3
- Version: 3.4.0
3
+ Version: 3.4.2
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
  License: MIT
6
6
  Keywords: mcp,model-context-protocol,code-indexer,ai-tools,codebase-navigation,file-descriptions,llm-tools
@@ -40,8 +40,8 @@ Description-Content-Type: text/markdown
40
40
 
41
41
  # MCP Code Indexer 🚀
42
42
 
43
- [![PyPI version](https://badge.fury.io/py/mcp-code-indexer.svg?31)](https://badge.fury.io/py/mcp-code-indexer)
44
- [![Python](https://img.shields.io/pypi/pyversions/mcp-code-indexer.svg?31)](https://pypi.org/project/mcp-code-indexer/)
43
+ [![PyPI version](https://badge.fury.io/py/mcp-code-indexer.svg?33)](https://badge.fury.io/py/mcp-code-indexer)
44
+ [![Python](https://img.shields.io/pypi/pyversions/mcp-code-indexer.svg?33)](https://pypi.org/project/mcp-code-indexer/)
45
45
  [![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
46
46
 
47
47
  A production-ready **Model Context Protocol (MCP) server** that revolutionizes how AI agents navigate and understand codebases. Built for high-concurrency environments with advanced database resilience, the server provides instant access to intelligent descriptions, semantic search, and context-aware recommendations while maintaining 800+ writes/sec throughput.
@@ -245,8 +245,14 @@ mcp-code-indexer --githook
245
245
  # Analyze a specific commit
246
246
  mcp-code-indexer --githook abc123def
247
247
 
248
+ # Analyze using HEAD syntax
249
+ mcp-code-indexer --githook HEAD
250
+ mcp-code-indexer --githook HEAD~1
251
+ mcp-code-indexer --githook HEAD~3
252
+
248
253
  # Analyze a commit range (perfect for rebases)
249
254
  mcp-code-indexer --githook abc123 def456
255
+ mcp-code-indexer --githook HEAD~5 HEAD
250
256
  ```
251
257
 
252
258
  **🎯 Perfect for**:
@@ -1,7 +1,7 @@
1
1
  # MCP Code Indexer 🚀
2
2
 
3
- [![PyPI version](https://badge.fury.io/py/mcp-code-indexer.svg?31)](https://badge.fury.io/py/mcp-code-indexer)
4
- [![Python](https://img.shields.io/pypi/pyversions/mcp-code-indexer.svg?31)](https://pypi.org/project/mcp-code-indexer/)
3
+ [![PyPI version](https://badge.fury.io/py/mcp-code-indexer.svg?33)](https://badge.fury.io/py/mcp-code-indexer)
4
+ [![Python](https://img.shields.io/pypi/pyversions/mcp-code-indexer.svg?33)](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. Built for high-concurrency environments with advanced database resilience, the server provides instant access to intelligent descriptions, semantic search, and context-aware recommendations while maintaining 800+ writes/sec throughput.
@@ -205,8 +205,14 @@ mcp-code-indexer --githook
205
205
  # Analyze a specific commit
206
206
  mcp-code-indexer --githook abc123def
207
207
 
208
+ # Analyze using HEAD syntax
209
+ mcp-code-indexer --githook HEAD
210
+ mcp-code-indexer --githook HEAD~1
211
+ mcp-code-indexer --githook HEAD~3
212
+
208
213
  # Analyze a commit range (perfect for rebases)
209
214
  mcp-code-indexer --githook abc123 def456
215
+ mcp-code-indexer --githook HEAD~5 HEAD
210
216
  ```
211
217
 
212
218
  **🎯 Perfect for**:
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
4
4
 
5
5
  [tool.poetry]
6
6
  name = "mcp-code-indexer"
7
- version = "3.4.0"
7
+ version = "3.4.2"
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
  authors = ["MCP Code Indexer Contributors"]
10
10
  maintainers = ["MCP Code Indexer Contributors"]
@@ -88,6 +88,21 @@ class GitHookHandler:
88
88
  "OPENROUTER_API_KEY environment variable is required for git hook mode"
89
89
  )
90
90
 
91
+ def _log_and_print(self, message: str, level: str = "info") -> None:
92
+ """
93
+ Log message and also print to stdout for user visibility.
94
+
95
+ Args:
96
+ message: Message to log and print
97
+ level: Log level (info, warning, error)
98
+ """
99
+ # Log to logger
100
+ getattr(self.logger, level)(message)
101
+
102
+ # Also print to stdout with prefix for visibility
103
+ prefix = "🔍" if level == "info" else "⚠️" if level == "warning" else "❌"
104
+ print(f"{prefix} {message}")
105
+
91
106
  async def run_githook_mode(
92
107
  self,
93
108
  commit_hash: Optional[str] = None,
@@ -103,21 +118,20 @@ class GitHookHandler:
103
118
  This is the main entry point for git hook functionality.
104
119
  """
105
120
  try:
106
- self.logger.info("=== Git Hook Analysis Started ===")
121
+ self._log_and_print("=== Git Hook Analysis Started ===")
107
122
  if commit_hash:
108
- self.logger.info(f"Mode: Single commit ({commit_hash})")
123
+ self._log_and_print(f"Mode: Single commit ({commit_hash})")
109
124
  elif commit_range:
110
- self.logger.info(
125
+ self._log_and_print(
111
126
  f"Mode: Commit range ({commit_range[0]}..{commit_range[1]})"
112
127
  )
113
128
  else:
114
- self.logger.info("Mode: Staged changes")
129
+ self._log_and_print("Mode: Staged changes")
115
130
 
116
131
  # Get git info from current directory
117
132
  project_info = await self._identify_project_from_git()
118
- self.logger.info(
119
- f"Project identified: {project_info.get('name', 'Unknown')} "
120
- f"at {project_info.get('folderPath', 'Unknown')}"
133
+ self._log_and_print(
134
+ f"Project: {project_info.get('name', 'Unknown')}"
121
135
  )
122
136
 
123
137
  # Get git diff and commit message based on mode
@@ -137,25 +151,23 @@ class GitHookHandler:
137
151
 
138
152
  # Log diff details
139
153
  if not git_diff:
140
- self.logger.info("Skipping git hook update - no git diff")
154
+ self._log_and_print("No changes detected, skipping analysis")
141
155
  return
142
156
 
143
157
  diff_tokens = self.token_counter.count_tokens(git_diff)
144
- self.logger.info(f"Git diff: {diff_tokens} tokens")
158
+ self._log_and_print(f"Analyzing diff: {diff_tokens:,} tokens")
145
159
 
146
160
  # Fetch current state
147
- self.logger.info("Fetching current project state...")
161
+ self._log_and_print("Fetching current project state...")
148
162
  current_overview = await self._get_project_overview(project_info)
149
163
  current_descriptions = await self._get_all_descriptions(project_info)
150
164
  changed_files = self._extract_changed_files(git_diff)
151
165
 
152
166
  if not changed_files:
153
- self.logger.info("No changed files detected in git diff")
167
+ self._log_and_print("No files changed, skipping analysis")
154
168
  return
155
169
 
156
- self.logger.info(
157
- f"Found {len(changed_files)} changed files: {', '.join(changed_files)}"
158
- )
170
+ self._log_and_print(f"Found {len(changed_files)} changed files")
159
171
  overview_tokens = (
160
172
  self.token_counter.count_tokens(current_overview)
161
173
  if current_overview
@@ -175,13 +187,23 @@ class GitHookHandler:
175
187
 
176
188
  # Apply updates to database
177
189
  await self._apply_updates(project_info, updates)
178
-
179
- self.logger.info(
180
- f"Git hook update completed successfully for {len(changed_files)} files"
181
- )
190
+
191
+ # Count actual updates
192
+ file_update_count = len(updates.get("file_updates", {}))
193
+ overview_updated = bool(updates.get("overview_update"))
194
+
195
+ if file_update_count > 0 or overview_updated:
196
+ update_parts = []
197
+ if file_update_count > 0:
198
+ update_parts.append(f"{file_update_count} file descriptions")
199
+ if overview_updated:
200
+ update_parts.append("project overview")
201
+ self._log_and_print(f"✅ Updated {' and '.join(update_parts)}")
202
+ else:
203
+ self._log_and_print("✅ Analysis complete, no updates needed")
182
204
 
183
205
  except Exception as e:
184
- self.logger.error(f"Git hook mode failed: {e}")
206
+ self._log_and_print(f"Git hook analysis failed: {e}", "error")
185
207
  self.logger.error(f"Exception details: {type(e).__name__}: {str(e)}")
186
208
  import traceback
187
209
 
@@ -230,16 +252,12 @@ class GitHookHandler:
230
252
 
231
253
  if prompt_tokens <= token_limit:
232
254
  # Use single-stage approach
233
- self.logger.info("Using single-stage analysis (within token limit)")
255
+ self._log_and_print("Using single-stage analysis")
234
256
  result = await self._call_openrouter(single_stage_prompt)
235
- self.logger.info("Single-stage analysis completed")
236
257
  return result
237
258
  else:
238
259
  # Fall back to two-stage approach
239
- self.logger.info(
240
- f"Single-stage prompt too large ({prompt_tokens} tokens), "
241
- f"falling back to two-stage analysis"
242
- )
260
+ self._log_and_print("Using two-stage analysis (large diff)")
243
261
 
244
262
  # Try two-stage analysis first
245
263
  try:
@@ -250,9 +268,8 @@ class GitHookHandler:
250
268
  except GitHookError as e:
251
269
  if "too large" in str(e).lower():
252
270
  # Fall back to chunked processing
253
- self.logger.info(
254
- "Two-stage analysis failed due to size, "
255
- "falling back to chunked processing"
271
+ self._log_and_print(
272
+ "Using chunked processing (very large diff)"
256
273
  )
257
274
  return await self._analyze_with_chunking(
258
275
  git_diff, commit_message, current_overview,
@@ -634,7 +651,7 @@ Return ONLY a JSON object:
634
651
  Returns:
635
652
  Dict containing file_updates and overview_update
636
653
  """
637
- self.logger.info(
654
+ self._log_and_print(
638
655
  f"Starting chunked processing for {len(changed_files)} files"
639
656
  )
640
657
 
@@ -650,7 +667,7 @@ Return ONLY a JSON object:
650
667
  git_diff, changed_files
651
668
  )
652
669
 
653
- self.logger.info(f"Using chunk size of {chunk_size} files per chunk")
670
+ self._log_and_print(f"Processing in {chunk_size}-file chunks")
654
671
 
655
672
  all_file_updates = {}
656
673
 
@@ -659,7 +676,7 @@ Return ONLY a JSON object:
659
676
  chunk_number = (i // chunk_size) + 1
660
677
  total_chunks = (len(changed_files) + chunk_size - 1) // chunk_size
661
678
 
662
- self.logger.info(
679
+ self._log_and_print(
663
680
  f"Processing chunk {chunk_number}/{total_chunks} "
664
681
  f"({len(chunk_files)} files)"
665
682
  )
@@ -63,7 +63,7 @@ def parse_arguments() -> argparse.Namespace:
63
63
  parser.add_argument(
64
64
  "--getprojects",
65
65
  action="store_true",
66
- help="List all projects with IDs, branches, and description counts",
66
+ help="List all projects with IDs and file description counts",
67
67
  )
68
68
 
69
69
  parser.add_argument(
@@ -75,10 +75,10 @@ def parse_arguments() -> argparse.Namespace:
75
75
  parser.add_argument(
76
76
  "--dumpdescriptions",
77
77
  nargs="+",
78
- metavar=("PROJECT_ID", "BRANCH"),
78
+ metavar="PROJECT_ID",
79
79
  help=(
80
80
  "Export descriptions for a project. Usage: "
81
- "--dumpdescriptions PROJECT_ID [BRANCH]"
81
+ "--dumpdescriptions PROJECT_ID"
82
82
  ),
83
83
  )
84
84
 
@@ -89,8 +89,8 @@ def parse_arguments() -> argparse.Namespace:
89
89
  help=(
90
90
  "Git hook mode: auto-update descriptions based on git diff using "
91
91
  "OpenRouter API. Usage: --githook (current changes), --githook HASH "
92
- "(specific commit), --githook HASH1 HASH2 (commit range from "
93
- "HASH1 to HASH2)"
92
+ "(specific commit), --githook HASH1 HASH2 (commit range). "
93
+ "Supports: commit hashes, HEAD, HEAD~1, HEAD~3, branch names, tags."
94
94
  ),
95
95
  )
96
96
 
@@ -138,17 +138,12 @@ async def handle_getprojects(args: argparse.Namespace) -> None:
138
138
  print(f"ID: {project.id}")
139
139
  print(f"Name: {project.name}")
140
140
 
141
- # Get branch information
141
+ # Get file description count
142
142
  try:
143
- branch_counts = await db_manager.get_branch_file_counts(project.id)
144
- if branch_counts:
145
- print("Branches:")
146
- for branch, count in branch_counts.items():
147
- print(f" - {branch}: {count} descriptions")
148
- else:
149
- print("Branches: No descriptions found")
143
+ file_count = await db_manager.get_file_count(project.id)
144
+ print(f"Files: {file_count} descriptions")
150
145
  except Exception as e:
151
- print(f"Branches: Error loading branch info - {e}")
146
+ print(f"Files: Error loading file count - {e}")
152
147
 
153
148
  print("-" * 80)
154
149
 
@@ -288,11 +283,6 @@ async def handle_runcommand(args: argparse.Namespace) -> None:
288
283
  tool_arguments = json_data
289
284
  logger.info("Auto-detected tool: update_file_description")
290
285
  print("Auto-detected tool: update_file_description", file=sys.stderr)
291
- elif "branch" in json_data:
292
- tool_name = "check_codebase_size"
293
- tool_arguments = json_data
294
- logger.info("Auto-detected tool: check_codebase_size")
295
- print("Auto-detected tool: check_codebase_size", file=sys.stderr)
296
286
  else:
297
287
  logger.error(
298
288
  "Could not auto-detect tool from arguments",
@@ -449,7 +439,6 @@ async def handle_dumpdescriptions(args: argparse.Namespace) -> None:
449
439
  sys.exit(1)
450
440
 
451
441
  project_id = args.dumpdescriptions[0]
452
- branch = args.dumpdescriptions[1] if len(args.dumpdescriptions) > 1 else None
453
442
 
454
443
  db_manager = None
455
444
  try:
@@ -461,16 +450,10 @@ async def handle_dumpdescriptions(args: argparse.Namespace) -> None:
461
450
  token_counter = TokenCounter(args.token_limit)
462
451
 
463
452
  # Get file descriptions
464
- if branch:
465
- file_descriptions = await db_manager.get_all_file_descriptions(
466
- project_id=project_id, branch=branch
467
- )
468
- print(f"File descriptions for project {project_id}, branch {branch}:")
469
- else:
470
- file_descriptions = await db_manager.get_all_file_descriptions(
471
- project_id=project_id
472
- )
473
- print(f"File descriptions for project {project_id} (all branches):")
453
+ file_descriptions = await db_manager.get_all_file_descriptions(
454
+ project_id=project_id
455
+ )
456
+ print(f"File descriptions for project {project_id}:")
474
457
 
475
458
  print("=" * 80)
476
459
 
@@ -481,8 +464,6 @@ async def handle_dumpdescriptions(args: argparse.Namespace) -> None:
481
464
  total_tokens = 0
482
465
  for desc in file_descriptions:
483
466
  print(f"File: {desc.file_path}")
484
- if branch is None:
485
- print(f"Branch: {desc.branch}")
486
467
  print(f"Description: {desc.description}")
487
468
  print("-" * 40)
488
469
 
@@ -714,7 +695,6 @@ async def handle_map(args: argparse.Namespace) -> None:
714
695
  sys.exit(1)
715
696
 
716
697
  project = project_data["project"]
717
- branch = project_data["branch"]
718
698
  overview = project_data["overview"]
719
699
  files = project_data["files"]
720
700
 
@@ -723,7 +703,6 @@ async def handle_map(args: argparse.Namespace) -> None:
723
703
  extra={
724
704
  "structured_data": {
725
705
  "project_name": project.name,
726
- "branch": branch,
727
706
  "file_count": len(files),
728
707
  "has_overview": overview is not None,
729
708
  }
@@ -732,7 +711,7 @@ async def handle_map(args: argparse.Namespace) -> None:
732
711
 
733
712
  # Generate markdown
734
713
  markdown_content = generate_project_markdown(
735
- project, branch, overview, files, logger
714
+ project, overview, files, logger
736
715
  )
737
716
 
738
717
  # Output the markdown
@@ -766,7 +745,7 @@ async def handle_map(args: argparse.Namespace) -> None:
766
745
  logger.removeHandler(handler)
767
746
 
768
747
 
769
- def generate_project_markdown(project, branch, overview, files, logger):
748
+ def generate_project_markdown(project, overview, files, logger):
770
749
  """Generate the markdown content for the project map."""
771
750
  import re
772
751
  from collections import defaultdict
@@ -779,10 +758,6 @@ def generate_project_markdown(project, branch, overview, files, logger):
779
758
  markdown_lines.append(f"# {project_name}")
780
759
  markdown_lines.append("")
781
760
 
782
- # Project metadata
783
- markdown_lines.append(f"**Branch:** {branch}")
784
- markdown_lines.append("")
785
-
786
761
  # Project overview (with header demotion if needed)
787
762
  if overview and overview.overview:
788
763
  markdown_lines.append("## Project Overview")