Files
ops-bridge/architecture/adr-001-cross-mode-capability-registry.md
tegwick 365c0d611a feat(BRIDGE-WP-0003): MCP server, /bridge-status skill, cross-mode coverage enforcement
Implements the full BRIDGE-WP-0003 workplan: 188 tests passing, 0 lint errors.

## What's added

**Capability registry** (`src/bridge/capabilities.py`):
- 10 capabilities with required_access_modes (cli/mcp/skill)
- Single source of truth for what OpsBridge does and where

**MCP server** (`src/bridge/mcp_server/server.py`):
- 10 FastMCP tools: bridge_up/down/restart/status/logs + 5 catalog_* tools
- 3 resources: bridge://status, catalog://domains, catalog://targets
- `.mcp.json` for project-scope auto-registration
- `scripts/register_mcp.py` for user-scope machine-global registration

**Skill** (`~/.claude/plugins/ops-bridge/bridge-status.md`):
- /bridge-status: health table with emoji indicators + remediation advice

**Cross-mode test coverage enforcement**:
- `tests/conftest.py`: capability/access_mode marks + collect_capability_coverage()
- `tests/test_mcp.py`: 31 FastMCP in-process client tests (Client(mcp) pattern)
- `tests/test_skill.py`: static skill lint against capability registry
- `tests/test_coverage_completeness.py`: meta-test that fails if any required
  (capability × mode) pair lacks a test; also validates CLI commands and MCP
  tools are registered in the capability registry

**ADR** (`architecture/adr-001-cross-mode-capability-registry.md`):
- Documents the registry pattern and FastMCP 3.x testing approach

Key implementation note: FastMCP 3.x in-process results are in
result.content[0].text (JSON string), not result.data directly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:33:16 +01:00

2.3 KiB

id, title, status, date
id title status date
ADR-001 Cross-Mode Capability Registry and Coverage Enforcement accepted 2026-03-12

Context

OpsBridge exposes its operations through three access modes: CLI (bridge CLI), MCP server (FastMCP stdio), and Skills (Claude plugin prompts). As the capability surface grows, there is no guarantee that a new capability will be implemented consistently across all required modes, or that tests exist for each mode.

Decision

Introduce a canonical Capability Registry (src/bridge/capabilities.py) that:

  1. Lists every operation as a Capability(name, description, required_access_modes) dataclass.
  2. Declares which access modes each capability must support.
  3. Is imported by the cross-mode meta-test to enforce complete test coverage.

Test coverage enforcement

Pytest marks @pytest.mark.capability(name) and @pytest.mark.access_mode(mode) are placed on the canonical test for each (capability, mode) pair. tests/test_coverage_completeness.py collects these marks at session scope and fails if any pair required by the registry has no corresponding test.

FastMCP in-process testing

MCP tools are tested in tests/test_mcp.py using fastmcp.Client(mcp_app) — an in-process client that calls tools without spawning a subprocess or opening a network socket. This is the preferred approach because:

  • Tests run in the same process as the server code, so patches/mocks work normally.
  • No port allocation, no cleanup, no flakiness from network timeouts.
  • FastMCP 3.x returns results via result.content[0].text (JSON string) for non-empty responses, and result.data (empty list/dict) when the return value is empty.

Skill static lint

tests/test_skill.py validates skill Markdown files in ~/.claude/plugins/ops-bridge/:

  • Required frontmatter: name, description.
  • Body must reference at least one registered capability name.
  • The bridge_status skill must reference bridge_status and the registry must declare skill as a required mode for that capability.

Consequences

  • Every new capability must be added to the registry before or alongside its implementation.
  • Every new (capability, mode) pair requires a marked test or the meta-test fails.
  • The registry is the single source of truth for "what does OpsBridge do and where".
  • Skills must reference capability names by their canonical registry IDs.