mcp-code-indexer 2.0.2__py3-none-any.whl → 2.2.0__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.
@@ -0,0 +1,465 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ DeepAsk Handler for MCP Code Indexer
4
+
5
+ Handles enhanced question-answering with two-stage processing:
6
+ 1. Extract search terms and compress overview
7
+ 2. Search file descriptions and provide enhanced answer
8
+ """
9
+
10
+ import logging
11
+ from pathlib import Path
12
+ from typing import Dict, List, Optional, Any, Tuple
13
+
14
+ from .claude_api_handler import ClaudeAPIHandler, ClaudeAPIError
15
+ from .database.database import DatabaseManager
16
+
17
+
18
+ class DeepAskError(ClaudeAPIError):
19
+ """Exception specific to DeepAsk operations."""
20
+ pass
21
+
22
+
23
+ class DeepAskHandler(ClaudeAPIHandler):
24
+ """
25
+ Handler for enhanced Q&A operations using two-stage Claude API processing.
26
+
27
+ Stage 1: Extract search terms and compress project overview
28
+ Stage 2: Search file descriptions and provide enhanced answer with context
29
+ """
30
+
31
+ def __init__(self, db_manager: DatabaseManager, cache_dir: Path, logger: Optional[logging.Logger] = None):
32
+ """
33
+ Initialize DeepAskHandler.
34
+
35
+ Args:
36
+ db_manager: Database manager instance
37
+ cache_dir: Cache directory for temporary files
38
+ logger: Logger instance to use (optional, creates default if not provided)
39
+ """
40
+ super().__init__(db_manager, cache_dir, logger)
41
+ self.logger = logger if logger is not None else logging.getLogger(__name__)
42
+
43
+ async def find_existing_project_by_name(self, project_name: str) -> Optional[Any]:
44
+ """
45
+ Find existing project by name for CLI usage.
46
+
47
+ Args:
48
+ project_name: Name of the project to find
49
+
50
+ Returns:
51
+ Project object if found, None otherwise
52
+ """
53
+ try:
54
+ all_projects = await self.db_manager.get_all_projects()
55
+ normalized_name = project_name.lower()
56
+
57
+ for project in all_projects:
58
+ if project.name.lower() == normalized_name:
59
+ self.logger.info(f"Found existing project: {project.name} (ID: {project.id})")
60
+ return project
61
+
62
+ self.logger.warning(f"No existing project found with name: {project_name}")
63
+ return None
64
+ except Exception as e:
65
+ self.logger.error(f"Error finding project by name: {e}")
66
+ return None
67
+
68
+ async def deepask_question(
69
+ self,
70
+ project_info: Dict[str, str],
71
+ question: str,
72
+ max_file_results: int = 10
73
+ ) -> Dict[str, Any]:
74
+ """
75
+ Ask an enhanced question about the project using two-stage Claude API processing.
76
+
77
+ Args:
78
+ project_info: Project information dict with projectName, folderPath, branch, etc.
79
+ question: User's question about the project
80
+ max_file_results: Maximum number of file descriptions to include
81
+
82
+ Returns:
83
+ Dict containing enhanced response and metadata
84
+ """
85
+ try:
86
+ self.logger.info(f"Processing deepask question for project: {project_info['projectName']}")
87
+ self.logger.info(f"Question: {question}")
88
+
89
+ # Validate inputs
90
+ if not question or not question.strip():
91
+ raise DeepAskError("Question cannot be empty")
92
+
93
+ if not project_info.get("projectName"):
94
+ raise DeepAskError("Project name is required")
95
+
96
+ # Stage 1: Extract search terms and compress overview
97
+ stage1_result = await self._stage1_extract_search_terms(project_info, question)
98
+
99
+ # Stage 2: Search files and provide enhanced answer
100
+ stage2_result = await self._stage2_enhanced_answer(
101
+ project_info,
102
+ question,
103
+ stage1_result["search_terms"],
104
+ stage1_result["compressed_overview"],
105
+ max_file_results
106
+ )
107
+
108
+ # Combine results
109
+ result = {
110
+ "answer": stage2_result["answer"],
111
+ "project_name": project_info["projectName"],
112
+ "question": question,
113
+ "search_terms": stage1_result["search_terms"],
114
+ "compressed_overview": stage1_result["compressed_overview"],
115
+ "relevant_files": stage2_result["relevant_files"],
116
+ "metadata": {
117
+ "model": self.config.model,
118
+ "stage1_tokens": stage1_result["token_usage"],
119
+ "stage2_tokens": stage2_result["token_usage"],
120
+ "total_files_found": stage2_result["total_files_found"],
121
+ "files_included": len(stage2_result["relevant_files"]),
122
+ "branch": project_info.get("branch", "unknown")
123
+ }
124
+ }
125
+
126
+ self.logger.info(f"DeepAsk question completed successfully")
127
+ self.logger.info(f"Search terms: {stage1_result['search_terms']}")
128
+ self.logger.info(f"Files found: {stage2_result['total_files_found']}")
129
+ self.logger.info(f"Files included: {len(stage2_result['relevant_files'])}")
130
+
131
+ return result
132
+
133
+ except Exception as e:
134
+ error_msg = f"Failed to process deepask question: {str(e)}"
135
+ self.logger.error(error_msg)
136
+ if isinstance(e, (ClaudeAPIError, DeepAskError)):
137
+ raise
138
+ else:
139
+ raise DeepAskError(error_msg)
140
+
141
+ async def _stage1_extract_search_terms(
142
+ self,
143
+ project_info: Dict[str, str],
144
+ question: str
145
+ ) -> Dict[str, Any]:
146
+ """
147
+ Stage 1: Extract search terms and compress project overview.
148
+
149
+ Args:
150
+ project_info: Project information
151
+ question: User's question
152
+
153
+ Returns:
154
+ Dict with search_terms, compressed_overview, and token_usage
155
+ """
156
+ self.logger.info("Stage 1: Extracting search terms and compressing overview")
157
+
158
+ # Get project overview
159
+ overview = await self.get_project_overview(project_info)
160
+ if not overview:
161
+ overview = "No project overview available."
162
+
163
+ # Build stage 1 prompt
164
+ prompt = self._build_stage1_prompt(project_info, question, overview)
165
+
166
+ # Validate token limits for stage 1
167
+ if not self.validate_token_limit(prompt):
168
+ raise DeepAskError(
169
+ f"Stage 1 prompt exceeds token limit of {self.config.token_limit}. "
170
+ "Project overview may be too large."
171
+ )
172
+
173
+ # Call Claude API for stage 1
174
+ system_prompt = self._get_stage1_system_prompt()
175
+ response = await self._call_claude_api(prompt, system_prompt)
176
+
177
+ # Parse and validate response
178
+ response_data = self.validate_json_response(
179
+ response.content,
180
+ required_keys=["search_terms", "compressed_overview"]
181
+ )
182
+
183
+ token_usage = {
184
+ "prompt_tokens": self.get_token_count(prompt),
185
+ "response_tokens": response.usage.get("completion_tokens") if response.usage else None,
186
+ "total_tokens": response.usage.get("total_tokens") if response.usage else None
187
+ }
188
+
189
+ return {
190
+ "search_terms": response_data["search_terms"],
191
+ "compressed_overview": response_data["compressed_overview"],
192
+ "token_usage": token_usage
193
+ }
194
+
195
+ async def _stage2_enhanced_answer(
196
+ self,
197
+ project_info: Dict[str, str],
198
+ question: str,
199
+ search_terms: List[str],
200
+ compressed_overview: str,
201
+ max_file_results: int
202
+ ) -> Dict[str, Any]:
203
+ """
204
+ Stage 2: Search file descriptions and provide enhanced answer.
205
+
206
+ Args:
207
+ project_info: Project information
208
+ question: User's question
209
+ search_terms: Search terms from stage 1
210
+ compressed_overview: Compressed overview from stage 1
211
+ max_file_results: Maximum number of files to include
212
+
213
+ Returns:
214
+ Dict with answer, relevant_files, total_files_found, and token_usage
215
+ """
216
+ self.logger.info(f"Stage 2: Searching files and generating enhanced answer")
217
+ self.logger.info(f"Search terms: {search_terms}")
218
+
219
+ # Search for relevant files
220
+ relevant_files = []
221
+ total_files_found = 0
222
+
223
+ try:
224
+ # Find existing project by name only (don't create new ones for Q&A)
225
+ project = await self.find_existing_project_by_name(project_info["projectName"])
226
+
227
+ if not project:
228
+ self.logger.warning(f"Project '{project_info['projectName']}' not found in database")
229
+ return {
230
+ "answer": f"Project '{project_info['projectName']}' not found in database. Please check the project name.",
231
+ "relevant_files": [],
232
+ "total_files_found": 0,
233
+ "token_usage": {"prompt_tokens": 0, "response_tokens": 0, "total_tokens": 0}
234
+ }
235
+
236
+ for search_term in search_terms:
237
+ try:
238
+ search_results = await self.db_manager.search_file_descriptions(
239
+ project_id=project.id,
240
+ branch=project_info["branch"],
241
+ query=search_term,
242
+ max_results=max_file_results
243
+ )
244
+
245
+ total_files_found += len(search_results)
246
+
247
+ # Add unique files to relevant_files
248
+ for result in search_results:
249
+ if not any(f["filePath"] == result.file_path for f in relevant_files):
250
+ relevant_files.append({
251
+ "filePath": result.file_path,
252
+ "description": result.description,
253
+ "search_term": search_term,
254
+ "relevance_score": result.relevance_score
255
+ })
256
+
257
+ # Stop if we have enough files
258
+ if len(relevant_files) >= max_file_results:
259
+ break
260
+
261
+ if len(relevant_files) >= max_file_results:
262
+ break
263
+
264
+ except Exception as e:
265
+ self.logger.warning(f"Search failed for term '{search_term}': {e}")
266
+ continue
267
+
268
+ except Exception as e:
269
+ self.logger.warning(f"Failed to search files: {e}")
270
+ # Continue with empty relevant_files list
271
+
272
+ # Build stage 2 prompt with file context
273
+ prompt = self._build_stage2_prompt(
274
+ project_info,
275
+ question,
276
+ compressed_overview,
277
+ relevant_files
278
+ )
279
+
280
+ # Validate token limits for stage 2
281
+ if not self.validate_token_limit(prompt):
282
+ # Try reducing file context
283
+ self.logger.warning("Stage 2 prompt exceeds token limit, reducing file context")
284
+ reduced_files = relevant_files[:max_file_results//2]
285
+ prompt = self._build_stage2_prompt(
286
+ project_info,
287
+ question,
288
+ compressed_overview,
289
+ reduced_files
290
+ )
291
+
292
+ if not self.validate_token_limit(prompt):
293
+ raise DeepAskError(
294
+ f"Stage 2 prompt still exceeds token limit even with reduced context. "
295
+ "Try a more specific question."
296
+ )
297
+
298
+ relevant_files = reduced_files
299
+
300
+ # Call Claude API for stage 2
301
+ system_prompt = self._get_stage2_system_prompt()
302
+ response = await self._call_claude_api(prompt, system_prompt)
303
+
304
+ token_usage = {
305
+ "prompt_tokens": self.get_token_count(prompt),
306
+ "response_tokens": response.usage.get("completion_tokens") if response.usage else None,
307
+ "total_tokens": response.usage.get("total_tokens") if response.usage else None
308
+ }
309
+
310
+ return {
311
+ "answer": response.content,
312
+ "relevant_files": relevant_files,
313
+ "total_files_found": total_files_found,
314
+ "token_usage": token_usage
315
+ }
316
+
317
+ def _build_stage1_prompt(
318
+ self,
319
+ project_info: Dict[str, str],
320
+ question: str,
321
+ overview: str
322
+ ) -> str:
323
+ """Build stage 1 prompt for extracting search terms."""
324
+ project_name = project_info["projectName"]
325
+ branch = project_info.get("branch", "unknown")
326
+
327
+ return f"""I need to answer a question about the codebase "{project_name}" (branch: {branch}). To provide the best answer, I need to search for relevant files and then answer the question.
328
+
329
+ PROJECT OVERVIEW:
330
+ {overview}
331
+
332
+ QUESTION:
333
+ {question}
334
+
335
+ Please analyze the question and project overview, then provide:
336
+
337
+ 1. A list of 3-5 search terms that would help find relevant files to answer this question
338
+ 2. A compressed version of the project overview (2-3 sentences max) that captures the most relevant information for this question
339
+
340
+ Respond with valid JSON in this format:
341
+ {{
342
+ "search_terms": ["term1", "term2", "term3"],
343
+ "compressed_overview": "Brief summary focusing on aspects relevant to the question..."
344
+ }}"""
345
+
346
+ def _build_stage2_prompt(
347
+ self,
348
+ project_info: Dict[str, str],
349
+ question: str,
350
+ compressed_overview: str,
351
+ relevant_files: List[Dict[str, Any]]
352
+ ) -> str:
353
+ """Build stage 2 prompt for enhanced answer."""
354
+ project_name = project_info["projectName"]
355
+ branch = project_info.get("branch", "unknown")
356
+
357
+ # Format file descriptions
358
+ file_context = ""
359
+ if relevant_files:
360
+ file_context = "\n\nRELEVANT FILES:\n"
361
+ for i, file_info in enumerate(relevant_files, 1):
362
+ file_context += f"\n{i}. {file_info['filePath']}\n"
363
+ file_context += f" Description: {file_info['description']}\n"
364
+ file_context += f" Found via search: {file_info['search_term']}\n"
365
+ else:
366
+ file_context = "\n\nNo relevant files found in the search."
367
+
368
+ return f"""Please answer the following question about the codebase "{project_name}" (branch: {branch}).
369
+
370
+ PROJECT OVERVIEW (COMPRESSED):
371
+ {compressed_overview}
372
+ {file_context}
373
+
374
+ QUESTION:
375
+ {question}
376
+
377
+ Please provide a comprehensive answer based on the project overview and relevant file descriptions above. Reference specific files when appropriate and explain how they relate to the question. If the available information is insufficient, clearly state what additional details would be needed."""
378
+
379
+ def _get_stage1_system_prompt(self) -> str:
380
+ """Get system prompt for stage 1."""
381
+ return """You are a technical assistant that analyzes software projects to extract relevant search terms and compress information.
382
+
383
+ Your task:
384
+ 1. Analyze the user's question about a codebase
385
+ 2. Extract 3-5 search terms that would help find relevant files to answer the question
386
+ 3. Compress the project overview to focus on information relevant to the question
387
+
388
+ Search terms should be:
389
+ - Technical keywords (function names, class names, concepts)
390
+ - File types or directory names if relevant
391
+ - Domain-specific terminology from the question
392
+
393
+ The compressed overview should:
394
+ - Be 2-3 sentences maximum
395
+ - Focus only on aspects relevant to answering the question
396
+ - Preserve the most important architectural or functional details
397
+
398
+ Always respond with valid JSON matching the requested format."""
399
+
400
+ def _get_stage2_system_prompt(self) -> str:
401
+ """Get system prompt for stage 2."""
402
+ return """You are a software engineering expert that provides detailed answers about codebases using available context.
403
+
404
+ When answering:
405
+ 1. Use the compressed project overview for high-level context
406
+ 2. Reference specific files from the relevant files list when they relate to the question
407
+ 3. Explain how different files work together if relevant
408
+ 4. Be specific and technical when appropriate
409
+ 5. If information is incomplete, clearly state what's missing and suggest next steps
410
+ 6. Provide actionable insights when possible
411
+
412
+ Your answer should be comprehensive but focused on the specific question asked."""
413
+
414
+ def format_response(self, result: Dict[str, Any], format_type: str = "text") -> str:
415
+ """
416
+ Format response for CLI output.
417
+
418
+ Args:
419
+ result: Result from deepask_question
420
+ format_type: Output format ("text" or "json")
421
+
422
+ Returns:
423
+ Formatted response string
424
+ """
425
+ if format_type == "json":
426
+ import json
427
+ return json.dumps(result, indent=2)
428
+
429
+ # Text format
430
+ answer = result["answer"]
431
+ metadata = result["metadata"]
432
+
433
+ output = []
434
+ output.append(f"Question: {result['question']}")
435
+ output.append(f"Project: {result['project_name']} (branch: {metadata['branch']})")
436
+ output.append("")
437
+ output.append("Answer:")
438
+ output.append(answer)
439
+ output.append("")
440
+
441
+ # Show search terms used
442
+ output.append(f"Search terms: {', '.join(result['search_terms'])}")
443
+ output.append("")
444
+
445
+ # Show relevant files
446
+ if result["relevant_files"]:
447
+ output.append("Relevant files analyzed:")
448
+ for i, file_info in enumerate(result["relevant_files"], 1):
449
+ output.append(f" {i}. {file_info['filePath']}")
450
+ else:
451
+ output.append("No relevant files found.")
452
+ output.append("")
453
+
454
+ # Show metadata
455
+ output.append("Metadata:")
456
+ output.append(f" Model: {metadata['model']}")
457
+ output.append(f" Total files found: {metadata['total_files_found']}")
458
+ output.append(f" Files included: {metadata['files_included']}")
459
+
460
+ stage1_tokens = metadata['stage1_tokens']['total_tokens']
461
+ stage2_tokens = metadata['stage2_tokens']['total_tokens']
462
+ if stage1_tokens and stage2_tokens:
463
+ output.append(f" Total tokens: {stage1_tokens + stage2_tokens} (Stage 1: {stage1_tokens}, Stage 2: {stage2_tokens})")
464
+
465
+ return "\n".join(output)
@@ -54,7 +54,10 @@ class MCPCodeIndexServer:
54
54
  db_retry_count: int = 5,
55
55
  db_timeout: float = 10.0,
56
56
  enable_wal_mode: bool = True,
57
- health_check_interval: float = 30.0
57
+ health_check_interval: float = 30.0,
58
+ retry_min_wait: float = 0.1,
59
+ retry_max_wait: float = 2.0,
60
+ retry_jitter: float = 0.2
58
61
  ):
59
62
  """
