"""MCP client to the external compliance-scanner-agent (pull-flow). We connect to THEIR MCP server (Streamable HTTP + Bearer), pull the findings they already produced (list_findings), and feed them into our deterministic CRA assessment. Their tool returns a JSON array of Finding docs as text content; the field shape is bridged by ScannerFinding.from_dict (scan_type/cvss_score/...). Config via env (SCANNER_MCP_URL, SCANNER_MCP_TOKEN) or per-call override. When no URL is configured, fetch_findings returns [] — callers fall back to their demo. """ import json import os from typing import Optional SCANNER_MCP_URL = os.getenv("SCANNER_MCP_URL", "") SCANNER_MCP_TOKEN = os.getenv("SCANNER_MCP_TOKEN", "") def parse_findings_text(text: str) -> list: """Parse the list_findings tool result (a JSON array, or {findings|results:[...]}).""" try: data = json.loads(text) except (json.JSONDecodeError, TypeError): return [] if isinstance(data, dict): data = data.get("findings") or data.get("results") or [] return data if isinstance(data, list) else [] async def fetch_findings( repo_id: Optional[str] = None, severity: Optional[str] = None, limit: int = 200, base_url: Optional[str] = None, token: Optional[str] = None, ) -> list: """Pull findings from the scanner's MCP server. Returns [] if unconfigured or on error.""" url = (base_url or SCANNER_MCP_URL).rstrip("/") tok = token or SCANNER_MCP_TOKEN if not url: return [] from mcp.client.session import ClientSession from mcp.client.streamable_http import streamablehttp_client headers = {"Authorization": f"Bearer {tok}"} if tok else None params: dict = {"limit": limit} if repo_id: params["repo_id"] = repo_id if severity: params["severity"] = severity async with streamablehttp_client(url, headers=headers) as (read, write, _): async with ClientSession(read, write) as session: await session.initialize() result = await session.call_tool("list_findings", params) texts = [c.text for c in (result.content or []) if getattr(c, "type", "") == "text"] return parse_findings_text(texts[0]) if texts else []