Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
514f2c7
Merge pull request #46 from CrackingShells/dev
LittleCoinCoin Feb 20, 2026
f7bb926
chore(release): 0.8.0
semantic-release-bot Feb 20, 2026
1056e52
chore: cleanup `__reports__/`
LittleCoinCoin Feb 22, 2026
c544cb3
chore: update cs-playbook submodule
LittleCoinCoin Feb 24, 2026
c08e064
docs(mcp): update field support matrix and field mapping documentation
LittleCoinCoin Feb 24, 2026
69d61cc
docs(mcp): update adapter template to validate_filtered()
LittleCoinCoin Feb 24, 2026
0b83b6e
docs(mcp): update strategy template with interface docs
LittleCoinCoin Feb 24, 2026
21c30d5
docs(mcp): document strategy, registration, and variant pattern
LittleCoinCoin Feb 24, 2026
24c6ebf
docs(mcp): rewrite testing section with data-driven infra
LittleCoinCoin Feb 24, 2026
5fc6f97
docs(mcp): rewrite testing section with data-driven docs
LittleCoinCoin Feb 24, 2026
d2a0df9
Merge branch 'task/update-extension-guide' into milestone/mcp-docs-re…
LittleCoinCoin Feb 24, 2026
fc07cd1
docs(roadmap): mark mcp-docs-refresh tasks as done
LittleCoinCoin Feb 24, 2026
896f4d2
docs(roadmap): add mcp-docs-refresh task files and gap analysis
LittleCoinCoin Feb 24, 2026
e48ea10
chore(roadmap): add adding-mcp-hosts-skill campaign
LittleCoinCoin Feb 24, 2026
cf9b807
feat(skill): add strategy contract reference
LittleCoinCoin Feb 24, 2026
070894c
feat(skill): add testing fixtures reference
LittleCoinCoin Feb 24, 2026
336fced
feat(skill): add adapter contract reference
LittleCoinCoin Feb 24, 2026
8061c5f
feat(skill): add discovery guide reference
LittleCoinCoin Feb 24, 2026
8984a3a
feat(skill): write SKILL.md with 5-step workflow
LittleCoinCoin Feb 24, 2026
c639322
Merge branch 'task/write-adapter-contract' into milestone/adding-mcp-…
LittleCoinCoin Feb 24, 2026
13b195c
Merge branch 'task/write-strategy-contract' into milestone/adding-mcp…
LittleCoinCoin Feb 24, 2026
3cc4175
Merge branch 'task/write-testing-fixtures' into milestone/adding-mcp-…
LittleCoinCoin Feb 24, 2026
d618f71
Merge branch 'task/write-skill-md' into milestone/adding-mcp-hosts-skill
LittleCoinCoin Feb 24, 2026
e5fbfa2
chore(skill): package adding-mcp-hosts skill
LittleCoinCoin Feb 24, 2026
b7e6c95
docs(roadmap): mark adding-mcp-hosts-skill campaign as done
LittleCoinCoin Feb 24, 2026
f739fed
chore: move skills directory location
LittleCoinCoin Feb 25, 2026
6f6165a
docs(adding-mcp-hosts): use parallel research over priority ladder
LittleCoinCoin Feb 25, 2026
bce3851
Merge branch 'milestone/adding-mcp-hosts-skill' into dev
LittleCoinCoin Feb 25, 2026
2bae600
feat(mcp-models): add opencode oauth fields to MCPServerConfig
LittleCoinCoin Feb 25, 2026
b9ddf43
feat(mcp-fields): add OPENCODE_FIELDS constant
LittleCoinCoin Feb 25, 2026
28e3bdf
feat(mcp-adapter): add OpenCodeAdapter with serialize transforms
LittleCoinCoin Feb 25, 2026
8bb590a
feat(mcp-strategy): add OpenCodeHostStrategy with JSONC read/write
LittleCoinCoin Feb 25, 2026
20e0fc8
feat(mcp-registry): register OpenCodeAdapter in adapter registry
LittleCoinCoin Feb 25, 2026
7d0b075
feat(mcp-wiring): add opencode to backup and reporting
LittleCoinCoin Feb 25, 2026
5ae3b57
test(mcp-fixtures): add opencode entry to canonical_configs.json
LittleCoinCoin Feb 25, 2026
734b3c0
test(mcp-fixtures): register opencode in host_registry.py
LittleCoinCoin Feb 25, 2026
ee1d915
fix(mcp-opencode): make serialize() canonical-form; add test fixes
LittleCoinCoin Feb 25, 2026
d8f3a75
fix(mcp-opencode): anchor JSONC comment regex to line start
LittleCoinCoin Feb 25, 2026
a35d3a2
fix(mcp-opencode): anchor JSONC comment regex in write_configuration
LittleCoinCoin Feb 25, 2026
793707d
Merge branch 'feat/opencode-mcp-host-support' into dev
LittleCoinCoin Feb 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
202 changes: 202 additions & 0 deletions .claude/skills/adding-mcp-hosts/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
---
name: adding-mcp-hosts
description: |
Adds support for a new MCP host platform to the Hatch CLI multi-host
configuration system. Use when asked to add, integrate, or extend MCP host
support for a new IDE, editor, or AI coding tool (e.g., Windsurf, Zed,
Copilot). Follows a 5-step workflow: discover host requirements via web
research or user questionnaire, add enum and field set declarations, create
adapter and strategy implementations, wire integration points across 4
registration files, and register test fixtures that auto-generate 20+ test
cases without writing test code.
---

