mcp-vector-search 0.12.6__py3-none-any.whl → 1.1.22__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.
Files changed (92) hide show
  1. mcp_vector_search/__init__.py +3 -3
  2. mcp_vector_search/analysis/__init__.py +111 -0
  3. mcp_vector_search/analysis/baseline/__init__.py +68 -0
  4. mcp_vector_search/analysis/baseline/comparator.py +462 -0
  5. mcp_vector_search/analysis/baseline/manager.py +621 -0
  6. mcp_vector_search/analysis/collectors/__init__.py +74 -0
  7. mcp_vector_search/analysis/collectors/base.py +164 -0
  8. mcp_vector_search/analysis/collectors/cohesion.py +463 -0
  9. mcp_vector_search/analysis/collectors/complexity.py +743 -0
  10. mcp_vector_search/analysis/collectors/coupling.py +1162 -0
  11. mcp_vector_search/analysis/collectors/halstead.py +514 -0
  12. mcp_vector_search/analysis/collectors/smells.py +325 -0
  13. mcp_vector_search/analysis/debt.py +516 -0
  14. mcp_vector_search/analysis/interpretation.py +685 -0
  15. mcp_vector_search/analysis/metrics.py +414 -0
  16. mcp_vector_search/analysis/reporters/__init__.py +7 -0
  17. mcp_vector_search/analysis/reporters/console.py +646 -0
  18. mcp_vector_search/analysis/reporters/markdown.py +480 -0
  19. mcp_vector_search/analysis/reporters/sarif.py +377 -0
  20. mcp_vector_search/analysis/storage/__init__.py +93 -0
  21. mcp_vector_search/analysis/storage/metrics_store.py +762 -0
  22. mcp_vector_search/analysis/storage/schema.py +245 -0
  23. mcp_vector_search/analysis/storage/trend_tracker.py +560 -0
  24. mcp_vector_search/analysis/trends.py +308 -0
  25. mcp_vector_search/analysis/visualizer/__init__.py +90 -0
  26. mcp_vector_search/analysis/visualizer/d3_data.py +534 -0
  27. mcp_vector_search/analysis/visualizer/exporter.py +484 -0
  28. mcp_vector_search/analysis/visualizer/html_report.py +2895 -0
  29. mcp_vector_search/analysis/visualizer/schemas.py +525 -0
  30. mcp_vector_search/cli/commands/analyze.py +1062 -0
  31. mcp_vector_search/cli/commands/chat.py +1455 -0
  32. mcp_vector_search/cli/commands/index.py +621 -5
  33. mcp_vector_search/cli/commands/index_background.py +467 -0
  34. mcp_vector_search/cli/commands/init.py +13 -0
  35. mcp_vector_search/cli/commands/install.py +597 -335
  36. mcp_vector_search/cli/commands/install_old.py +8 -4
  37. mcp_vector_search/cli/commands/mcp.py +78 -6
  38. mcp_vector_search/cli/commands/reset.py +68 -26
  39. mcp_vector_search/cli/commands/search.py +224 -8
  40. mcp_vector_search/cli/commands/setup.py +1184 -0
  41. mcp_vector_search/cli/commands/status.py +339 -5
  42. mcp_vector_search/cli/commands/uninstall.py +276 -357
  43. mcp_vector_search/cli/commands/visualize/__init__.py +39 -0
  44. mcp_vector_search/cli/commands/visualize/cli.py +292 -0
  45. mcp_vector_search/cli/commands/visualize/exporters/__init__.py +12 -0
  46. mcp_vector_search/cli/commands/visualize/exporters/html_exporter.py +33 -0
  47. mcp_vector_search/cli/commands/visualize/exporters/json_exporter.py +33 -0
  48. mcp_vector_search/cli/commands/visualize/graph_builder.py +647 -0
  49. mcp_vector_search/cli/commands/visualize/layout_engine.py +469 -0
  50. mcp_vector_search/cli/commands/visualize/server.py +600 -0
  51. mcp_vector_search/cli/commands/visualize/state_manager.py +428 -0
  52. mcp_vector_search/cli/commands/visualize/templates/__init__.py +16 -0
  53. mcp_vector_search/cli/commands/visualize/templates/base.py +234 -0
  54. mcp_vector_search/cli/commands/visualize/templates/scripts.py +4542 -0
  55. mcp_vector_search/cli/commands/visualize/templates/styles.py +2522 -0
  56. mcp_vector_search/cli/didyoumean.py +27 -2
  57. mcp_vector_search/cli/main.py +127 -160
  58. mcp_vector_search/cli/output.py +158 -13
  59. mcp_vector_search/config/__init__.py +4 -0
  60. mcp_vector_search/config/default_thresholds.yaml +52 -0
  61. mcp_vector_search/config/settings.py +12 -0
  62. mcp_vector_search/config/thresholds.py +273 -0
  63. mcp_vector_search/core/__init__.py +16 -0
  64. mcp_vector_search/core/auto_indexer.py +3 -3
  65. mcp_vector_search/core/boilerplate.py +186 -0
  66. mcp_vector_search/core/config_utils.py +394 -0
  67. mcp_vector_search/core/database.py +406 -94
  68. mcp_vector_search/core/embeddings.py +24 -0
  69. mcp_vector_search/core/exceptions.py +11 -0
  70. mcp_vector_search/core/git.py +380 -0
  71. mcp_vector_search/core/git_hooks.py +4 -4
  72. mcp_vector_search/core/indexer.py +632 -54
  73. mcp_vector_search/core/llm_client.py +756 -0
  74. mcp_vector_search/core/models.py +91 -1
  75. mcp_vector_search/core/project.py +17 -0
  76. mcp_vector_search/core/relationships.py +473 -0
  77. mcp_vector_search/core/scheduler.py +11 -11
  78. mcp_vector_search/core/search.py +179 -29
  79. mcp_vector_search/mcp/server.py +819 -9
  80. mcp_vector_search/parsers/python.py +285 -5
  81. mcp_vector_search/utils/__init__.py +2 -0
  82. mcp_vector_search/utils/gitignore.py +0 -3
  83. mcp_vector_search/utils/gitignore_updater.py +212 -0
  84. mcp_vector_search/utils/monorepo.py +66 -4
  85. mcp_vector_search/utils/timing.py +10 -6
  86. {mcp_vector_search-0.12.6.dist-info → mcp_vector_search-1.1.22.dist-info}/METADATA +184 -53
  87. mcp_vector_search-1.1.22.dist-info/RECORD +120 -0
  88. {mcp_vector_search-0.12.6.dist-info → mcp_vector_search-1.1.22.dist-info}/WHEEL +1 -1
  89. {mcp_vector_search-0.12.6.dist-info → mcp_vector_search-1.1.22.dist-info}/entry_points.txt +1 -0
  90. mcp_vector_search/cli/commands/visualize.py +0 -1467
  91. mcp_vector_search-0.12.6.dist-info/RECORD +0 -68
  92. {mcp_vector_search-0.12.6.dist-info → mcp_vector_search-1.1.22.dist-info}/licenses/LICENSE +0 -0
