mcp-code-indexer 2.4.0__py3-none-any.whl → 3.0.2__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/ask_handler.py +5 -7
- mcp_code_indexer/claude_api_handler.py +2 -2
- mcp_code_indexer/cleanup_manager.py +255 -0
- mcp_code_indexer/database/database.py +125 -98
- mcp_code_indexer/database/models.py +3 -5
- mcp_code_indexer/deepask_handler.py +5 -9
- mcp_code_indexer/error_handler.py +3 -1
- mcp_code_indexer/git_hook_handler.py +2 -9
- mcp_code_indexer/migrations/001_initial.sql +100 -0
- mcp_code_indexer/migrations/002_performance_indexes.sql +61 -0
- mcp_code_indexer/migrations/003_project_overviews.sql +20 -0
- mcp_code_indexer/migrations/004_remove_branch_dependency.sql +166 -0
- mcp_code_indexer/server/mcp_server.py +33 -211
- {mcp_code_indexer-2.4.0.dist-info → mcp_code_indexer-3.0.2.dist-info}/METADATA +3 -3
- {mcp_code_indexer-2.4.0.dist-info → mcp_code_indexer-3.0.2.dist-info}/RECORD +19 -14
- {mcp_code_indexer-2.4.0.dist-info → mcp_code_indexer-3.0.2.dist-info}/WHEEL +0 -0
- {mcp_code_indexer-2.4.0.dist-info → mcp_code_indexer-3.0.2.dist-info}/entry_points.txt +0 -0
- {mcp_code_indexer-2.4.0.dist-info → mcp_code_indexer-3.0.2.dist-info}/licenses/LICENSE +0 -0
- {mcp_code_indexer-2.4.0.dist-info → mcp_code_indexer-3.0.2.dist-info}/top_level.txt +0 -0
@@ -32,7 +32,7 @@ from mcp_code_indexer.database.models import (
|
|
32
32
|
from mcp_code_indexer.error_handler import setup_error_handling, ErrorHandler
|
33
33
|
from mcp_code_indexer.middleware.error_middleware import create_tool_middleware, AsyncTaskManager
|
34
34
|
from mcp_code_indexer.logging_config import get_logger
|
35
|
-
|
35
|
+
|
36
36
|
|
37
37
|
logger = logging.getLogger(__name__)
|
38
38
|
|
@@ -104,7 +104,6 @@ class MCPCodeIndexServer:
|
|
104
104
|
retry_jitter=retry_jitter
|
105
105
|
)
|
106
106
|
self.token_counter = TokenCounter(token_limit)
|
107
|
-
self.merge_handler = MergeHandler(self.db_manager)
|
108
107
|
|
109
108
|
# Setup error handling
|
110
109
|
self.logger = get_logger(__name__)
|
@@ -248,10 +247,7 @@ class MCPCodeIndexServer:
|
|
248
247
|
"type": "string",
|
249
248
|
"description": "Absolute path to the project folder on disk"
|
250
249
|
},
|
251
|
-
|
252
|
-
"type": "string",
|
253
|
-
"description": "Git branch name (e.g., 'main', 'develop')"
|
254
|
-
},
|
250
|
+
|
255
251
|
"remoteOrigin": {
|
256
252
|
"type": "string",
|
257
253
|
"description": "Git remote origin URL if available"
|
@@ -265,7 +261,7 @@ class MCPCodeIndexServer:
|
|
265
261
|
"description": "Relative path to the file from project root"
|
266
262
|
}
|
267
263
|
},
|
268
|
-
"required": ["projectName", "folderPath", "
|
264
|
+
"required": ["projectName", "folderPath", "filePath"],
|
269
265
|
"additionalProperties": False
|
270
266
|
}
|
271
267
|
),
|
@@ -277,31 +273,29 @@ class MCPCodeIndexServer:
|
|
277
273
|
"properties": {
|
278
274
|
"projectName": {"type": "string", "description": "The name of the project"},
|
279
275
|
"folderPath": {"type": "string", "description": "Absolute path to the project folder on disk"},
|
280
|
-
"branch": {"type": "string", "description": "Git branch name"},
|
281
276
|
"remoteOrigin": {"type": "string", "description": "Git remote origin URL if available"},
|
282
277
|
"upstreamOrigin": {"type": "string", "description": "Upstream repository URL if this is a fork"},
|
283
278
|
"filePath": {"type": "string", "description": "Relative path to the file from project root"},
|
284
279
|
"description": {"type": "string", "description": "Detailed description of the file's contents"},
|
285
280
|
"fileHash": {"type": "string", "description": "SHA-256 hash of the file contents (optional)"}
|
286
281
|
},
|
287
|
-
"required": ["projectName", "folderPath", "
|
282
|
+
"required": ["projectName", "folderPath", "filePath", "description"],
|
288
283
|
"additionalProperties": False
|
289
284
|
}
|
290
285
|
),
|
291
286
|
types.Tool(
|
292
287
|
name="check_codebase_size",
|
293
|
-
description="Checks the total token count of a codebase's file structure and descriptions
|
288
|
+
description="Checks the total token count of a codebase's file structure and descriptions. Returns whether the codebase is 'large' and recommends using search instead of the full overview.",
|
294
289
|
inputSchema={
|
295
290
|
"type": "object",
|
296
291
|
"properties": {
|
297
292
|
"projectName": {"type": "string", "description": "The name of the project"},
|
298
293
|
"folderPath": {"type": "string", "description": "Absolute path to the project folder on disk"},
|
299
|
-
"branch": {"type": "string", "description": "Git branch name"},
|
300
294
|
"remoteOrigin": {"type": "string", "description": "Git remote origin URL if available"},
|
301
295
|
"upstreamOrigin": {"type": "string", "description": "Upstream repository URL if this is a fork"},
|
302
296
|
"tokenLimit": {"type": "integer", "description": "Optional token limit override (defaults to server configuration)"}
|
303
297
|
},
|
304
|
-
"required": ["projectName", "folderPath"
|
298
|
+
"required": ["projectName", "folderPath"],
|
305
299
|
"additionalProperties": False
|
306
300
|
}
|
307
301
|
),
|
@@ -313,12 +307,11 @@ class MCPCodeIndexServer:
|
|
313
307
|
"properties": {
|
314
308
|
"projectName": {"type": "string", "description": "The name of the project"},
|
315
309
|
"folderPath": {"type": "string", "description": "Absolute path to the project folder on disk"},
|
316
|
-
"branch": {"type": "string", "description": "Git branch name"},
|
317
310
|
"remoteOrigin": {"type": "string", "description": "Git remote origin URL if available"},
|
318
311
|
"upstreamOrigin": {"type": "string", "description": "Upstream repository URL if this is a fork"},
|
319
312
|
"limit": {"type": "integer", "description": "Maximum number of missing files to return (optional)"}
|
320
313
|
},
|
321
|
-
"required": ["projectName", "folderPath"
|
314
|
+
"required": ["projectName", "folderPath"],
|
322
315
|
"additionalProperties": False
|
323
316
|
}
|
324
317
|
),
|
@@ -330,13 +323,12 @@ class MCPCodeIndexServer:
|
|
330
323
|
"properties": {
|
331
324
|
"projectName": {"type": "string", "description": "The name of the project"},
|
332
325
|
"folderPath": {"type": "string", "description": "Absolute path to the project folder on disk"},
|
333
|
-
"branch": {"type": "string", "description": "Git branch to search in"},
|
334
326
|
"remoteOrigin": {"type": "string", "description": "Git remote origin URL if available"},
|
335
327
|
"upstreamOrigin": {"type": "string", "description": "Upstream repository URL if this is a fork"},
|
336
328
|
"query": {"type": "string", "description": "Search query (e.g., 'authentication middleware', 'database models')"},
|
337
329
|
"maxResults": {"type": "integer", "default": 20, "description": "Maximum number of results to return"}
|
338
330
|
},
|
339
|
-
"required": ["projectName", "folderPath", "
|
331
|
+
"required": ["projectName", "folderPath", "query"],
|
340
332
|
"additionalProperties": False
|
341
333
|
}
|
342
334
|
),
|
@@ -348,57 +340,25 @@ class MCPCodeIndexServer:
|
|
348
340
|
"properties": {
|
349
341
|
"projectName": {"type": "string", "description": "The name of the project"},
|
350
342
|
"folderPath": {"type": "string", "description": "Absolute path to the project folder on disk"},
|
351
|
-
"branch": {"type": "string", "description": "Git branch name"},
|
352
343
|
"remoteOrigin": {"type": "string", "description": "Git remote origin URL if available"},
|
353
344
|
"upstreamOrigin": {"type": "string", "description": "Upstream repository URL if this is a fork"}
|
354
345
|
},
|
355
|
-
"required": ["projectName", "folderPath"
|
356
|
-
"additionalProperties": False
|
357
|
-
}
|
358
|
-
),
|
359
|
-
types.Tool(
|
360
|
-
name="merge_branch_descriptions",
|
361
|
-
description="Merges file descriptions from one branch to another. This is a two-stage process: first call without resolutions returns conflicts where the same file has different descriptions in each branch. Second call with resolutions completes the merge.",
|
362
|
-
inputSchema={
|
363
|
-
"type": "object",
|
364
|
-
"properties": {
|
365
|
-
"projectName": {"type": "string", "description": "The name of the project"},
|
366
|
-
"folderPath": {"type": "string", "description": "Absolute path to the project folder"},
|
367
|
-
"remoteOrigin": {"type": "string", "description": "Git remote origin URL"},
|
368
|
-
"upstreamOrigin": {"type": "string", "description": "Upstream repository URL if this is a fork"},
|
369
|
-
"sourceBranch": {"type": "string", "description": "Branch to merge from (e.g., 'feature/new-ui')"},
|
370
|
-
"targetBranch": {"type": "string", "description": "Branch to merge into (e.g., 'main')"},
|
371
|
-
"conflictResolutions": {
|
372
|
-
"type": ["array", "null"],
|
373
|
-
"description": "Array of resolved conflicts (only for second stage)",
|
374
|
-
"items": {
|
375
|
-
"type": "object",
|
376
|
-
"properties": {
|
377
|
-
"conflictId": {"type": "string", "description": "ID of the conflict to resolve"},
|
378
|
-
"resolvedDescription": {"type": "string", "description": "Final description to use after merge"}
|
379
|
-
},
|
380
|
-
"required": ["conflictId", "resolvedDescription"],
|
381
|
-
"additionalProperties": False
|
382
|
-
}
|
383
|
-
}
|
384
|
-
},
|
385
|
-
"required": ["projectName", "folderPath", "sourceBranch", "targetBranch"],
|
346
|
+
"required": ["projectName", "folderPath"],
|
386
347
|
"additionalProperties": False
|
387
348
|
}
|
388
349
|
),
|
389
350
|
types.Tool(
|
390
351
|
name="get_codebase_overview",
|
391
|
-
description="Returns a condensed, interpretive overview of the entire codebase
|
352
|
+
description="Returns a condensed, interpretive overview of the entire codebase. This is a single comprehensive narrative that captures the architecture, key components, relationships, and design patterns. Unlike get_all_descriptions which lists every file, this provides a holistic view suitable for understanding the codebase's structure and purpose. If no overview exists, returns empty string.",
|
392
353
|
inputSchema={
|
393
354
|
"type": "object",
|
394
355
|
"properties": {
|
395
356
|
"projectName": {"type": "string", "description": "The name of the project"},
|
396
357
|
"folderPath": {"type": "string", "description": "Absolute path to the project folder on disk"},
|
397
|
-
"branch": {"type": "string", "description": "Git branch name"},
|
398
358
|
"remoteOrigin": {"type": "string", "description": "Git remote origin URL if available"},
|
399
359
|
"upstreamOrigin": {"type": "string", "description": "Upstream repository URL if this is a fork"}
|
400
360
|
},
|
401
|
-
"required": ["projectName", "folderPath"
|
361
|
+
"required": ["projectName", "folderPath"],
|
402
362
|
"additionalProperties": False
|
403
363
|
}
|
404
364
|
),
|
@@ -444,29 +404,27 @@ src/
|
|
444
404
|
"properties": {
|
445
405
|
"projectName": {"type": "string", "description": "The name of the project"},
|
446
406
|
"folderPath": {"type": "string", "description": "Absolute path to the project folder on disk"},
|
447
|
-
"branch": {"type": "string", "description": "Git branch name"},
|
448
407
|
"remoteOrigin": {"type": "string", "description": "Git remote origin URL if available"},
|
449
408
|
"upstreamOrigin": {"type": "string", "description": "Upstream repository URL if this is a fork"},
|
450
409
|
"overview": {"type": "string", "description": "Comprehensive narrative overview of the codebase (10-30k tokens recommended)"}
|
451
410
|
},
|
452
|
-
"required": ["projectName", "folderPath", "
|
411
|
+
"required": ["projectName", "folderPath", "overview"],
|
453
412
|
"additionalProperties": False
|
454
413
|
}
|
455
414
|
),
|
456
415
|
types.Tool(
|
457
416
|
name="get_word_frequency",
|
458
|
-
description="Analyzes all file descriptions to find the most frequently used technical terms
|
417
|
+
description="Analyzes all file descriptions to find the most frequently used technical terms. Filters out common English stop words and symbols, returning the top 200 meaningful terms. Useful for understanding the codebase's domain vocabulary and finding all functions/files related to specific concepts.",
|
459
418
|
inputSchema={
|
460
419
|
"type": "object",
|
461
420
|
"properties": {
|
462
421
|
"projectName": {"type": "string", "description": "The name of the project"},
|
463
422
|
"folderPath": {"type": "string", "description": "Absolute path to the project folder on disk"},
|
464
|
-
"branch": {"type": "string", "description": "Git branch name"},
|
465
423
|
"remoteOrigin": {"type": "string", "description": "Git remote origin URL if available"},
|
466
424
|
"upstreamOrigin": {"type": "string", "description": "Upstream repository URL if this is a fork"},
|
467
425
|
"limit": {"type": "integer", "default": 200, "description": "Number of top terms to return"}
|
468
426
|
},
|
469
|
-
"required": ["projectName", "folderPath"
|
427
|
+
"required": ["projectName", "folderPath"],
|
470
428
|
"additionalProperties": False
|
471
429
|
}
|
472
430
|
),
|
@@ -487,12 +445,11 @@ src/
|
|
487
445
|
"properties": {
|
488
446
|
"projectName": {"type": "string", "description": "The name of the project"},
|
489
447
|
"folderPath": {"type": "string", "description": "Absolute path to the project folder on disk"},
|
490
|
-
"branch": {"type": "string", "description": "Git branch name"},
|
491
448
|
"remoteOrigin": {"type": "string", "description": "Git remote origin URL if available"},
|
492
449
|
"upstreamOrigin": {"type": "string", "description": "Upstream repository URL if this is a fork"},
|
493
450
|
"searchWord": {"type": "string", "description": "Single word to search for in the overview"}
|
494
451
|
},
|
495
|
-
"required": ["projectName", "folderPath", "
|
452
|
+
"required": ["projectName", "folderPath", "searchWord"],
|
496
453
|
"additionalProperties": False
|
497
454
|
}
|
498
455
|
)
|
@@ -518,7 +475,7 @@ src/
|
|
518
475
|
"get_codebase_overview": self._handle_get_condensed_overview,
|
519
476
|
"update_codebase_overview": self._handle_update_codebase_overview,
|
520
477
|
"get_word_frequency": self._handle_get_word_frequency,
|
521
|
-
|
478
|
+
|
522
479
|
"check_database_health": self._handle_check_database_health,
|
523
480
|
"search_codebase_overview": self._handle_search_codebase_overview,
|
524
481
|
}
|
@@ -574,7 +531,7 @@ src/
|
|
574
531
|
remote_origin = arguments.get("remoteOrigin")
|
575
532
|
upstream_origin = arguments.get("upstreamOrigin")
|
576
533
|
folder_path = arguments["folderPath"]
|
577
|
-
|
534
|
+
|
578
535
|
|
579
536
|
# Normalize project name for case-insensitive matching
|
580
537
|
normalized_name = project_name.lower()
|
@@ -590,7 +547,7 @@ src/
|
|
590
547
|
# Check if upstream inheritance is needed
|
591
548
|
if upstream_origin and await self.db_manager.check_upstream_inheritance_needed(project):
|
592
549
|
try:
|
593
|
-
inherited_count = await self.db_manager.inherit_from_upstream(project
|
550
|
+
inherited_count = await self.db_manager.inherit_from_upstream(project)
|
594
551
|
if inherited_count > 0:
|
595
552
|
logger.info(f"Auto-inherited {inherited_count} descriptions from upstream for {normalized_name}")
|
596
553
|
except Exception as e:
|
@@ -613,7 +570,7 @@ src/
|
|
613
570
|
# Auto-inherit from upstream if needed
|
614
571
|
if upstream_origin:
|
615
572
|
try:
|
616
|
-
inherited_count = await self.db_manager.inherit_from_upstream(project
|
573
|
+
inherited_count = await self.db_manager.inherit_from_upstream(project)
|
617
574
|
if inherited_count > 0:
|
618
575
|
logger.info(f"Auto-inherited {inherited_count} descriptions from upstream for {normalized_name}")
|
619
576
|
except Exception as e:
|
@@ -707,7 +664,7 @@ src/
|
|
707
664
|
return False
|
708
665
|
|
709
666
|
# Get files already indexed for this project
|
710
|
-
indexed_files = await self.db_manager.get_all_file_descriptions(project.id
|
667
|
+
indexed_files = await self.db_manager.get_all_file_descriptions(project.id)
|
711
668
|
indexed_basenames = {Path(fd.file_path).name for fd in indexed_files}
|
712
669
|
|
713
670
|
if not indexed_basenames:
|
@@ -764,65 +721,14 @@ src/
|
|
764
721
|
await self.db_manager.update_project(project)
|
765
722
|
logger.debug(f"Updated project metadata for {project.name}")
|
766
723
|
|
767
|
-
|
768
|
-
"""
|
769
|
-
Resolve the actual branch to use for operations.
|
770
|
-
|
771
|
-
For new projects or branches with no existing files, uses the requested branch.
|
772
|
-
For existing projects, tries to find a consistent branch to avoid data fragmentation.
|
773
|
-
|
774
|
-
Returns the resolved branch name.
|
775
|
-
"""
|
776
|
-
try:
|
777
|
-
# Get all branches and their file counts for this project
|
778
|
-
branch_counts = await self.db_manager.get_branch_file_counts(project_id)
|
779
|
-
|
780
|
-
# If no existing data, use the requested branch
|
781
|
-
if not branch_counts:
|
782
|
-
return requested_branch
|
783
|
-
|
784
|
-
# If requested branch has files, use it
|
785
|
-
if requested_branch in branch_counts and branch_counts[requested_branch] > 0:
|
786
|
-
return requested_branch
|
787
|
-
|
788
|
-
# Try common branch name variations to find existing data
|
789
|
-
common_variations = {
|
790
|
-
'main': ['master', 'develop', 'development', 'dev'],
|
791
|
-
'master': ['main', 'develop', 'development', 'dev'],
|
792
|
-
'develop': ['development', 'main', 'master', 'dev'],
|
793
|
-
'development': ['develop', 'main', 'master', 'dev'],
|
794
|
-
'dev': ['develop', 'development', 'main', 'master']
|
795
|
-
}
|
796
|
-
|
797
|
-
# Try variations of the requested branch
|
798
|
-
if requested_branch.lower() in common_variations:
|
799
|
-
for variation in common_variations[requested_branch.lower()]:
|
800
|
-
if variation in branch_counts and branch_counts[variation] > 0:
|
801
|
-
logger.info(f"Resolved branch '{requested_branch}' to existing branch '{variation}' with {branch_counts[variation]} files")
|
802
|
-
return variation
|
803
|
-
|
804
|
-
# If no variations found, check if we should use the main data branch
|
805
|
-
# (to avoid fragmenting data across multiple branches)
|
806
|
-
best_branch = max(branch_counts.items(), key=lambda x: x[1])
|
807
|
-
if best_branch[1] > 10: # Only if there's substantial existing data
|
808
|
-
logger.info(f"Using primary branch '{best_branch[0]}' instead of '{requested_branch}' to avoid data fragmentation")
|
809
|
-
return best_branch[0]
|
810
|
-
|
811
|
-
# Fall back to requested branch for new/small projects
|
812
|
-
return requested_branch
|
813
|
-
|
814
|
-
except Exception as e:
|
815
|
-
logger.warning(f"Error resolving branch: {e}")
|
816
|
-
return requested_branch
|
724
|
+
|
817
725
|
|
818
726
|
async def _handle_get_file_description(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
|
819
727
|
"""Handle get_file_description tool calls."""
|
820
728
|
project_id = await self._get_or_create_project_id(arguments)
|
821
|
-
resolved_branch = await self._resolve_branch(project_id, arguments["branch"])
|
822
729
|
|
823
730
|
file_desc = await self.db_manager.get_file_description(
|
824
731
|
project_id=project_id,
|
825
|
-
branch=resolved_branch,
|
826
732
|
file_path=arguments["filePath"]
|
827
733
|
)
|
828
734
|
|
@@ -844,19 +750,16 @@ src/
|
|
844
750
|
"""Handle update_file_description tool calls."""
|
845
751
|
logger.info(f"Updating file description for: {arguments['filePath']}")
|
846
752
|
logger.info(f"Project: {arguments.get('projectName', 'Unknown')}")
|
847
|
-
logger.info(f"Branch: {arguments.get('branch', 'Unknown')}")
|
848
753
|
|
849
754
|
description_length = len(arguments.get("description", ""))
|
850
755
|
logger.info(f"Description length: {description_length} characters")
|
851
756
|
|
852
757
|
project_id = await self._get_or_create_project_id(arguments)
|
853
|
-
resolved_branch = await self._resolve_branch(project_id, arguments["branch"])
|
854
758
|
|
855
|
-
logger.info(f"Resolved project_id: {project_id}
|
759
|
+
logger.info(f"Resolved project_id: {project_id}")
|
856
760
|
|
857
761
|
file_desc = FileDescription(
|
858
762
|
project_id=project_id,
|
859
|
-
branch=resolved_branch,
|
860
763
|
file_path=arguments["filePath"],
|
861
764
|
description=arguments["description"],
|
862
765
|
file_hash=arguments.get("fileHash"),
|
@@ -879,28 +782,24 @@ src/
|
|
879
782
|
"""Handle check_codebase_size tool calls."""
|
880
783
|
logger.info(f"Checking codebase size for: {arguments.get('projectName', 'Unknown')}")
|
881
784
|
logger.info(f"Folder path: {arguments.get('folderPath', 'Unknown')}")
|
882
|
-
logger.info(f"Branch: {arguments.get('branch', 'Unknown')}")
|
883
785
|
|
884
786
|
project_id = await self._get_or_create_project_id(arguments)
|
885
|
-
resolved_branch = await self._resolve_branch(project_id, arguments["branch"])
|
886
787
|
folder_path = Path(arguments["folderPath"])
|
887
788
|
|
888
|
-
logger.info(f"Resolved project_id: {project_id}
|
789
|
+
logger.info(f"Resolved project_id: {project_id}")
|
889
790
|
|
890
791
|
# Clean up descriptions for files that no longer exist
|
891
792
|
logger.info("Cleaning up descriptions for missing files...")
|
892
793
|
cleaned_up_files = await self.db_manager.cleanup_missing_files(
|
893
794
|
project_id=project_id,
|
894
|
-
branch=resolved_branch,
|
895
795
|
project_root=folder_path
|
896
796
|
)
|
897
797
|
logger.info(f"Cleaned up {len(cleaned_up_files)} missing files")
|
898
798
|
|
899
|
-
# Get file descriptions for this project
|
799
|
+
# Get file descriptions for this project (after cleanup)
|
900
800
|
logger.info("Retrieving file descriptions...")
|
901
801
|
file_descriptions = await self.db_manager.get_all_file_descriptions(
|
902
|
-
project_id=project_id
|
903
|
-
branch=resolved_branch
|
802
|
+
project_id=project_id
|
904
803
|
)
|
905
804
|
logger.info(f"Found {len(file_descriptions)} file descriptions")
|
906
805
|
|
@@ -912,7 +811,7 @@ src/
|
|
912
811
|
descriptions_tokens = self.token_counter.calculate_codebase_tokens(file_descriptions)
|
913
812
|
|
914
813
|
# Get overview tokens if available
|
915
|
-
overview = await self.db_manager.get_project_overview(project_id
|
814
|
+
overview = await self.db_manager.get_project_overview(project_id)
|
916
815
|
overview_tokens = 0
|
917
816
|
if overview and overview.overview:
|
918
817
|
overview_tokens = self.token_counter.count_tokens(overview.overview)
|
@@ -943,16 +842,14 @@ src/
|
|
943
842
|
logger.info(f"Folder path: {arguments.get('folderPath', 'Unknown')}")
|
944
843
|
|
945
844
|
project_id = await self._get_or_create_project_id(arguments)
|
946
|
-
resolved_branch = await self._resolve_branch(project_id, arguments["branch"])
|
947
845
|
folder_path = Path(arguments["folderPath"])
|
948
846
|
|
949
|
-
logger.info(f"Resolved project_id: {project_id}
|
847
|
+
logger.info(f"Resolved project_id: {project_id}")
|
950
848
|
|
951
849
|
# Get existing file descriptions
|
952
850
|
logger.info("Retrieving existing file descriptions...")
|
953
851
|
existing_descriptions = await self.db_manager.get_all_file_descriptions(
|
954
|
-
project_id=project_id
|
955
|
-
branch=resolved_branch
|
852
|
+
project_id=project_id
|
956
853
|
)
|
957
854
|
existing_paths = {desc.file_path for desc in existing_descriptions}
|
958
855
|
logger.info(f"Found {len(existing_paths)} existing descriptions")
|
@@ -993,13 +890,11 @@ src/
|
|
993
890
|
async def _handle_search_descriptions(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
|
994
891
|
"""Handle search_descriptions tool calls."""
|
995
892
|
project_id = await self._get_or_create_project_id(arguments)
|
996
|
-
resolved_branch = await self._resolve_branch(project_id, arguments["branch"])
|
997
893
|
max_results = arguments.get("maxResults", 20)
|
998
894
|
|
999
895
|
# Perform search
|
1000
896
|
search_results = await self.db_manager.search_file_descriptions(
|
1001
897
|
project_id=project_id,
|
1002
|
-
branch=resolved_branch,
|
1003
898
|
query=arguments["query"],
|
1004
899
|
max_results=max_results
|
1005
900
|
)
|
@@ -1023,12 +918,10 @@ src/
|
|
1023
918
|
async def _handle_get_codebase_overview(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
|
1024
919
|
"""Handle get_codebase_overview tool calls."""
|
1025
920
|
project_id = await self._get_or_create_project_id(arguments)
|
1026
|
-
resolved_branch = await self._resolve_branch(project_id, arguments["branch"])
|
1027
921
|
|
1028
922
|
# Get all file descriptions
|
1029
923
|
file_descriptions = await self.db_manager.get_all_file_descriptions(
|
1030
|
-
project_id=project_id
|
1031
|
-
branch=resolved_branch
|
924
|
+
project_id=project_id
|
1032
925
|
)
|
1033
926
|
|
1034
927
|
# Calculate total tokens
|
@@ -1040,8 +933,6 @@ src/
|
|
1040
933
|
|
1041
934
|
return {
|
1042
935
|
"projectName": arguments["projectName"],
|
1043
|
-
"branch": resolved_branch,
|
1044
|
-
"requestedBranch": arguments["branch"] if arguments["branch"] != resolved_branch else None,
|
1045
936
|
"totalFiles": len(file_descriptions),
|
1046
937
|
"totalTokens": total_tokens,
|
1047
938
|
"isLarge": is_large,
|
@@ -1092,77 +983,14 @@ src/
|
|
1092
983
|
|
1093
984
|
return convert_structure(root)
|
1094
985
|
|
1095
|
-
|
1096
|
-
"""Handle merge_branch_descriptions tool calls."""
|
1097
|
-
project_id = await self._get_or_create_project_id(arguments)
|
1098
|
-
source_branch = arguments["sourceBranch"]
|
1099
|
-
target_branch = arguments["targetBranch"]
|
1100
|
-
conflict_resolutions = arguments.get("conflictResolutions")
|
1101
|
-
|
1102
|
-
if conflict_resolutions is None:
|
1103
|
-
# Phase 1: Detect conflicts
|
1104
|
-
session = await self.merge_handler.start_merge_phase1(
|
1105
|
-
project_id, source_branch, target_branch
|
1106
|
-
)
|
1107
|
-
|
1108
|
-
if session.get_conflict_count() == 0:
|
1109
|
-
# No conflicts, can merge immediately
|
1110
|
-
return {
|
1111
|
-
"phase": "completed",
|
1112
|
-
"conflicts": [],
|
1113
|
-
"message": f"No conflicts detected. Merge from {source_branch} to {target_branch} can proceed automatically.",
|
1114
|
-
"sourceBranch": source_branch,
|
1115
|
-
"targetBranch": target_branch,
|
1116
|
-
"conflictCount": 0
|
1117
|
-
}
|
1118
|
-
else:
|
1119
|
-
# Return conflicts for resolution
|
1120
|
-
return {
|
1121
|
-
"phase": "conflicts_detected",
|
1122
|
-
"sessionId": session.session_id,
|
1123
|
-
"conflicts": [conflict.to_dict() for conflict in session.conflicts],
|
1124
|
-
"conflictCount": session.get_conflict_count(),
|
1125
|
-
"sourceBranch": source_branch,
|
1126
|
-
"targetBranch": target_branch,
|
1127
|
-
"message": f"Found {session.get_conflict_count()} conflicts that need resolution."
|
1128
|
-
}
|
1129
|
-
else:
|
1130
|
-
# Phase 2: Apply resolutions
|
1131
|
-
# Find the session ID from conflict resolutions
|
1132
|
-
if not conflict_resolutions:
|
1133
|
-
from ..error_handler import ValidationError
|
1134
|
-
raise ValidationError("Conflict resolutions required for phase 2")
|
1135
|
-
|
1136
|
-
# For simplicity, create a new session and resolve immediately
|
1137
|
-
# In a production system, you'd want to track session IDs properly
|
1138
|
-
session = await self.merge_handler.start_merge_phase1(
|
1139
|
-
project_id, source_branch, target_branch
|
1140
|
-
)
|
1141
|
-
|
1142
|
-
if session.get_conflict_count() == 0:
|
1143
|
-
return {
|
1144
|
-
"phase": "completed",
|
1145
|
-
"message": "No conflicts to resolve",
|
1146
|
-
"sourceBranch": source_branch,
|
1147
|
-
"targetBranch": target_branch
|
1148
|
-
}
|
1149
|
-
|
1150
|
-
result = await self.merge_handler.complete_merge_phase2(
|
1151
|
-
session.session_id, conflict_resolutions
|
1152
|
-
)
|
1153
|
-
|
1154
|
-
return {
|
1155
|
-
"phase": "completed",
|
1156
|
-
**result
|
1157
|
-
}
|
986
|
+
|
1158
987
|
|
1159
988
|
async def _handle_get_condensed_overview(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
|
1160
989
|
"""Handle get_codebase_overview tool calls for condensed overviews."""
|
1161
990
|
project_id = await self._get_or_create_project_id(arguments)
|
1162
|
-
resolved_branch = await self._resolve_branch(project_id, arguments["branch"])
|
1163
991
|
|
1164
992
|
# Try to get existing overview
|
1165
|
-
overview = await self.db_manager.get_project_overview(project_id
|
993
|
+
overview = await self.db_manager.get_project_overview(project_id)
|
1166
994
|
|
1167
995
|
if overview:
|
1168
996
|
return {
|
@@ -1182,13 +1010,11 @@ src/
|
|
1182
1010
|
async def _handle_update_codebase_overview(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
|
1183
1011
|
"""Handle update_codebase_overview tool calls."""
|
1184
1012
|
project_id = await self._get_or_create_project_id(arguments)
|
1185
|
-
resolved_branch = await self._resolve_branch(project_id, arguments["branch"])
|
1186
1013
|
folder_path = Path(arguments["folderPath"])
|
1187
1014
|
|
1188
1015
|
# Get current file count and total tokens for context
|
1189
1016
|
file_descriptions = await self.db_manager.get_all_file_descriptions(
|
1190
|
-
project_id=project_id
|
1191
|
-
branch=resolved_branch
|
1017
|
+
project_id=project_id
|
1192
1018
|
)
|
1193
1019
|
|
1194
1020
|
total_files = len(file_descriptions)
|
@@ -1197,7 +1023,6 @@ src/
|
|
1197
1023
|
# Create overview record
|
1198
1024
|
overview = ProjectOverview(
|
1199
1025
|
project_id=project_id,
|
1200
|
-
branch=resolved_branch,
|
1201
1026
|
overview=arguments["overview"],
|
1202
1027
|
last_modified=datetime.utcnow(),
|
1203
1028
|
total_files=total_files,
|
@@ -1217,13 +1042,11 @@ src/
|
|
1217
1042
|
async def _handle_get_word_frequency(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
|
1218
1043
|
"""Handle get_word_frequency tool calls."""
|
1219
1044
|
project_id = await self._get_or_create_project_id(arguments)
|
1220
|
-
resolved_branch = await self._resolve_branch(project_id, arguments["branch"])
|
1221
1045
|
limit = arguments.get("limit", 200)
|
1222
1046
|
|
1223
1047
|
# Analyze word frequency
|
1224
1048
|
result = await self.db_manager.analyze_word_frequency(
|
1225
1049
|
project_id=project_id,
|
1226
|
-
branch=resolved_branch,
|
1227
1050
|
limit=limit
|
1228
1051
|
)
|
1229
1052
|
|
@@ -1236,11 +1059,10 @@ src/
|
|
1236
1059
|
async def _handle_search_codebase_overview(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
|
1237
1060
|
"""Handle search_codebase_overview tool calls."""
|
1238
1061
|
project_id = await self._get_or_create_project_id(arguments)
|
1239
|
-
resolved_branch = await self._resolve_branch(project_id, arguments["branch"])
|
1240
1062
|
search_word = arguments["searchWord"].lower()
|
1241
1063
|
|
1242
1064
|
# Get the overview
|
1243
|
-
overview = await self.db_manager.get_project_overview(project_id
|
1065
|
+
overview = await self.db_manager.get_project_overview(project_id)
|
1244
1066
|
|
1245
1067
|
if not overview or not overview.overview:
|
1246
1068
|
return {
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: mcp-code-indexer
|
3
|
-
Version:
|
3
|
+
Version: 3.0.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
|
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
|
-
[](https://badge.fury.io/py/mcp-code-indexer)
|
63
|
+
[](https://pypi.org/project/mcp-code-indexer/)
|
64
64
|
[](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,11 +1,12 @@
|
|
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=
|
4
|
-
mcp_code_indexer/claude_api_handler.py,sha256=
|
5
|
-
mcp_code_indexer/
|
6
|
-
mcp_code_indexer/
|
3
|
+
mcp_code_indexer/ask_handler.py,sha256=rFljJtqP_YL3E9H2Hgk04yURzHw_sqm6muB5RTlP_-o,8397
|
4
|
+
mcp_code_indexer/claude_api_handler.py,sha256=4lgp-KsDMOmjrln3QhdJuM5pHvaB3kUwHOUk9l5adi4,13628
|
5
|
+
mcp_code_indexer/cleanup_manager.py,sha256=1x2de8Mr9dL92q4ubEebsWSF_2n8Yxk549ZohYHNIkU,9358
|
6
|
+
mcp_code_indexer/deepask_handler.py,sha256=iAFA1pKfAnurHBprIyP1TaecPzZ5YhBs-oR8Eccxoe4,18323
|
7
|
+
mcp_code_indexer/error_handler.py,sha256=x6dHezVeKcD2ealNLBndt-3SiPiMfh9VOUNoqQSk3rI,11660
|
7
8
|
mcp_code_indexer/file_scanner.py,sha256=ctXeZMROgDThEtjzsANTK9TbK-fhTScMBd4iyuleBT4,11734
|
8
|
-
mcp_code_indexer/git_hook_handler.py,sha256=
|
9
|
+
mcp_code_indexer/git_hook_handler.py,sha256=OMPfQlykqR2_cE5IxGqbAI92afLOOJxsvXbAQIZrdLU,36579
|
9
10
|
mcp_code_indexer/logging_config.py,sha256=tf_U-Zz_axDXRV9s7TfHEeUrBjT1QBWkzPuiyZMffBU,10252
|
10
11
|
mcp_code_indexer/main.py,sha256=abCHbNFUYjkJcNYsU0EPdZQI-_Gz9cQCH7dYJ5Jp7I8,31627
|
11
12
|
mcp_code_indexer/merge_handler.py,sha256=lJR8eVq2qSrF6MW9mR3Fy8UzrNAaQ7RsI2FMNXne3vQ,14692
|
@@ -14,19 +15,23 @@ mcp_code_indexer/token_counter.py,sha256=WrifOkbF99nWWHlRlhCHAB2KN7qr83GOHl7apE-
|
|
14
15
|
mcp_code_indexer/data/stop_words_english.txt,sha256=7Zdd9ameVgA6tN_zuXROvHXD4hkWeELVywPhb7FJEkw,6343
|
15
16
|
mcp_code_indexer/database/__init__.py,sha256=aPq_aaRp0aSwOBIq9GkuMNjmLxA411zg2vhdrAuHm-w,38
|
16
17
|
mcp_code_indexer/database/connection_health.py,sha256=s2r9L_KipH5NlemAUDnhBQO90Dn4b_0Ht9UDs7F6QPk,24432
|
17
|
-
mcp_code_indexer/database/database.py,sha256=
|
18
|
+
mcp_code_indexer/database/database.py,sha256=4y2JqK0opz4MZk1R_zMBi5B-hRyXd_jt4cksWIiU34A,52724
|
18
19
|
mcp_code_indexer/database/exceptions.py,sha256=AgpRA9Z5R-GoWYdQSPeSdYvAXDopFCQkLGN3jD7Ha4E,10215
|
19
|
-
mcp_code_indexer/database/models.py,sha256=
|
20
|
+
mcp_code_indexer/database/models.py,sha256=FbNtP9Z0bDCoe8JjsYT1HWp0uYsxgZFHR0Blt3d8TBY,7054
|
20
21
|
mcp_code_indexer/database/retry_executor.py,sha256=QUayjkCk8OsckVMYiJ_HBQ9NTUss-H8GQeUIUbbw4_U,13419
|
21
22
|
mcp_code_indexer/middleware/__init__.py,sha256=p-mP0pMsfiU2yajCPvokCUxUEkh_lu4XJP1LyyMW2ug,220
|
22
23
|
mcp_code_indexer/middleware/error_middleware.py,sha256=5agJTAkkPogfPGnja1V9JtG9RG-BiOALIJYctK3byJQ,11730
|
24
|
+
mcp_code_indexer/migrations/001_initial.sql,sha256=hIXkCP4LA_4A9HJ1CHU0a1DD-a6EN6u-uJPMqW0c2Yo,4120
|
25
|
+
mcp_code_indexer/migrations/002_performance_indexes.sql,sha256=FlKbmcJyKAHTKmjxmpk8ABe6eMcQahz8RciRYcREY_E,2846
|
26
|
+
mcp_code_indexer/migrations/003_project_overviews.sql,sha256=pPzn7UmJ_Bda9mJ1nYTN1GeuYwdQHC7Fva6PvWaucUw,891
|
27
|
+
mcp_code_indexer/migrations/004_remove_branch_dependency.sql,sha256=x3aBHJHS8rN1RA7Ba5JdGudR-dYzxH0jiuylGy0X4D0,5844
|
23
28
|
mcp_code_indexer/server/__init__.py,sha256=16xMcuriUOBlawRqWNBk6niwrvtv_JD5xvI36X1Vsmk,41
|
24
|
-
mcp_code_indexer/server/mcp_server.py,sha256=
|
29
|
+
mcp_code_indexer/server/mcp_server.py,sha256=EQnwRbjF17AdPvO_HPb6d7cmpxUN51qbyMuhQXrtetU,63168
|
25
30
|
mcp_code_indexer/tiktoken_cache/9b5ad71b2ce5302211f9c61530b329a4922fc6a4,sha256=Ijkht27pm96ZW3_3OFE-7xAPtR0YyTWXoRO8_-hlsqc,1681126
|
26
31
|
mcp_code_indexer/tools/__init__.py,sha256=m01mxML2UdD7y5rih_XNhNSCMzQTz7WQ_T1TeOcYlnE,49
|
27
|
-
mcp_code_indexer-
|
28
|
-
mcp_code_indexer-
|
29
|
-
mcp_code_indexer-
|
30
|
-
mcp_code_indexer-
|
31
|
-
mcp_code_indexer-
|
32
|
-
mcp_code_indexer-
|
32
|
+
mcp_code_indexer-3.0.2.dist-info/licenses/LICENSE,sha256=JN9dyPPgYwH9C-UjYM7FLNZjQ6BF7kAzpF3_4PwY4rY,1086
|
33
|
+
mcp_code_indexer-3.0.2.dist-info/METADATA,sha256=JLmX7xUbSANmBoLVdqXE8zEBQoYERiahC2fHzoR_RFM,20165
|
34
|
+
mcp_code_indexer-3.0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
35
|
+
mcp_code_indexer-3.0.2.dist-info/entry_points.txt,sha256=8HqWOw1Is7jOP1bvIgaSwouvT9z_Boe-9hd4NzyJOhY,68
|
36
|
+
mcp_code_indexer-3.0.2.dist-info/top_level.txt,sha256=yKYCM-gMGt-cnupGfAhnZaoEsROLB6DQ1KFUuyKx4rw,17
|
37
|
+
mcp_code_indexer-3.0.2.dist-info/RECORD,,
|
File without changes
|