## Workflow Checklist

```
- [ ] Step 1: Discover host requirements
- [ ] Step 2: Add enum and field set
- [ ] Step 3: Create adapter and strategy
- [ ] Step 4: Wire integration points
- [ ] Step 5: Register test fixtures
```

---

## Step 1: Discover Host Requirements

Read [references/discovery-guide.md](references/discovery-guide.md) for the full discovery workflow.

Use web search, Context7, and codebase retrieval to find the target host's MCP
configuration: config file path per platform, format (JSON/JSONC/TOML), top-level key,
every supported field name and type, and any field name differences from the universal
set (`command`, `args`, `env`, `url`, `headers`).

If research leaves blockers unresolved, present the structured questionnaire from the
discovery guide to the user.

Write `__reports__/<host-name>/00-parameter_analysis_v0.md` (field-level discovery) and
`__reports__/<host-name>/01-architecture_analysis_v0.md` (integration analysis and NO-GO
assessment). Also produce the Host Spec YAML block — it feeds all subsequent steps.

---

## Step 2: Add Enum and Field Set

Add `MCPHostType` enum value in `hatch/mcp_host_config/models.py`:

```python
class MCPHostType(str, Enum):
# ... existing members ...
YOUR_HOST = "your-host" # lowercase-hyphenated, matching Host Spec slug
```

Add field set constant in `hatch/mcp_host_config/fields.py`:

```python
YOUR_HOST_FIELDS: FrozenSet[str] = UNIVERSAL_FIELDS | frozenset(
{
# host-specific fields from Host Spec
}
)
```

Include `"type"` (via `CLAUDE_FIELDS` base) only if the host uses a transport type
discriminator. If the host uses different field names for universal concepts, add a
mappings dict (see `CODEX_FIELD_MAPPINGS` pattern in `fields.py`).

If the host introduces fields not in `MCPServerConfig`, add them as `Optional` fields
with `Field(None, description="...")` under a new section comment block in `models.py`.

Verify:

```bash
python -c "from hatch.mcp_host_config.models import MCPHostType; print(MCPHostType.YOUR_HOST)"
python -c "from hatch.mcp_host_config.fields import YOUR_HOST_FIELDS; print(YOUR_HOST_FIELDS)"
```

---

## Step 3: Create Adapter and Strategy

Read [references/adapter-contract.md](references/adapter-contract.md) for the `BaseAdapter`
interface, the `validate_filtered()` pipeline, and field mapping details.

Read [references/strategy-contract.md](references/strategy-contract.md) for the
`MCPHostStrategy` interface, `@register_host_strategy` decorator, platform path resolution,
and config serialization.

### Adapter

Create `hatch/mcp_host_config/adapters/your_host.py`. Implement `BaseAdapter` with:

- `host_name` property returning the slug
- `get_supported_fields()` returning the field set from Step 2
- `validate_filtered(filtered)` enforcing host-specific transport rules
- `serialize(config)` calling `filter_fields()` then `validate_filtered()` then returning
the dict (apply field mappings if needed)

**Variant shortcut:** If the new host is functionally identical to an existing host,
register it as a variant instead of creating a new file. See
`ClaudeAdapter(variant=...)` in `hatch/mcp_host_config/adapters/claude.py`.

### Strategy

Add a strategy class in `hatch/mcp_host_config/strategies.py` decorated with
`@register_host_strategy(MCPHostType.YOUR_HOST)`. Decide the family:

- `ClaudeHostStrategy` -- JSON format with `mcpServers` key
- `CursorBasedHostStrategy` -- `.cursor/mcp.json`-like layout
- `MCPHostStrategy` (direct) -- standalone hosts with unique formats