@@ -2,27 +2,39 @@
2
2
 
3
3
  This module provides installation commands for:
4
4
  1. Project initialization (main command)
5
- 2. Platform-specific MCP integrations (subcommands)
5
+ 2. Platform-specific MCP integrations using py-mcp-installer library
6
6
 
7
7
  Examples:
8
8
  # Install in current project
9
9
  $ mcp-vector-search install
10
10
 
11
- # Install Claude Code integration
12
- $ mcp-vector-search install claude-code
11
+ # Install MCP integration (auto-detect platforms)
12
+ $ mcp-vector-search install mcp
13
13
 
14
- # Install all available integrations
15
- $ mcp-vector-search install --all
14
+ # Install to specific platform
15
+ $ mcp-vector-search install mcp --platform cursor
16
+
17
+ # Install to all detected platforms
18
+ $ mcp-vector-search install mcp --all
16
19
  """
17
20
 
18
21
  import asyncio
19
- import json
20
- import shutil
21
22
  from pathlib import Path
22
- from typing import Any
23
23
 
24
24
  import typer
25
25
  from loguru import logger
26
+
27
+ # Import from py-mcp-installer library
28
+ from py_mcp_installer import (
29
+ InstallationError,
30
+ InstallationResult,
31
+ MCPInspector,
32
+ MCPInstaller,
33
+ MCPServerConfig,
34
+ Platform,
35
+ PlatformDetector,
36
+ PlatformInfo,
37
+ )
26
38
  from rich.console import Console
27
39
  from rich.panel import Panel
28
40
  from rich.table import Table
@@ -53,23 +65,25 @@ install_app = create_enhanced_typer(
53
65
  [code]$ mcp-vector-search install[/code]
54
66
 
55
67
  [green]2. MCP Platform Integration[/green]
56
- Add MCP integration for specific platforms:
57
- [code]$ mcp-vector-search install claude-code[/code]
58
- [code]$ mcp-vector-search install cursor[/code]
59
- [code]$ mcp-vector-search install windsurf[/code]
68
+ Add MCP integration with auto-detection:
69
+ [code]$ mcp-vector-search install mcp[/code]
70
+ [code]$ mcp-vector-search install mcp --platform cursor[/code]
71
+ [code]$ mcp-vector-search install mcp --all[/code]
60
72
 
61
73
  [green]3. Complete Setup[/green]
62
74
  Install project + all MCP integrations:
63
75
  [code]$ mcp-vector-search install --with-mcp[/code]
64
76
 
65
77
  [bold cyan]Supported Platforms:[/bold cyan]
66
- • [green]claude-code[/green] - Claude Code (project-scoped .mcp.json)
67
- • [green]claude-desktop[/green] - Claude Desktop (~/.claude/config.json)
68
- • [green]cursor[/green] - Cursor IDE (~/.cursor/mcp.json)
69
- • [green]windsurf[/green] - Windsurf IDE (~/.codeium/windsurf/mcp_config.json)
70
- • [green]vscode[/green] - VS Code (~/.vscode/mcp.json)
71
-
72
- [dim]💡 Use 'mcp-vector-search uninstall <platform>' to remove integrations[/dim]
78
+ • [green]claude-code[/green] - Claude Code
79
+ • [green]claude-desktop[/green] - Claude Desktop
80
+ • [green]cursor[/green] - Cursor IDE
81
+ • [green]auggie[/green] - Auggie
82
+ • [green]codex[/green] - Codex
83
+ • [green]windsurf[/green] - Windsurf IDE
84
+ [green]gemini-cli[/green] - Gemini CLI
85
+
86
+ [dim]💡 Use 'mcp-vector-search uninstall mcp' to remove integrations[/dim]
73
87
  """,
74
88
  invoke_without_command=True,
75
89
  no_args_is_help=False,
