|
12 | 12 | import logging |
13 | 13 | import sys |
14 | 14 | from pathlib import Path |
| 15 | +from typing import Optional |
15 | 16 |
|
16 | 17 | from hatch.environment_manager import HatchEnvironmentManager |
17 | 18 | from hatch_validator import HatchPackageValidator |
@@ -89,6 +90,156 @@ def get_package_mcp_server_config(env_manager: HatchEnvironmentManager, env_name |
89 | 90 | except Exception as e: |
90 | 91 | raise ValueError(f"Failed to get MCP server config for package '{package_name}': {e}") |
91 | 92 |
|
| 93 | +def handle_mcp_discover_hosts(): |
| 94 | + """Handle 'hatch mcp discover hosts' command.""" |
| 95 | + try: |
| 96 | + # Import strategies to trigger registration |
| 97 | + import hatch.mcp_host_config.strategies |
| 98 | + |
| 99 | + available_hosts = MCPHostRegistry.detect_available_hosts() |
| 100 | + print("Available MCP host platforms:") |
| 101 | + |
| 102 | + for host_type in MCPHostType: |
| 103 | + try: |
| 104 | + strategy = MCPHostRegistry.get_strategy(host_type) |
| 105 | + config_path = strategy.get_config_path() |
| 106 | + is_available = host_type in available_hosts |
| 107 | + |
| 108 | + status = "✓ Available" if is_available else "✗ Not detected" |
| 109 | + print(f" {host_type.value}: {status}") |
| 110 | + if config_path: |
| 111 | + print(f" Config path: {config_path}") |
| 112 | + except Exception as e: |
| 113 | + print(f" {host_type.value}: Error - {e}") |
| 114 | + |
| 115 | + return 0 |
| 116 | + except Exception as e: |
| 117 | + print(f"Error discovering hosts: {e}") |
| 118 | + return 1 |
| 119 | + |
| 120 | +def handle_mcp_discover_servers(env_manager: HatchEnvironmentManager, env_name: Optional[str] = None): |
| 121 | + """Handle 'hatch mcp discover servers' command.""" |
| 122 | + try: |
| 123 | + env_name = env_name or env_manager.get_current_environment() |
| 124 | + |
| 125 | + if not env_manager.environment_exists(env_name): |
| 126 | + print(f"Error: Environment '{env_name}' does not exist") |
| 127 | + return 1 |
| 128 | + |
| 129 | + packages = env_manager.list_packages(env_name) |
| 130 | + mcp_packages = [] |
| 131 | + |
| 132 | + for package in packages: |
| 133 | + try: |
| 134 | + # Check if package has MCP server entry point |
| 135 | + server_config = get_package_mcp_server_config(env_manager, env_name, package['name']) |
| 136 | + mcp_packages.append({ |
| 137 | + 'package': package, |
| 138 | + 'server_config': server_config |
| 139 | + }) |
| 140 | + except ValueError: |
| 141 | + # Package doesn't have MCP server |
| 142 | + continue |
| 143 | + |
| 144 | + if not mcp_packages: |
| 145 | + print(f"No MCP servers found in environment '{env_name}'") |
| 146 | + return 0 |
| 147 | + |
| 148 | + print(f"MCP servers in environment '{env_name}':") |
| 149 | + for item in mcp_packages: |
| 150 | + package = item['package'] |
| 151 | + server_config = item['server_config'] |
| 152 | + print(f" {server_config.name}:") |
| 153 | + print(f" Package: {package['name']} v{package.get('version', 'unknown')}") |
| 154 | + print(f" Command: {server_config.command}") |
| 155 | + print(f" Args: {server_config.args}") |
| 156 | + if server_config.env: |
| 157 | + print(f" Environment: {server_config.env}") |
| 158 | + |
| 159 | + return 0 |
| 160 | + except Exception as e: |
| 161 | + print(f"Error discovering servers: {e}") |
| 162 | + return 1 |
| 163 | + |
| 164 | +def handle_mcp_list_hosts(): |
| 165 | + """Handle 'hatch mcp list hosts' command.""" |
| 166 | + try: |
| 167 | + # Import strategies to trigger registration |
| 168 | + import hatch.mcp_host_config.strategies |
| 169 | + |
| 170 | + available_hosts = MCPHostRegistry.detect_available_hosts() |
| 171 | + all_hosts = list(MCPHostType) |
| 172 | + |
| 173 | + print("MCP host platforms status:") |
| 174 | + print(f"{'Host Platform':<20} {'Status':<15} {'Config Path'}") |
| 175 | + print("-" * 70) |
| 176 | + |
| 177 | + for host_type in all_hosts: |
| 178 | + try: |
| 179 | + strategy = MCPHostRegistry.get_strategy(host_type) |
| 180 | + config_path = strategy.get_config_path() |
| 181 | + is_available = host_type in available_hosts |
| 182 | + |
| 183 | + status = "Available" if is_available else "Not detected" |
| 184 | + config_display = str(config_path) if config_path else "N/A" |
| 185 | + |
| 186 | + print(f"{host_type.value:<20} {status:<15} {config_display}") |
| 187 | + except Exception as e: |
| 188 | + print(f"{host_type.value:<20} {'Error':<15} {str(e)}") |
| 189 | + |
| 190 | + return 0 |
| 191 | + except Exception as e: |
| 192 | + print(f"Error listing hosts: {e}") |
| 193 | + return 1 |
| 194 | + |
| 195 | +def handle_mcp_list_servers(env_manager: HatchEnvironmentManager, env_name: Optional[str] = None): |
| 196 | + """Handle 'hatch mcp list servers' command.""" |
| 197 | + try: |
| 198 | + env_name = env_name or env_manager.get_current_environment() |
| 199 | + |
| 200 | + if not env_manager.environment_exists(env_name): |
| 201 | + print(f"Error: Environment '{env_name}' does not exist") |
| 202 | + return 1 |
| 203 | + |
| 204 | + packages = env_manager.list_packages(env_name) |
| 205 | + mcp_packages = [] |
| 206 | + |
| 207 | + for package in packages: |
| 208 | + try: |
| 209 | + # Check if package has MCP server entry point |
| 210 | + server_config = get_package_mcp_server_config(env_manager, env_name, package['name']) |
| 211 | + mcp_packages.append({ |
| 212 | + 'package': package, |
| 213 | + 'server_config': server_config |
| 214 | + }) |
| 215 | + except ValueError: |
| 216 | + # Package doesn't have MCP server |
| 217 | + continue |
| 218 | + |
| 219 | + if not mcp_packages: |
| 220 | + print(f"No MCP servers configured in environment '{env_name}'") |
| 221 | + return 0 |
| 222 | + |
| 223 | + print(f"MCP servers in environment '{env_name}':") |
| 224 | + print(f"{'Server Name':<20} {'Package':<20} {'Version':<10} {'Command'}") |
| 225 | + print("-" * 80) |
| 226 | + |
| 227 | + for item in mcp_packages: |
| 228 | + package = item['package'] |
| 229 | + server_config = item['server_config'] |
| 230 | + |
| 231 | + server_name = server_config.name |
| 232 | + package_name = package['name'] |
| 233 | + version = package.get('version', 'unknown') |
| 234 | + command = f"{server_config.command} {' '.join(server_config.args)}" |
| 235 | + |
| 236 | + print(f"{server_name:<20} {package_name:<20} {version:<10} {command}") |
| 237 | + |
| 238 | + return 0 |
| 239 | + except Exception as e: |
| 240 | + print(f"Error listing servers: {e}") |
| 241 | + return 1 |
| 242 | + |
92 | 243 | def main(): |
93 | 244 | """Main entry point for Hatch CLI. |
94 | 245 | |
@@ -188,6 +339,35 @@ def main(): |
188 | 339 | python_shell_parser.add_argument("--hatch_env", default=None, help="Hatch environment name in which the Python environment is located (default: current environment)") |
189 | 340 | python_shell_parser.add_argument("--cmd", help="Command to run in the shell (optional)") |
190 | 341 |
|
| 342 | + # MCP host configuration commands |
| 343 | + mcp_subparsers = subparsers.add_parser("mcp", help="MCP host configuration commands").add_subparsers( |
| 344 | + dest="mcp_command", help="MCP command to execute" |
| 345 | + ) |
| 346 | + |
| 347 | + # MCP discovery commands |
| 348 | + mcp_discover_subparsers = mcp_subparsers.add_parser("discover", help="Discover MCP hosts and servers").add_subparsers( |
| 349 | + dest="discover_command", help="Discovery command to execute" |
| 350 | + ) |
| 351 | + |
| 352 | + # Discover hosts command |
| 353 | + mcp_discover_hosts_parser = mcp_discover_subparsers.add_parser("hosts", help="Discover available MCP host platforms") |
| 354 | + |
| 355 | + # Discover servers command |
| 356 | + mcp_discover_servers_parser = mcp_discover_subparsers.add_parser("servers", help="Discover configured MCP servers") |
| 357 | + mcp_discover_servers_parser.add_argument("--env", "-e", default=None, help="Environment name (default: current environment)") |
| 358 | + |
| 359 | + # MCP list commands |
| 360 | + mcp_list_subparsers = mcp_subparsers.add_parser("list", help="List MCP hosts and servers").add_subparsers( |
| 361 | + dest="list_command", help="List command to execute" |
| 362 | + ) |
| 363 | + |
| 364 | + # List hosts command |
| 365 | + mcp_list_hosts_parser = mcp_list_subparsers.add_parser("hosts", help="List detected MCP host platforms with status") |
| 366 | + |
| 367 | + # List servers command |
| 368 | + mcp_list_servers_parser = mcp_list_subparsers.add_parser("servers", help="List configured MCP servers from environment") |
| 369 | + mcp_list_servers_parser.add_argument("--env", "-e", default=None, help="Environment name (default: current environment)") |
| 370 | + |
191 | 371 | # Package management commands |
192 | 372 | pkg_subparsers = subparsers.add_parser("package", help="Package management commands").add_subparsers( |
193 | 373 | dest="pkg_command", help="Package command to execute" |
@@ -623,10 +803,33 @@ def main(): |
623 | 803 | except ValueError as e: |
624 | 804 | print(f"Error: {e}") |
625 | 805 | return 1 |
626 | | - |
| 806 | + |
627 | 807 | else: |
628 | 808 | parser.print_help() |
629 | 809 | return 1 |
| 810 | + |
| 811 | + elif args.command == "mcp": |
| 812 | + if args.mcp_command == "discover": |
| 813 | + if args.discover_command == "hosts": |
| 814 | + return handle_mcp_discover_hosts() |
| 815 | + elif args.discover_command == "servers": |
| 816 | + return handle_mcp_discover_servers(env_manager, args.env) |
| 817 | + else: |
| 818 | + print("Unknown discover command") |
| 819 | + return 1 |
| 820 | + |
| 821 | + elif args.mcp_command == "list": |
| 822 | + if args.list_command == "hosts": |
| 823 | + return handle_mcp_list_hosts() |
| 824 | + elif args.list_command == "servers": |
| 825 | + return handle_mcp_list_servers(env_manager, args.env) |
| 826 | + else: |
| 827 | + print("Unknown list command") |
| 828 | + return 1 |
| 829 | + else: |
| 830 | + print("Unknown MCP command") |
| 831 | + return 1 |
| 832 | + |
630 | 833 | else: |
631 | 834 | parser.print_help() |
632 | 835 | return 1 |
|
0 commit comments