60
63
  Initialize the MCP Code Index Server.
@@ -68,6 +71,9 @@ class MCPCodeIndexServer:
68
71
  db_timeout: Database transaction timeout in seconds
69
72
  enable_wal_mode: Enable WAL mode for better concurrent access
70
73
  health_check_interval: Database health check interval in seconds
74
+ retry_min_wait: Minimum wait time between retries in seconds
75
+ retry_max_wait: Maximum wait time between retries in seconds
76
+ retry_jitter: Maximum jitter to add to retry delays in seconds
71
77
  """
72
78
  self.token_limit = token_limit
73
79
  self.db_path = db_path or Path.home() / ".mcp-code-index" / "tracker.db"
@@ -79,7 +85,10 @@ class MCPCodeIndexServer:
79
85
  "retry_count": db_retry_count,
80
86
  "timeout": db_timeout,
81
87
  "enable_wal_mode": enable_wal_mode,
82
- "health_check_interval": health_check_interval
88
+ "health_check_interval": health_check_interval,
89
+ "retry_min_wait": retry_min_wait,
90
+ "retry_max_wait": retry_max_wait,
91
+ "retry_jitter": retry_jitter
83
92
  }
84
93
 
85
94
  # Initialize components
@@ -89,7 +98,10 @@ class MCPCodeIndexServer:
89
98
  retry_count=db_retry_count,
90
99
  timeout=db_timeout,
91
100
  enable_wal_mode=enable_wal_mode,
92
- health_check_interval=health_check_interval
101
+ health_check_interval=health_check_interval,
102
+ retry_min_wait=retry_min_wait,
103
+ retry_max_wait=retry_max_wait,
104
+ retry_jitter=retry_jitter
93
105
  )
94
106
  self.token_counter = TokenCounter(token_limit)
95
107
  self.merge_handler = MergeHandler(self.db_manager)
@@ -312,7 +324,7 @@ class MCPCodeIndexServer:
312
324
  ),
313
325
  types.Tool(
314
326
  name="search_descriptions",
315
- description="Searches through all file descriptions in a project to find files related to specific functionality. Use this for large codebases instead of loading the entire structure.",
327
+ description="Searches through all file descriptions in a project to find files related to specific functionality. Use this for large codebases instead of loading the entire structure. Always start with the fewest terms possible; if the tool returns a lot of results (more than 20) or the results are not relevant, then narrow it down by increasing the number of search terms. Start broad, then narrow the focus only if needed!",
316
328
  inputSchema={
317
329
  "type": "object",
318
330
  "properties": {
@@ -1194,21 +1206,76 @@ src/
1194
1206
  }
1195
1207
 
1196
1208
  async def _handle_check_database_health(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
1197
- """Handle check_database_health tool calls."""
1198
- # Get comprehensive database health and statistics
1199
- health_check = await self.db_manager.check_health()
1209
+ """
1210
+ Handle check_database_health tool calls with comprehensive diagnostics.
1211
+
1212
+ Returns detailed database health information including retry statistics,
1213
+ performance analysis, and resilience indicators.
1214
+ """
1215
+ # Get comprehensive health diagnostics from the enhanced monitor
1216
+ if hasattr(self.db_manager, '_health_monitor') and self.db_manager._health_monitor:
1217
+ comprehensive_diagnostics = self.db_manager._health_monitor.get_comprehensive_diagnostics()
1218
+ else:
1219
+ # Fallback to basic health check if monitor not available
1220
+ health_check = await self.db_manager.check_health()
1221
+ comprehensive_diagnostics = {
1222
+ "basic_health_check": health_check,
1223
+ "note": "Enhanced health monitoring not available"
1224
+ }
1225
+
1226
+ # Get additional database-level statistics
1200
1227
  database_stats = self.db_manager.get_database_stats()
1201
1228
 
1202
1229
  return {
1203
- "health_check": health_check,
1204
- "database_stats": database_stats,
1205
- "configuration": self.db_config,
1230
+ "comprehensive_diagnostics": comprehensive_diagnostics,
1231
+ "database_statistics": database_stats,
1232
+ "configuration": {
1233
+ **self.db_config,
1234
+ "retry_executor_config": (
1235
+ self.db_manager._retry_executor.config.__dict__
1236
+ if hasattr(self.db_manager, '_retry_executor') and self.db_manager._retry_executor
1237
+ else {}
1238
+ )
1239
+ },
1206
1240
  "server_info": {
1207
1241
  "token_limit": self.token_limit,
1208
1242
  "db_path": str(self.db_path),
1209
- "cache_dir": str(self.cache_dir)
1243
+ "cache_dir": str(self.cache_dir),
1244
+ "health_monitoring_enabled": (
1245
+ hasattr(self.db_manager, '_health_monitor') and
1246
+ self.db_manager._health_monitor is not None
1247
+ )
1210
1248
  },
1211
- "timestamp": datetime.utcnow().isoformat()
1249
+ "timestamp": datetime.utcnow().isoformat(),
1250
+ "status_summary": self._generate_health_summary(comprehensive_diagnostics)
1251
+ }
1252
+
1253
+ def _generate_health_summary(self, diagnostics: Dict[str, Any]) -> Dict[str, Any]:
1254
+ """Generate a concise health summary from comprehensive diagnostics."""
1255
+ if "resilience_indicators" not in diagnostics:
1256
+ return {"status": "limited_diagnostics_available"}
1257
+
1258
+ resilience = diagnostics["resilience_indicators"]
1259
+ performance = diagnostics.get("performance_analysis", {})
1260
+
1261
+ # Overall status based on health score
1262
+ health_score = resilience.get("overall_health_score", 0)
1263
+ if health_score >= 90:
1264
+ status = "excellent"
1265
+ elif health_score >= 75:
1266
+ status = "good"
1267
+ elif health_score >= 50:
1268
+ status = "fair"
1269
+ else:
1270
+ status = "poor"
1271
+
1272
+ return {
1273
+ "overall_status": status,
1274
+ "health_score": health_score,
1275
+ "retry_effectiveness": resilience.get("retry_effectiveness", {}).get("is_effective", False),
1276
+ "connection_stability": resilience.get("connection_stability", {}).get("is_stable", False),
1277
+ "key_recommendations": resilience.get("recommendations", [])[:3], # Top 3 recommendations
1278
+ "performance_trend": performance.get("health_check_performance", {}).get("recent_performance_trend", "unknown")
1212
1279
  }
1213
1280
 
1214
1281
  async def _run_session_with_retry(self, read_stream, write_stream, initialization_options) -> None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp-code-indexer
3
- Version: 2.0.2
3
+ Version: 2.2.0
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
@@ -59,8 +59,8 @@ Dynamic: requires-python
59
59
 
60
60
  # MCP Code Indexer 🚀
61
61
 
62
- [![PyPI version](https://badge.fury.io/py/mcp-code-indexer.svg?13)](https://badge.fury.io/py/mcp-code-indexer)
63
- [![Python](https://img.shields.io/pypi/pyversions/mcp-code-indexer.svg?13)](https://pypi.org/project/mcp-code-indexer/)
62
+ [![PyPI version](https://badge.fury.io/py/mcp-code-indexer.svg?15)](https://badge.fury.io/py/mcp-code-indexer)
63
+ [![Python](https://img.shields.io/pypi/pyversions/mcp-code-indexer.svg?15)](https://pypi.org/project/mcp-code-indexer/)
64
64
  [![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
65
65
 
66
66
  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.
@@ -1,5 +1,8 @@
1
1
  mcp_code_indexer/__init__.py,sha256=GhY2NLQ6lH3n5mxqw0t8T1gmZGKhM6KvjhZH8xW5O-A,1686
2
2
  mcp_code_indexer/__main__.py,sha256=4Edinoe0ug43hobuLYcjTmGp2YJnlFYN4_8iKvUBJ0Q,213
3
+ mcp_code_indexer/ask_handler.py,sha256=71hwd3FLMBBxplmNMQMki03BFpeCy0sf4HtdTrVe4ks,8598
4
+ mcp_code_indexer/claude_api_handler.py,sha256=s69aZVQx4SUFXuGvqKVBLYtEsSFHY9_uTCzDT-kpgcE,13660
5
+ mcp_code_indexer/deepask_handler.py,sha256=bNwZFuiSzU8s7gYM_TB-3QyY7jRy38k752MRrj-s7_s,18634
3
6
  mcp_code_indexer/error_handler.py,sha256=cNSUFFrGBMLDv4qa78c7495L1wSl_dXCRbzCJOidx-Q,11590
4
7
  mcp_code_indexer/file_scanner.py,sha256=ctXeZMROgDThEtjzsANTK9TbK-fhTScMBd4iyuleBT4,11734
5
8
  mcp_code_indexer/git_hook_handler.py,sha256=k6QpoLI-5D9EvrLQrHWMII2qNu21daRX_jXlk9U6bGI,36976
@@ -9,19 +12,20 @@ mcp_code_indexer/merge_handler.py,sha256=lJR8eVq2qSrF6MW9mR3Fy8UzrNAaQ7RsI2FMNXn
9
12
  mcp_code_indexer/token_counter.py,sha256=WrifOkbF99nWWHlRlhCHAB2KN7qr83GOHl7apE-hJcE,8460
10
13
  mcp_code_indexer/data/stop_words_english.txt,sha256=7Zdd9ameVgA6tN_zuXROvHXD4hkWeELVywPhb7FJEkw,6343
11
14
  mcp_code_indexer/database/__init__.py,sha256=aPq_aaRp0aSwOBIq9GkuMNjmLxA411zg2vhdrAuHm-w,38
12
- mcp_code_indexer/database/connection_health.py,sha256=XJvUrHRhIroZlIPScVGdKb69lNP67lT9ZTTO67cFSEs,16721
13
- mcp_code_indexer/database/database.py,sha256=5G_1E-jSVMDJuyFCVki9tbNmoyNvHJa3x7dRIBB2UQE,49603
15
+ mcp_code_indexer/database/connection_health.py,sha256=s2r9L_KipH5NlemAUDnhBQO90Dn4b_0Ht9UDs7F6QPk,24432
16
+ mcp_code_indexer/database/database.py,sha256=86XL1b49cTeTzkJ1mVbkYPq_QyQrVQOy8w_b1MxZR-E,50856
17
+ mcp_code_indexer/database/exceptions.py,sha256=AgpRA9Z5R-GoWYdQSPeSdYvAXDopFCQkLGN3jD7Ha4E,10215
14
18
  mcp_code_indexer/database/models.py,sha256=_vCmJnPXZSiInRzyvs4c7QUWuNNW8qsOoDlGX8J-Gnk,7124
15
- mcp_code_indexer/database/retry_handler.py,sha256=zwwZ0V1PzzS1rtcfVQOI-CXqWnPGF-KGH4L_3d-_h1Y,11932
19
+ mcp_code_indexer/database/retry_executor.py,sha256=QUayjkCk8OsckVMYiJ_HBQ9NTUss-H8GQeUIUbbw4_U,13419
16
20
  mcp_code_indexer/middleware/__init__.py,sha256=p-mP0pMsfiU2yajCPvokCUxUEkh_lu4XJP1LyyMW2ug,220
17
21
  mcp_code_indexer/middleware/error_middleware.py,sha256=5agJTAkkPogfPGnja1V9JtG9RG-BiOALIJYctK3byJQ,11730
18
22
  mcp_code_indexer/server/__init__.py,sha256=16xMcuriUOBlawRqWNBk6niwrvtv_JD5xvI36X1Vsmk,41
19
- mcp_code_indexer/server/mcp_server.py,sha256=tQTZFnAnASStkbnQOsj2-3eH1DYSmY97q1pwo7geFXI,66934
23
+ mcp_code_indexer/server/mcp_server.py,sha256=KJAGkhYIR3MVJZECKWL9rpMP3Yb8uO9k7gj5dQ3Wpbc,70436
20
24
  mcp_code_indexer/tiktoken_cache/9b5ad71b2ce5302211f9c61530b329a4922fc6a4,sha256=Ijkht27pm96ZW3_3OFE-7xAPtR0YyTWXoRO8_-hlsqc,1681126
21
25
  mcp_code_indexer/tools/__init__.py,sha256=m01mxML2UdD7y5rih_XNhNSCMzQTz7WQ_T1TeOcYlnE,49
22
- mcp_code_indexer-2.0.2.dist-info/licenses/LICENSE,sha256=JN9dyPPgYwH9C-UjYM7FLNZjQ6BF7kAzpF3_4PwY4rY,1086
23
- mcp_code_indexer-2.0.2.dist-info/METADATA,sha256=BsEhIKscok46a5pfTyigsaMIHjMHZgmg-7-O_OyTrZY,20165
24
- mcp_code_indexer-2.0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
25
- mcp_code_indexer-2.0.2.dist-info/entry_points.txt,sha256=8HqWOw1Is7jOP1bvIgaSwouvT9z_Boe-9hd4NzyJOhY,68
26
- mcp_code_indexer-2.0.2.dist-info/top_level.txt,sha256=yKYCM-gMGt-cnupGfAhnZaoEsROLB6DQ1KFUuyKx4rw,17
27
- mcp_code_indexer-2.0.2.dist-info/RECORD,,
26
+ mcp_code_indexer-2.2.0.dist-info/licenses/LICENSE,sha256=JN9dyPPgYwH9C-UjYM7FLNZjQ6BF7kAzpF3_4PwY4rY,1086
27
+ mcp_code_indexer-2.2.0.dist-info/METADATA,sha256=r40SpRrTsPmIGQu2z-2I0tEBzXQUM5U3qqAXnWBPbbE,20165
28
+ mcp_code_indexer-2.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
29
+ mcp_code_indexer-2.2.0.dist-info/entry_points.txt,sha256=8HqWOw1Is7jOP1bvIgaSwouvT9z_Boe-9hd4NzyJOhY,68
30
+ mcp_code_indexer-2.2.0.dist-info/top_level.txt,sha256=yKYCM-gMGt-cnupGfAhnZaoEsROLB6DQ1KFUuyKx4rw,17
31
+ mcp_code_indexer-2.2.0.dist-info/RECORD,,