Implement `get_config_path()`, `get_config_key()`, `validate_server_config()`,
`read_config()`, and `write_config()`.

Verify:

```bash
python -c "from hatch.mcp_host_config.adapters.your_host import YourHostAdapter; print(YourHostAdapter().host_name)"
```

---

## Step 4: Wire Integration Points

Four files need one-liner additions.

**`hatch/mcp_host_config/adapters/__init__.py`** -- Import and add to `__all__`:

```python
from hatch.mcp_host_config.adapters.your_host import YourHostAdapter
# Append "YourHostAdapter" to __all__
```

**`hatch/mcp_host_config/adapters/registry.py`** -- Import adapter, add
`self.register(YourHostAdapter())` inside `_register_defaults()`.

**`hatch/mcp_host_config/backup.py`** -- Add `"your-host"` to the `supported_hosts` set
in `BackupInfo.validate_hostname()`. Also update the `supported_hosts` set in
`EnvironmentPackageEntry.validate_host_names()` in `models.py`.

**`hatch/mcp_host_config/reporting.py`** -- Add `MCPHostType.YOUR_HOST: "your-host"` to
the `mapping` dict in `_get_adapter_host_name()`.

Verify:

```bash
python -c "
from hatch.mcp_host_config.adapters.registry import AdapterRegistry
r = AdapterRegistry()
print('your-host' in r.get_supported_hosts())
"
```

---

## Step 5: Register Test Fixtures

Read [references/testing-fixtures.md](references/testing-fixtures.md) for fixture schemas,
auto-generated test case details, and pytest commands.

Add canonical config entry in `tests/test_data/mcp_adapters/canonical_configs.json`:

```json
"your-host": {
"command": "python",
"args": ["-m", "mcp_server"],
"env": {"API_KEY": "test_key"},
"url": null,
"headers": null
}
```

Include all host-specific fields with representative values. Use `null` for unused
transport fields.

Add host registry entries in `tests/test_data/mcp_adapters/host_registry.py`:

1. Import the new field set and adapter.
2. Add `FIELD_SETS` entry: `"your-host": YOUR_HOST_FIELDS`.
3. Add `adapter_map` entry in `HostSpec.get_adapter()`.
4. Add reverse mappings if the host has field name mappings.
5. Add the new field set to `all_possible_fields` in `generate_unsupported_field_test_cases()`.

Verify:

```bash
python -m pytest tests/integration/mcp/ tests/unit/mcp/ tests/regression/mcp/ -v
```

All existing tests must pass. The new host auto-generates test cases for cross-host sync
(N x N matrix), field filtering, transport validation, and property checks.

---

## Cross-References

| Reference | Covers | Read when |
|---|---|---|
| [references/discovery-guide.md](references/discovery-guide.md) | Host research, questionnaire, Host Spec YAML | Step 1 (always) |
| [references/adapter-contract.md](references/adapter-contract.md) | BaseAdapter interface, field sets, registry wiring | Step 3 (always) |
| [references/strategy-contract.md](references/strategy-contract.md) | MCPHostStrategy interface, families, platform paths | Step 3 (always) |
| [references/testing-fixtures.md](references/testing-fixtures.md) | Fixture schema, auto-generated tests, pytest commands | Step 5 (always) |
157 changes: 157 additions & 0 deletions .claude/skills/adding-mcp-hosts/references/adapter-contract.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
# Adapter Contract Reference

Interface contract for implementing a new MCP host adapter in the Hatch CLI.

## 1. MCPHostType Enum

File: `hatch/mcp_host_config/models.py`. Convention: `UPPER_SNAKE = "kebab-case"`.

```python
class MCPHostType(str, Enum):
# ... existing members ...
NEW_HOST = "new-host"
```

The enum value string is the canonical host identifier used everywhere.

## 2. Field Set Declaration

File: `hatch/mcp_host_config/fields.py`. Define a `<HOST>_FIELDS` frozenset.

```python
# Without 'type' support — build from UNIVERSAL_FIELDS
NEW_HOST_FIELDS: FrozenSet[str] = UNIVERSAL_FIELDS | frozenset({"host_specific_field"})

# With 'type' support — build from CLAUDE_FIELDS (which is UNIVERSAL_FIELDS | {"type"})
NEW_HOST_FIELDS: FrozenSet[str] = CLAUDE_FIELDS | frozenset({"host_specific_field"})
```

