Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
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
9d873aa
Merge pull request #47 from LittleCoinCoin/dev
LittleCoinCoin Feb 26, 2026
e31b347
chore(release): 0.8.1-dev.1
semantic-release-bot Feb 26, 2026
038be8c
chore: clean up temporary reports
LittleCoinCoin Feb 26, 2026
8b22594
feat(mcp-augment): add enum value and constant
LittleCoinCoin Feb 26, 2026
5af34d1
feat(mcp-augment): implement AugmentAdapter
LittleCoinCoin Feb 26, 2026
b13d9d0
feat(mcp-augment): implement AugmentHostStrategy
LittleCoinCoin Feb 26, 2026
367b736
feat(mcp-augment): wire AugmentAdapter into integration points
LittleCoinCoin Feb 26, 2026
294d0d8
test(mcp-augment): register test fixtures and update tests
LittleCoinCoin Feb 26, 2026
67bb767
Merge branch 'feat/augment-mcp-host-support' into dev
LittleCoinCoin Feb 26, 2026
3a58908
docs(adding-mcp-hosts): add test_adapter_protocol.py to fixture guide
LittleCoinCoin Feb 26, 2026
9d7f0e5
fix(mcp-hosts): close validation and test coverage gaps
LittleCoinCoin Feb 26, 2026
fb2ee4c
refactor(logging): remove forced setLevel(INFO) from all module loggers
LittleCoinCoin Mar 4, 2026
df97e58
refactor(registry): demote startup and fetch INFO logs to DEBUG
LittleCoinCoin Mar 4, 2026
09dd517
feat(registry): add transient dim status on cache refresh
LittleCoinCoin Mar 4, 2026
1e3817f
feat(cli): add --log-level flag and default log output to WARNING
LittleCoinCoin Mar 4, 2026
5aa2e9d
docs(logging): expose --log-level flag in CLI reference global options
LittleCoinCoin Mar 4, 2026
5fd15dd
Merge branch `milestone/fix-logging-clutter` into dev
LittleCoinCoin Mar 4, 2026
0bc06fb
Merge pull request #48 from LittleCoinCoin/dev
LittleCoinCoin Mar 4, 2026
3f58954
chore(release): 0.8.1-dev.2
semantic-release-bot Mar 4, 2026
db0fb91
ci: semantic-release streamlining
LittleCoinCoin Mar 8, 2026
f213971
feat(mcp-hosts): let hatch manage mistral vibe configs
LittleCoinCoin Mar 11, 2026
0e801d0
feat(cli): let shared mcp configure target mistral vibe
LittleCoinCoin Mar 11, 2026
1a81ae0
test(mcp-hosts): keep mistral vibe in shared adapter coverage
LittleCoinCoin Mar 11, 2026
4ff2758
docs(mcp-hosts): capture mistral vibe host analysis
LittleCoinCoin Mar 11, 2026
5130c84
docs(mcp-hosts): add mistral vibe to supported platforms
LittleCoinCoin Mar 16, 2026
bfa8b9b
docs: correct Mistral Vibe terminology to CLI coding agent
LittleCoinCoin Mar 16, 2026
62f99cf
fix(mcp-hosts): add explicit HTTP transport type for Claude URL-based co
LittleCoinCoin Mar 13, 2026
b5c7191
test(mcp): fix whitespace in Claude transport serialization test
LittleCoinCoin Mar 13, 2026
904f22b
fix(mcp-hosts): always set HTTP transport type for Claude URL-based conf
LittleCoinCoin Mar 13, 2026
d1cc2b0
test(mcp): add fixture for claude remote setup
LittleCoinCoin Mar 21, 2026
d6a75a8
fix(mcp-hosts): remove redundant Claude Desktop/Code URL validation
LittleCoinCoin Mar 21, 2026
73666d9
Merge pull request #49 from LittleCoinCoin/dev
LittleCoinCoin Mar 23, 2026
7aaa6eb
chore(release): 0.8.1-dev.3 [skip ci]
semantic-release-bot Mar 23, 2026
7d2634d
fix(ci): rename caller workflow to match pypi trusted publisher
LittleCoinCoin Mar 23, 2026
f7221c3
chore(release): 0.8.1-dev.4 [skip ci]
semantic-release-bot Mar 23, 2026
fc81e78
fix(ci): inline pypi publish jobs to satisfy trusted publishing
LittleCoinCoin Mar 23, 2026
c605bc7
chore(release): 0.8.1-dev.5 [skip ci]
semantic-release-bot Mar 23, 2026
cff376a
chore(git): add .DS_Store to .gitignore
LittleCoinCoin Mar 24, 2026
c5cec5b
style(docs): add CrackingShells brand theme stylesheets for MkDocs
LittleCoinCoin Mar 24, 2026
7749d48
style(docs): apply CrackingShells org theme to MkDocs
LittleCoinCoin Mar 24, 2026
2d75bcd
docs(cli): update to match current CLI state
LittleCoinCoin Mar 24, 2026
ed98ea4
docs(known-issues): sync appendix with v0.8.1 codebase state
LittleCoinCoin Mar 25, 2026
dbdba35
docs(mcp-host-config): surface opencode and augment in all host lists
LittleCoinCoin Mar 31, 2026
d4d0bb9
docs(entry-docs): refresh content for primary use case identification
LittleCoinCoin Mar 31, 2026
8f6c5f8
Merge pull request #50 from LittleCoinCoin/dev
LittleCoinCoin Apr 1, 2026
10a2e48
chore(release): 0.8.1-dev.6 [skip ci]
semantic-release-bot Apr 1, 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