Skip to content

Commit 55efeaa

Browse files
author
LittleCoinCoin
committed
fix: resolve configuration file corruption and data loss issues
Root cause: MCPServerConfig model was rejecting host-specific fields and configuration writing was overwriting existing servers instead of merging. Critical fixes: 1. **Allow host-specific fields**: Changed MCPServerConfig.Config.extra from 'forbid' to 'allow' to support Gemini's cwd, timeout, trust fields 2. **Preserve existing servers**: Modified write_configuration to merge new servers with existing ones instead of complete replacement 3. **Enhanced JSON validation**: Added JSON verification after write to prevent invalid output with missing brackets 4. **Improved error handling**: Better cleanup and error reporting for JSON serialization failures Resolves Issue 1: Configuration File Corruption and Data Loss - ✅ Existing mcpServers configuration preserved (no data loss) - ✅ Valid JSON output with proper structure - ✅ Host-specific fields (cwd, timeout, trust) supported Tested: All MCP CLI tests pass (100% success rate) Verified: Configuration merge preserves existing servers while adding new ones
1 parent 6119fe2 commit 55efeaa

File tree

2 files changed

+27
-13
lines changed

2 files changed

+27
-13
lines changed

hatch/mcp_host_config/models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ def is_remote_server(self) -> bool:
101101

102102
class Config:
103103
"""Pydantic configuration."""
104-
extra = "forbid" # Prevent additional fields for strict validation
104+
extra = "allow" # Allow additional fields for host-specific extensions
105105
json_encoders = {
106106
Path: str
107107
}

hatch/mcp_host_config/strategies.py

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -458,21 +458,35 @@ def write_configuration(self, config: HostConfiguration, no_backup: bool = False
458458
except Exception:
459459
pass
460460

461-
# Convert MCPServerConfig objects to dict
462-
servers_dict = {}
461+
# Preserve existing servers and add/update new ones
462+
existing_servers = existing_config.get(self.get_config_key(), {})
463+
464+
# Convert MCPServerConfig objects to dict and merge with existing
463465
for name, server_config in config.servers.items():
464-
servers_dict[name] = server_config.model_dump(exclude_none=True)
465-
466-
# Update configuration
467-
existing_config[self.get_config_key()] = servers_dict
466+
existing_servers[name] = server_config.model_dump(exclude_none=True)
467+
468+
# Update configuration with merged servers
469+
existing_config[self.get_config_key()] = existing_servers
468470

469-
# Write atomically
471+
# Write atomically with enhanced error handling
470472
temp_path = config_path.with_suffix('.tmp')
471-
with open(temp_path, 'w') as f:
472-
json.dump(existing_config, f, indent=2)
473-
474-
temp_path.replace(config_path)
475-
return True
473+
try:
474+
with open(temp_path, 'w') as f:
475+
json.dump(existing_config, f, indent=2, ensure_ascii=False)
476+
477+
# Verify the JSON is valid by reading it back
478+
with open(temp_path, 'r') as f:
479+
json.load(f) # This will raise an exception if JSON is invalid
480+
481+
# Only replace if verification succeeds
482+
temp_path.replace(config_path)
483+
return True
484+
except Exception as json_error:
485+
# Clean up temp file on JSON error
486+
if temp_path.exists():
487+
temp_path.unlink()
488+
logger.error(f"JSON serialization/verification failed: {json_error}")
489+
raise
476490

477491
except Exception as e:
478492
logger.error(f"Failed to write Gemini configuration: {e}")

0 commit comments

Comments
 (0)