diff --git a/README.txt b/README.txt index 5445989..4db40ee 100644 --- a/README.txt +++ b/README.txt @@ -181,6 +181,34 @@ bridge_reconnecting, health_check_failed, health_check_recovered, bridge_stopped +MCP INTEGRATION +--------------- + +OpsBridge exposes its capabilities as a FastMCP server so Claude Code agents +can call bridge_up(), bridge_status(), catalog_list_targets(), etc. as +first-class MCP tools — no Bash required, structured JSON in/out. + +Available tools: bridge_up, bridge_down, bridge_restart, bridge_status, + bridge_logs, catalog_list_targets, catalog_show_target, + catalog_list_domains, catalog_validate, catalog_show_bridge + +Available resources: bridge://status, catalog://domains, catalog://targets + +Project-scope (auto, inside ops-bridge/): + Already configured in .mcp.json. Claude Code sessions inside this repo + see the tools automatically. + +User-scope (machine-global, any repo): + python scripts/register_mcp.py + +Human operator skill: + /bridge-status — natural-language tunnel health summary + (skill file: ~/.claude/plugins/ops-bridge/bridge-status.md) + +Run the server directly (for debugging): + uv run python src/bridge/mcp_server/server.py + + DEVELOPMENT ----------- diff --git a/tests/test_coverage_completeness.py b/tests/test_coverage_completeness.py index fd52927..91e66a9 100644 --- a/tests/test_coverage_completeness.py +++ b/tests/test_coverage_completeness.py @@ -134,6 +134,78 @@ async def test_mcp_tools_in_registry(): ) +# --------------------------------------------------------------------------- +# T12 — Self-validation: sentinel fixture proves the gap-checker catches gaps +# --------------------------------------------------------------------------- + +def test_meta_test_catches_missing_mode_gap(): + """Self-validation: the coverage checker must detect a missing-mode gap. + + Injects a synthetic _test_sentinel capability requiring both cli and mcp. + Creates mock items with *only* a cli test for it (deliberately omitting mcp). + Asserts that collect_capability_coverage reports the mcp gap — proving the + meta-test mechanism is functional, not a silent no-op. + + This test validates Goal #4 from BRIDGE-WP-0003: + "The gap-detection mechanism is itself tested: a synthetic missing-mode + fixture asserts the meta-test catches it." + """ + from bridge.capabilities import Capability + + sentinel = Capability( + name="_test_sentinel", + description="Synthetic capability for meta-test self-validation", + required_access_modes=frozenset({"cli", "mcp"}), + ) + patched_caps = CAPABILITIES + [sentinel] + + # Minimal mock: an iterable of items that respond to iter_markers() + class _Mark: + def __init__(self, arg: str): + self.args = (arg,) + + class _MockItem: + def __init__(self, capability: str, mode: str): + self._cap = capability + self._mode = mode + + def iter_markers(self, name: str): + if name == "capability": + return [_Mark(self._cap)] + if name == "access_mode": + return [_Mark(self._mode)] + return [] + + # Only supply a cli test for the sentinel — the mcp test is intentionally absent + mock_items = [_MockItem("_test_sentinel", "cli")] + + covered = collect_capability_coverage(mock_items) + + # The cli mode should be registered + assert ("_test_sentinel", "cli") in covered, ( + "collect_capability_coverage failed to record the cli mock item" + ) + # The mcp mode must NOT be covered — this is the gap we want to catch + assert ("_test_sentinel", "mcp") not in covered, ( + "collect_capability_coverage incorrectly registered an mcp test that was not provided" + ) + + # Run the same gap-detection logic used by test_all_required_modes_have_tests + gaps = [ + (cap.name, mode) + for cap in patched_caps + for mode in cap.required_access_modes + if (cap.name, mode) not in covered + ] + + assert ("_test_sentinel", "mcp") in gaps, ( + "Gap checker failed to detect the missing mcp mode for _test_sentinel. " + "The meta-test mechanism is broken." + ) + # Sanity: cli mode should NOT appear as a gap (it was covered) + assert ("_test_sentinel", "cli") not in gaps + + def test_no_orphan_capability_marks(capability_coverage): """Every (capability, mode) pair in the test suite must exist in the registry.