Skip to content

Commit 2824de7

Browse files
author
LittleCoinCoin
committed
fix(host): configuration cleanup after package and environment removal
- Enhanced remove_package() to clean up MCP server configurations from all configured hosts - Enhanced remove_environment() to clean up all packages' MCP servers from host configurations - Integrates with MCPHostConfigurationManager for proper server removal with backup support - Prevents orphaned MCP server entries in host configuration files - Maintains graceful error handling with warning logs for cleanup failures Resolves host configuration cleanup gaps in package/environment removal workflows. Ensures complete cleanup when packages or environments are removed from Hatch.
1 parent c06378f commit 2824de7

File tree

1 file changed

+80
-15
lines changed

1 file changed

+80
-15
lines changed

hatch/environment_manager.py

Lines changed: 80 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -459,39 +459,73 @@ def install_mcp_server(self, env_name: Optional[str] = None, tag: Optional[str]
459459
def remove_environment(self, name: str) -> bool:
460460
"""
461461
Remove an environment.
462-
462+
463463
Args:
464464
name: Name of the environment to remove
465-
465+
466466
Returns:
467467
bool: True if removed successfully, False otherwise
468468
"""
469469
# Cannot remove default environment
470470
if name == "default":
471471
self.logger.error("Cannot remove default environment")
472472
return False
473-
473+
474474
# Check if environment exists
475475
if name not in self._environments:
476476
self.logger.warning(f"Environment does not exist: {name}")
477477
return False
478-
478+
479479
# If removing current environment, switch to default
480480
if name == self._current_env_name:
481481
self.set_current_environment("default")
482-
483-
# Remove Python environment if it exists
482+
483+
# Clean up MCP server configurations for all packages in this environment
484484
env_data = self._environments[name]
485+
packages = env_data.get("packages", [])
486+
if packages:
487+
self.logger.info(f"Cleaning up MCP server configurations for {len(packages)} packages in environment {name}")
488+
try:
489+
from .mcp_host_config.host_management import MCPHostConfigurationManager
490+
mcp_manager = MCPHostConfigurationManager()
491+
492+
for pkg in packages:
493+
package_name = pkg.get("name")
494+
configured_hosts = pkg.get("configured_hosts", {})
495+
496+
if configured_hosts and package_name:
497+
for hostname in configured_hosts.keys():
498+
try:
499+
# Remove server from host configuration file
500+
result = mcp_manager.remove_server(
501+
server_name=package_name, # In current 1:1 design, package name = server name
502+
hostname=hostname,
503+
no_backup=False # Create backup for safety
504+
)
505+
506+
if result.success:
507+
self.logger.info(f"Removed MCP server '{package_name}' from host '{hostname}' (env removal)")
508+
else:
509+
self.logger.warning(f"Failed to remove MCP server '{package_name}' from host '{hostname}': {result.error_message}")
510+
except Exception as e:
511+
self.logger.warning(f"Error removing MCP server '{package_name}' from host '{hostname}': {e}")
512+
513+
except ImportError:
514+
self.logger.warning("MCP host configuration manager not available for cleanup")
515+
except Exception as e:
516+
self.logger.warning(f"Error during MCP server cleanup for environment removal: {e}")
517+
518+
# Remove Python environment if it exists
485519
if env_data.get("python_environment", False):
486520
try:
487521
self.python_env_manager.remove_python_environment(name)
488522
self.logger.info(f"Removed Python environment for {name}")
489523
except PythonEnvironmentError as e:
490524
self.logger.warning(f"Failed to remove Python environment: {e}")
491-
525+
492526
# Remove environment
493527
del self._environments[name]
494-
528+
495529
# Save environments and update cache
496530
self._save_environments()
497531
self.logger.info(f"Removed environment: {name}")
@@ -855,31 +889,62 @@ def list_packages(self, env_name: Optional[str] = None) -> List[Dict]:
855889
def remove_package(self, package_name: str, env_name: Optional[str] = None) -> bool:
856890
"""
857891
Remove a package from an environment.
858-
892+
859893
Args:
860894
package_name: Name of the package to remove
861895
env_name: Environment to remove from (uses current if None)
862-
896+
863897
Returns:
864898
bool: True if successful, False otherwise
865899
"""
866900
env_name = env_name or self._current_env_name
867901
if not self.environment_exists(env_name):
868902
self.logger.error(f"Environment {env_name} does not exist")
869903
return False
870-
904+
871905
# Check if package exists in environment
872906
env_packages = self._environments[env_name].get("packages", [])
873907
pkg_index = None
908+
package_to_remove = None
874909
for i, pkg in enumerate(env_packages):
875910
if pkg.get("name") == package_name:
876911
pkg_index = i
912+
package_to_remove = pkg
877913
break
878-
914+
879915
if pkg_index is None:
880916
self.logger.warning(f"Package {package_name} not found in environment {env_name}")
881917
return False
882-
918+
919+
# Clean up MCP server configurations from all configured hosts
920+
configured_hosts = package_to_remove.get("configured_hosts", {})
921+
if configured_hosts:
922+
self.logger.info(f"Cleaning up MCP server configurations for package {package_name}")
923+
try:
924+
from .mcp_host_config.host_management import MCPHostConfigurationManager
925+
mcp_manager = MCPHostConfigurationManager()
926+
927+
for hostname in configured_hosts.keys():
928+
try:
929+
# Remove server from host configuration file
930+
result = mcp_manager.remove_server(
931+
server_name=package_name, # In current 1:1 design, package name = server name
932+
hostname=hostname,
933+
no_backup=False # Create backup for safety
934+
)
935+
936+
if result.success:
937+
self.logger.info(f"Removed MCP server '{package_name}' from host '{hostname}'")
938+
else:
939+
self.logger.warning(f"Failed to remove MCP server '{package_name}' from host '{hostname}': {result.error_message}")
940+
except Exception as e:
941+
self.logger.warning(f"Error removing MCP server '{package_name}' from host '{hostname}': {e}")
942+
943+
except ImportError:
944+
self.logger.warning("MCP host configuration manager not available for cleanup")
945+
except Exception as e:
946+
self.logger.warning(f"Error during MCP server cleanup: {e}")
947+
883948
# Remove package from filesystem
884949
pkg_path = self.get_environment_path(env_name) / package_name
885950
try:
@@ -889,11 +954,11 @@ def remove_package(self, package_name: str, env_name: Optional[str] = None) -> b
889954
except Exception as e:
890955
self.logger.error(f"Failed to remove package files for {package_name}: {e}")
891956
return False
892-
957+
893958
# Remove package from environment data
894959
env_packages.pop(pkg_index)
895960
self._save_environments()
896-
961+
897962
self.logger.info(f"Removed package {package_name} from environment {env_name}")
898963
return True
899964

0 commit comments

Comments
 (0)