mcp-vector-search 0.15.7__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.
Potentially problematic release.
This version of mcp-vector-search might be problematic. Click here for more details.
- mcp_vector_search/__init__.py +10 -0
- mcp_vector_search/cli/__init__.py +1 -0
- mcp_vector_search/cli/commands/__init__.py +1 -0
- mcp_vector_search/cli/commands/auto_index.py +397 -0
- mcp_vector_search/cli/commands/chat.py +534 -0
- mcp_vector_search/cli/commands/config.py +393 -0
- mcp_vector_search/cli/commands/demo.py +358 -0
- mcp_vector_search/cli/commands/index.py +762 -0
- mcp_vector_search/cli/commands/init.py +658 -0
- mcp_vector_search/cli/commands/install.py +869 -0
- mcp_vector_search/cli/commands/install_old.py +700 -0
- mcp_vector_search/cli/commands/mcp.py +1254 -0
- mcp_vector_search/cli/commands/reset.py +393 -0
- mcp_vector_search/cli/commands/search.py +796 -0
- mcp_vector_search/cli/commands/setup.py +1133 -0
- mcp_vector_search/cli/commands/status.py +584 -0
- mcp_vector_search/cli/commands/uninstall.py +404 -0
- mcp_vector_search/cli/commands/visualize/__init__.py +39 -0
- mcp_vector_search/cli/commands/visualize/cli.py +265 -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 +709 -0
- mcp_vector_search/cli/commands/visualize/layout_engine.py +469 -0
- mcp_vector_search/cli/commands/visualize/server.py +201 -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 +218 -0
- mcp_vector_search/cli/commands/visualize/templates/scripts.py +3670 -0
- mcp_vector_search/cli/commands/visualize/templates/styles.py +779 -0
- mcp_vector_search/cli/commands/visualize.py.original +2536 -0
- mcp_vector_search/cli/commands/watch.py +287 -0
- mcp_vector_search/cli/didyoumean.py +520 -0
- mcp_vector_search/cli/export.py +320 -0
- mcp_vector_search/cli/history.py +295 -0
- mcp_vector_search/cli/interactive.py +342 -0
- mcp_vector_search/cli/main.py +484 -0
- mcp_vector_search/cli/output.py +414 -0
- mcp_vector_search/cli/suggestions.py +375 -0
- mcp_vector_search/config/__init__.py +1 -0
- mcp_vector_search/config/constants.py +24 -0
- mcp_vector_search/config/defaults.py +200 -0
- mcp_vector_search/config/settings.py +146 -0
- mcp_vector_search/core/__init__.py +1 -0
- mcp_vector_search/core/auto_indexer.py +298 -0
- mcp_vector_search/core/config_utils.py +394 -0
- mcp_vector_search/core/connection_pool.py +360 -0
- mcp_vector_search/core/database.py +1237 -0
- mcp_vector_search/core/directory_index.py +318 -0
- mcp_vector_search/core/embeddings.py +294 -0
- mcp_vector_search/core/exceptions.py +89 -0
- mcp_vector_search/core/factory.py +318 -0
- mcp_vector_search/core/git_hooks.py +345 -0
- mcp_vector_search/core/indexer.py +1002 -0
- mcp_vector_search/core/llm_client.py +453 -0
- mcp_vector_search/core/models.py +294 -0
- mcp_vector_search/core/project.py +350 -0
- mcp_vector_search/core/scheduler.py +330 -0
- mcp_vector_search/core/search.py +952 -0
- mcp_vector_search/core/watcher.py +322 -0
- mcp_vector_search/mcp/__init__.py +5 -0
- mcp_vector_search/mcp/__main__.py +25 -0
- mcp_vector_search/mcp/server.py +752 -0
- mcp_vector_search/parsers/__init__.py +8 -0
- mcp_vector_search/parsers/base.py +296 -0
- mcp_vector_search/parsers/dart.py +605 -0
- mcp_vector_search/parsers/html.py +413 -0
- mcp_vector_search/parsers/javascript.py +643 -0
- mcp_vector_search/parsers/php.py +694 -0
- mcp_vector_search/parsers/python.py +502 -0
- mcp_vector_search/parsers/registry.py +223 -0
- mcp_vector_search/parsers/ruby.py +678 -0
- mcp_vector_search/parsers/text.py +186 -0
- mcp_vector_search/parsers/utils.py +265 -0
- mcp_vector_search/py.typed +1 -0
- mcp_vector_search/utils/__init__.py +42 -0
- mcp_vector_search/utils/gitignore.py +250 -0
- mcp_vector_search/utils/gitignore_updater.py +212 -0
- mcp_vector_search/utils/monorepo.py +339 -0
- mcp_vector_search/utils/timing.py +338 -0
- mcp_vector_search/utils/version.py +47 -0
- mcp_vector_search-0.15.7.dist-info/METADATA +884 -0
- mcp_vector_search-0.15.7.dist-info/RECORD +86 -0
- mcp_vector_search-0.15.7.dist-info/WHEEL +4 -0
- mcp_vector_search-0.15.7.dist-info/entry_points.txt +3 -0
- mcp_vector_search-0.15.7.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,1254 @@
|
|
|
1
|
+
"""MCP integration commands for multiple AI tools."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import shutil
|
|
6
|
+
import subprocess
|
|
7
|
+
import sys
|
|
8
|
+
import tomllib
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
import typer
|
|
13
|
+
from rich.console import Console
|
|
14
|
+
from rich.panel import Panel
|
|
15
|
+
from rich.table import Table
|
|
16
|
+
|
|
17
|
+
from ...core.exceptions import ProjectNotFoundError
|
|
18
|
+
from ...core.project import ProjectManager
|
|
19
|
+
from ..didyoumean import create_enhanced_typer
|
|
20
|
+
from ..output import print_error, print_info, print_success, print_warning
|
|
21
|
+
|
|
22
|
+
# Create MCP subcommand app with "did you mean" functionality
|
|
23
|
+
mcp_app = create_enhanced_typer(
|
|
24
|
+
help="""🤖 Manage MCP integration for AI tools
|
|
25
|
+
|
|
26
|
+
Configure mcp-vector-search as an MCP server for various AI coding assistants.
|
|
27
|
+
Each tool has its own configuration format and location.
|
|
28
|
+
|
|
29
|
+
[bold cyan]Supported Tools:[/bold cyan]
|
|
30
|
+
• [green]auggie[/green] - Augment Code AI assistant
|
|
31
|
+
• [green]claude-code[/green] - Claude Code (project-scoped)
|
|
32
|
+
• [green]codex[/green] - OpenAI Codex CLI
|
|
33
|
+
• [green]gemini[/green] - Google Gemini CLI
|
|
34
|
+
|
|
35
|
+
[bold cyan]Quick Start:[/bold cyan]
|
|
36
|
+
1. List tools: [green]mcp-vector-search mcp list[/green]
|
|
37
|
+
2. Configure tool: [green]mcp-vector-search mcp <tool>[/green]
|
|
38
|
+
3. Test setup: [green]mcp-vector-search mcp test[/green]
|
|
39
|
+
|
|
40
|
+
[dim]Use --force to overwrite existing configurations[/dim]
|
|
41
|
+
""",
|
|
42
|
+
no_args_is_help=False, # Allow running without subcommand
|
|
43
|
+
invoke_without_command=True, # Call callback even without subcommand
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
console = Console()
|
|
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
|
+
|
|
85
|
+
# Supported AI tools and their configuration details
|
|
86
|
+
SUPPORTED_TOOLS = {
|
|
87
|
+
"auggie": {
|
|
88
|
+
"name": "Auggie",
|
|
89
|
+
"config_path": "~/.augment/settings.json",
|
|
90
|
+
"format": "json",
|
|
91
|
+
"description": "Augment Code AI assistant",
|
|
92
|
+
},
|
|
93
|
+
"claude-code": {
|
|
94
|
+
"name": "Claude Code",
|
|
95
|
+
"config_path": ".mcp.json",
|
|
96
|
+
"format": "json",
|
|
97
|
+
"description": "Claude Code (project-scoped)",
|
|
98
|
+
},
|
|
99
|
+
"codex": {
|
|
100
|
+
"name": "Codex",
|
|
101
|
+
"config_path": "~/.codex/config.toml",
|
|
102
|
+
"format": "toml",
|
|
103
|
+
"description": "OpenAI Codex CLI",
|
|
104
|
+
},
|
|
105
|
+
"gemini": {
|
|
106
|
+
"name": "Gemini",
|
|
107
|
+
"config_path": "~/.gemini/mcp.json",
|
|
108
|
+
"format": "json",
|
|
109
|
+
"description": "Google Gemini CLI",
|
|
110
|
+
},
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def get_claude_command() -> str | None:
|
|
115
|
+
"""Get the Claude Code command path."""
|
|
116
|
+
# Check if claude command is available
|
|
117
|
+
claude_cmd = shutil.which("claude")
|
|
118
|
+
if claude_cmd:
|
|
119
|
+
return "claude"
|
|
120
|
+
|
|
121
|
+
# Check common installation paths
|
|
122
|
+
possible_paths = [
|
|
123
|
+
"/usr/local/bin/claude",
|
|
124
|
+
"/opt/homebrew/bin/claude",
|
|
125
|
+
os.path.expanduser("~/.local/bin/claude"),
|
|
126
|
+
]
|
|
127
|
+
|
|
128
|
+
for path in possible_paths:
|
|
129
|
+
if os.path.exists(path) and os.access(path, os.X_OK):
|
|
130
|
+
return path
|
|
131
|
+
|
|
132
|
+
return None
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def check_claude_code_available() -> bool:
|
|
136
|
+
"""Check if Claude Code is available."""
|
|
137
|
+
claude_cmd = get_claude_command()
|
|
138
|
+
if not claude_cmd:
|
|
139
|
+
return False
|
|
140
|
+
|
|
141
|
+
try:
|
|
142
|
+
result = subprocess.run(
|
|
143
|
+
[claude_cmd, "--version"], capture_output=True, text=True, timeout=10
|
|
144
|
+
)
|
|
145
|
+
return result.returncode == 0
|
|
146
|
+
except (subprocess.TimeoutExpired, FileNotFoundError):
|
|
147
|
+
return False
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def get_mcp_server_command(
|
|
151
|
+
project_root: Path, enable_file_watching: bool = True
|
|
152
|
+
) -> str:
|
|
153
|
+
"""Get the command to run the MCP server.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
project_root: Path to the project root directory
|
|
157
|
+
enable_file_watching: Whether to enable file watching (default: True)
|
|
158
|
+
"""
|
|
159
|
+
# Always use the current Python executable for project-scoped installation
|
|
160
|
+
python_exe = sys.executable
|
|
161
|
+
watch_flag = "" if enable_file_watching else " --no-watch"
|
|
162
|
+
return f"{python_exe} -m mcp_vector_search.mcp.server{watch_flag} {project_root}"
|
|
163
|
+
|
|
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
|
+
|
|
188
|
+
def get_mcp_server_config_for_tool(
|
|
189
|
+
project_root: Path,
|
|
190
|
+
tool_name: str,
|
|
191
|
+
server_name: str,
|
|
192
|
+
enable_file_watching: bool = True,
|
|
193
|
+
) -> dict[str, Any]:
|
|
194
|
+
"""Generate MCP server configuration for a specific tool."""
|
|
195
|
+
command, args = detect_install_method()
|
|
196
|
+
|
|
197
|
+
base_config = {
|
|
198
|
+
"command": command,
|
|
199
|
+
"args": args,
|
|
200
|
+
"env": {
|
|
201
|
+
"MCP_ENABLE_FILE_WATCHING": "true" if enable_file_watching else "false"
|
|
202
|
+
},
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if tool_name == "auggie":
|
|
206
|
+
# Auggie uses stdio transport
|
|
207
|
+
return base_config
|
|
208
|
+
elif tool_name == "claude-code":
|
|
209
|
+
# Claude Code requires type: stdio and no cwd
|
|
210
|
+
return {"type": "stdio", **base_config}
|
|
211
|
+
elif tool_name == "codex":
|
|
212
|
+
# Codex uses TOML format with different structure
|
|
213
|
+
return {
|
|
214
|
+
"command": base_config["command"],
|
|
215
|
+
"args": base_config["args"],
|
|
216
|
+
"env": base_config["env"],
|
|
217
|
+
}
|
|
218
|
+
elif tool_name == "gemini":
|
|
219
|
+
# Gemini uses standard format with cwd
|
|
220
|
+
return {**base_config, "cwd": str(project_root.absolute())}
|
|
221
|
+
else:
|
|
222
|
+
# Default configuration
|
|
223
|
+
return {**base_config, "cwd": str(project_root.absolute())}
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def create_project_claude_config(
|
|
227
|
+
project_root: Path, server_name: str, enable_file_watching: bool = True
|
|
228
|
+
) -> None:
|
|
229
|
+
"""Create or update project-level .mcp.json file.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
project_root: Path to the project root directory
|
|
233
|
+
server_name: Name for the MCP server
|
|
234
|
+
enable_file_watching: Whether to enable file watching (default: True)
|
|
235
|
+
"""
|
|
236
|
+
# Path to .mcp.json in project root (recommended by Claude Code)
|
|
237
|
+
mcp_config_path = project_root / ".mcp.json"
|
|
238
|
+
|
|
239
|
+
# Load existing config or create new one
|
|
240
|
+
if mcp_config_path.exists():
|
|
241
|
+
with open(mcp_config_path) as f:
|
|
242
|
+
config = json.load(f)
|
|
243
|
+
else:
|
|
244
|
+
config = {}
|
|
245
|
+
|
|
246
|
+
# Ensure mcpServers section exists
|
|
247
|
+
if "mcpServers" not in config:
|
|
248
|
+
config["mcpServers"] = {}
|
|
249
|
+
|
|
250
|
+
# Detect installation method and use appropriate command
|
|
251
|
+
command, args = detect_install_method()
|
|
252
|
+
config["mcpServers"][server_name] = {
|
|
253
|
+
"type": "stdio",
|
|
254
|
+
"command": command,
|
|
255
|
+
"args": args,
|
|
256
|
+
"env": {
|
|
257
|
+
"MCP_ENABLE_FILE_WATCHING": "true" if enable_file_watching else "false"
|
|
258
|
+
},
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
# Write the config
|
|
262
|
+
with open(mcp_config_path, "w") as f:
|
|
263
|
+
json.dump(config, f, indent=2)
|
|
264
|
+
|
|
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
|
+
|
|
273
|
+
if enable_file_watching:
|
|
274
|
+
print_info("File watching is enabled for automatic reindexing")
|
|
275
|
+
else:
|
|
276
|
+
print_info("File watching is disabled")
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def configure_tool_mcp(
|
|
280
|
+
tool_name: str,
|
|
281
|
+
project_root: Path,
|
|
282
|
+
server_name: str = "mcp-vector-search",
|
|
283
|
+
enable_file_watching: bool = True,
|
|
284
|
+
force: bool = False,
|
|
285
|
+
) -> bool:
|
|
286
|
+
"""Configure MCP integration for a specific AI tool."""
|
|
287
|
+
if tool_name not in SUPPORTED_TOOLS:
|
|
288
|
+
print_error(f"Unsupported tool: {tool_name}")
|
|
289
|
+
print_info(f"Supported tools: {', '.join(SUPPORTED_TOOLS.keys())}")
|
|
290
|
+
return False
|
|
291
|
+
|
|
292
|
+
tool_info = SUPPORTED_TOOLS[tool_name]
|
|
293
|
+
config_path_str = tool_info["config_path"]
|
|
294
|
+
|
|
295
|
+
# Handle path expansion
|
|
296
|
+
if config_path_str.startswith("~/"):
|
|
297
|
+
config_path = Path.home() / config_path_str[2:]
|
|
298
|
+
elif config_path_str.startswith("."):
|
|
299
|
+
config_path = project_root / config_path_str
|
|
300
|
+
else:
|
|
301
|
+
config_path = Path(config_path_str)
|
|
302
|
+
|
|
303
|
+
try:
|
|
304
|
+
if tool_name == "auggie":
|
|
305
|
+
return configure_auggie_mcp(
|
|
306
|
+
config_path, project_root, server_name, enable_file_watching, force
|
|
307
|
+
)
|
|
308
|
+
elif tool_name == "claude-code":
|
|
309
|
+
return configure_claude_code_mcp(
|
|
310
|
+
config_path, project_root, server_name, enable_file_watching, force
|
|
311
|
+
)
|
|
312
|
+
elif tool_name == "codex":
|
|
313
|
+
return configure_codex_mcp(
|
|
314
|
+
config_path, project_root, server_name, enable_file_watching, force
|
|
315
|
+
)
|
|
316
|
+
elif tool_name == "gemini":
|
|
317
|
+
return configure_gemini_mcp(
|
|
318
|
+
config_path, project_root, server_name, enable_file_watching, force
|
|
319
|
+
)
|
|
320
|
+
else:
|
|
321
|
+
print_error(f"Configuration for {tool_name} not implemented yet")
|
|
322
|
+
return False
|
|
323
|
+
except Exception as e:
|
|
324
|
+
print_error(f"Failed to configure {tool_name}: {e}")
|
|
325
|
+
return False
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def configure_auggie_mcp(
|
|
329
|
+
config_path: Path,
|
|
330
|
+
project_root: Path,
|
|
331
|
+
server_name: str,
|
|
332
|
+
enable_file_watching: bool,
|
|
333
|
+
force: bool,
|
|
334
|
+
) -> bool:
|
|
335
|
+
"""Configure Auggie MCP integration."""
|
|
336
|
+
# Create backup if file exists
|
|
337
|
+
backup_path = config_path.with_suffix(config_path.suffix + ".backup")
|
|
338
|
+
|
|
339
|
+
# Load existing config or create new one
|
|
340
|
+
if config_path.exists():
|
|
341
|
+
if not force:
|
|
342
|
+
with open(config_path) as f:
|
|
343
|
+
config = json.load(f)
|
|
344
|
+
if config.get("mcpServers", {}).get(server_name):
|
|
345
|
+
print_warning(
|
|
346
|
+
f"MCP server '{server_name}' already exists in Auggie config"
|
|
347
|
+
)
|
|
348
|
+
print_info("Use --force to overwrite")
|
|
349
|
+
return False
|
|
350
|
+
shutil.copy2(config_path, backup_path)
|
|
351
|
+
with open(config_path) as f:
|
|
352
|
+
config = json.load(f)
|
|
353
|
+
else:
|
|
354
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
355
|
+
config = {}
|
|
356
|
+
|
|
357
|
+
# Ensure mcpServers section exists
|
|
358
|
+
if "mcpServers" not in config:
|
|
359
|
+
config["mcpServers"] = {}
|
|
360
|
+
|
|
361
|
+
# Get server configuration
|
|
362
|
+
server_config = get_mcp_server_config_for_tool(
|
|
363
|
+
project_root, "auggie", server_name, enable_file_watching
|
|
364
|
+
)
|
|
365
|
+
config["mcpServers"][server_name] = server_config
|
|
366
|
+
|
|
367
|
+
# Write updated config
|
|
368
|
+
with open(config_path, "w") as f:
|
|
369
|
+
json.dump(config, f, indent=2)
|
|
370
|
+
|
|
371
|
+
print_success(f"✅ Configured Auggie at {config_path}")
|
|
372
|
+
return True
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
def configure_claude_code_mcp(
|
|
376
|
+
config_path: Path,
|
|
377
|
+
project_root: Path,
|
|
378
|
+
server_name: str,
|
|
379
|
+
enable_file_watching: bool,
|
|
380
|
+
force: bool,
|
|
381
|
+
) -> bool:
|
|
382
|
+
"""Configure Claude Code MCP integration."""
|
|
383
|
+
# Use existing function for Claude Code
|
|
384
|
+
if config_path.exists() and not force:
|
|
385
|
+
with open(config_path) as f:
|
|
386
|
+
config = json.load(f)
|
|
387
|
+
if config.get("mcpServers", {}).get(server_name):
|
|
388
|
+
print_warning(
|
|
389
|
+
f"MCP server '{server_name}' already exists in Claude Code config"
|
|
390
|
+
)
|
|
391
|
+
print_info("Use --force to overwrite")
|
|
392
|
+
return False
|
|
393
|
+
|
|
394
|
+
create_project_claude_config(project_root, server_name, enable_file_watching)
|
|
395
|
+
print_success(f"✅ Configured Claude Code at {config_path}")
|
|
396
|
+
return True
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
def configure_codex_mcp(
|
|
400
|
+
config_path: Path,
|
|
401
|
+
project_root: Path,
|
|
402
|
+
server_name: str,
|
|
403
|
+
enable_file_watching: bool,
|
|
404
|
+
force: bool,
|
|
405
|
+
) -> bool:
|
|
406
|
+
"""Configure Codex MCP integration."""
|
|
407
|
+
# Create backup if file exists
|
|
408
|
+
backup_path = config_path.with_suffix(config_path.suffix + ".backup")
|
|
409
|
+
|
|
410
|
+
# Load existing config or create new one
|
|
411
|
+
if config_path.exists():
|
|
412
|
+
if not force:
|
|
413
|
+
try:
|
|
414
|
+
with open(config_path, "rb") as f:
|
|
415
|
+
config = tomllib.load(f)
|
|
416
|
+
if config.get("mcp_servers", {}).get(server_name):
|
|
417
|
+
print_warning(
|
|
418
|
+
f"MCP server '{server_name}' already exists in Codex config"
|
|
419
|
+
)
|
|
420
|
+
print_info("Use --force to overwrite")
|
|
421
|
+
return False
|
|
422
|
+
except Exception as e:
|
|
423
|
+
print_warning(f"Could not parse existing Codex config: {e}")
|
|
424
|
+
|
|
425
|
+
shutil.copy2(config_path, backup_path)
|
|
426
|
+
# Read as text to preserve existing content
|
|
427
|
+
with open(config_path) as f:
|
|
428
|
+
config_text = f.read()
|
|
429
|
+
else:
|
|
430
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
431
|
+
config_text = ""
|
|
432
|
+
|
|
433
|
+
# Get server configuration
|
|
434
|
+
server_config = get_mcp_server_config_for_tool(
|
|
435
|
+
project_root, "codex", server_name, enable_file_watching
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
# Generate TOML section for the server
|
|
439
|
+
toml_section = f"\n[mcp_servers.{server_name}]\n"
|
|
440
|
+
toml_section += f'command = "{server_config["command"]}"\n'
|
|
441
|
+
toml_section += f"args = {server_config['args']}\n"
|
|
442
|
+
|
|
443
|
+
if server_config.get("env"):
|
|
444
|
+
toml_section += f"\n[mcp_servers.{server_name}.env]\n"
|
|
445
|
+
for key, value in server_config["env"].items():
|
|
446
|
+
toml_section += f'{key} = "{value}"\n'
|
|
447
|
+
|
|
448
|
+
# Append or replace the section
|
|
449
|
+
if f"[mcp_servers.{server_name}]" in config_text:
|
|
450
|
+
# Replace existing section (simple approach)
|
|
451
|
+
lines = config_text.split("\n")
|
|
452
|
+
new_lines = []
|
|
453
|
+
skip_section = False
|
|
454
|
+
|
|
455
|
+
for line in lines:
|
|
456
|
+
if line.strip() == f"[mcp_servers.{server_name}]":
|
|
457
|
+
skip_section = True
|
|
458
|
+
continue
|
|
459
|
+
elif line.strip().startswith("[") and skip_section:
|
|
460
|
+
skip_section = False
|
|
461
|
+
new_lines.append(line)
|
|
462
|
+
elif not skip_section:
|
|
463
|
+
new_lines.append(line)
|
|
464
|
+
|
|
465
|
+
config_text = "\n".join(new_lines) + toml_section
|
|
466
|
+
else:
|
|
467
|
+
config_text += toml_section
|
|
468
|
+
|
|
469
|
+
# Write updated config
|
|
470
|
+
with open(config_path, "w") as f:
|
|
471
|
+
f.write(config_text)
|
|
472
|
+
|
|
473
|
+
print_success(f"✅ Configured Codex at {config_path}")
|
|
474
|
+
return True
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
def configure_gemini_mcp(
|
|
478
|
+
config_path: Path,
|
|
479
|
+
project_root: Path,
|
|
480
|
+
server_name: str,
|
|
481
|
+
enable_file_watching: bool,
|
|
482
|
+
force: bool,
|
|
483
|
+
) -> bool:
|
|
484
|
+
"""Configure Gemini MCP integration."""
|
|
485
|
+
# Create backup if file exists
|
|
486
|
+
backup_path = config_path.with_suffix(config_path.suffix + ".backup")
|
|
487
|
+
|
|
488
|
+
# Load existing config or create new one
|
|
489
|
+
if config_path.exists():
|
|
490
|
+
if not force:
|
|
491
|
+
with open(config_path) as f:
|
|
492
|
+
config = json.load(f)
|
|
493
|
+
if config.get("mcpServers", {}).get(server_name):
|
|
494
|
+
print_warning(
|
|
495
|
+
f"MCP server '{server_name}' already exists in Gemini config"
|
|
496
|
+
)
|
|
497
|
+
print_info("Use --force to overwrite")
|
|
498
|
+
return False
|
|
499
|
+
shutil.copy2(config_path, backup_path)
|
|
500
|
+
with open(config_path) as f:
|
|
501
|
+
config = json.load(f)
|
|
502
|
+
else:
|
|
503
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
504
|
+
config = {}
|
|
505
|
+
|
|
506
|
+
# Ensure mcpServers section exists
|
|
507
|
+
if "mcpServers" not in config:
|
|
508
|
+
config["mcpServers"] = {}
|
|
509
|
+
|
|
510
|
+
# Get server configuration
|
|
511
|
+
server_config = get_mcp_server_config_for_tool(
|
|
512
|
+
project_root, "gemini", server_name, enable_file_watching
|
|
513
|
+
)
|
|
514
|
+
config["mcpServers"][server_name] = server_config
|
|
515
|
+
|
|
516
|
+
# Write updated config
|
|
517
|
+
with open(config_path, "w") as f:
|
|
518
|
+
json.dump(config, f, indent=2)
|
|
519
|
+
|
|
520
|
+
print_success(f"✅ Configured Gemini at {config_path}")
|
|
521
|
+
return True
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
# Tool-specific commands
|
|
525
|
+
@mcp_app.command("auggie")
|
|
526
|
+
def configure_auggie(
|
|
527
|
+
ctx: typer.Context,
|
|
528
|
+
server_name: str = typer.Option(
|
|
529
|
+
"mcp-vector-search",
|
|
530
|
+
"--name",
|
|
531
|
+
help="Name for the MCP server",
|
|
532
|
+
rich_help_panel="📁 Configuration",
|
|
533
|
+
),
|
|
534
|
+
force: bool = typer.Option(
|
|
535
|
+
False,
|
|
536
|
+
"--force",
|
|
537
|
+
"-f",
|
|
538
|
+
help="Force installation even if server already exists",
|
|
539
|
+
rich_help_panel="⚙️ Advanced Options",
|
|
540
|
+
),
|
|
541
|
+
no_watch: bool = typer.Option(
|
|
542
|
+
False,
|
|
543
|
+
"--no-watch",
|
|
544
|
+
help="Disable file watching for automatic reindexing",
|
|
545
|
+
rich_help_panel="⚙️ Advanced Options",
|
|
546
|
+
),
|
|
547
|
+
) -> None:
|
|
548
|
+
"""🤖 Configure MCP integration for Auggie AI.
|
|
549
|
+
|
|
550
|
+
Sets up mcp-vector-search as an MCP server for Auggie AI assistant.
|
|
551
|
+
Configuration is stored in ~/.augment/settings.json.
|
|
552
|
+
|
|
553
|
+
[bold cyan]Examples:[/bold cyan]
|
|
554
|
+
|
|
555
|
+
[green]Configure with defaults:[/green]
|
|
556
|
+
$ mcp-vector-search mcp auggie
|
|
557
|
+
|
|
558
|
+
[green]Force overwrite existing config:[/green]
|
|
559
|
+
$ mcp-vector-search mcp auggie --force
|
|
560
|
+
|
|
561
|
+
[green]Disable file watching:[/green]
|
|
562
|
+
$ mcp-vector-search mcp auggie --no-watch
|
|
563
|
+
"""
|
|
564
|
+
try:
|
|
565
|
+
project_root = ctx.obj.get("project_root") or Path.cwd()
|
|
566
|
+
project_manager = ProjectManager(project_root)
|
|
567
|
+
if not project_manager.is_initialized():
|
|
568
|
+
print_error("Project not initialized. Run 'mcp-vector-search init' first.")
|
|
569
|
+
raise typer.Exit(1)
|
|
570
|
+
|
|
571
|
+
enable_file_watching = not no_watch
|
|
572
|
+
success = configure_tool_mcp(
|
|
573
|
+
"auggie", project_root, server_name, enable_file_watching, force
|
|
574
|
+
)
|
|
575
|
+
|
|
576
|
+
if success:
|
|
577
|
+
print_info("Auggie will automatically detect the server when restarted")
|
|
578
|
+
else:
|
|
579
|
+
raise typer.Exit(1)
|
|
580
|
+
|
|
581
|
+
except Exception as e:
|
|
582
|
+
print_error(f"Configuration failed: {e}")
|
|
583
|
+
raise typer.Exit(1)
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
@mcp_app.command("claude-code")
|
|
587
|
+
def configure_claude_code(
|
|
588
|
+
ctx: typer.Context,
|
|
589
|
+
server_name: str = typer.Option(
|
|
590
|
+
"mcp-vector-search",
|
|
591
|
+
"--name",
|
|
592
|
+
help="Name for the MCP server",
|
|
593
|
+
rich_help_panel="📁 Configuration",
|
|
594
|
+
),
|
|
595
|
+
force: bool = typer.Option(
|
|
596
|
+
False,
|
|
597
|
+
"--force",
|
|
598
|
+
"-f",
|
|
599
|
+
help="Force installation even if server already exists",
|
|
600
|
+
rich_help_panel="⚙️ Advanced Options",
|
|
601
|
+
),
|
|
602
|
+
no_watch: bool = typer.Option(
|
|
603
|
+
False,
|
|
604
|
+
"--no-watch",
|
|
605
|
+
help="Disable file watching for automatic reindexing",
|
|
606
|
+
rich_help_panel="⚙️ Advanced Options",
|
|
607
|
+
),
|
|
608
|
+
) -> None:
|
|
609
|
+
"""🤖 Configure MCP integration for Claude Code.
|
|
610
|
+
|
|
611
|
+
Creates .mcp.json to enable semantic code search in Claude Code.
|
|
612
|
+
Configuration is project-scoped for team sharing.
|
|
613
|
+
|
|
614
|
+
[bold cyan]Examples:[/bold cyan]
|
|
615
|
+
|
|
616
|
+
[green]Configure with defaults:[/green]
|
|
617
|
+
$ mcp-vector-search mcp claude-code
|
|
618
|
+
|
|
619
|
+
[green]Force overwrite existing config:[/green]
|
|
620
|
+
$ mcp-vector-search mcp claude-code --force
|
|
621
|
+
|
|
622
|
+
[green]Disable file watching:[/green]
|
|
623
|
+
$ mcp-vector-search mcp claude-code --no-watch
|
|
624
|
+
"""
|
|
625
|
+
try:
|
|
626
|
+
project_root = ctx.obj.get("project_root") or Path.cwd()
|
|
627
|
+
project_manager = ProjectManager(project_root)
|
|
628
|
+
if not project_manager.is_initialized():
|
|
629
|
+
print_error("Project not initialized. Run 'mcp-vector-search init' first.")
|
|
630
|
+
raise typer.Exit(1)
|
|
631
|
+
|
|
632
|
+
enable_file_watching = not no_watch
|
|
633
|
+
success = configure_tool_mcp(
|
|
634
|
+
"claude-code", project_root, server_name, enable_file_watching, force
|
|
635
|
+
)
|
|
636
|
+
|
|
637
|
+
if success:
|
|
638
|
+
print_info(
|
|
639
|
+
"Claude Code will automatically detect the server when you open this project"
|
|
640
|
+
)
|
|
641
|
+
else:
|
|
642
|
+
raise typer.Exit(1)
|
|
643
|
+
|
|
644
|
+
except Exception as e:
|
|
645
|
+
print_error(f"Configuration failed: {e}")
|
|
646
|
+
raise typer.Exit(1)
|
|
647
|
+
|
|
648
|
+
|
|
649
|
+
@mcp_app.command("codex")
|
|
650
|
+
def configure_codex(
|
|
651
|
+
ctx: typer.Context,
|
|
652
|
+
server_name: str = typer.Option(
|
|
653
|
+
"mcp-vector-search",
|
|
654
|
+
"--name",
|
|
655
|
+
help="Name for the MCP server",
|
|
656
|
+
rich_help_panel="📁 Configuration",
|
|
657
|
+
),
|
|
658
|
+
force: bool = typer.Option(
|
|
659
|
+
False,
|
|
660
|
+
"--force",
|
|
661
|
+
"-f",
|
|
662
|
+
help="Force installation even if server already exists",
|
|
663
|
+
rich_help_panel="⚙️ Advanced Options",
|
|
664
|
+
),
|
|
665
|
+
no_watch: bool = typer.Option(
|
|
666
|
+
False,
|
|
667
|
+
"--no-watch",
|
|
668
|
+
help="Disable file watching for automatic reindexing",
|
|
669
|
+
rich_help_panel="⚙️ Advanced Options",
|
|
670
|
+
),
|
|
671
|
+
) -> None:
|
|
672
|
+
"""🤖 Configure MCP integration for OpenAI Codex.
|
|
673
|
+
|
|
674
|
+
Sets up mcp-vector-search as an MCP server for OpenAI Codex CLI.
|
|
675
|
+
Configuration is stored in ~/.codex/config.toml.
|
|
676
|
+
|
|
677
|
+
[bold cyan]Examples:[/bold cyan]
|
|
678
|
+
|
|
679
|
+
[green]Configure with defaults:[/green]
|
|
680
|
+
$ mcp-vector-search mcp codex
|
|
681
|
+
|
|
682
|
+
[green]Force overwrite existing config:[/green]
|
|
683
|
+
$ mcp-vector-search mcp codex --force
|
|
684
|
+
|
|
685
|
+
[green]Disable file watching:[/green]
|
|
686
|
+
$ mcp-vector-search mcp codex --no-watch
|
|
687
|
+
"""
|
|
688
|
+
try:
|
|
689
|
+
project_root = ctx.obj.get("project_root") or Path.cwd()
|
|
690
|
+
project_manager = ProjectManager(project_root)
|
|
691
|
+
if not project_manager.is_initialized():
|
|
692
|
+
print_error("Project not initialized. Run 'mcp-vector-search init' first.")
|
|
693
|
+
raise typer.Exit(1)
|
|
694
|
+
|
|
695
|
+
enable_file_watching = not no_watch
|
|
696
|
+
success = configure_tool_mcp(
|
|
697
|
+
"codex", project_root, server_name, enable_file_watching, force
|
|
698
|
+
)
|
|
699
|
+
|
|
700
|
+
if success:
|
|
701
|
+
print_info("Codex will automatically detect the server when restarted")
|
|
702
|
+
else:
|
|
703
|
+
raise typer.Exit(1)
|
|
704
|
+
|
|
705
|
+
except Exception as e:
|
|
706
|
+
print_error(f"Configuration failed: {e}")
|
|
707
|
+
raise typer.Exit(1)
|
|
708
|
+
|
|
709
|
+
|
|
710
|
+
@mcp_app.command("gemini")
|
|
711
|
+
def configure_gemini(
|
|
712
|
+
ctx: typer.Context,
|
|
713
|
+
server_name: str = typer.Option(
|
|
714
|
+
"mcp-vector-search",
|
|
715
|
+
"--name",
|
|
716
|
+
help="Name for the MCP server",
|
|
717
|
+
rich_help_panel="📁 Configuration",
|
|
718
|
+
),
|
|
719
|
+
force: bool = typer.Option(
|
|
720
|
+
False,
|
|
721
|
+
"--force",
|
|
722
|
+
"-f",
|
|
723
|
+
help="Force installation even if server already exists",
|
|
724
|
+
rich_help_panel="⚙️ Advanced Options",
|
|
725
|
+
),
|
|
726
|
+
no_watch: bool = typer.Option(
|
|
727
|
+
False,
|
|
728
|
+
"--no-watch",
|
|
729
|
+
help="Disable file watching for automatic reindexing",
|
|
730
|
+
rich_help_panel="⚙️ Advanced Options",
|
|
731
|
+
),
|
|
732
|
+
) -> None:
|
|
733
|
+
"""🤖 Configure MCP integration for Google Gemini.
|
|
734
|
+
|
|
735
|
+
Sets up mcp-vector-search as an MCP server for Google Gemini CLI.
|
|
736
|
+
Configuration is stored in ~/.gemini/mcp.json.
|
|
737
|
+
|
|
738
|
+
[bold cyan]Examples:[/bold cyan]
|
|
739
|
+
|
|
740
|
+
[green]Configure with defaults:[/green]
|
|
741
|
+
$ mcp-vector-search mcp gemini
|
|
742
|
+
|
|
743
|
+
[green]Force overwrite existing config:[/green]
|
|
744
|
+
$ mcp-vector-search mcp gemini --force
|
|
745
|
+
|
|
746
|
+
[green]Disable file watching:[/green]
|
|
747
|
+
$ mcp-vector-search mcp gemini --no-watch
|
|
748
|
+
"""
|
|
749
|
+
try:
|
|
750
|
+
project_root = ctx.obj.get("project_root") or Path.cwd()
|
|
751
|
+
project_manager = ProjectManager(project_root)
|
|
752
|
+
if not project_manager.is_initialized():
|
|
753
|
+
print_error("Project not initialized. Run 'mcp-vector-search init' first.")
|
|
754
|
+
raise typer.Exit(1)
|
|
755
|
+
|
|
756
|
+
enable_file_watching = not no_watch
|
|
757
|
+
success = configure_tool_mcp(
|
|
758
|
+
"gemini", project_root, server_name, enable_file_watching, force
|
|
759
|
+
)
|
|
760
|
+
|
|
761
|
+
if success:
|
|
762
|
+
print_info("Gemini will automatically detect the server when restarted")
|
|
763
|
+
else:
|
|
764
|
+
raise typer.Exit(1)
|
|
765
|
+
|
|
766
|
+
except Exception as e:
|
|
767
|
+
print_error(f"Configuration failed: {e}")
|
|
768
|
+
raise typer.Exit(1)
|
|
769
|
+
|
|
770
|
+
|
|
771
|
+
# Legacy install command (deprecated)
|
|
772
|
+
@mcp_app.command("install", hidden=True)
|
|
773
|
+
@mcp_app.command("init", hidden=True) # Add 'init' as an alias
|
|
774
|
+
def install_mcp_integration(
|
|
775
|
+
ctx: typer.Context,
|
|
776
|
+
server_name: str = typer.Option(
|
|
777
|
+
"mcp-vector-search",
|
|
778
|
+
"--name",
|
|
779
|
+
help="Name for the MCP server",
|
|
780
|
+
rich_help_panel="📁 Configuration",
|
|
781
|
+
),
|
|
782
|
+
force: bool = typer.Option(
|
|
783
|
+
False,
|
|
784
|
+
"--force",
|
|
785
|
+
"-f",
|
|
786
|
+
help="Force installation even if server already exists",
|
|
787
|
+
rich_help_panel="⚙️ Advanced Options",
|
|
788
|
+
),
|
|
789
|
+
no_watch: bool = typer.Option(
|
|
790
|
+
False,
|
|
791
|
+
"--no-watch",
|
|
792
|
+
help="Disable file watching for automatic reindexing",
|
|
793
|
+
rich_help_panel="⚙️ Advanced Options",
|
|
794
|
+
),
|
|
795
|
+
) -> None:
|
|
796
|
+
"""[DEPRECATED] Use tool-specific commands instead.
|
|
797
|
+
|
|
798
|
+
This command is deprecated. Use the tool-specific commands instead:
|
|
799
|
+
|
|
800
|
+
[bold cyan]New Commands:[/bold cyan]
|
|
801
|
+
|
|
802
|
+
[green]For Auggie:[/green]
|
|
803
|
+
$ mcp-vector-search mcp auggie
|
|
804
|
+
|
|
805
|
+
[green]For Claude Code:[/green]
|
|
806
|
+
$ mcp-vector-search mcp claude-code
|
|
807
|
+
|
|
808
|
+
[green]For Codex:[/green]
|
|
809
|
+
$ mcp-vector-search mcp codex
|
|
810
|
+
|
|
811
|
+
[green]For Gemini:[/green]
|
|
812
|
+
$ mcp-vector-search mcp gemini
|
|
813
|
+
"""
|
|
814
|
+
print_warning("⚠️ The 'mcp install' command is deprecated.")
|
|
815
|
+
print_info("Use tool-specific commands instead:")
|
|
816
|
+
print_info(" • mcp-vector-search mcp auggie")
|
|
817
|
+
print_info(" • mcp-vector-search mcp claude-code")
|
|
818
|
+
print_info(" • mcp-vector-search mcp codex")
|
|
819
|
+
print_info(" • mcp-vector-search mcp gemini")
|
|
820
|
+
print_info("")
|
|
821
|
+
print_info("Defaulting to Claude Code configuration...")
|
|
822
|
+
|
|
823
|
+
try:
|
|
824
|
+
project_root = ctx.obj.get("project_root") or Path.cwd()
|
|
825
|
+
project_manager = ProjectManager(project_root)
|
|
826
|
+
if not project_manager.is_initialized():
|
|
827
|
+
print_error("Project not initialized. Run 'mcp-vector-search init' first.")
|
|
828
|
+
raise typer.Exit(1)
|
|
829
|
+
|
|
830
|
+
enable_file_watching = not no_watch
|
|
831
|
+
success = configure_tool_mcp(
|
|
832
|
+
"claude-code", project_root, server_name, enable_file_watching, force
|
|
833
|
+
)
|
|
834
|
+
|
|
835
|
+
if success:
|
|
836
|
+
print_info(
|
|
837
|
+
"Claude Code will automatically detect the server when you open this project"
|
|
838
|
+
)
|
|
839
|
+
|
|
840
|
+
# Test the server (using project_root for the server command)
|
|
841
|
+
print_info("Testing server startup...")
|
|
842
|
+
|
|
843
|
+
# Get the server command
|
|
844
|
+
server_command = get_mcp_server_command(project_root, enable_file_watching)
|
|
845
|
+
test_process = subprocess.Popen(
|
|
846
|
+
server_command.split(),
|
|
847
|
+
stdin=subprocess.PIPE,
|
|
848
|
+
stdout=subprocess.PIPE,
|
|
849
|
+
stderr=subprocess.PIPE,
|
|
850
|
+
text=True,
|
|
851
|
+
)
|
|
852
|
+
|
|
853
|
+
# Send a simple initialization request
|
|
854
|
+
init_request = {
|
|
855
|
+
"jsonrpc": "2.0",
|
|
856
|
+
"id": 1,
|
|
857
|
+
"method": "initialize",
|
|
858
|
+
"params": {
|
|
859
|
+
"protocolVersion": "2024-11-05",
|
|
860
|
+
"capabilities": {},
|
|
861
|
+
"clientInfo": {"name": "test", "version": "1.0.0"},
|
|
862
|
+
},
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
try:
|
|
866
|
+
test_process.stdin.write(json.dumps(init_request) + "\n")
|
|
867
|
+
test_process.stdin.flush()
|
|
868
|
+
|
|
869
|
+
# Wait for response with timeout
|
|
870
|
+
test_process.wait(timeout=5)
|
|
871
|
+
|
|
872
|
+
if test_process.returncode == 0:
|
|
873
|
+
print_success("✅ MCP server starts successfully")
|
|
874
|
+
else:
|
|
875
|
+
stderr_output = test_process.stderr.read()
|
|
876
|
+
print_warning(f"⚠️ Server startup test inconclusive: {stderr_output}")
|
|
877
|
+
|
|
878
|
+
except subprocess.TimeoutExpired:
|
|
879
|
+
test_process.terminate()
|
|
880
|
+
print_success("✅ MCP server is responsive")
|
|
881
|
+
|
|
882
|
+
# Show available tools
|
|
883
|
+
table = Table(title="Available MCP Tools")
|
|
884
|
+
table.add_column("Tool", style="cyan")
|
|
885
|
+
table.add_column("Description", style="white")
|
|
886
|
+
|
|
887
|
+
table.add_row("search_code", "Search for code using semantic similarity")
|
|
888
|
+
table.add_row(
|
|
889
|
+
"search_similar", "Find code similar to a specific file or function"
|
|
890
|
+
)
|
|
891
|
+
table.add_row(
|
|
892
|
+
"search_context", "Search for code based on contextual description"
|
|
893
|
+
)
|
|
894
|
+
table.add_row(
|
|
895
|
+
"get_project_status", "Get project indexing status and statistics"
|
|
896
|
+
)
|
|
897
|
+
table.add_row("index_project", "Index or reindex the project codebase")
|
|
898
|
+
|
|
899
|
+
if enable_file_watching:
|
|
900
|
+
console.print(
|
|
901
|
+
"\n[green]✅ File watching is enabled[/green] - Changes will be automatically indexed"
|
|
902
|
+
)
|
|
903
|
+
else:
|
|
904
|
+
console.print(
|
|
905
|
+
"\n[yellow]⚠️ File watching is disabled[/yellow] - Manual reindexing required for changes"
|
|
906
|
+
)
|
|
907
|
+
|
|
908
|
+
console.print(table)
|
|
909
|
+
|
|
910
|
+
print_info("\nTo test the integration, run: mcp-vector-search mcp test")
|
|
911
|
+
|
|
912
|
+
except ProjectNotFoundError:
|
|
913
|
+
print_error(f"Project not initialized at {project_root}")
|
|
914
|
+
print_info("Run 'mcp-vector-search init' in the project directory first")
|
|
915
|
+
raise typer.Exit(1)
|
|
916
|
+
except Exception as e:
|
|
917
|
+
print_error(f"Installation failed: {e}")
|
|
918
|
+
raise typer.Exit(1)
|
|
919
|
+
|
|
920
|
+
|
|
921
|
+
@mcp_app.command("list")
|
|
922
|
+
def list_tools() -> None:
|
|
923
|
+
"""📋 List supported AI tools and their configuration status.
|
|
924
|
+
|
|
925
|
+
Shows all supported AI tools, their configuration paths, and whether
|
|
926
|
+
they are currently configured with mcp-vector-search.
|
|
927
|
+
|
|
928
|
+
[bold cyan]Examples:[/bold cyan]
|
|
929
|
+
|
|
930
|
+
[green]List all tools:[/green]
|
|
931
|
+
$ mcp-vector-search mcp list
|
|
932
|
+
"""
|
|
933
|
+
console.print("\n[bold blue]🤖 Supported AI Tools[/bold blue]\n")
|
|
934
|
+
|
|
935
|
+
table = Table(show_header=True, header_style="bold magenta")
|
|
936
|
+
table.add_column("Tool", style="cyan", no_wrap=True)
|
|
937
|
+
table.add_column("Name", style="white")
|
|
938
|
+
table.add_column("Config Path", style="dim")
|
|
939
|
+
table.add_column("Status", justify="center")
|
|
940
|
+
|
|
941
|
+
for tool_id, tool_info in SUPPORTED_TOOLS.items():
|
|
942
|
+
config_path_str = tool_info["config_path"]
|
|
943
|
+
|
|
944
|
+
# Handle path expansion
|
|
945
|
+
if config_path_str.startswith("~/"):
|
|
946
|
+
config_path = Path.home() / config_path_str[2:]
|
|
947
|
+
elif config_path_str.startswith("."):
|
|
948
|
+
config_path = Path.cwd() / config_path_str
|
|
949
|
+
else:
|
|
950
|
+
config_path = Path(config_path_str)
|
|
951
|
+
|
|
952
|
+
# Check if configured
|
|
953
|
+
status = "❌ Not configured"
|
|
954
|
+
try:
|
|
955
|
+
if config_path.exists():
|
|
956
|
+
if tool_info["format"] == "json":
|
|
957
|
+
with open(config_path) as f:
|
|
958
|
+
config = json.load(f)
|
|
959
|
+
if config.get("mcpServers", {}).get("mcp-vector-search"):
|
|
960
|
+
status = "✅ Configured"
|
|
961
|
+
else:
|
|
962
|
+
status = "⚠️ Config exists"
|
|
963
|
+
elif tool_info["format"] == "toml":
|
|
964
|
+
with open(config_path, "rb") as f:
|
|
965
|
+
config = tomllib.load(f)
|
|
966
|
+
if config.get("mcp_servers", {}).get("mcp-vector-search"):
|
|
967
|
+
status = "✅ Configured"
|
|
968
|
+
else:
|
|
969
|
+
status = "⚠️ Config exists"
|
|
970
|
+
else:
|
|
971
|
+
status = "❌ No config file"
|
|
972
|
+
except Exception:
|
|
973
|
+
status = "❓ Unknown"
|
|
974
|
+
|
|
975
|
+
table.add_row(tool_id, tool_info["name"], str(config_path), status)
|
|
976
|
+
|
|
977
|
+
console.print(table)
|
|
978
|
+
console.print(
|
|
979
|
+
"\n[dim]💡 Use 'mcp-vector-search mcp <tool>' to configure a specific tool[/dim]"
|
|
980
|
+
)
|
|
981
|
+
|
|
982
|
+
|
|
983
|
+
@mcp_app.command("tools")
|
|
984
|
+
def list_tools_alias() -> None:
|
|
985
|
+
"""📋 Alias for 'list' command."""
|
|
986
|
+
list_tools()
|
|
987
|
+
|
|
988
|
+
|
|
989
|
+
@mcp_app.command("test")
|
|
990
|
+
def test_mcp_integration(
|
|
991
|
+
ctx: typer.Context,
|
|
992
|
+
server_name: str = typer.Option(
|
|
993
|
+
"mcp-vector-search",
|
|
994
|
+
"--name",
|
|
995
|
+
help="Name of the MCP server to test",
|
|
996
|
+
rich_help_panel="📁 Configuration",
|
|
997
|
+
),
|
|
998
|
+
) -> None:
|
|
999
|
+
"""🧪 Test the MCP integration.
|
|
1000
|
+
|
|
1001
|
+
Verifies that the MCP server is properly configured and can start successfully.
|
|
1002
|
+
Use this to diagnose integration issues.
|
|
1003
|
+
|
|
1004
|
+
[bold cyan]Examples:[/bold cyan]
|
|
1005
|
+
|
|
1006
|
+
[green]Test default server:[/green]
|
|
1007
|
+
$ mcp-vector-search mcp test
|
|
1008
|
+
|
|
1009
|
+
[green]Test custom server:[/green]
|
|
1010
|
+
$ mcp-vector-search mcp test --name my-search-server
|
|
1011
|
+
|
|
1012
|
+
[dim]💡 Tip: Run this after installation to verify everything works.[/dim]
|
|
1013
|
+
"""
|
|
1014
|
+
try:
|
|
1015
|
+
# Get project root
|
|
1016
|
+
project_root = ctx.obj.get("project_root") or Path.cwd()
|
|
1017
|
+
|
|
1018
|
+
# Check if Claude Code is available
|
|
1019
|
+
if not check_claude_code_available():
|
|
1020
|
+
print_error("Claude Code not found. Please install Claude Code first.")
|
|
1021
|
+
raise typer.Exit(1)
|
|
1022
|
+
|
|
1023
|
+
claude_cmd = get_claude_command()
|
|
1024
|
+
|
|
1025
|
+
# Check if server exists
|
|
1026
|
+
print_info(f"Testing MCP server '{server_name}'...")
|
|
1027
|
+
|
|
1028
|
+
try:
|
|
1029
|
+
result = subprocess.run(
|
|
1030
|
+
[claude_cmd, "mcp", "get", server_name],
|
|
1031
|
+
capture_output=True,
|
|
1032
|
+
text=True,
|
|
1033
|
+
timeout=10,
|
|
1034
|
+
)
|
|
1035
|
+
|
|
1036
|
+
if result.returncode != 0:
|
|
1037
|
+
print_error(f"MCP server '{server_name}' not found.")
|
|
1038
|
+
print_info(
|
|
1039
|
+
"Run 'mcp-vector-search mcp install' or 'mcp-vector-search mcp init' first"
|
|
1040
|
+
)
|
|
1041
|
+
raise typer.Exit(1)
|
|
1042
|
+
|
|
1043
|
+
print_success(f"✅ MCP server '{server_name}' is configured")
|
|
1044
|
+
|
|
1045
|
+
# Test if we can run the server directly
|
|
1046
|
+
print_info("Testing server startup...")
|
|
1047
|
+
|
|
1048
|
+
server_command = get_mcp_server_command(project_root)
|
|
1049
|
+
test_process = subprocess.Popen(
|
|
1050
|
+
server_command.split(),
|
|
1051
|
+
stdin=subprocess.PIPE,
|
|
1052
|
+
stdout=subprocess.PIPE,
|
|
1053
|
+
stderr=subprocess.PIPE,
|
|
1054
|
+
text=True,
|
|
1055
|
+
)
|
|
1056
|
+
|
|
1057
|
+
# Send a simple initialization request
|
|
1058
|
+
init_request = {
|
|
1059
|
+
"jsonrpc": "2.0",
|
|
1060
|
+
"id": 1,
|
|
1061
|
+
"method": "initialize",
|
|
1062
|
+
"params": {
|
|
1063
|
+
"protocolVersion": "2024-11-05",
|
|
1064
|
+
"capabilities": {},
|
|
1065
|
+
"clientInfo": {"name": "test", "version": "1.0.0"},
|
|
1066
|
+
},
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
try:
|
|
1070
|
+
test_process.stdin.write(json.dumps(init_request) + "\n")
|
|
1071
|
+
test_process.stdin.flush()
|
|
1072
|
+
|
|
1073
|
+
# Wait for response with timeout
|
|
1074
|
+
test_process.wait(timeout=5)
|
|
1075
|
+
|
|
1076
|
+
if test_process.returncode == 0:
|
|
1077
|
+
print_success("✅ MCP server starts successfully")
|
|
1078
|
+
else:
|
|
1079
|
+
stderr_output = test_process.stderr.read()
|
|
1080
|
+
print_warning(
|
|
1081
|
+
f"⚠️ Server startup test inconclusive: {stderr_output}"
|
|
1082
|
+
)
|
|
1083
|
+
|
|
1084
|
+
except subprocess.TimeoutExpired:
|
|
1085
|
+
test_process.terminate()
|
|
1086
|
+
print_success("✅ MCP server is responsive")
|
|
1087
|
+
|
|
1088
|
+
print_success("🎉 MCP integration test completed!")
|
|
1089
|
+
print_info("You can now use the vector search tools in Claude Code.")
|
|
1090
|
+
|
|
1091
|
+
except subprocess.TimeoutExpired:
|
|
1092
|
+
print_error("Timeout testing MCP server")
|
|
1093
|
+
raise typer.Exit(1)
|
|
1094
|
+
|
|
1095
|
+
except Exception as e:
|
|
1096
|
+
print_error(f"Test failed: {e}")
|
|
1097
|
+
raise typer.Exit(1)
|
|
1098
|
+
|
|
1099
|
+
|
|
1100
|
+
@mcp_app.command("remove")
|
|
1101
|
+
def remove_mcp_integration(
|
|
1102
|
+
ctx: typer.Context,
|
|
1103
|
+
server_name: str = typer.Option(
|
|
1104
|
+
"mcp-vector-search", "--name", help="Name of the MCP server to remove"
|
|
1105
|
+
),
|
|
1106
|
+
confirm: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation prompt"),
|
|
1107
|
+
) -> None:
|
|
1108
|
+
"""Remove MCP integration from the current project.
|
|
1109
|
+
|
|
1110
|
+
Removes the server configuration from .mcp.json in the project root.
|
|
1111
|
+
"""
|
|
1112
|
+
try:
|
|
1113
|
+
# Get project root
|
|
1114
|
+
project_root = ctx.obj.get("project_root") or Path.cwd()
|
|
1115
|
+
mcp_config_path = project_root / ".mcp.json"
|
|
1116
|
+
|
|
1117
|
+
# Check if .mcp.json exists
|
|
1118
|
+
if not mcp_config_path.exists():
|
|
1119
|
+
print_warning(f"No .mcp.json found at {mcp_config_path}")
|
|
1120
|
+
return
|
|
1121
|
+
|
|
1122
|
+
# Load configuration
|
|
1123
|
+
with open(mcp_config_path) as f:
|
|
1124
|
+
config = json.load(f)
|
|
1125
|
+
|
|
1126
|
+
# Check if server exists in configuration
|
|
1127
|
+
if "mcpServers" not in config or server_name not in config["mcpServers"]:
|
|
1128
|
+
print_warning(f"MCP server '{server_name}' not found in .mcp.json")
|
|
1129
|
+
return
|
|
1130
|
+
|
|
1131
|
+
# Confirm removal
|
|
1132
|
+
if not confirm:
|
|
1133
|
+
confirmed = typer.confirm(
|
|
1134
|
+
f"Remove MCP server '{server_name}' from .mcp.json?"
|
|
1135
|
+
)
|
|
1136
|
+
if not confirmed:
|
|
1137
|
+
print_info("Removal cancelled.")
|
|
1138
|
+
return
|
|
1139
|
+
|
|
1140
|
+
# Remove the MCP server from configuration
|
|
1141
|
+
print_info(f"Removing MCP server '{server_name}' from .mcp.json...")
|
|
1142
|
+
|
|
1143
|
+
del config["mcpServers"][server_name]
|
|
1144
|
+
|
|
1145
|
+
# Clean up empty mcpServers section
|
|
1146
|
+
if not config["mcpServers"]:
|
|
1147
|
+
del config["mcpServers"]
|
|
1148
|
+
|
|
1149
|
+
# Write updated configuration
|
|
1150
|
+
with open(mcp_config_path, "w") as f:
|
|
1151
|
+
json.dump(config, f, indent=2)
|
|
1152
|
+
|
|
1153
|
+
print_success(f"✅ MCP server '{server_name}' removed from .mcp.json!")
|
|
1154
|
+
print_info("The server is no longer available for this project")
|
|
1155
|
+
|
|
1156
|
+
except Exception as e:
|
|
1157
|
+
print_error(f"Removal failed: {e}")
|
|
1158
|
+
raise typer.Exit(1)
|
|
1159
|
+
|
|
1160
|
+
|
|
1161
|
+
@mcp_app.command("status")
|
|
1162
|
+
def show_mcp_status(
|
|
1163
|
+
ctx: typer.Context,
|
|
1164
|
+
server_name: str = typer.Option(
|
|
1165
|
+
"mcp-vector-search",
|
|
1166
|
+
"--name",
|
|
1167
|
+
help="Name of the MCP server to check",
|
|
1168
|
+
rich_help_panel="📁 Configuration",
|
|
1169
|
+
),
|
|
1170
|
+
) -> None:
|
|
1171
|
+
"""📊 Show MCP integration status.
|
|
1172
|
+
|
|
1173
|
+
Displays comprehensive status of MCP integration including Claude Code availability,
|
|
1174
|
+
server configuration, and project status.
|
|
1175
|
+
|
|
1176
|
+
[bold cyan]Examples:[/bold cyan]
|
|
1177
|
+
|
|
1178
|
+
[green]Check integration status:[/green]
|
|
1179
|
+
$ mcp-vector-search mcp status
|
|
1180
|
+
|
|
1181
|
+
[green]Check specific server:[/green]
|
|
1182
|
+
$ mcp-vector-search mcp status --name my-search-server
|
|
1183
|
+
|
|
1184
|
+
[dim]💡 Tip: Use this to verify Claude Code can detect the MCP server.[/dim]
|
|
1185
|
+
"""
|
|
1186
|
+
try:
|
|
1187
|
+
# Check if Claude Code is available
|
|
1188
|
+
claude_available = check_claude_code_available()
|
|
1189
|
+
|
|
1190
|
+
# Create status panel
|
|
1191
|
+
status_lines = []
|
|
1192
|
+
|
|
1193
|
+
if claude_available:
|
|
1194
|
+
status_lines.append("✅ Claude Code: Available")
|
|
1195
|
+
else:
|
|
1196
|
+
status_lines.append("❌ Claude Code: Not available")
|
|
1197
|
+
status_lines.append(" Install from: https://claude.ai/download")
|
|
1198
|
+
|
|
1199
|
+
# Check project configuration
|
|
1200
|
+
project_root = ctx.obj.get("project_root") or Path.cwd()
|
|
1201
|
+
mcp_config_path = project_root / ".mcp.json"
|
|
1202
|
+
if mcp_config_path.exists():
|
|
1203
|
+
with open(mcp_config_path) as f:
|
|
1204
|
+
project_config = json.load(f)
|
|
1205
|
+
|
|
1206
|
+
if (
|
|
1207
|
+
"mcpServers" in project_config
|
|
1208
|
+
and server_name in project_config["mcpServers"]
|
|
1209
|
+
):
|
|
1210
|
+
status_lines.append(
|
|
1211
|
+
f"✅ Project Config (.mcp.json): Server '{server_name}' installed"
|
|
1212
|
+
)
|
|
1213
|
+
server_info = project_config["mcpServers"][server_name]
|
|
1214
|
+
if "command" in server_info:
|
|
1215
|
+
status_lines.append(f" Command: {server_info['command']}")
|
|
1216
|
+
if "args" in server_info:
|
|
1217
|
+
status_lines.append(f" Args: {' '.join(server_info['args'])}")
|
|
1218
|
+
if "env" in server_info:
|
|
1219
|
+
file_watching = server_info["env"].get(
|
|
1220
|
+
"MCP_ENABLE_FILE_WATCHING", "true"
|
|
1221
|
+
)
|
|
1222
|
+
if file_watching.lower() in ("true", "1", "yes", "on"):
|
|
1223
|
+
status_lines.append(" File Watching: ✅ Enabled")
|
|
1224
|
+
else:
|
|
1225
|
+
status_lines.append(" File Watching: ❌ Disabled")
|
|
1226
|
+
else:
|
|
1227
|
+
status_lines.append(
|
|
1228
|
+
f"❌ Project Config (.mcp.json): Server '{server_name}' not found"
|
|
1229
|
+
)
|
|
1230
|
+
else:
|
|
1231
|
+
status_lines.append("❌ Project Config (.mcp.json): Not found")
|
|
1232
|
+
|
|
1233
|
+
# Check project status
|
|
1234
|
+
project_root = ctx.obj.get("project_root") or Path.cwd()
|
|
1235
|
+
project_manager = ProjectManager(project_root)
|
|
1236
|
+
|
|
1237
|
+
if project_manager.is_initialized():
|
|
1238
|
+
status_lines.append(f"✅ Project: Initialized at {project_root}")
|
|
1239
|
+
else:
|
|
1240
|
+
status_lines.append(f"❌ Project: Not initialized at {project_root}")
|
|
1241
|
+
|
|
1242
|
+
# Display status
|
|
1243
|
+
panel = Panel(
|
|
1244
|
+
"\n".join(status_lines), title="MCP Integration Status", border_style="blue"
|
|
1245
|
+
)
|
|
1246
|
+
console.print(panel)
|
|
1247
|
+
|
|
1248
|
+
except Exception as e:
|
|
1249
|
+
print_error(f"Status check failed: {e}")
|
|
1250
|
+
raise typer.Exit(1)
|
|
1251
|
+
|
|
1252
|
+
|
|
1253
|
+
if __name__ == "__main__":
|
|
1254
|
+
mcp_app()
|