mcp-vector-search 0.12.6__py3-none-any.whl → 1.0.3__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_vector_search/__init__.py +2 -2
- mcp_vector_search/analysis/__init__.py +64 -0
- mcp_vector_search/analysis/collectors/__init__.py +39 -0
- mcp_vector_search/analysis/collectors/base.py +164 -0
- mcp_vector_search/analysis/collectors/complexity.py +743 -0
- mcp_vector_search/analysis/metrics.py +341 -0
- mcp_vector_search/analysis/reporters/__init__.py +5 -0
- mcp_vector_search/analysis/reporters/console.py +222 -0
- mcp_vector_search/cli/commands/analyze.py +408 -0
- mcp_vector_search/cli/commands/chat.py +1262 -0
- mcp_vector_search/cli/commands/index.py +21 -3
- mcp_vector_search/cli/commands/init.py +13 -0
- mcp_vector_search/cli/commands/install.py +597 -335
- mcp_vector_search/cli/commands/install_old.py +8 -4
- mcp_vector_search/cli/commands/mcp.py +78 -6
- mcp_vector_search/cli/commands/reset.py +68 -26
- mcp_vector_search/cli/commands/search.py +30 -7
- mcp_vector_search/cli/commands/setup.py +1133 -0
- mcp_vector_search/cli/commands/status.py +37 -2
- mcp_vector_search/cli/commands/uninstall.py +276 -357
- mcp_vector_search/cli/commands/visualize/__init__.py +39 -0
- mcp_vector_search/cli/commands/visualize/cli.py +276 -0
- mcp_vector_search/cli/commands/visualize/exporters/__init__.py +12 -0
- mcp_vector_search/cli/commands/visualize/exporters/html_exporter.py +33 -0
- mcp_vector_search/cli/commands/visualize/exporters/json_exporter.py +29 -0
- mcp_vector_search/cli/commands/visualize/graph_builder.py +714 -0
- mcp_vector_search/cli/commands/visualize/layout_engine.py +469 -0
- mcp_vector_search/cli/commands/visualize/server.py +311 -0
- mcp_vector_search/cli/commands/visualize/state_manager.py +428 -0
- mcp_vector_search/cli/commands/visualize/templates/__init__.py +16 -0
- mcp_vector_search/cli/commands/visualize/templates/base.py +180 -0
- mcp_vector_search/cli/commands/visualize/templates/scripts.py +2507 -0
- mcp_vector_search/cli/commands/visualize/templates/styles.py +1313 -0
- mcp_vector_search/cli/commands/visualize.py.original +2536 -0
- mcp_vector_search/cli/didyoumean.py +22 -2
- mcp_vector_search/cli/main.py +115 -159
- mcp_vector_search/cli/output.py +24 -8
- mcp_vector_search/config/__init__.py +4 -0
- mcp_vector_search/config/default_thresholds.yaml +52 -0
- mcp_vector_search/config/settings.py +12 -0
- mcp_vector_search/config/thresholds.py +185 -0
- mcp_vector_search/core/auto_indexer.py +3 -3
- mcp_vector_search/core/boilerplate.py +186 -0
- mcp_vector_search/core/config_utils.py +394 -0
- mcp_vector_search/core/database.py +369 -94
- mcp_vector_search/core/exceptions.py +11 -0
- mcp_vector_search/core/git_hooks.py +4 -4
- mcp_vector_search/core/indexer.py +221 -4
- mcp_vector_search/core/llm_client.py +751 -0
- mcp_vector_search/core/models.py +3 -0
- mcp_vector_search/core/project.py +17 -0
- mcp_vector_search/core/scheduler.py +11 -11
- mcp_vector_search/core/search.py +179 -29
- mcp_vector_search/mcp/server.py +24 -5
- mcp_vector_search/utils/__init__.py +2 -0
- mcp_vector_search/utils/gitignore_updater.py +212 -0
- mcp_vector_search/utils/monorepo.py +66 -4
- mcp_vector_search/utils/timing.py +10 -6
- {mcp_vector_search-0.12.6.dist-info → mcp_vector_search-1.0.3.dist-info}/METADATA +182 -52
- mcp_vector_search-1.0.3.dist-info/RECORD +97 -0
- {mcp_vector_search-0.12.6.dist-info → mcp_vector_search-1.0.3.dist-info}/WHEEL +1 -1
- {mcp_vector_search-0.12.6.dist-info → mcp_vector_search-1.0.3.dist-info}/entry_points.txt +1 -0
- mcp_vector_search/cli/commands/visualize.py +0 -1467
- mcp_vector_search-0.12.6.dist-info/RECORD +0 -68
- {mcp_vector_search-0.12.6.dist-info → mcp_vector_search-1.0.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -578,7 +578,8 @@ def demo(
|
|
|
578
578
|
demo_dir.mkdir()
|
|
579
579
|
|
|
580
580
|
# Create sample files
|
|
581
|
-
(demo_dir / "main.py").write_text(
|
|
581
|
+
(demo_dir / "main.py").write_text(
|
|
582
|
+
"""
|
|
582
583
|
def main():
|
|
583
584
|
'''Main entry point for the application.'''
|
|
584
585
|
print("Hello, World!")
|
|
@@ -600,9 +601,11 @@ class UserService:
|
|
|
600
601
|
|
|
601
602
|
if __name__ == "__main__":
|
|
602
603
|
main()
|
|
603
|
-
"""
|
|
604
|
+
"""
|
|
605
|
+
)
|
|
604
606
|
|
|
605
|
-
(demo_dir / "utils.py").write_text(
|
|
607
|
+
(demo_dir / "utils.py").write_text(
|
|
608
|
+
"""
|
|
606
609
|
import json
|
|
607
610
|
from typing import Dict, Any
|
|
608
611
|
|
|
@@ -619,7 +622,8 @@ def hash_password(password: str) -> str:
|
|
|
619
622
|
'''Hash password for secure storage.'''
|
|
620
623
|
import hashlib
|
|
621
624
|
return hashlib.sha256(password.encode()).hexdigest()
|
|
622
|
-
"""
|
|
625
|
+
"""
|
|
626
|
+
)
|
|
623
627
|
|
|
624
628
|
console.print(
|
|
625
629
|
f"\n[bold blue]📁 Created demo project at:[/bold blue] {demo_dir}"
|
|
@@ -38,11 +38,50 @@ Each tool has its own configuration format and location.
|
|
|
38
38
|
3. Test setup: [green]mcp-vector-search mcp test[/green]
|
|
39
39
|
|
|
40
40
|
[dim]Use --force to overwrite existing configurations[/dim]
|
|
41
|
-
"""
|
|
41
|
+
""",
|
|
42
|
+
no_args_is_help=False, # Allow running without subcommand
|
|
43
|
+
invoke_without_command=True, # Call callback even without subcommand
|
|
42
44
|
)
|
|
43
45
|
|
|
44
46
|
console = Console()
|
|
45
47
|
|
|
48
|
+
|
|
49
|
+
@mcp_app.callback()
|
|
50
|
+
def mcp_callback(ctx: typer.Context):
|
|
51
|
+
"""MCP server management.
|
|
52
|
+
|
|
53
|
+
When invoked without a subcommand, starts the MCP server over stdio.
|
|
54
|
+
Use subcommands to configure MCP integration for different AI tools.
|
|
55
|
+
"""
|
|
56
|
+
# Store context for subcommands
|
|
57
|
+
if not ctx.obj:
|
|
58
|
+
ctx.obj = {}
|
|
59
|
+
|
|
60
|
+
# If a subcommand was invoked, let it handle things (check this FIRST)
|
|
61
|
+
if ctx.invoked_subcommand is not None:
|
|
62
|
+
return
|
|
63
|
+
|
|
64
|
+
# No subcommand - start the MCP server
|
|
65
|
+
import asyncio
|
|
66
|
+
from pathlib import Path
|
|
67
|
+
|
|
68
|
+
from ...mcp.server import run_mcp_server
|
|
69
|
+
|
|
70
|
+
project_root = ctx.obj.get("project_root") if ctx.obj else None
|
|
71
|
+
if project_root is None:
|
|
72
|
+
project_root = Path.cwd()
|
|
73
|
+
|
|
74
|
+
# Start the MCP server over stdio
|
|
75
|
+
try:
|
|
76
|
+
asyncio.run(run_mcp_server(project_root))
|
|
77
|
+
raise typer.Exit(0)
|
|
78
|
+
except KeyboardInterrupt:
|
|
79
|
+
raise typer.Exit(0)
|
|
80
|
+
except Exception as e:
|
|
81
|
+
print(f"MCP server error: {e}", file=sys.stderr)
|
|
82
|
+
raise typer.Exit(1)
|
|
83
|
+
|
|
84
|
+
|
|
46
85
|
# Supported AI tools and their configuration details
|
|
47
86
|
SUPPORTED_TOOLS = {
|
|
48
87
|
"auggie": {
|
|
@@ -123,6 +162,29 @@ def get_mcp_server_command(
|
|
|
123
162
|
return f"{python_exe} -m mcp_vector_search.mcp.server{watch_flag} {project_root}"
|
|
124
163
|
|
|
125
164
|
|
|
165
|
+
def detect_install_method() -> tuple[str, list[str]]:
|
|
166
|
+
"""Detect how mcp-vector-search is installed and return appropriate command.
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
Tuple of (command, args) for running mcp-vector-search mcp
|
|
170
|
+
"""
|
|
171
|
+
# Check if we're in a uv-managed environment
|
|
172
|
+
# uv sets UV_PROJECT_ENVIRONMENT or has .venv structure
|
|
173
|
+
if os.environ.get("VIRTUAL_ENV") and ".venv" in os.environ.get("VIRTUAL_ENV", ""):
|
|
174
|
+
# Likely uv project environment
|
|
175
|
+
if shutil.which("uv"):
|
|
176
|
+
return ("uv", ["run", "mcp-vector-search", "mcp"])
|
|
177
|
+
|
|
178
|
+
# Check if mcp-vector-search is directly available in PATH
|
|
179
|
+
mcp_cmd = shutil.which("mcp-vector-search")
|
|
180
|
+
if mcp_cmd:
|
|
181
|
+
# Installed via pipx or pip - use direct command
|
|
182
|
+
return ("mcp-vector-search", ["mcp"])
|
|
183
|
+
|
|
184
|
+
# Fallback to uv run (development mode)
|
|
185
|
+
return ("uv", ["run", "mcp-vector-search", "mcp"])
|
|
186
|
+
|
|
187
|
+
|
|
126
188
|
def get_mcp_server_config_for_tool(
|
|
127
189
|
project_root: Path,
|
|
128
190
|
tool_name: str,
|
|
@@ -130,9 +192,11 @@ def get_mcp_server_config_for_tool(
|
|
|
130
192
|
enable_file_watching: bool = True,
|
|
131
193
|
) -> dict[str, Any]:
|
|
132
194
|
"""Generate MCP server configuration for a specific tool."""
|
|
195
|
+
command, args = detect_install_method()
|
|
196
|
+
|
|
133
197
|
base_config = {
|
|
134
|
-
"command":
|
|
135
|
-
"args":
|
|
198
|
+
"command": command,
|
|
199
|
+
"args": args,
|
|
136
200
|
"env": {
|
|
137
201
|
"MCP_ENABLE_FILE_WATCHING": "true" if enable_file_watching else "false"
|
|
138
202
|
},
|
|
@@ -183,11 +247,12 @@ def create_project_claude_config(
|
|
|
183
247
|
if "mcpServers" not in config:
|
|
184
248
|
config["mcpServers"] = {}
|
|
185
249
|
|
|
186
|
-
#
|
|
250
|
+
# Detect installation method and use appropriate command
|
|
251
|
+
command, args = detect_install_method()
|
|
187
252
|
config["mcpServers"][server_name] = {
|
|
188
253
|
"type": "stdio",
|
|
189
|
-
"command":
|
|
190
|
-
"args":
|
|
254
|
+
"command": command,
|
|
255
|
+
"args": args,
|
|
191
256
|
"env": {
|
|
192
257
|
"MCP_ENABLE_FILE_WATCHING": "true" if enable_file_watching else "false"
|
|
193
258
|
},
|
|
@@ -198,6 +263,13 @@ def create_project_claude_config(
|
|
|
198
263
|
json.dump(config, f, indent=2)
|
|
199
264
|
|
|
200
265
|
print_success("Created project-level .mcp.json with MCP server configuration")
|
|
266
|
+
|
|
267
|
+
# Show which command will be used
|
|
268
|
+
if command == "uv":
|
|
269
|
+
print_info(f"Using uv: {command} {' '.join(args)}")
|
|
270
|
+
else:
|
|
271
|
+
print_info(f"Using direct command: {command} {' '.join(args)}")
|
|
272
|
+
|
|
201
273
|
if enable_file_watching:
|
|
202
274
|
print_info("File watching is enabled for automatic reindexing")
|
|
203
275
|
else:
|
|
@@ -82,27 +82,58 @@ def reset_index(
|
|
|
82
82
|
console.print("[yellow]Reset cancelled[/yellow]")
|
|
83
83
|
raise typer.Exit(0)
|
|
84
84
|
|
|
85
|
-
# Get the database directory
|
|
86
|
-
project_manager.load_config()
|
|
87
|
-
db_path =
|
|
85
|
+
# Get the database directory from config
|
|
86
|
+
config = project_manager.load_config()
|
|
87
|
+
db_path = Path(config.index_path)
|
|
88
|
+
|
|
89
|
+
# Check if index exists (look for chroma.sqlite3 or collection directories)
|
|
90
|
+
has_index = (db_path / "chroma.sqlite3").exists()
|
|
88
91
|
|
|
89
|
-
if not
|
|
92
|
+
if not has_index:
|
|
90
93
|
print_warning("No index found. Nothing to reset.")
|
|
91
94
|
raise typer.Exit(0)
|
|
92
95
|
|
|
96
|
+
# Files/dirs to remove (index data)
|
|
97
|
+
index_files = [
|
|
98
|
+
"chroma.sqlite3",
|
|
99
|
+
"cache",
|
|
100
|
+
"indexing_errors.log",
|
|
101
|
+
"index_metadata.json",
|
|
102
|
+
"directory_index.json",
|
|
103
|
+
]
|
|
104
|
+
|
|
105
|
+
# Also remove any UUID-named directories (ChromaDB collections)
|
|
106
|
+
if db_path.exists():
|
|
107
|
+
for item in db_path.iterdir():
|
|
108
|
+
if item.is_dir() and len(item.name) == 36 and "-" in item.name:
|
|
109
|
+
# Looks like a UUID directory
|
|
110
|
+
index_files.append(item.name)
|
|
111
|
+
|
|
93
112
|
# Create backup if requested
|
|
94
113
|
if backup:
|
|
95
|
-
backup_dir =
|
|
114
|
+
backup_dir = db_path / "backups"
|
|
96
115
|
backup_dir.mkdir(exist_ok=True)
|
|
97
116
|
|
|
98
117
|
import time
|
|
99
118
|
|
|
100
119
|
timestamp = int(time.time())
|
|
101
|
-
backup_path = backup_dir / f"
|
|
120
|
+
backup_path = backup_dir / f"index_backup_{timestamp}"
|
|
121
|
+
backup_path.mkdir(exist_ok=True)
|
|
102
122
|
|
|
103
123
|
try:
|
|
104
|
-
|
|
105
|
-
|
|
124
|
+
backed_up = []
|
|
125
|
+
for file in index_files:
|
|
126
|
+
src = db_path / file
|
|
127
|
+
if src.exists():
|
|
128
|
+
dest = backup_path / file
|
|
129
|
+
if src.is_dir():
|
|
130
|
+
shutil.copytree(src, dest)
|
|
131
|
+
else:
|
|
132
|
+
shutil.copy2(src, dest)
|
|
133
|
+
backed_up.append(file)
|
|
134
|
+
|
|
135
|
+
if backed_up:
|
|
136
|
+
print_success(f"Created backup at: {backup_path.relative_to(root)}")
|
|
106
137
|
except Exception as e:
|
|
107
138
|
print_warning(f"Could not create backup: {e}")
|
|
108
139
|
if not force:
|
|
@@ -110,12 +141,25 @@ def reset_index(
|
|
|
110
141
|
console.print("[yellow]Reset cancelled[/yellow]")
|
|
111
142
|
raise typer.Exit(0)
|
|
112
143
|
|
|
113
|
-
# Clear the index
|
|
144
|
+
# Clear the index files
|
|
114
145
|
console.print("[cyan]Clearing index...[/cyan]")
|
|
146
|
+
removed_count = 0
|
|
115
147
|
try:
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
148
|
+
for file in index_files:
|
|
149
|
+
path = db_path / file
|
|
150
|
+
if path.exists():
|
|
151
|
+
if path.is_dir():
|
|
152
|
+
shutil.rmtree(path)
|
|
153
|
+
else:
|
|
154
|
+
path.unlink()
|
|
155
|
+
removed_count += 1
|
|
156
|
+
|
|
157
|
+
if removed_count > 0:
|
|
158
|
+
print_success(
|
|
159
|
+
f"Index cleared successfully! ({removed_count} items removed)"
|
|
160
|
+
)
|
|
161
|
+
else:
|
|
162
|
+
print_warning("No index files found to remove.")
|
|
119
163
|
except Exception as e:
|
|
120
164
|
print_error(f"Failed to clear index: {e}")
|
|
121
165
|
raise typer.Exit(1)
|
|
@@ -215,19 +259,9 @@ def reset_all(
|
|
|
215
259
|
raise typer.Exit(1)
|
|
216
260
|
|
|
217
261
|
|
|
218
|
-
@reset_app.command("health")
|
|
219
262
|
async def check_health(
|
|
220
|
-
project_root: Path
|
|
221
|
-
|
|
222
|
-
"--project-root",
|
|
223
|
-
"-p",
|
|
224
|
-
help="Project root directory",
|
|
225
|
-
),
|
|
226
|
-
fix: bool = typer.Option(
|
|
227
|
-
False,
|
|
228
|
-
"--fix",
|
|
229
|
-
help="Attempt to fix issues if found",
|
|
230
|
-
),
|
|
263
|
+
project_root: Path,
|
|
264
|
+
fix: bool,
|
|
231
265
|
) -> None:
|
|
232
266
|
"""Check the health of the search index.
|
|
233
267
|
|
|
@@ -254,7 +288,7 @@ async def check_health(
|
|
|
254
288
|
from ...core.embeddings import create_embedding_function
|
|
255
289
|
|
|
256
290
|
config = project_manager.load_config()
|
|
257
|
-
db_path =
|
|
291
|
+
db_path = Path(config.index_path)
|
|
258
292
|
|
|
259
293
|
# Setup embedding function and cache
|
|
260
294
|
cache_dir = get_default_cache_path(root) if config.cache_embeddings else None
|
|
@@ -376,6 +410,7 @@ main = reset_main
|
|
|
376
410
|
|
|
377
411
|
|
|
378
412
|
# Make health check synchronous for CLI
|
|
413
|
+
@reset_app.command("health")
|
|
379
414
|
def health_main(
|
|
380
415
|
project_root: Path = typer.Option(
|
|
381
416
|
None,
|
|
@@ -389,5 +424,12 @@ def health_main(
|
|
|
389
424
|
help="Attempt to fix issues if found",
|
|
390
425
|
),
|
|
391
426
|
) -> None:
|
|
392
|
-
"""Check the health of the search index
|
|
427
|
+
"""Check the health of the search index.
|
|
428
|
+
|
|
429
|
+
This command will:
|
|
430
|
+
- Verify database connectivity
|
|
431
|
+
- Check for index corruption
|
|
432
|
+
- Validate collection integrity
|
|
433
|
+
- Optionally attempt repairs with --fix
|
|
434
|
+
"""
|
|
393
435
|
asyncio.run(check_health(project_root, fix))
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"""Search command for MCP Vector Search CLI."""
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
+
import os
|
|
5
|
+
from fnmatch import fnmatch
|
|
4
6
|
from pathlib import Path
|
|
5
7
|
|
|
6
8
|
import typer
|
|
@@ -62,7 +64,7 @@ def search_main(
|
|
|
62
64
|
None,
|
|
63
65
|
"--files",
|
|
64
66
|
"-f",
|
|
65
|
-
help="Filter by file patterns (e.g., '*.py'
|
|
67
|
+
help="Filter by file glob patterns (e.g., '*.py', 'src/*.js', 'tests/*.ts'). Matches basename or relative path.",
|
|
66
68
|
rich_help_panel="🔍 Filters",
|
|
67
69
|
),
|
|
68
70
|
language: str | None = typer.Option(
|
|
@@ -153,8 +155,10 @@ def search_main(
|
|
|
153
155
|
|
|
154
156
|
[bold cyan]Advanced Search:[/bold cyan]
|
|
155
157
|
|
|
156
|
-
[green]Filter by file pattern:[/green]
|
|
157
|
-
$ mcp-vector-search search "validation" --files "
|
|
158
|
+
[green]Filter by file pattern (glob):[/green]
|
|
159
|
+
$ mcp-vector-search search "validation" --files "*.py"
|
|
160
|
+
$ mcp-vector-search search "component" --files "src/*.tsx"
|
|
161
|
+
$ mcp-vector-search search "test utils" --files "tests/*.ts"
|
|
158
162
|
|
|
159
163
|
[green]Find similar code:[/green]
|
|
160
164
|
$ mcp-vector-search search "src/auth.py" --similar
|
|
@@ -326,7 +330,7 @@ async def run_search(
|
|
|
326
330
|
similarity_threshold=similarity_threshold or config.similarity_threshold,
|
|
327
331
|
)
|
|
328
332
|
|
|
329
|
-
# Build filters
|
|
333
|
+
# Build filters (exclude file_path - will be handled with post-filtering)
|
|
330
334
|
filters = {}
|
|
331
335
|
if language:
|
|
332
336
|
filters["language"] = language
|
|
@@ -334,9 +338,6 @@ async def run_search(
|
|
|
334
338
|
filters["function_name"] = function_name
|
|
335
339
|
if class_name:
|
|
336
340
|
filters["class_name"] = class_name
|
|
337
|
-
if files:
|
|
338
|
-
# Simple file pattern matching (could be enhanced)
|
|
339
|
-
filters["file_path"] = files
|
|
340
341
|
|
|
341
342
|
try:
|
|
342
343
|
async with database:
|
|
@@ -348,6 +349,28 @@ async def run_search(
|
|
|
348
349
|
include_context=show_content,
|
|
349
350
|
)
|
|
350
351
|
|
|
352
|
+
# Post-filter results by file pattern if specified
|
|
353
|
+
if files and results:
|
|
354
|
+
filtered_results = []
|
|
355
|
+
for result in results:
|
|
356
|
+
# Get relative path from project root
|
|
357
|
+
try:
|
|
358
|
+
rel_path = str(result.file_path.relative_to(project_root))
|
|
359
|
+
except ValueError:
|
|
360
|
+
# If file is outside project root, use absolute path
|
|
361
|
+
rel_path = str(result.file_path)
|
|
362
|
+
|
|
363
|
+
# Match against glob pattern (both full path and basename)
|
|
364
|
+
if fnmatch(rel_path, files) or fnmatch(
|
|
365
|
+
os.path.basename(rel_path), files
|
|
366
|
+
):
|
|
367
|
+
filtered_results.append(result)
|
|
368
|
+
|
|
369
|
+
results = filtered_results
|
|
370
|
+
logger.debug(
|
|
371
|
+
f"File pattern '{files}' filtered results to {len(results)} matches"
|
|
372
|
+
)
|
|
373
|
+
|
|
351
374
|
# Handle export if requested
|
|
352
375
|
if export_format:
|
|
353
376
|
from ..export import SearchResultExporter, get_export_path
|