""" opax_mcp_client.py — REST-klient for opax-mcp Cloud Run service. Bruk denne for å kalle alle 25 tools frå Jason/Emma eller anna Python-kode. Auth-lag: 1. Cloud Run IAM : Authorization: Bearer (automatisk) 2. Tool-level : X-MCP-Secret: (frå Secret Manager) URL: https://opax-mcp-zjbqp3prqq-uc.a.run.app """ import os import httpx import google.auth import google.auth.transport.requests OPAX_MCP_URL = os.environ.get("MCP_SERVER_URL", "https://opax-mcp-zjbqp3prqq-uc.a.run.app") MCP_SECRET = os.environ.get("MCP_SECRET", "") def _identity_token() -> str: """Hent Cloud Run identity token (GCE metadata) eller ADC access token (lokal dev).""" metadata_url = ( "http://metadata.google.internal/computeMetadata/v1/instance" f"/service-accounts/default/identity?audience={OPAX_MCP_URL}&format=full" ) try: resp = httpx.get(metadata_url, headers={"Metadata-Flavor": "Google"}, timeout=5) if resp.status_code == 200 and resp.text.strip(): return resp.text.strip() except Exception: pass credentials, _ = google.auth.default() credentials.refresh(google.auth.transport.requests.Request()) return credentials.token def _headers() -> dict: return { "Authorization": f"Bearer {_identity_token()}", "X-MCP-Secret": MCP_SECRET, "Content-Type": "application/json", } def call_tool(tool: str, params: dict = None) -> dict: """Kall eit opax-mcp tool. Returnerer {tool, result}.""" resp = httpx.post( f"{OPAX_MCP_URL}/tools/call", headers=_headers(), json={"tool": tool, "params": params or {}}, timeout=30, ) resp.raise_for_status() return resp.json() def list_tools() -> list: """List alle tilgjengelige tools i opax-mcp.""" resp = httpx.get(f"{OPAX_MCP_URL}/tools", headers=_headers(), timeout=10) resp.raise_for_status() return resp.json().get("tools", []) # --------------------------------------------------------------------------- # Billing # --------------------------------------------------------------------------- def get_billing_summary() -> dict: """Hent billing-oversikt for OSVauco (GCP-kostnader, token-forbruk).""" return call_tool("get_billing_summary") def get_billing_forecast() -> dict: """Hent token-estimat og kostnadsframskriving.""" return call_tool("get_billing_forecast") def get_billing_credits() -> dict: """Hent gjenværende GCP-kreditter.""" return call_tool("get_billing_credits") def get_billing_anomalies() -> dict: """Sjekk for kostnadanomalier og uventede spiker.""" return call_tool("get_billing_anomalies") def get_billing_history() -> dict: """Hent historisk kostnadsdata.""" return call_tool("get_billing_history") def get_billing_budget() -> dict: """Hent gjeldande budsjett for OSVauco.""" return call_tool("get_billing_budget") def set_billing_budget(amount: float, currency: str = "USD") -> dict: """Sett nytt budsjett. amount er beløp i angitt valuta.""" return call_tool("set_billing_budget", {"amount": amount, "currency": currency}) # --------------------------------------------------------------------------- # Onboarding # --------------------------------------------------------------------------- def create_invite(email: str, name: str = "", tier: str = "starter") -> dict: """Opprett invite for ny bruker. tier: starter | pro | enterprise.""" return call_tool("create_invite", {"email": email, "name": name, "tier": tier}) def list_customers() -> dict: """List alle onboardede kunder.""" return call_tool("list_customers") # --------------------------------------------------------------------------- # Notify # --------------------------------------------------------------------------- def send_webhook(message: str, url: str = "") -> dict: """Send webhook-varsling. url er valgfri override.""" return call_tool("send_webhook", {"message": message, "url": url}) def send_email(to: str, subject: str, body: str) -> dict: """Send e-post via OPAX notify-modul.""" return call_tool("send_email", {"to": to, "subject": subject, "body": body}) def send_sms(to: str, message: str) -> dict: """Send SMS via OPAX notify-modul.""" return call_tool("send_sms", {"to": to, "message": message}) def get_notify_channels() -> dict: """List konfigurerte varslingskanalar.""" return call_tool("get_notify_channels") # --------------------------------------------------------------------------- # Agents # --------------------------------------------------------------------------- def run_jason(prompt: str) -> dict: """Kall Jason-agenten med ein prompt via OPAX.""" return call_tool("run_jason", {"prompt": prompt}) def run_emma(prompt: str) -> dict: """Kall Emma-agenten med ein prompt via OPAX.""" return call_tool("run_emma", {"prompt": prompt}) # --------------------------------------------------------------------------- # Platform # --------------------------------------------------------------------------- def get_health() -> dict: """Sjekk helsestatus for OPAX-plattformen.""" return call_tool("get_health") def get_build_status() -> dict: """Hent status på siste Cloud Build-kjøring.""" return call_tool("get_build_status") def get_state() -> dict: """Hent gjeldande systemtilstand for OSVauco.""" return call_tool("get_state") def get_telemetry() -> dict: """Hent token-forbruk per modul.""" return call_tool("get_telemetry") def run_terminal(command: str) -> dict: """Kjør kommando i OPAX terminal-exec. Krever HITL-godkjenning.""" return call_tool("run_terminal", {"command": command}) # --------------------------------------------------------------------------- # GitHub # --------------------------------------------------------------------------- def list_commits(repo: str = "", limit: int = 10) -> dict: """List siste commits i repoet.""" return call_tool("list_commits", {"repo": repo, "limit": limit}) def get_file(path: str, repo: str = "") -> dict: """Hent innhald i ein fil frå GitHub.""" return call_tool("get_file", {"path": path, "repo": repo}) def list_open_issues(repo: str = "") -> dict: """List alle opne issues i repoet.""" return call_tool("list_open_issues", {"repo": repo}) def create_github_issue(title: str, body: str = "", repo: str = "") -> dict: """Opprett nytt GitHub issue.""" return call_tool("create_github_issue", {"title": title, "body": body, "repo": repo}) def push_file(path: str, content: str, message: str = "", sha: str = "", repo: str = "") -> dict: """Push/oppdater ein fil i GitHub. sha krevst ved oppdatering av eksisterande fil.""" return call_tool("push_file", {"path": path, "content": content, "message": message, "sha": sha, "repo": repo}) # --------------------------------------------------------------------------- # A2H2A # --------------------------------------------------------------------------- def create_a2h2a_ticket( ticket_id: str, source: dict, proposed_action: dict, context: dict, severity: str, governance: dict, ) -> dict: """Creates an A2H2A ticket.""" return call_tool( "create_a2h2a_ticket", { "ticket_id": ticket_id, "source": source, "proposed_action": proposed_action, "context": context, "severity": severity, "governance": governance, }, ) # --------------------------------------------------------------------------- # ADK FunctionTools — for bruk i agent.py # --------------------------------------------------------------------------- def get_all_function_tools() -> list: """Returner alle 25 tools som ADK FunctionTool-liste for Jason/Emma.""" from google.adk.tools import FunctionTool return [ # Billing FunctionTool(func=get_billing_summary), FunctionTool(func=get_billing_forecast), FunctionTool(func=get_billing_credits), FunctionTool(func=get_billing_anomalies), FunctionTool(func=get_billing_history), FunctionTool(func=get_billing_budget), FunctionTool(func=set_billing_budget), # Onboarding FunctionTool(func=create_invite), FunctionTool(func=list_customers), # Notify FunctionTool(func=send_webhook), FunctionTool(func=send_email), FunctionTool(func=send_sms), FunctionTool(func=get_notify_channels), # Agents FunctionTool(func=run_jason), FunctionTool(func=run_emma), # Platform FunctionTool(func=get_health), FunctionTool(func=get_build_status), FunctionTool(func=get_state), FunctionTool(func=get_telemetry), FunctionTool(func=run_terminal), # GitHub FunctionTool(func=list_commits), FunctionTool(func=get_file), FunctionTool(func=list_open_issues), FunctionTool(func=create_github_issue), FunctionTool(func=push_file), # A2H2A FunctionTool(func=create_a2h2a_ticket), ]