|
| 1 | +# Codex MCP Host Support Feasibility Analysis |
| 2 | + |
| 3 | +## Executive Summary |
| 4 | + |
| 5 | +**FEASIBILITY: ✅ HIGHLY FEASIBLE** |
| 6 | + |
| 7 | +Adding Codex MCP host support to Hatch is highly feasible within the current architecture. The existing strategy pattern with decorator-based registration is excellently designed for format diversity, requiring only moderate enhancements to support TOML configuration files. No extensive refactoring is needed. |
| 8 | + |
| 9 | +## Current Architecture Analysis |
| 10 | + |
| 11 | +### Strategy Pattern Excellence |
| 12 | + |
| 13 | +The MCP host configuration system uses a well-designed strategy pattern that perfectly accommodates format diversity: |
| 14 | + |
| 15 | +```python |
| 16 | +# hatch/mcp_host_config/strategies.py |
| 17 | +@register_host_strategy(MCPHostType.CLAUDE_DESKTOP) |
| 18 | +class ClaudeDesktopStrategy(ClaudeHostStrategy): |
| 19 | + def read_configuration(self) -> HostConfiguration: # Format-specific |
| 20 | + def write_configuration(self, config: HostConfiguration) -> bool: # Format-specific |
| 21 | +``` |
| 22 | + |
| 23 | +**Key Architectural Strengths:** |
| 24 | +- **Format Encapsulation**: Each strategy completely encapsulates its file format handling |
| 25 | +- **Interface Consistency**: All strategies work with format-agnostic `HostConfiguration` objects |
| 26 | +- **Automatic Registration**: Decorator system automatically discovers new host types |
| 27 | +- **Family Inheritance**: Base classes (`ClaudeHostStrategy`, `CursorBasedHostStrategy`) enable code reuse |
| 28 | + |
| 29 | +### Data Model Flexibility |
| 30 | + |
| 31 | +The Pydantic model system is already designed for host-specific extensions: |
| 32 | + |
| 33 | +```python |
| 34 | +# hatch/mcp_host_config/models.py |
| 35 | +class MCPServerConfigBase(BaseModel): |
| 36 | + # Universal fields: command, args, env, url, headers, type |
| 37 | + |
| 38 | +class MCPServerConfigGemini(MCPServerConfigBase): |
| 39 | + # Gemini-specific: cwd, timeout, trust, oauth_* fields |
| 40 | + |
| 41 | +class MCPServerConfigVSCode(MCPServerConfigBase): |
| 42 | + # VS Code-specific: envFile, inputs fields |
| 43 | +``` |
| 44 | + |
| 45 | +**Pattern Compatibility:** |
| 46 | +- `MCPServerConfigCodex` can extend `MCPServerConfigBase` following established patterns |
| 47 | +- `HOST_MODEL_REGISTRY` already supports host-specific model mapping |
| 48 | +- Pydantic validation works regardless of serialization format |
| 49 | + |
| 50 | +### File Operations Infrastructure |
| 51 | + |
| 52 | +Current file operations are JSON-focused but architecturally sound: |
| 53 | + |
| 54 | +```python |
| 55 | +# hatch/mcp_host_config/backup.py |
| 56 | +class AtomicFileOperations: |
| 57 | + def atomic_write_with_backup(self, file_path: Path, data: Dict[str, Any], ...): |
| 58 | + # Currently hardcoded to json.dump() |
| 59 | +``` |
| 60 | + |
| 61 | +**Infrastructure Assessment:** |
| 62 | +- ✅ **Backup Creation**: Uses `shutil.copy2()` - format independent |
| 63 | +- ✅ **Atomic Operations**: Core logic is format-agnostic |
| 64 | +- ❌ **Serialization**: Hardcoded to JSON format |
| 65 | +- ❌ **File Extensions**: Assumes `.json` in backup naming |
| 66 | + |
| 67 | +## Codex Configuration Requirements |
| 68 | + |
| 69 | +### TOML Structure Analysis |
| 70 | + |
| 71 | +Codex uses TOML configuration at `~/.codex/config.toml`: |
| 72 | + |
| 73 | +```toml |
| 74 | +[features] |
| 75 | +rmcp_client = true |
| 76 | + |
| 77 | +[mcp_servers.context7] |
| 78 | +command = "npx" |
| 79 | +args = ["-y", "@upstash/context7-mcp"] |
| 80 | +startup_timeout_sec = 10 |
| 81 | +tool_timeout_sec = 60 |
| 82 | +enabled = true |
| 83 | +enabled_tools = ["tool1", "tool2"] |
| 84 | + |
| 85 | +[mcp_servers.context7.env] |
| 86 | +MY_VAR = "value" |
| 87 | + |
| 88 | +[mcp_servers.figma] |
| 89 | +url = "https://mcp.figma.com/mcp" |
| 90 | +bearer_token_env_var = "FIGMA_OAUTH_TOKEN" |
| 91 | +http_headers = { "X-Figma-Region" = "us-east-1" } |
| 92 | +``` |
| 93 | + |
| 94 | +### Codex-Specific Fields |
| 95 | + |
| 96 | +**Standard Fields** (already supported): |
| 97 | +- `command`, `args`, `env` - Local server configuration |
| 98 | +- `url` - Remote server configuration |
| 99 | + |
| 100 | +**Codex-Specific Fields** (require new model): |
| 101 | +- `env_vars: List[str]` - Environment variables to forward |
| 102 | +- `cwd: str` - Working directory for server |
| 103 | +- `startup_timeout_sec: int` - Server startup timeout |
| 104 | +- `tool_timeout_sec: int` - Tool execution timeout |
| 105 | +- `enabled: bool` - Enable/disable server |
| 106 | +- `enabled_tools: List[str]` - Tool allowlist |
| 107 | +- `disabled_tools: List[str]` - Tool denylist |
| 108 | +- `bearer_token_env_var: str` - Bearer token environment variable |
| 109 | +- `http_headers: Dict[str, str]` - Static HTTP headers |
| 110 | +- `env_http_headers: Dict[str, str]` - HTTP headers from environment |
| 111 | + |
| 112 | +**Global Configuration:** |
| 113 | +- `[features].rmcp_client: bool` - Enable Rust MCP client |
| 114 | + |
| 115 | +## Implementation Architecture |
| 116 | + |
| 117 | +### Phase 1: Data Model Extension |
| 118 | + |
| 119 | +```python |
| 120 | +# hatch/mcp_host_config/models.py |
| 121 | +class MCPHostType(str, Enum): |
| 122 | + # ... existing hosts ... |
| 123 | + CODEX = "codex" |
| 124 | + |
| 125 | +class MCPServerConfigCodex(MCPServerConfigBase): |
| 126 | + """Codex-specific MCP server configuration.""" |
| 127 | + |
| 128 | + # Codex-specific fields |
| 129 | + env_vars: Optional[List[str]] = Field(None, description="Environment variables to forward") |
| 130 | + cwd: Optional[str] = Field(None, description="Working directory") |
| 131 | + startup_timeout_sec: Optional[int] = Field(None, description="Server startup timeout") |
| 132 | + tool_timeout_sec: Optional[int] = Field(None, description="Tool execution timeout") |
| 133 | + enabled: Optional[bool] = Field(None, description="Enable/disable server") |
| 134 | + enabled_tools: Optional[List[str]] = Field(None, description="Tool allowlist") |
| 135 | + disabled_tools: Optional[List[str]] = Field(None, description="Tool denylist") |
| 136 | + |
| 137 | + # HTTP-specific fields |
| 138 | + bearer_token_env_var: Optional[str] = Field(None, description="Bearer token env var") |
| 139 | + http_headers: Optional[Dict[str, str]] = Field(None, description="Static HTTP headers") |
| 140 | + env_http_headers: Optional[Dict[str, str]] = Field(None, description="HTTP headers from env") |
| 141 | + |
| 142 | +# Update registry |
| 143 | +HOST_MODEL_REGISTRY[MCPHostType.CODEX] = MCPServerConfigCodex |
| 144 | +``` |
| 145 | + |
| 146 | +### Phase 2: Strategy Implementation |
| 147 | + |
| 148 | +```python |
| 149 | +# hatch/mcp_host_config/strategies.py |
| 150 | +@register_host_strategy(MCPHostType.CODEX) |
| 151 | +class CodexHostStrategy(MCPHostStrategy): |
| 152 | + """Configuration strategy for Codex IDE with TOML support.""" |
| 153 | + |
| 154 | + def get_config_path(self) -> Optional[Path]: |
| 155 | + return Path.home() / ".codex" / "config.toml" |
| 156 | + |
| 157 | + def read_configuration(self) -> HostConfiguration: |
| 158 | + # TOML parsing logic |
| 159 | + # Handle [mcp_servers.*] sections |
| 160 | + # Convert to HostConfiguration |
| 161 | + |
| 162 | + def write_configuration(self, config: HostConfiguration, no_backup: bool = False) -> bool: |
| 163 | + # Preserve [features] section |
| 164 | + # Convert HostConfiguration to TOML structure |
| 165 | + # Atomic TOML write operation |
| 166 | +``` |
| 167 | + |
| 168 | +### Phase 3: Backup System Enhancement |
| 169 | + |
| 170 | +```python |
| 171 | +# hatch/mcp_host_config/backup.py |
| 172 | +class AtomicFileOperations: |
| 173 | + def atomic_write_with_serializer(self, file_path: Path, data: Any, |
| 174 | + serializer: Callable[[Any, TextIO], None], |
| 175 | + backup_manager: "MCPHostConfigBackupManager", |
| 176 | + hostname: str, skip_backup: bool = False) -> bool: |
| 177 | + # Generalized atomic write with custom serializer |
| 178 | + |
| 179 | + def atomic_write_with_backup(self, file_path: Path, data: Dict[str, Any], ...): |
| 180 | + # Backward compatibility wrapper using JSON serializer |
| 181 | +``` |
| 182 | + |
| 183 | +## Technical Implementation Details |
| 184 | + |
| 185 | +### TOML Serialization Strategy |
| 186 | + |
| 187 | +```python |
| 188 | +def _convert_to_toml_structure(self, config: HostConfiguration) -> Dict[str, Any]: |
| 189 | + """Convert HostConfiguration to Codex TOML structure.""" |
| 190 | + toml_data = {} |
| 191 | + |
| 192 | + # Preserve existing [features] section |
| 193 | + if self._existing_features: |
| 194 | + toml_data["features"] = self._existing_features |
| 195 | + |
| 196 | + # Convert servers to [mcp_servers.*] sections |
| 197 | + toml_data["mcp_servers"] = {} |
| 198 | + for name, server_config in config.servers.items(): |
| 199 | + server_dict = server_config.model_dump(exclude_none=True) |
| 200 | + |
| 201 | + # Handle nested env section |
| 202 | + if "env" in server_dict: |
| 203 | + env_data = server_dict.pop("env") |
| 204 | + toml_data["mcp_servers"][name] = server_dict |
| 205 | + toml_data["mcp_servers"][name]["env"] = env_data |
| 206 | + else: |
| 207 | + toml_data["mcp_servers"][name] = server_dict |
| 208 | + |
| 209 | + return toml_data |
| 210 | +``` |
| 211 | + |
| 212 | +### Dependency Requirements |
| 213 | + |
| 214 | +```python |
| 215 | +# pyproject.toml |
| 216 | +[project] |
| 217 | +dependencies = [ |
| 218 | + # ... existing dependencies ... |
| 219 | + "tomli-w>=1.0.0", # TOML writing |
| 220 | + "tomli>=1.2.0; python_version<'3.11'", # TOML reading for Python <3.11 |
| 221 | +] |
| 222 | +``` |
| 223 | + |
| 224 | +## Risk Assessment |
| 225 | + |
| 226 | +### Low Risk Components |
| 227 | +- **Strategy Registration**: Existing decorator system handles new hosts automatically |
| 228 | +- **Data Validation**: Pydantic models provide robust validation regardless of format |
| 229 | +- **Interface Compatibility**: No changes to core interfaces required |
| 230 | + |
| 231 | +### Medium Risk Components |
| 232 | +- **TOML Serialization**: Need to handle nested structures and preserve global sections |
| 233 | +- **Backup System**: Requires refactoring to support multiple formats |
| 234 | +- **File Extension Handling**: Update backup naming for `.toml` files |
| 235 | + |
| 236 | +### Mitigation Strategies |
| 237 | +- **Comprehensive Testing**: Unit tests for TOML serialization/deserialization |
| 238 | +- **Backward Compatibility**: Ensure existing JSON-based hosts remain unaffected |
| 239 | +- **Incremental Implementation**: Phase-based approach allows validation at each step |
| 240 | + |
| 241 | +## Architectural Workflow Diagram |
| 242 | + |
| 243 | +```mermaid |
| 244 | +sequenceDiagram |
| 245 | + participant Client as Hatch CLI |
| 246 | + participant Manager as MCPHostConfigurationManager |
| 247 | + participant Registry as MCPHostRegistry |
| 248 | + participant Strategy as CodexHostStrategy |
| 249 | + participant FileOps as AtomicFileOperations |
| 250 | + participant TOML as TOML Parser |
| 251 | +
|
| 252 | + Client->>Manager: configure_server(codex_config) |
| 253 | + Manager->>Registry: get_strategy(MCPHostType.CODEX) |
| 254 | + Registry->>Strategy: return CodexHostStrategy instance |
| 255 | + Manager->>Strategy: validate_server_config() |
| 256 | + Strategy->>Manager: validation result |
| 257 | + Manager->>Strategy: read_configuration() |
| 258 | + Strategy->>TOML: parse ~/.codex/config.toml |
| 259 | + TOML->>Strategy: parsed TOML data |
| 260 | + Strategy->>Manager: HostConfiguration object |
| 261 | + Manager->>FileOps: atomic_write_with_serializer() |
| 262 | + FileOps->>Strategy: TOML serialization callback |
| 263 | + Strategy->>TOML: serialize to TOML format |
| 264 | + TOML->>FileOps: TOML string |
| 265 | + FileOps->>Manager: write success |
| 266 | + Manager->>Client: ConfigurationResult |
| 267 | +``` |
| 268 | + |
| 269 | +## Class Relationship Diagram |
| 270 | + |
| 271 | +```mermaid |
| 272 | +classDiagram |
| 273 | + class MCPHostStrategy { |
| 274 | + <<abstract>> |
| 275 | + +get_config_path() Path |
| 276 | + +read_configuration() HostConfiguration |
| 277 | + +write_configuration() bool |
| 278 | + +validate_server_config() bool |
| 279 | + } |
| 280 | + |
| 281 | + class CodexHostStrategy { |
| 282 | + +get_config_path() Path |
| 283 | + +read_configuration() HostConfiguration |
| 284 | + +write_configuration() bool |
| 285 | + +validate_server_config() bool |
| 286 | + -_parse_toml() Dict |
| 287 | + -_serialize_toml() str |
| 288 | + -_preserve_features() Dict |
| 289 | + } |
| 290 | + |
| 291 | + class MCPServerConfigBase { |
| 292 | + +command: Optional[str] |
| 293 | + +args: Optional[List[str]] |
| 294 | + +env: Optional[Dict] |
| 295 | + +url: Optional[str] |
| 296 | + +type: Optional[str] |
| 297 | + } |
| 298 | + |
| 299 | + class MCPServerConfigCodex { |
| 300 | + +env_vars: Optional[List[str]] |
| 301 | + +cwd: Optional[str] |
| 302 | + +startup_timeout_sec: Optional[int] |
| 303 | + +tool_timeout_sec: Optional[int] |
| 304 | + +enabled: Optional[bool] |
| 305 | + +enabled_tools: Optional[List[str]] |
| 306 | + +disabled_tools: Optional[List[str]] |
| 307 | + +bearer_token_env_var: Optional[str] |
| 308 | + +http_headers: Optional[Dict] |
| 309 | + +env_http_headers: Optional[Dict] |
| 310 | + } |
| 311 | + |
| 312 | + class AtomicFileOperations { |
| 313 | + +atomic_write_with_backup() bool |
| 314 | + +atomic_write_with_serializer() bool |
| 315 | + +atomic_copy() bool |
| 316 | + } |
| 317 | + |
| 318 | + MCPHostStrategy <|-- CodexHostStrategy |
| 319 | + MCPServerConfigBase <|-- MCPServerConfigCodex |
| 320 | + CodexHostStrategy --> AtomicFileOperations |
| 321 | + CodexHostStrategy --> MCPServerConfigCodex |
| 322 | +``` |
| 323 | + |
| 324 | +## Conclusion |
| 325 | + |
| 326 | +The current MCP host configuration architecture is excellently designed for extensibility. Adding Codex support with TOML configuration files requires: |
| 327 | + |
| 328 | +1. **Minimal Changes**: Add enum value, create Codex-specific model, implement strategy |
| 329 | +2. **Moderate Enhancements**: Generalize backup system for multi-format support |
| 330 | +3. **No Refactoring**: Core interfaces and existing strategies remain unchanged |
| 331 | + |
| 332 | +The strategy pattern's encapsulation of format-specific logic makes this extension natural and low-risk. The implementation follows established patterns and maintains architectural consistency. |
| 333 | + |
| 334 | +**Recommendation**: Proceed with implementation using the phased approach outlined above. |
| 335 | + |
| 336 | +--- |
| 337 | + |
| 338 | +**Analysis Date**: December 14, 2025 |
| 339 | +**Architecture Version**: Current state as of analysis |
| 340 | +**Risk Level**: Low to Medium |
| 341 | +**Implementation Effort**: Moderate (estimated 2-3 development cycles) |
0 commit comments