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>
This commit is contained in:
2026-03-12 11:33:16 +01:00
parent 44b5a9426a
commit 365c0d611a
30 changed files with 2845 additions and 47 deletions

View File

@@ -4,7 +4,7 @@ type: workplan
title: "OpsBridge MCP Server, Skill, and Cross-Mode Test Coverage"
domain: custodian
repo: ops-bridge
status: active
status: done
owner: Bernd
topic_slug: custodian
state_hub_workstream_id: 97009d3f-fd92-4fd9-a308-6c2445b4d623
@@ -152,7 +152,7 @@ existing CLI command and the planned MCP tool set appears in the registry.
```task
id: BRIDGE-WP-0003-T01
state_hub_task_id: 1397a838-b225-4452-ad53-29ad65388060
status: todo
status: done
priority: high
```
@@ -165,7 +165,7 @@ dependencies — pure stdlib.
```task
id: BRIDGE-WP-0003-T02
state_hub_task_id: 97467243-9237-4e63-a860-cc49587546ad
status: todo
status: done
priority: high
```
@@ -186,7 +186,7 @@ returns `{"started": ["x"]}` or `{"already_running": ["x"]}`.
```task
id: BRIDGE-WP-0003-T03
state_hub_task_id: f2fd64f5-31c6-493b-b48b-d13980467cca
status: todo
status: done
priority: high
```
@@ -205,7 +205,7 @@ if __name__ == "__main__":
```task
id: BRIDGE-WP-0003-T04
state_hub_task_id: 1bfc9b36-2be3-4606-a6e9-d611d1ac33ab
status: todo
status: done
priority: high
```
@@ -227,7 +227,7 @@ All return JSON-serialisable dicts/lists. `tunnel=None` means all tunnels.
```task
id: BRIDGE-WP-0003-T05
state_hub_task_id: ef7fa23c-d2e1-4fe0-9e26-994c1a6ce1fb
status: todo
status: done
priority: high
```
@@ -247,7 +247,7 @@ When `catalog_path` is not configured in `tunnels.yaml`, return
```task
id: BRIDGE-WP-0003-T06
state_hub_task_id: 71c9ee45-6928-416c-b4f3-dfb785a0ec8f
status: todo
status: done
priority: medium
```
@@ -271,7 +271,7 @@ parameterised queries. Both are needed.
```task
id: BRIDGE-WP-0003-T07
state_hub_task_id: 618c011d-bd1b-4c8f-8750-f3d2f9fcaf88
status: todo
status: done
priority: medium
```
@@ -303,7 +303,7 @@ calls `bridge_status` MCP tool, and returns a natural-language health summary.
```task
id: BRIDGE-WP-0003-T08
state_hub_task_id: 2c070f34-12b5-4dd9-ab24-bb7b6836773c
status: todo
status: done
priority: medium
```
@@ -330,7 +330,7 @@ gap matrix. The meta-test is itself verified by a synthetic failing fixture.
```task
id: BRIDGE-WP-0003-T09
state_hub_task_id: a8f3f5fb-fcd6-47e9-aad5-85dc803f796d
status: todo
status: done
priority: high
```
@@ -350,7 +350,7 @@ least one marked test in the CLI layer.
```task
id: BRIDGE-WP-0003-T10
state_hub_task_id: acb7ada6-111d-4b8d-b201-45748c394c43
status: todo
status: done
priority: high
```
@@ -372,7 +372,7 @@ graceful when `catalog_path` unset, resource URIs return valid JSON.
```task
id: BRIDGE-WP-0003-T11
state_hub_task_id: 071adfa4-2ccb-466b-b298-35130876267f
status: todo
status: done
priority: medium
```
@@ -397,7 +397,7 @@ def test_skill_covers_required_capabilities():
```task
id: BRIDGE-WP-0003-T12
state_hub_task_id: f1277a48-1790-42bd-8c70-8ba10c68312b
status: todo
status: done
priority: critical
```
@@ -428,7 +428,7 @@ gap.
```task
id: BRIDGE-WP-0003-T13
state_hub_task_id: c518662a-9a5b-40de-86f5-582a16489cd3
status: todo
status: done
priority: medium
```
@@ -459,7 +459,7 @@ user scope; `bridge --help` still works; `uv run pytest` passes.
```task
id: BRIDGE-WP-0003-T14
state_hub_task_id: b86916ba-59f3-44c1-b874-8af92d30e470
status: todo
status: done
priority: medium
```
@@ -482,7 +482,7 @@ User-scope (machine-global, any repo):
```task
id: BRIDGE-WP-0003-T15
state_hub_task_id: d826764f-e2f1-4f6a-842c-a1852a88b209
status: todo
status: done
priority: medium
```