Skip to content

Commit ff05ad5

Browse files
docs(mcp-host-config): write new architecture documentation
Create comprehensive architecture documentation for the Unified Adapter Architecture that replaces the legacy inheritance-based approach. Key sections: - Unified Adapter Pattern: 3-layer architecture (CLI → Adapter → Strategy) - Unified Data Model: MCPServerConfig with all fields from all hosts - Key Components: AdapterRegistry, BaseAdapter protocol, field constants - Field Support Matrix: Which fields each host supports - Integration Points: Adapter, backup, and environment integrations - Extension Points: 4-step process for adding new hosts - Design Patterns: Declarative field support, field mappings, atomic ops - Module Organization: Directory structure and file purposes - Error Handling: AdapterValidationError and ConfigurationResult - Testing Strategy: Three-tier test architecture This documentation aligns with the implemented adapter architecture and provides a clear guide for understanding and extending the system.
1 parent f172a51 commit ff05ad5

1 file changed

Lines changed: 346 additions & 0 deletions

File tree

Lines changed: 346 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,346 @@
1+
# MCP Host Configuration Architecture
2+
3+
This article covers:
4+
5+
- Unified Adapter Architecture for MCP host configuration
6+
- Adapter pattern for host-specific validation and serialization
7+
- Unified data model (`MCPServerConfig`)
8+
- Extension points for adding new host platforms
9+
- Integration with backup and environment systems
10+
11+
## Overview
12+
13+
The MCP host configuration system manages Model Context Protocol server configurations across multiple host platforms (Claude Desktop, VS Code, Cursor, Gemini, Kiro, Codex, LM Studio). It uses the **Unified Adapter Architecture**: a single data model with host-specific adapters for validation and serialization.
14+
15+
> **Adding a new host?** See the [Implementation Guide](../implementation_guides/mcp_host_configuration_extension.md) for step-by-step instructions.
16+
17+
## Core Architecture
18+
19+
### Unified Adapter Pattern
20+
21+
The architecture separates concerns into three layers:
22+
23+
```
24+
┌─────────────────────────────────────────────────────────────────┐
25+
│ CLI Layer │
26+
│ Creates MCPServerConfig with all user-provided fields │
27+
└─────────────────────────────────┬───────────────────────────────┘
28+
29+
30+
┌─────────────────────────────────────────────────────────────────┐
31+
│ Adapter Layer │
32+
│ Validates + serializes to host-specific format │
33+
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
34+
│ │ Claude │ │ VSCode │ │ Gemini │ │ Kiro │ ... │
35+
│ │ Adapter │ │ Adapter │ │ Adapter │ │ Adapter │ │
36+
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
37+
└─────────────────────────────────┬───────────────────────────────┘
38+
39+
40+
┌─────────────────────────────────────────────────────────────────┐
41+
│ Strategy Layer │
42+
│ Handles file I/O (read/write configuration files) │
43+
└─────────────────────────────────────────────────────────────────┘
44+
```
45+
46+
**Benefits:**
47+
48+
- Single unified data model accepts all fields
49+
- Adapters declaratively define supported fields per host
50+
- No inheritance hierarchies or model conversion methods
51+
- Easy addition of new hosts (3 steps instead of 10)
52+
53+
### Unified Data Model
54+
55+
`MCPServerConfig` contains ALL possible fields from ALL hosts:
56+
57+
```python
58+
class MCPServerConfig(BaseModel):
59+
"""Unified model containing ALL possible fields."""
60+
model_config = ConfigDict(extra="allow")
61+
62+
# Hatch metadata (never serialized)
63+
name: Optional[str] = None
64+
65+
# Transport fields
66+
command: Optional[str] = None # stdio transport
67+
url: Optional[str] = None # sse transport
68+
httpUrl: Optional[str] = None # http transport (Gemini)
69+
70+
# Universal fields (all hosts)
71+
args: Optional[List[str]] = None
72+
env: Optional[Dict[str, str]] = None
73+
headers: Optional[Dict[str, str]] = None
74+
type: Optional[Literal["stdio", "sse", "http"]] = None
75+
76+
# Host-specific fields
77+
envFile: Optional[str] = None # VSCode/Cursor
78+
disabled: Optional[bool] = None # Kiro
79+
trust: Optional[bool] = None # Gemini
80+
# ... additional fields per host
81+
```
82+
83+
**Design principles:**
84+
85+
- `extra="allow"` for forward compatibility with unknown fields
86+
- Adapters handle validation (not the model)
87+
- `name` field is Hatch metadata, never serialized to host configs
88+
89+
## Key Components
90+
91+
### AdapterRegistry
92+
93+
Central registry mapping host names to adapter instances:
94+
95+
```python
96+
from hatch.mcp_host_config.adapters import get_adapter, AdapterRegistry
97+
98+
# Get adapter for a specific host
99+
adapter = get_adapter("claude-desktop")
100+
101+
# Or use registry directly
102+
registry = AdapterRegistry()
103+
adapter = registry.get_adapter("gemini")
104+
supported = registry.get_supported_hosts() # List all hosts
105+
```
106+
107+
**Supported hosts:**
108+
109+
- `claude-desktop`, `claude-code`
110+
- `vscode`, `cursor`, `lmstudio`
111+
- `gemini`, `kiro`, `codex`
112+
113+
### BaseAdapter Protocol
114+
115+
All adapters implement this interface:
116+
117+
```python
118+
class BaseAdapter(ABC):
119+
@property
120+
@abstractmethod
121+
def host_name(self) -> str:
122+
"""Return host identifier (e.g., 'claude-desktop')."""
123+
...
124+
125+
@abstractmethod
126+
def get_supported_fields(self) -> FrozenSet[str]:
127+
"""Return fields this host accepts."""
128+
...
129+
130+
@abstractmethod
131+
def validate(self, config: MCPServerConfig) -> None:
132+
"""Validate config, raise AdapterValidationError if invalid."""
133+
...
134+
135+
@abstractmethod
136+
def serialize(self, config: MCPServerConfig) -> Dict[str, Any]:
137+
"""Convert config to host's expected format."""
138+
...
139+
```
140+
141+
### Field Constants
142+
143+
Field support is defined in `fields.py`:
144+
145+
```python
146+
# Universal fields (all hosts)
147+
UNIVERSAL_FIELDS = frozenset({"command", "args", "env", "url", "headers"})
148+
149+
# Host-specific field sets
150+
CLAUDE_FIELDS = UNIVERSAL_FIELDS | frozenset({"type"})
151+
VSCODE_FIELDS = CLAUDE_FIELDS | frozenset({"envFile", "inputs"})
152+
GEMINI_FIELDS = UNIVERSAL_FIELDS | frozenset({"httpUrl", "timeout", "trust", ...})
153+
KIRO_FIELDS = UNIVERSAL_FIELDS | frozenset({"disabled", "autoApprove", ...})
154+
```
155+
156+
## Field Support Matrix
157+
158+
| Field | Claude | VSCode | Cursor | Gemini | Kiro | Codex |
159+
|-------|--------|--------|--------|--------|------|-------|
160+
| command, args, env |||||||
161+
| url, headers |||||||
162+
| type |||| - | - | - |
163+
| envFile | - ||| - | - | - |
164+
| inputs | - || - | - | - | - |
165+
| httpUrl | - | - | - || - | - |
166+
| trust, timeout | - | - | - || - | - |
167+
| disabled, autoApprove | - | - | - | - || - |
168+
| enabled, enabled_tools | - | - | - | - | - ||
169+
170+
## Integration Points
171+
172+
### Adapter Integration
173+
174+
Every adapter integrates with the validation and serialization system:
175+
176+
```python
177+
from hatch.mcp_host_config.adapters import get_adapter
178+
from hatch.mcp_host_config import MCPServerConfig
179+
180+
# Create unified config
181+
config = MCPServerConfig(
182+
name="my-server",
183+
command="python",
184+
args=["server.py"],
185+
env={"DEBUG": "true"},
186+
)
187+
188+
# Validate and serialize for specific host
189+
adapter = get_adapter("claude-desktop")
190+
adapter.validate(config) # Raises AdapterValidationError if invalid
191+
data = adapter.serialize(config) # Returns host-specific dict
192+
# Result: {"command": "python", "args": ["server.py"], "env": {"DEBUG": "true"}}
193+
```
194+
195+
### Backup System Integration
196+
197+
Strategy classes integrate with the backup system via `MCPHostConfigBackupManager`:
198+
199+
```python
200+
from hatch.mcp_host_config.backup import MCPHostConfigBackupManager, AtomicFileOperations
201+
202+
def write_configuration(self, config: HostConfiguration, no_backup: bool = False) -> bool:
203+
backup_manager = MCPHostConfigBackupManager()
204+
atomic_ops = AtomicFileOperations()
205+
atomic_ops.atomic_write_with_backup(
206+
file_path=config_path,
207+
data=existing_data,
208+
backup_manager=backup_manager,
209+
hostname="your-host",
210+
skip_backup=no_backup
211+
)
212+
```
213+
214+
### Environment Manager Integration
215+
216+
The system integrates with environment management:
217+
218+
- **Single-server-per-package constraint**: One MCP server per installed package
219+
- **Multi-host configuration**: One server can be configured across multiple hosts
220+
- **Synchronization support**: Environment data can be synced to available hosts
221+
222+
## Extension Points
223+
224+
### Adding New Host Platforms
225+
226+
To add a new host, complete these steps:
227+
228+
| Step | Files to Modify |
229+
|------|-----------------|
230+
| 1. Add host type enum | `models.py` (MCPHostType) |
231+
| 2. Create adapter class | `adapters/your_host.py` + `adapters/__init__.py` |
232+
| 3. Create strategy class | `strategies.py` |
233+
| 4. Add tests | `tests/unit/mcp/`, `tests/integration/mcp/` |
234+
235+
**Minimal adapter implementation:**
236+
237+
```python
238+
from hatch.mcp_host_config.adapters.base import BaseAdapter
239+
from hatch.mcp_host_config.fields import UNIVERSAL_FIELDS
240+
241+
class NewHostAdapter(BaseAdapter):
242+
@property
243+
def host_name(self) -> str:
244+
return "new-host"
245+
246+
def get_supported_fields(self) -> FrozenSet[str]:
247+
return UNIVERSAL_FIELDS | frozenset({"your_specific_field"})
248+
249+
def validate(self, config: MCPServerConfig) -> None:
250+
if not config.command and not config.url:
251+
raise AdapterValidationError("Need command or url")
252+
253+
def serialize(self, config: MCPServerConfig) -> Dict[str, Any]:
254+
self.validate(config)
255+
return self.filter_fields(config)
256+
```
257+
258+
See [Implementation Guide](../implementation_guides/mcp_host_configuration_extension.md) for complete instructions.
259+
260+
## Design Patterns
261+
262+
### Declarative Field Support
263+
264+
Each adapter declares its supported fields as a `FrozenSet`:
265+
266+
```python
267+
class YourAdapter(BaseAdapter):
268+
def get_supported_fields(self) -> FrozenSet[str]:
269+
return UNIVERSAL_FIELDS | frozenset({"your_field"})
270+
```
271+
272+
The base class provides `filter_fields()` which:
273+
1. Filters to only supported fields
274+
2. Removes excluded fields (`name`)
275+
3. Removes `None` values
276+
277+
### Field Mappings (Optional)
278+
279+
If your host uses different field names:
280+
281+
```python
282+
CODEX_FIELD_MAPPINGS = {
283+
"args": "arguments", # Universal → Codex naming
284+
"headers": "http_headers", # Universal → Codex naming
285+
}
286+
```
287+
288+
### Atomic Operations Pattern
289+
290+
All configuration changes use atomic operations:
291+
292+
1. **Create backup** of current configuration
293+
2. **Perform modification** to configuration file
294+
3. **Verify success** and update state
295+
4. **Clean up** or rollback on failure
296+
297+
## Module Organization
298+
299+
```
300+
hatch/mcp_host_config/
301+
├── __init__.py # Public API exports
302+
├── models.py # MCPServerConfig, MCPHostType, HostConfiguration
303+
├── fields.py # Field constants (UNIVERSAL_FIELDS, etc.)
304+
├── host_management.py # Registry and configuration manager
305+
├── strategies.py # Host strategy implementations (I/O)
306+
├── backup.py # Backup manager and atomic operations
307+
└── adapters/
308+
├── __init__.py # Adapter exports
309+
├── base.py # BaseAdapter abstract class
310+
├── registry.py # AdapterRegistry
311+
├── claude.py # ClaudeAdapter
312+
├── vscode.py # VSCodeAdapter
313+
├── cursor.py # CursorAdapter
314+
├── gemini.py # GeminiAdapter
315+
├── kiro.py # KiroAdapter
316+
├── codex.py # CodexAdapter
317+
└── lmstudio.py # LMStudioAdapter
318+
```
319+
320+
## Error Handling
321+
322+
The system uses both exceptions and result objects:
323+
324+
- **Validation errors**: `AdapterValidationError` with field and host context
325+
- **Configuration operations**: `ConfigurationResult` with success status and messages
326+
327+
```python
328+
try:
329+
adapter.validate(config)
330+
except AdapterValidationError as e:
331+
print(f"Validation failed: {e.message}")
332+
print(f"Field: {e.field}, Host: {e.host_name}")
333+
```
334+
335+
## Testing Strategy
336+
337+
The test architecture follows a three-tier structure:
338+
339+
| Tier | Location | Purpose |
340+
|------|----------|---------|
341+
| Unit | `tests/unit/mcp/` | Adapter protocol, model validation, registry |
342+
| Integration | `tests/integration/mcp/` | CLI → Adapter → Strategy flow |
343+
| Regression | `tests/regression/mcp/` | Field filtering edge cases |
344+
345+
See [Test Architecture](../../devs/architecture/test_architecture.md) for details.
346+

0 commit comments

Comments
 (0)