@@ -77,190 +91,184 @@ install_app = create_enhanced_typer(
77
91
 
78
92
 
79
93
  # ==============================================================================
80
- # Platform Configuration
94
+ # Helper Functions
81
95
  # ==============================================================================
82
96
 
83
- SUPPORTED_PLATFORMS = {
84
- "claude-code": {
85
- "name": "Claude Code",
86
- "config_path": ".mcp.json", # Project-scoped
87
- "description": "Claude Code with project-scoped configuration",
88
- "scope": "project",
89
- },
90
- "claude-desktop": {
91
- "name": "Claude Desktop",
92
- "config_path": "~/Library/Application Support/Claude/claude_desktop_config.json",
93
- "description": "Claude Desktop application",
94
- "scope": "global",
95
- },
96
- "cursor": {
97
- "name": "Cursor",
98
- "config_path": "~/.cursor/mcp.json",
99
- "description": "Cursor IDE",
100
- "scope": "global",
101
- },
102
- "windsurf": {
103
- "name": "Windsurf",
104
- "config_path": "~/.codeium/windsurf/mcp_config.json",
105
- "description": "Windsurf IDE",
106
- "scope": "global",
107
- },
108
- "vscode": {
109
- "name": "VS Code",
110
- "config_path": "~/.vscode/mcp.json",
111
- "description": "Visual Studio Code",
112
- "scope": "global",
113
- },
114
- }
115
-
116
-
117
- def get_platform_config_path(platform: str, project_root: Path) -> Path:
118
- """Get the configuration file path for a platform.
97
+
98
+ def detect_project_root(start_path: Path | None = None) -> Path:
99
+ """Auto-detect project root directory.
100
+
101
+ Detection priority:
102
+ 1. Directory with .mcp-vector-search/ (project initialized)
103
+ 2. Git repository root
104
+ 3. Current working directory (fallback)
119
105
 
120
106
  Args:
121
- platform: Platform name (e.g., "claude-code", "cursor")
122
- project_root: Project root directory (for project-scoped configs)
107
+ start_path: Starting path for detection (default: current directory)
123
108
 
124
109
  Returns:
125
- Path to the configuration file
110
+ Path to detected project root
126
111
  """
127
- if platform not in SUPPORTED_PLATFORMS:
128
- raise ValueError(f"Unsupported platform: {platform}")
112
+ current = start_path or Path.cwd()
129
113
 
130
- config_info = SUPPORTED_PLATFORMS[platform]
131
- config_path_str = config_info["config_path"]
114
+ # Check for .mcp-vector-search directory (initialized project)
115
+ if (current / ".mcp-vector-search").exists():
116
+ logger.debug(f"Detected project root via .mcp-vector-search: {current}")
117
+ return current
132
118
 
133
- # Resolve project-scoped vs global paths
134
- if config_info["scope"] == "project":
135
- return project_root / config_path_str
136
- else:
137
- return Path(config_path_str).expanduser()
119
+ # Check if we're in a git repository
120
+ git_root = find_git_root(current)
121
+ if git_root and (git_root / ".mcp-vector-search").exists():
122
+ logger.debug(f"Detected project root via git + .mcp-vector-search: {git_root}")
123
+ return git_root
124
+
125
+ # Fallback to current directory
126
+ logger.debug(f"Using current directory as project root: {current}")
127
+ return current
138
128
 
139
129
 
140
- def get_mcp_server_config(
141
- project_root: Path,
142
- platform: str,
143
- enable_watch: bool = True,
144
- ) -> dict[str, Any]:
145
- """Generate MCP server configuration for a platform.
130
+ def find_git_root(path: Path) -> Path | None:
131
+ """Find git repository root by walking up directory tree.
146
132
 
147
133
  Args:
148
- project_root: Project root directory
149
- platform: Platform name
150
- enable_watch: Whether to enable file watching
134
+ path: Starting path
151
135
 
152
136
  Returns:
153
- Dictionary containing MCP server configuration
137
+ Path to git root or None if not in a git repo
154
138
  """
155
- # Base configuration using uv for compatibility
156
- config: dict[str, Any] = {
157
- "command": "uv",
158
- "args": ["run", "mcp-vector-search", "mcp"],
159
- "env": {
160
- "MCP_ENABLE_FILE_WATCHING": "true" if enable_watch else "false",
161
- },
162
- }
139
+ current = path.resolve()
140
+ while current != current.parent:
141
+ if (current / ".git").exists():
142
+ logger.debug(f"Found git root: {current}")
143
+ return current
144
+ current = current.parent
145
+ return None
146
+
163
147
 
164
- # Platform-specific adjustments
165
- if platform in ("claude-code", "cursor", "windsurf", "vscode"):
166
- # These platforms require "type": "stdio"
167
- config["type"] = "stdio"
148
+ def _get_claude_desktop_config_path() -> Path | None:
149
+ """Get the default config path for Claude Desktop based on OS.
168
150
 
169
- # Only add cwd for global-scope platforms (not project-scoped)
170
- if SUPPORTED_PLATFORMS[platform]["scope"] == "global":
171
- config["cwd"] = str(project_root.absolute())
151
+ Returns:
152
+ Path to Claude Desktop config or None if unknown OS
153
+ """
154
+ import sys
155
+
156
+ if sys.platform == "darwin":
157
+ # macOS
158
+ return (
159
+ Path.home()
160
+ / "Library"
161
+ / "Application Support"
162
+ / "Claude"
163
+ / "claude_desktop_config.json"
164
+ )
165
+ elif sys.platform == "win32":
166
+ # Windows
167
+ import os
172
168
 
173
- return config
169
+ appdata = os.environ.get("APPDATA", "")
170
+ if appdata:
171
+ return Path(appdata) / "Claude" / "claude_desktop_config.json"
172
+ else:
173
+ # Linux
174
+ return Path.home() / ".config" / "Claude" / "claude_desktop_config.json"
175
+ return None
174
176
 
175
177
 
176
- def detect_installed_platforms() -> dict[str, Path]:
177
- """Detect which MCP platforms are installed on the system.
178
+ def detect_all_platforms() -> list[PlatformInfo]:
179
+ """Detect all available platforms on the system.
178
180
 
179
181
  Returns:
180
- Dictionary mapping platform names to their config paths
182
+ List of detected platforms with confidence scores
181
183
  """
182
- detected = {}
184
+ detector = PlatformDetector()
185
+ detected_platforms = []
186
+
187
+ # Try to detect each platform
188
+ platform_detectors = {
189
+ Platform.CLAUDE_CODE: detector.detect_claude_code,
190
+ Platform.CLAUDE_DESKTOP: detector.detect_claude_desktop,
191
+ Platform.CURSOR: detector.detect_cursor,
192
+ Platform.AUGGIE: detector.detect_auggie,
193
+ Platform.CODEX: detector.detect_codex,
194
+ Platform.WINDSURF: detector.detect_windsurf,
195
+ Platform.GEMINI_CLI: detector.detect_gemini_cli,
196
+ }
183
197
 
184
- for platform, info in SUPPORTED_PLATFORMS.items():
185
- # For project-scoped platforms, always include them
186
- if info["scope"] == "project":
187
- detected[platform] = Path(info["config_path"])
198
+ for platform_enum, detector_func in platform_detectors.items():
199
+ try:
200
+ confidence, config_path = detector_func()
201
+
202
+ # Determine CLI availability
203
+ cli_available = False
204
+ from py_mcp_installer.utils import resolve_command_path
205
+
206
+ if platform_enum in (Platform.CLAUDE_CODE, Platform.CLAUDE_DESKTOP):
207
+ cli_available = resolve_command_path("claude") is not None
208
+ elif platform_enum == Platform.CURSOR:
209
+ cli_available = resolve_command_path("cursor") is not None
210
+
211
+ # Include platform if:
212
+ # 1. Has config file with confidence > 0, OR
213
+ # 2. Has CLI available (can create config)
214
+ # For CLI-based platforms, we can configure even without existing config
215
+ if confidence > 0.0 and config_path:
216
+ # Has existing config file
217
+ platform_info = PlatformInfo(
218
+ platform=platform_enum,
219
+ confidence=confidence,
220
+ config_path=config_path,
221
+ cli_available=cli_available,
222
+ )
223
+ detected_platforms.append(platform_info)
224
+ elif cli_available and platform_enum in (
225
+ Platform.CLAUDE_CODE,
226
+ Platform.CLAUDE_DESKTOP,
227
+ Platform.CURSOR,
228
+ ):
229
+ # CLI available but no config yet - we can still configure it
230
+ # Use default config path for the platform
231
+ default_config_paths = {
232
+ Platform.CLAUDE_CODE: Path.home()
233
+ / ".config"
234
+ / "claude"
235
+ / "mcp.json",
236
+ Platform.CLAUDE_DESKTOP: _get_claude_desktop_config_path(),
237
+ Platform.CURSOR: Path.home() / ".cursor" / "mcp.json",
238
+ }
239
+ platform_info = PlatformInfo(
240
+ platform=platform_enum,
241
+ confidence=0.2, # Low confidence since no config exists yet
242
+ config_path=default_config_paths.get(platform_enum),
243
+ cli_available=cli_available,
244
+ )
245
+ detected_platforms.append(platform_info)
246
+ except Exception as e:
247
+ logger.debug(f"Failed to detect {platform_enum.value}: {e}")
188
248
  continue
189
249
 
190
- # For global platforms, check if config directory exists
191
- config_path = Path(info["config_path"]).expanduser()
192
- if config_path.parent.exists():
193
- detected[platform] = config_path
194
-
195
- return detected
250
+ return detected_platforms
196
251
 
197
252
 
198
- def configure_platform(
199
- platform: str,
200
- project_root: Path,
201
- server_name: str = "mcp-vector-search",
202
- enable_watch: bool = True,
203
- force: bool = False,
204
- ) -> bool:
205
- """Configure MCP integration for a specific platform.
253
+ def platform_name_to_enum(name: str) -> Platform | None:
254
+ """Convert platform name to enum.
206
255
 
207
256
  Args:
208
- platform: Platform name (e.g., "claude-code", "cursor")
209
- project_root: Project root directory
210
- server_name: Name for the MCP server entry
211
- enable_watch: Whether to enable file watching
212
- force: Whether to overwrite existing configuration
257
+ name: Platform name (e.g., "cursor", "claude-code")
213
258
 
214
259
  Returns:
215
- True if configuration was successful, False otherwise
260
+ Platform enum or None if not found
216
261
  """
217
- try:
218
- config_path = get_platform_config_path(platform, project_root)
219
-
220
- # Create backup if file exists
221
- if config_path.exists():
222
- backup_path = config_path.with_suffix(config_path.suffix + ".backup")
223
- shutil.copy2(config_path, backup_path)
224
-
225
- # Load existing config
226
- with open(config_path) as f:
227
- config = json.load(f)
228
-
229
- # Check if server already exists
230
- if "mcpServers" in config and server_name in config["mcpServers"]:
231
- if not force:
232
- print_warning(
233
- f" ⚠️ Server '{server_name}' already exists in {platform} config"
234
- )
235
- print_info(" Use --force to overwrite")
236
- return False
237
- else:
238
- # Create new config
239
- config_path.parent.mkdir(parents=True, exist_ok=True)
240
- config = {}
241
-
242
- # Ensure mcpServers section exists
243
- if "mcpServers" not in config:
244
- config["mcpServers"] = {}
245
-
246
- # Add server configuration
247
- server_config = get_mcp_server_config(project_root, platform, enable_watch)
248
- config["mcpServers"][server_name] = server_config
249
-
250
- # Write configuration
251
- with open(config_path, "w") as f:
252
- json.dump(config, f, indent=2)
253
-
254
- platform_name = SUPPORTED_PLATFORMS[platform]["name"]
255
- print_success(f" ✅ Configured {platform_name}")
256
- print_info(f" Config: {config_path}")
257
-
258
- return True
259
-
260
- except Exception as e:
261
- logger.error(f"Failed to configure {platform}: {e}")
262
- print_error(f" ❌ Failed to configure {platform}: {e}")
263
- return False
262
+ name_map = {
263
+ "claude-code": Platform.CLAUDE_CODE,
264
+ "claude-desktop": Platform.CLAUDE_DESKTOP,
265
+ "cursor": Platform.CURSOR,
266
+ "auggie": Platform.AUGGIE,
267
+ "codex": Platform.CODEX,
268
+ "windsurf": Platform.WINDSURF,
269
+ "gemini-cli": Platform.GEMINI_CLI,
270
+ }
271
+ return name_map.get(name.lower())
264
272
 
265
273
 
266
274
  # ==============================================================================
@@ -409,15 +417,15 @@ def main(
409
417
  # Install MCP integrations if requested
410
418
  if with_mcp:
411
419
  console.print("\n[bold blue]🔗 Installing MCP integrations...[/bold blue]")
412
- detected = detect_installed_platforms()
420
+ detected = detect_all_platforms()
413
421
 
414
422
  if detected:
415
- for platform in detected:
416
- configure_platform(platform, project_root, enable_watch=True)
423
+ for platform_info in detected:
424
+ _install_to_platform(platform_info, project_root)
417
425
  else:
418
426
  print_warning("No MCP platforms detected")
419
427
  print_info("Install platforms manually using:")
420
- print_info(" mcp-vector-search install <platform>")
428
+ print_info(" mcp-vector-search install mcp --platform <platform>")
421
429
 
422
430
  # Success message
423
431
  console.print("\n[bold green]🎉 Installation Complete![/bold green]")
@@ -429,11 +437,14 @@ def main(
429
437
 
430
438
  if not with_mcp:
431
439
  next_steps.append(
432
- "[cyan]mcp-vector-search install claude-code[/cyan] - Add MCP integration"
440
+ "[cyan]mcp-vector-search install mcp[/cyan] - Add MCP integration"
433
441
  )
434
442
 
435
443
  print_next_steps(next_steps, title="Ready to Use")
436
444
 
445
+ except typer.Exit:
446
+ # Re-raise typer.Exit to allow proper exit handling
447
+ raise
437
448
  except ProjectInitializationError as e:
438
449
  print_error(f"Installation failed: {e}")
439
450
  raise typer.Exit(1)
@@ -444,231 +455,482 @@ def main(
444
455
 
445
456
 
446
457
  # ==============================================================================
447
- # Platform-Specific Installation Commands
458
+ # MCP Installation Command
448
459
  # ==============================================================================
449
460
 
450
461
 
451
- @install_app.command("claude-code")
452
- def install_claude_code(
462
+ def _install_to_platform(
463
+ platform_info: PlatformInfo, project_root: Path, force: bool = True
464
+ ) -> bool:
465
+ """Install to a specific platform.
466
+
467
+ Args:
468
+ platform_info: Platform information
469
+ project_root: Project root directory
470
+ force: If True, overwrite existing installation (default: True)
471
+
472
+ Returns:
473
+ True if installation succeeded
474
+ """
475
+ import io
476
+ import sys
477
+
478
+ try:
479
+ # Create installer for this platform
480
+ installer = MCPInstaller(platform=platform_info.platform)
481
+
482
+ # Detect installation method (uv vs direct command)
483
+ import shutil
484
+
485
+ use_uv = shutil.which("uv") is not None
486
+ mcp_cmd = shutil.which("mcp-vector-search")
487
+
488
+ if use_uv:
489
+ # Development mode with uv
490
+ command = "uv"
491
+ args = ["run", "--directory", str(project_root), "mcp-vector-search", "mcp"]
492
+ elif mcp_cmd:
493
+ # Production mode with installed package
494
+ command = "mcp-vector-search"
495
+ args = ["mcp"]
496
+ else:
497
+ # Fallback to uv (will fail if not available)
498
+ command = "uv"
499
+ args = ["run", "mcp-vector-search", "mcp"]
500
+
501
+ # Create server configuration
502
+ server_config = MCPServerConfig(
503
+ name="mcp-vector-search",
504
+ command=command,
505
+ args=args,
506
+ env={
507
+ "PROJECT_ROOT": str(project_root.resolve()),
508
+ "MCP_PROJECT_ROOT": str(project_root.resolve()),
509
+ },
510
+ description=f"Semantic code search for {project_root.name}",
511
+ )
512
+
513
+ def do_install() -> InstallationResult:
514
+ """Execute the installation."""
515
+ return installer.install_server(
516
+ name=server_config.name,
517
+ command=server_config.command,
518
+ args=server_config.args,
519
+ env=server_config.env,
520
+ description=server_config.description,
521
+ )
522
+
523
+ # Try to install, suppressing verbose stderr output from py-mcp-installer
524
+ try:
525
+ # Capture stderr to suppress verbose traceback output
526
+ old_stderr = sys.stderr
527
+ sys.stderr = io.StringIO()
528
+ try:
529
+ result = do_install()
530
+ finally:
531
+ captured_stderr = sys.stderr.getvalue()
532
+ sys.stderr = old_stderr
533
+ # Log captured output at debug level for troubleshooting
534
+ if captured_stderr.strip():
535
+ logger.debug(f"Installation stderr: {captured_stderr[:500]}")
536
+ except InstallationError as e:
537
+ # Restore stderr before handling error
538
+ if sys.stderr != old_stderr:
539
+ sys.stderr = old_stderr
540
+
541
+ # Check if it's an "already exists" error
542
+ error_msg = str(e).lower()
543
+ if "already exists" in error_msg and force:
544
+ # Silently uninstall first, then reinstall
545
+ logger.debug(
546
+ f"Server already exists on {platform_info.platform.value}, "
547
+ "removing and reinstalling..."
548
+ )
549
+ try:
550
+ # Suppress stderr during uninstall too
551
+ old_stderr = sys.stderr
552
+ sys.stderr = io.StringIO()
553
+ try:
554
+ installer.uninstall_server(server_config.name)
555
+ result = do_install()
556
+ finally:
557
+ sys.stderr = old_stderr
558
+ except Exception as uninstall_err:
559
+ logger.debug(
560
+ f"Failed to uninstall existing server: {uninstall_err}"
561
+ )
562
+ # Server already exists is not a failure - it's already configured
563
+ print_success(f" ✅ Installed to {platform_info.platform.value}")
564
+ if platform_info.config_path:
565
+ print_info(f" Config: {platform_info.config_path}")
566
+ return True
567
+ else:
568
+ raise
569
+
570
+ if result.success:
571
+ print_success(f" ✅ Installed to {platform_info.platform.value}")
572
+ if result.config_path:
573
+ print_info(f" Config: {result.config_path}")
574
+
575
+ # Validate installation
576
+ inspector = MCPInspector(platform_info)
577
+ report = inspector.inspect()
578
+
579
+ if report.has_errors():
580
+ print_warning(" ⚠️ Configuration has issues:")
581
+ for issue in report.issues:
582
+ if issue.severity == "error":
583
+ print_warning(f" • {issue.message}")
584
+
585
+ return True
586
+ else:
587
+ print_error(
588
+ f" ❌ Failed to install to {platform_info.platform.value}: {result.message}"
589
+ )
590
+ return False
591
+
592
+ except Exception as e:
593
+ logger.debug(f"Installation to {platform_info.platform.value} failed: {e}")
594
+ # Don't print full exception - just a clean error message
595
+ error_str = str(e)
596
+ # Extract just the main error message, not the full traceback
597
+ if "already exists" in error_str.lower():
598
+ # Already exists is a success case - server is configured
599
+ print_success(f" ✅ Installed to {platform_info.platform.value}")
600
+ if platform_info.config_path:
601
+ print_info(f" Config: {platform_info.config_path}")
602
+ return True
603
+ else:
604
+ # Extract first line of error for clean output
605
+ short_error = error_str.split("\n")[0][:100]
606
+ print_error(f" ❌ Failed: {platform_info.platform.value} - {short_error}")
607
+ return False
608
+
609
+
610
+ @install_app.command(name="mcp")
611
+ def install_mcp(
453
612
  ctx: typer.Context,
454
- enable_watch: bool = typer.Option(
613
+ platform: str | None = typer.Option(
614
+ None,
615
+ "--platform",
616
+ "-p",
617
+ help="Specific platform to install to (e.g., cursor, claude-code)",
618
+ ),
619
+ all_platforms: bool = typer.Option(
620
+ False,
621
+ "--all",
622
+ "-a",
623
+ help="Install to all detected platforms",
624
+ ),
625
+ auto: bool = typer.Option(
455
626
  True,
456
- "--watch/--no-watch",
457
- help="Enable file watching for auto-reindex",
627
+ "--auto/--no-auto",
628
+ help="Auto-detect project root (default: enabled)",
458
629
  ),
459
- force: bool = typer.Option(
630
+ dry_run: bool = typer.Option(
460
631
  False,
461
- "--force",
462
- "-f",
463
- help="Force overwrite existing configuration",
632
+ "--dry-run",
633
+ help="Preview changes without applying them",
464
634
  ),
465
635
  ) -> None:
466
- """Install Claude Code MCP integration (project-scoped).
636
+ """Install MCP integration to platforms.
637
+
638
+ Auto-detects available platforms and installs mcp-vector-search as an MCP server.
639
+ Automatically detects project root from current directory or git repository.
640
+
641
+ [bold cyan]Examples:[/bold cyan]
642
+
643
+ [green]Auto-detect and install (recommended):[/green]
644
+ $ mcp-vector-search install mcp
645
+ $ mcp-vector-search install mcp --auto
467
646
 
468
- Creates .mcp.json in the project root for team sharing.
469
- This file should be committed to version control.
647
+ [green]Install to specific platform:[/green]
648
+ $ mcp-vector-search install mcp --platform cursor
649
+
650
+ [green]Install to all detected platforms:[/green]
651
+ $ mcp-vector-search install mcp --all
652
+
653
+ [green]Preview changes (dry run):[/green]
654
+ $ mcp-vector-search install mcp --dry-run
655
+
656
+ [green]Use current directory as project root (no auto-detection):[/green]
657
+ $ mcp-vector-search install mcp --no-auto
470
658
  """
471
- project_root = ctx.obj.get("project_root") or Path.cwd()
659
+ # Auto-detect project root if enabled
660
+ if auto:
661
+ project_root = detect_project_root()
662
+ console.print(f"[dim]🔍 Auto-detected project root: {project_root}[/dim]\n")
663
+ else:
664
+ project_root = ctx.obj.get("project_root") or Path.cwd()
665
+ console.print(f"[dim]📁 Using project root: {project_root}[/dim]\n")
472
666
 
473
667
  console.print(
474
668
  Panel.fit(
475
- "[bold cyan]Installing Claude Code Integration[/bold cyan]\n"
476
- "📁 Project-scoped configuration",
669
+ "[bold cyan]Installing MCP Integration[/bold cyan]\n"
670
+ f"📁 Project: {project_root}",
477
671
  border_style="cyan",
478
672
  )
479
673
  )
480
674
 
481
- success = configure_platform(
482
- "claude-code", project_root, enable_watch=enable_watch, force=force
483
- )
675
+ try:
676
+ # Detect available platforms
677
+ print_info("🔍 Detecting available MCP platforms...")
678
+ detected = detect_all_platforms()
679
+
680
+ if not detected:
681
+ print_warning("No MCP platforms detected.")
682
+ print_info(
683
+ "Supported platforms: claude-code, claude-desktop, cursor, auggie, codex, windsurf, gemini-cli"
684
+ )
685
+ raise typer.Exit(0)
686
+
687
+ # Display detected platforms
688
+ table = Table(title="Detected MCP Platforms")
689
+ table.add_column("Platform", style="cyan")
690
+ table.add_column("Config Path", style="green")
691
+ table.add_column("Confidence", style="yellow")
692
+ table.add_column("CLI", style="magenta")
693
+
694
+ for p in detected:
695
+ table.add_row(
696
+ p.platform.value,
697
+ str(p.config_path) if p.config_path else "N/A",
698
+ f"{p.confidence:.2f}",
699
+ "✅" if p.cli_available else "❌",
700
+ )
701
+
702
+ console.print(table)
703
+
704
+ # Filter platforms
705
+ target_platforms = detected
706
+
707
+ if platform:
708
+ # Install to specific platform
709
+ platform_enum = platform_name_to_enum(platform)
710
+ if not platform_enum:
711
+ print_error(f"Unknown platform: {platform}")
712
+ print_info(
713
+ "Supported: claude-code, claude-desktop, cursor, auggie, codex, windsurf, gemini-cli"
714
+ )
715
+ raise typer.Exit(1)
716
+
717
+ target_platforms = [p for p in detected if p.platform == platform_enum]
718
+
719
+ if not target_platforms:
720
+ print_error(f"Platform '{platform}' not detected on this system")
721
+ raise typer.Exit(1)
722
+
723
+ elif not all_platforms:
724
+ # By default, install to highest confidence platform only
725
+ if detected:
726
+ max_confidence_platform = max(detected, key=lambda p: p.confidence)
727
+ target_platforms = [max_confidence_platform]
728
+ print_info(
729
+ f"Installing to highest confidence platform: {max_confidence_platform.platform.value}"
730
+ )
731
+ print_info("Use --all to install to all detected platforms")
732
+
733
+ # Show what will be installed
734
+ console.print("\n[bold]Target platforms:[/bold]")
735
+ for p in target_platforms:
736
+ console.print(f" • {p.platform.value}")
737
+
738
+ if dry_run:
739
+ console.print("\n[bold yellow]🔍 DRY RUN MODE[/bold yellow]")
740
+ print_info("No changes will be applied")
741
+ return
742
+
743
+ # Install to each platform
744
+ console.print("\n[bold]Installing...[/bold]")
745
+ successful = 0
746
+ failed = 0
747
+
748
+ for platform_info in target_platforms:
749
+ if _install_to_platform(platform_info, project_root):
750
+ successful += 1
751
+ else:
752
+ failed += 1
753
+
754
+ # Summary
755
+ console.print("\n[bold green]✨ Installation Summary[/bold green]")
756
+ console.print(f" ✅ Successful: {successful}")
757
+ if failed > 0:
758
+ console.print(f" ❌ Failed: {failed}")
484
759
 
485
- if success:
486
- console.print(
487
- "\n[bold green]✨ Claude Code Integration Installed![/bold green]"
488
- )
489
760
  console.print("\n[bold blue]Next Steps:[/bold blue]")
490
- console.print(" 1. Open Claude Code in this project directory")
761
+ console.print(" 1. Restart your AI coding tool")
491
762
  console.print(" 2. The MCP server will be available automatically")
492
763
  console.print(" 3. Try: 'Search my code for authentication functions'")
493
- console.print("\n[dim]💡 Commit .mcp.json to share with your team[/dim]")
494
- else:
764
+
765
+ except typer.Exit:
766
+ raise
767
+ except Exception as e:
768
+ logger.exception("MCP installation failed")
769
+ print_error(f"Installation failed: {e}")
495
770
  raise typer.Exit(1)
496
771
 
497
772
 
498
- @install_app.command("cursor")
499
- def install_cursor(
500
- ctx: typer.Context,
501
- enable_watch: bool = typer.Option(True, "--watch/--no-watch"),
502
- force: bool = typer.Option(False, "--force", "-f"),
503
- ) -> None:
504
- """Install Cursor IDE MCP integration (global)."""
505
- project_root = ctx.obj.get("project_root") or Path.cwd()
773
+ # ==============================================================================
774
+ # MCP Status Command
775
+ # ==============================================================================
506
776
 
507
- console.print(
508
- Panel.fit(
509
- "[bold cyan]Installing Cursor Integration[/bold cyan]\n"
510
- "🌐 Global configuration (~/.cursor/mcp.json)",
511
- border_style="cyan",
512
- )
513
- )
514
777
 
515
- success = configure_platform(
516
- "cursor", project_root, enable_watch=enable_watch, force=force
517
- )
778
+ @install_app.command("mcp-status")
779
+ def mcp_status(ctx: typer.Context) -> None:
780
+ """Show MCP integration status for all platforms.
518
781
 
519
- if success:
520
- console.print("\n[bold green]✨ Cursor Integration Installed![/bold green]")
521
- console.print("\n[bold blue]Next Steps:[/bold blue]")
522
- console.print(" 1. Restart Cursor IDE")
523
- console.print(" 2. Open this project in Cursor")
524
- console.print(" 3. MCP tools should be available")
525
- else:
526
- raise typer.Exit(1)
782
+ Displays which platforms have mcp-vector-search configured,
783
+ the detected project root, and configuration details.
527
784
 
785
+ [bold cyan]Examples:[/bold cyan]
528
786
 
529
- @install_app.command("windsurf")
530
- def install_windsurf(
531
- ctx: typer.Context,
532
- enable_watch: bool = typer.Option(True, "--watch/--no-watch"),
533
- force: bool = typer.Option(False, "--force", "-f"),
534
- ) -> None:
535
- """Install Windsurf IDE MCP integration (global)."""
536
- project_root = ctx.obj.get("project_root") or Path.cwd()
787
+ [green]Check status:[/green]
788
+ $ mcp-vector-search install mcp-status
789
+ """
790
+ # Auto-detect project root
791
+ project_root = detect_project_root()
537
792
 
538
793
  console.print(
539
794
  Panel.fit(
540
- "[bold cyan]Installing Windsurf Integration[/bold cyan]\n"
541
- "🌐 Global configuration (~/.codeium/windsurf/mcp_config.json)",
795
+ f"[bold cyan]MCP Integration Status[/bold cyan]\n"
796
+ f"📁 Detected Project: {project_root}",
542
797
  border_style="cyan",
543
798
  )
544
799
  )
545
800
 
546
- success = configure_platform(
547
- "windsurf", project_root, enable_watch=enable_watch, force=force
548
- )
801
+ try:
802
+ # Detect all platforms
803
+ detected = detect_all_platforms()
549
804
 
550
- if success:
551
- console.print("\n[bold green]✨ Windsurf Integration Installed![/bold green]")
552
- console.print("\n[bold blue]Next Steps:[/bold blue]")
553
- console.print(" 1. Restart Windsurf IDE")
554
- console.print(" 2. Open this project in Windsurf")
555
- console.print(" 3. MCP tools should be available")
556
- else:
557
- raise typer.Exit(1)
805
+ if not detected:
806
+ print_warning("No MCP platforms detected")
807
+ return
558
808
 
809
+ # Create status table
810
+ table = Table(show_header=True, header_style="bold cyan")
811
+ table.add_column("Platform", style="cyan")
812
+ table.add_column("Status", style="green")
813
+ table.add_column("Config Path")
814
+ table.add_column("Project Root")
559
815
 
560
- @install_app.command("claude-desktop")
561
- def install_claude_desktop(
562
- ctx: typer.Context,
563
- enable_watch: bool = typer.Option(True, "--watch/--no-watch"),
564
- force: bool = typer.Option(False, "--force", "-f"),
565
- ) -> None:
566
- """Install Claude Desktop MCP integration (global)."""
567
- project_root = ctx.obj.get("project_root") or Path.cwd()
816
+ for platform_info in detected:
817
+ try:
818
+ # Check if mcp-vector-search is configured
819
+ installer = MCPInstaller(platform=platform_info.platform)
820
+ server = installer.get_server("mcp-vector-search")
821
+
822
+ if server:
823
+ status = "✅ Configured"
824
+ # Extract project root from env
825
+ env = server.get("env", {})
826
+ configured_root = env.get("MCP_PROJECT_ROOT") or env.get(
827
+ "PROJECT_ROOT", "N/A"
828
+ )
568
829
 
569
- console.print(
570
- Panel.fit(
571
- "[bold cyan]Installing Claude Desktop Integration[/bold cyan]\n"
572
- "🌐 Global configuration (~/.claude/config.json)",
573
- border_style="cyan",
574
- )
575
- )
830
+ # Check if it matches current project
831
+ if configured_root != "N/A":
832
+ configured_path = Path(configured_root)
833
+ if configured_path == project_root:
834
+ status = "✅ Configured (current project)"
835
+ else:
836
+ status = "⚠️ Configured (different project)"
837
+ else:
838
+ status = "❌ Not configured"
839
+ configured_root = "N/A"
576
840
 
577
- success = configure_platform(
578
- "claude-desktop", project_root, enable_watch=enable_watch, force=force
579
- )
841
+ except Exception as e:
842
+ logger.debug(f"Failed to check {platform_info.platform.value}: {e}")
843
+ status = "❓ Unknown"
844
+ configured_root = "N/A"
845
+
846
+ table.add_row(
847
+ platform_info.platform.value,
848
+ status,
849
+ str(platform_info.config_path) if platform_info.config_path else "N/A",
850
+ configured_root,
851
+ )
852
+
853
+ console.print(table)
580
854
 
581
- if success:
855
+ # Show next steps
856
+ console.print("\n[bold blue]Quick Actions:[/bold blue]")
582
857
  console.print(
583
- "\n[bold green]✨ Claude Desktop Integration Installed![/bold green]"
858
+ " mcp-vector-search install mcp # Install to auto-detected platform"
584
859
  )
585
- console.print("\n[bold blue]Next Steps:[/bold blue]")
586
- console.print(" 1. Restart Claude Desktop")
587
- console.print(" 2. The mcp-vector-search server will be available")
588
- console.print(" 3. Open conversations in the project directory")
589
- else:
590
- raise typer.Exit(1)
591
-
592
-
593
- @install_app.command("vscode")
594
- def install_vscode(
595
- ctx: typer.Context,
596
- enable_watch: bool = typer.Option(True, "--watch/--no-watch"),
597
- force: bool = typer.Option(False, "--force", "-f"),
598
- ) -> None:
599
- """Install VS Code MCP integration (global)."""
600
- project_root = ctx.obj.get("project_root") or Path.cwd()
601
-
602
- console.print(
603
- Panel.fit(
604
- "[bold cyan]Installing VS Code Integration[/bold cyan]\n"
605
- "🌐 Global configuration (~/.vscode/mcp.json)",
606
- border_style="cyan",
860
+ console.print(
861
+ " mcp-vector-search install mcp --all # Install to all platforms"
862
+ )
863
+ console.print(
864
+ " mcp-vector-search install mcp --platform <name> # Install to specific platform"
607
865
  )
608
- )
609
-
610
- success = configure_platform(
611
- "vscode", project_root, enable_watch=enable_watch, force=force
612
- )
613
866
 
614
- if success:
615
- console.print("\n[bold green]✨ VS Code Integration Installed![/bold green]")
616
- console.print("\n[bold blue]Next Steps:[/bold blue]")
617
- console.print(" 1. Restart VS Code")
618
- console.print(" 2. Open this project in VS Code")
619
- console.print(" 3. MCP tools should be available")
620
- else:
867
+ except Exception as e:
868
+ logger.exception("Failed to check MCP status")
869
+ print_error(f"Status check failed: {e}")
621
870
  raise typer.Exit(1)
622
871
 
623
872
 
624
- @install_app.command("list")
625
- def list_platforms(ctx: typer.Context) -> None:
626
- """List all supported MCP platforms and their installation status."""
627
- project_root = ctx.obj.get("project_root") or Path.cwd()
873
+ # ==============================================================================
874
+ # List Platforms Command
875
+ # ==============================================================================
628
876
 
877
+
878
+ @install_app.command("list-platforms")
879
+ def list_platforms(ctx: typer.Context) -> None:
880
+ """List all detected MCP platforms and their status."""
629
881
  console.print(
630
882
  Panel.fit("[bold cyan]MCP Platform Status[/bold cyan]", border_style="cyan")
631
883
  )
632
884
 
633
- table = Table(show_header=True, header_style="bold cyan")
634
- table.add_column("Platform", style="cyan")
635
- table.add_column("Name")
636
- table.add_column("Status")
637
- table.add_column("Config Location")
885
+ try:
886
+ detected = detect_all_platforms()
638
887
 
639
- detected = detect_installed_platforms()
888
+ if not detected:
889
+ print_warning("No MCP platforms detected")
890
+ print_info(
891
+ "Supported platforms: claude-code, claude-desktop, cursor, auggie, codex, windsurf, gemini-cli"
892
+ )
893
+ return
640
894
 
641
- for platform, info in SUPPORTED_PLATFORMS.items():
642
- config_path = get_platform_config_path(platform, project_root)
895
+ table = Table(show_header=True, header_style="bold cyan")
896
+ table.add_column("Platform", style="cyan")
897
+ table.add_column("Status", style="green")
898
+ table.add_column("Config Path")
899
+ table.add_column("Confidence", style="yellow")
643
900
 
644
- # Check if configured
645
- is_configured = False
646
- if config_path.exists():
901
+ for platform_info in detected:
902
+ # Check if mcp-vector-search is configured
647
903
  try:
648
- with open(config_path) as f:
649
- config = json.load(f)
650
- is_configured = "mcp-vector-search" in config.get("mcpServers", {})
904
+ installer = MCPInstaller(platform=platform_info.platform)
905
+ server = installer.get_server("mcp-vector-search")
906
+ status = "✅ Configured" if server else "⚠️ Available"
651
907
  except Exception:
652
- pass
908
+ status = "⚠️ Available"
653
909
 
654
- status = (
655
- "✅ Configured"
656
- if is_configured
657
- else ("⚠️ Available" if platform in detected else "❌ Not Found")
658
- )
910
+ table.add_row(
911
+ platform_info.platform.value,
912
+ status,
913
+ str(platform_info.config_path) if platform_info.config_path else "N/A",
914
+ f"{platform_info.confidence:.2f}",
915
+ )
659
916
 
660
- table.add_row(
661
- platform,
662
- info["name"],
663
- status,
664
- str(config_path) if info["scope"] == "project" else info["config_path"],
665
- )
917
+ console.print(table)
666
918
 
667
- console.print(table)
919
+ console.print("\n[bold blue]Installation Commands:[/bold blue]")
920
+ console.print(
921
+ " mcp-vector-search install mcp # Auto-detect"
922
+ )
923
+ console.print(
924
+ " mcp-vector-search install mcp --platform <name> # Specific platform"
925
+ )
926
+ console.print(
927
+ " mcp-vector-search install mcp --all # All platforms"
928
+ )
668
929
 
669
- console.print("\n[bold blue]Installation Commands:[/bold blue]")
670
- for platform in SUPPORTED_PLATFORMS:
671
- console.print(f" mcp-vector-search install {platform}")
930
+ except Exception as e:
931
+ logger.exception("Failed to list platforms")
932
+ print_error(f"Failed to list platforms: {e}")
933
+ raise typer.Exit(1)
672
934
 
673
935
 
674
936
  if __name__ == "__main__":