Skip to content

Commit f8fdbe9

Browse files
author
LittleCoinCoin
committed
feat: implement MCP host discovery and listing commands (Phase 3c)
Add comprehensive MCP discovery and listing functionality: Core Commands: - hatch mcp discover hosts: Detect available MCP host platforms - hatch mcp discover servers: Find MCP servers in environment packages - hatch mcp list hosts: List all host platforms with status and config paths - hatch mcp list servers: List configured MCP servers in tabular format Implementation Details: - Integration with MCPHostRegistry.detect_available_hosts() API - Environment-aware server discovery using existing PackageService patterns - Formatted table output for improved user experience - Comprehensive error handling for nonexistent environments - Support for default environment when --env not specified Testing: - 12 comprehensive test cases covering argument parsing and backend integration - Component-level integration tests with mocked dependencies - Error scenario validation and output format verification - Regression tests for CLI argument handling Next: Implement backup management commands (Phase 3d) for complete MCP host configuration management functionality.
1 parent f4dd2fc commit f8fdbe9

File tree

2 files changed

+498
-1
lines changed

2 files changed

+498
-1
lines changed

hatch/cli_hatch.py

Lines changed: 204 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import logging
1313
import sys
1414
from pathlib import Path
15+
from typing import Optional
1516

1617
from hatch.environment_manager import HatchEnvironmentManager
1718
from hatch_validator import HatchPackageValidator
@@ -89,6 +90,156 @@ def get_package_mcp_server_config(env_manager: HatchEnvironmentManager, env_name
8990
except Exception as e:
9091
raise ValueError(f"Failed to get MCP server config for package '{package_name}': {e}")
9192

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+
92243
def main():
93244
"""Main entry point for Hatch CLI.
94245
@@ -188,6 +339,35 @@ def main():
188339
python_shell_parser.add_argument("--hatch_env", default=None, help="Hatch environment name in which the Python environment is located (default: current environment)")
189340
python_shell_parser.add_argument("--cmd", help="Command to run in the shell (optional)")
190341

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+
191371
# Package management commands
192372
pkg_subparsers = subparsers.add_parser("package", help="Package management commands").add_subparsers(
193373
dest="pkg_command", help="Package command to execute"
@@ -623,10 +803,33 @@ def main():
623803
except ValueError as e:
624804
print(f"Error: {e}")
625805
return 1
626-
806+
627807
else:
628808
parser.print_help()
629809
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+
630833
else:
631834
parser.print_help()
632835
return 1

0 commit comments

Comments
 (0)