If the host supports the `type` discriminator, also add its kebab-case name to `TYPE_SUPPORTING_HOSTS`. Hosts without `type` support (Gemini, Kiro, Codex) omit this.

## 3. MCPServerConfig Fields

File: `hatch/mcp_host_config/models.py`. Add new fields to `MCPServerConfig` only when the host introduces fields not already in the model. Every field: `Optional`, default `None`.

```python
disabled: Optional[bool] = Field(None, description="Whether server is disabled")
```

If the host reuses existing fields only (e.g., LMStudio reuses `CLAUDE_FIELDS`), skip this step. The model uses `extra="allow"` but explicit declarations are preferred.

## 4. Adapter Class

File: `hatch/mcp_host_config/adapters/<host>.py`. Extend `BaseAdapter`.

```python
from typing import Any, Dict, FrozenSet
from hatch.mcp_host_config.adapters.base import AdapterValidationError, BaseAdapter
from hatch.mcp_host_config.fields import NEW_HOST_FIELDS
from hatch.mcp_host_config.models import MCPServerConfig

class NewHostAdapter(BaseAdapter):

@property
def host_name(self) -> str:
return "new-host"

def get_supported_fields(self) -> FrozenSet[str]:
return NEW_HOST_FIELDS

def validate(self, config: MCPServerConfig) -> None:
pass # DEPRECATED — kept for ABC compliance until v0.9.0

def validate_filtered(self, filtered: Dict[str, Any]) -> None:
has_command = "command" in filtered
has_url = "url" in filtered
if not has_command and not has_url:
raise AdapterValidationError(
"Either 'command' (local) or 'url' (remote) must be specified",
host_name=self.host_name,
)
if has_command and has_url:
raise AdapterValidationError(
"Cannot specify both 'command' and 'url' - choose one transport",
host_name=self.host_name,
)

def serialize(self, config: MCPServerConfig) -> Dict[str, Any]:
filtered = self.filter_fields(config)
self.validate_filtered(filtered)
return filtered # add apply_transformations() call if field mappings exist
```

**validate_filtered() rules:** Transport mutual exclusion (`command` XOR `url` for most hosts; Gemini enforces exactly-one-of-three including `httpUrl`). If host supports `type`, verify consistency (`type='stdio'` requires `command`, etc.).

**serialize() pipeline:** Always `filter_fields` -> `validate_filtered` -> optionally `apply_transformations` -> return.

## 5. Field Mappings

File: `hatch/mcp_host_config/fields.py`. Define only when the host uses different field names. Pattern: `{"universal_name": "host_name"}`. Canonical example:

```python
CODEX_FIELD_MAPPINGS: dict[str, str] = {
"args": "arguments",
"headers": "http_headers",
"includeTools": "enabled_tools",
"excludeTools": "disabled_tools",
}
```

Reference in `apply_transformations()`:

```python
def apply_transformations(self, filtered: Dict[str, Any]) -> Dict[str, Any]:
result = filtered.copy()
for universal_name, host_name in NEW_HOST_FIELD_MAPPINGS.items():
if universal_name in result:
result[host_name] = result.pop(universal_name)
return result
```

Skip entirely if the host uses standard field names (most do).

## 6. Variant Pattern

Reuse one adapter class with a `variant` parameter when two host identifiers share identical fields and validation. Canonical example:

```python
class ClaudeAdapter(BaseAdapter):
def __init__(self, variant: str = "desktop"):
if variant not in ("desktop", "code"):
raise ValueError(f"Invalid Claude variant: {variant}")
self._variant = variant

@property
def host_name(self) -> str:
return f"claude-{self._variant}"
```

Use when field set, validation, and serialization are identical. If any diverge, create a separate class.

## 7. Wiring and Integration Points

Four files require one-liner additions for every new host.

**`hatch/mcp_host_config/adapters/__init__.py`** -- Add import and `__all__` entry:
```python
from hatch.mcp_host_config.adapters.new_host import NewHostAdapter
# add "NewHostAdapter" to __all__
```

**`hatch/mcp_host_config/adapters/registry.py`** -- Add to `_register_defaults()`:
```python
self.register(NewHostAdapter()) # import at top of file
```

**`hatch/mcp_host_config/backup.py`** -- Add hostname string to `supported_hosts` set in `BackupInfo.validate_hostname()`:
```python
supported_hosts = {
# ... existing hosts ...
"new-host",
}
```

**`hatch/mcp_host_config/reporting.py`** -- Add entry to mapping dict in `_get_adapter_host_name()`:
```python
MCPHostType.NEW_HOST: "new-host",
```
Loading