|
2 | 2 | Install command for MCP |
3 | 3 | """ |
4 | 4 |
|
| 5 | +import os |
| 6 | +import json |
| 7 | +import subprocess |
| 8 | +from datetime import datetime |
| 9 | + |
5 | 10 | import click |
6 | 11 | from rich.console import Console |
7 | | -from rich.progress import Progress |
| 12 | +from rich.progress import Progress, SpinnerColumn, TextColumn |
| 13 | +from rich.prompt import Confirm |
| 14 | +from rich.panel import Panel |
8 | 15 |
|
9 | 16 | from mcp.utils.repository import RepositoryManager |
10 | 17 | from mcp.utils.config import ConfigManager |
| 18 | +from mcp.utils.client_detector import detect_installed_clients |
11 | 19 |
|
12 | 20 | console = Console() |
13 | 21 | repo_manager = RepositoryManager() |
14 | 22 | config_manager = ConfigManager() |
15 | 23 |
|
16 | 24 | @click.command() |
17 | 25 | @click.argument("server_name") |
18 | | -@click.option("--version", help="Specific version to install") |
19 | | -@click.option("--client", help="Enable for a specific client after installation") |
20 | | -def install(server_name, version, client=None): |
| 26 | +@click.option("--force", is_flag=True, help="Force reinstall if server is already installed") |
| 27 | +def install(server_name, force=False): |
21 | 28 | """Install an MCP server. |
22 | 29 | |
23 | 30 | Examples: |
24 | | - mcp install filesystem |
25 | | - mcp install filesystem --version=1.0.0 |
26 | | - mcp install filesystem --client=cursor |
| 31 | + mcp install time |
| 32 | + mcp install github |
| 33 | + mcp install everything --force |
27 | 34 | """ |
28 | | - if version: |
29 | | - console.print(f"[bold green]Installing MCP server:[/] {server_name} (version {version})") |
30 | | - else: |
31 | | - console.print(f"[bold green]Installing latest version of MCP server:[/] {server_name}") |
32 | | - |
33 | 35 | # Check if already installed |
34 | | - if config_manager.get_server_info(server_name): |
35 | | - console.print(f"[yellow]Server '{server_name}' is already installed.[/]") |
36 | | - console.print("Use 'mcp update' to update it to a newer version.") |
| 36 | + existing_server = config_manager.get_server_info(server_name) |
| 37 | + if existing_server and not force: |
| 38 | + console.print(f"[yellow]Server '{server_name}' is already installed (v{existing_server.get('version', 'unknown')}).[/]") |
| 39 | + console.print("Use 'mcp install --force' to reinstall or 'mcp update' to update it to a newer version.") |
37 | 40 | return |
38 | 41 |
|
39 | | - # Search for the server |
| 42 | + # Get server metadata from repository |
40 | 43 | server_metadata = repo_manager.get_server_metadata(server_name) |
41 | 44 | if not server_metadata: |
42 | | - console.print(f"[bold red]Error:[/] Server '{server_name}' not found in repository.") |
| 45 | + console.print(f"[bold red]Error:[/] Server '{server_name}' not found in registry.") |
| 46 | + console.print(f"Available servers: {', '.join(repo_manager._fetch_servers().keys())}") |
43 | 47 | return |
44 | 48 |
|
45 | | - # Check version compatibility |
46 | | - if version and server_metadata["version"] != version: |
47 | | - console.print(f"[bold red]Error:[/] Version {version} not found. Available version: {server_metadata['version']}") |
48 | | - return |
| 49 | + # Display server information |
| 50 | + display_name = server_metadata.get("display_name", server_name) |
| 51 | + description = server_metadata.get("description", "No description available") |
| 52 | + available_version = server_metadata.get("version") |
| 53 | + author_info = server_metadata.get("author", {}) |
| 54 | + author_name = author_info.get("name", "Unknown") |
| 55 | + license_info = server_metadata.get("license", "Unknown") |
49 | 56 |
|
50 | | - if not version: |
51 | | - version = server_metadata["version"] |
52 | | - console.print(f"Using latest version: {version}") |
| 57 | + # Use the available version |
| 58 | + version = available_version |
53 | 59 |
|
54 | | - # Download server (in a real implementation, this would actually download files) |
55 | | - with Progress() as progress: |
56 | | - task = progress.add_task(f"Downloading {server_name}...", total=100) |
57 | | - # Simulate download progress |
58 | | - for i in range(101): |
59 | | - progress.update(task, completed=i) |
60 | | - if i < 100: |
61 | | - import time |
62 | | - time.sleep(0.02) |
| 60 | + # Display server information |
| 61 | + console.print(Panel( |
| 62 | + f"[bold]{display_name}[/] [dim]v{version}[/]\n" + |
| 63 | + f"[italic]{description}[/]\n\n" + |
| 64 | + f"Author: {author_name}\n" + |
| 65 | + f"License: {license_info}", |
| 66 | + title="Server Information", |
| 67 | + border_style="green", |
| 68 | + )) |
63 | 69 |
|
64 | | - # Register the server in our config |
| 70 | + # Check for API key requirements |
| 71 | + requirements = server_metadata.get("requirements", {}) |
| 72 | + needs_api_key = requirements.get("api_key", False) |
| 73 | + auth_type = requirements.get("authentication") |
| 74 | + |
| 75 | + if needs_api_key: |
| 76 | + console.print("[yellow]Note:[/] This server requires an API key or authentication.") |
| 77 | + if auth_type: |
| 78 | + console.print(f"Authentication type: [bold]{auth_type}[/]") |
| 79 | + |
| 80 | + # Installation preparation |
| 81 | + if not force and existing_server: |
| 82 | + if not Confirm.ask(f"Server '{server_name}' is already installed. Do you want to reinstall?"): |
| 83 | + return |
| 84 | + |
| 85 | + # Create server directory |
| 86 | + server_dir = os.path.expanduser(f"~/.config/mcp/servers/{server_name}") |
| 87 | + os.makedirs(server_dir, exist_ok=True) |
| 88 | + |
| 89 | + # Get installation instructions |
| 90 | + installation = server_metadata.get("installation", {}) |
| 91 | + install_command = installation.get("command") |
| 92 | + install_args = installation.get("args", []) |
| 93 | + package_name = installation.get("package") |
| 94 | + env_vars = installation.get("env", {}) |
| 95 | + |
| 96 | + if not install_command or not install_args: |
| 97 | + console.print(f"[bold red]Error:[/] Invalid installation information for server '{server_name}'.") |
| 98 | + return |
| 99 | + |
| 100 | + # Download and install server |
| 101 | + with Progress( |
| 102 | + SpinnerColumn(), |
| 103 | + TextColumn("[bold green]{task.description}[/]"), |
| 104 | + console=console |
| 105 | + ) as progress: |
| 106 | + # Download metadata |
| 107 | + progress.add_task("Downloading server metadata...", total=None) |
| 108 | + # Save metadata to server directory |
| 109 | + metadata_path = os.path.join(server_dir, "metadata.json") |
| 110 | + with open(metadata_path, "w") as f: |
| 111 | + json.dump(server_metadata, f, indent=2) |
| 112 | + |
| 113 | + # Install using the specified command and args |
| 114 | + progress.add_task(f"Installing {server_name} v{version}...", total=None) |
| 115 | + |
| 116 | + try: |
| 117 | + # Prepare environment variables |
| 118 | + env = os.environ.copy() |
| 119 | + |
| 120 | + # Replace variable placeholders with values from environment |
| 121 | + for key, value in env_vars.items(): |
| 122 | + if isinstance(value, str) and value.startswith("${"): |
| 123 | + env_var_name = value[2:-1] # Extract variable name from ${NAME} |
| 124 | + env_value = os.environ.get(env_var_name, "") |
| 125 | + env[key] = env_value |
| 126 | + |
| 127 | + # Warn if variable is not set |
| 128 | + if not env_value and needs_api_key: |
| 129 | + console.print(f"[yellow]Warning:[/] Environment variable {env_var_name} is not set") |
| 130 | + else: |
| 131 | + env[key] = value |
| 132 | + |
| 133 | + # Run installation command |
| 134 | + if install_command: |
| 135 | + full_command = [install_command] + install_args |
| 136 | + console.print(f"Running: [dim]{' '.join(full_command)}[/]") |
| 137 | + |
| 138 | + # Capture installation process output |
| 139 | + result = subprocess.run( |
| 140 | + full_command, |
| 141 | + env=env, |
| 142 | + stdout=subprocess.PIPE, |
| 143 | + stderr=subprocess.PIPE, |
| 144 | + text=True, |
| 145 | + check=False |
| 146 | + ) |
| 147 | + |
| 148 | + if result.returncode != 0: |
| 149 | + console.print(f"[bold red]Installation failed with error code {result.returncode}[/]") |
| 150 | + console.print(f"[red]{result.stderr}[/]") |
| 151 | + return |
| 152 | + except Exception as e: |
| 153 | + console.print(f"[bold red]Error during installation:[/] {str(e)}") |
| 154 | + return |
| 155 | + |
| 156 | + # Create server configuration |
65 | 157 | server_info = { |
66 | 158 | "name": server_name, |
67 | | - "display_name": server_metadata["display_name"], |
| 159 | + "display_name": display_name, |
68 | 160 | "version": version, |
69 | | - "description": server_metadata["description"], |
| 161 | + "description": description, |
70 | 162 | "status": "stopped", |
71 | | - "install_date": "2025-03-22", # In a real implementation, use current date |
72 | | - "path": f"~/.config/mcp/servers/{server_name}" |
| 163 | + "install_date": datetime.now().strftime("%Y-%m-%d"), |
| 164 | + "path": server_dir, |
| 165 | + "package": package_name |
73 | 166 | } |
| 167 | + |
| 168 | + # Register the server in our config |
74 | 169 | config_manager.register_server(server_name, server_info) |
75 | 170 |
|
76 | | - console.print(f"[bold green]Successfully installed {server_name} v{version}![/]") |
| 171 | + console.print(f"[bold green]Successfully installed {display_name} v{version}![/]") |
77 | 172 |
|
78 | | - # If client option specified, enable for that client |
79 | | - if client: |
80 | | - if client not in config_manager.get_config()["clients"]: |
81 | | - console.print(f"[yellow]Warning: Unknown client '{client}'. Server not enabled for any client.[/]") |
| 173 | + # Handle client enablement - automatically enable for active client |
| 174 | + active_client = config_manager.get_active_client() |
| 175 | + installed_clients = detect_installed_clients() |
| 176 | + |
| 177 | + # Enable for active client if installed |
| 178 | + if active_client and installed_clients.get(active_client, False): |
| 179 | + success = config_manager.enable_server_for_client(server_name, active_client) |
| 180 | + if success: |
| 181 | + console.print(f"[green]Enabled {server_name} for active client: {active_client}[/]") |
82 | 182 | else: |
83 | | - success = config_manager.enable_server_for_client(server_name, client) |
84 | | - if success: |
85 | | - console.print(f"[green]Enabled {server_name} for {client}[/]") |
86 | | - else: |
87 | | - console.print(f"[yellow]Failed to enable {server_name} for {client}[/]") |
| 183 | + console.print(f"[yellow]Failed to enable {server_name} for {active_client}[/]") |
| 184 | + |
| 185 | + # Display usage examples if available |
| 186 | + examples = server_metadata.get("examples", []) |
| 187 | + if examples: |
| 188 | + console.print("\n[bold]Usage Examples:[/]") |
| 189 | + for i, example in enumerate(examples, 1): |
| 190 | + title = example.get("title", f"Example {i}") |
| 191 | + description = example.get("description", "") |
| 192 | + prompt = example.get("prompt", "") |
| 193 | + |
| 194 | + console.print(f" [cyan]{title}[/]: {description}") |
| 195 | + if prompt: |
| 196 | + console.print(f" Try: [italic]\"{prompt}\"[/]\n") |
0 commit comments