From 66b4d78cfa445827ad51ae0c8922ed19a333fa82 Mon Sep 17 00:00:00 2001 From: zion Date: Fri, 5 Sep 2025 06:37:09 +0600 Subject: [PATCH 1/2] fix: accept proxy as optional arg in the constructor and replace hardcoded config --- .../secops-soar/secops_soar_mcp/bindings.py | 5 +- .../secops_soar_mcp/http_client.py | 56 +++++++++++++++---- .../secops_soar_mcp/utils/consts.py | 1 + 3 files changed, 50 insertions(+), 12 deletions(-) diff --git a/server/secops-soar/secops_soar_mcp/bindings.py b/server/secops-soar/secops_soar_mcp/bindings.py index 094c1f1a..674a8733 100644 --- a/server/secops-soar/secops_soar_mcp/bindings.py +++ b/server/secops-soar/secops_soar_mcp/bindings.py @@ -17,6 +17,7 @@ import dotenv from logger_utils import get_logger + from secops_soar_mcp.http_client import HttpClient from secops_soar_mcp.utils import consts @@ -42,7 +43,9 @@ async def bind(): """Binds global variables.""" global http_client, valid_scopes http_client = HttpClient( - os.getenv(consts.ENV_SOAR_URL), os.getenv(consts.ENV_SOAR_APP_KEY) + os.getenv(consts.ENV_SOAR_URL), + os.getenv(consts.ENV_SOAR_APP_KEY), + os.getenv(consts.ENV_SOAR_PROXY), ) valid_scopes = await _get_valid_scopes() diff --git a/server/secops-soar/secops_soar_mcp/http_client.py b/server/secops-soar/secops_soar_mcp/http_client.py index 1c03768b..53cf4b20 100644 --- a/server/secops-soar/secops_soar_mcp/http_client.py +++ b/server/secops-soar/secops_soar_mcp/http_client.py @@ -14,7 +14,7 @@ """HTTP client for making requests to the SecOps SOAR API.""" import json -from typing import Any, Dict +from typing import Any, Dict, Optional import aiohttp from logger_utils import get_logger @@ -25,9 +25,10 @@ class HttpClient: """HTTP client for making requests to the SecOps SOAR API.""" - def __init__(self, base_url: str, app_key: str): + def __init__(self, base_url: str, app_key: str, proxy: Optional[str] = None): self.base_url = base_url self.app_key = app_key + self.proxy = proxy self._session = None def _get_session(self) -> aiohttp.ClientSession: @@ -56,16 +57,21 @@ async def get( The response as a JSON object, or None if an error occurred. """ headers = await self._get_headers() + request_kwargs = {"params": params, "headers": headers} + if self.proxy: + request_kwargs["proxy"] = self.proxy try: async with self._get_session().get( - self.base_url + endpoint, params=params, headers=headers + self.base_url + endpoint, **request_kwargs ) as response: response.raise_for_status() # Raise an exception for 4xx/5xx responses return await response.json() except aiohttp.ClientResponseError as e: - logger.debug("HTTP error occurred: %s", e) + logger.error("HTTP error occurred for endpoint %s: %s", endpoint, e) + return {"error": f"HTTP error: {e}"} except Exception as e: - logger.debug("An error occurred: %s", e) + logger.error("An error occurred for endpoint %s: %s", endpoint, e) + return {"error": f"Unexpected error: {e}"} return None async def post( @@ -85,18 +91,41 @@ async def post( The response as a JSON object, or None if an error occurred. """ headers = await self._get_headers() + request_kwargs = {"json": req, "params": params, "headers": headers} + if self.proxy: + request_kwargs["proxy"] = self.proxy try: async with self._get_session().post( - self.base_url + endpoint, json=req, params=params, headers=headers + self.base_url + endpoint, **request_kwargs ) as response: + if response.status == 500: + try: + error_data = await response.json() + logger.error( + "Server error (500) for endpoint %s: %s", + endpoint, + error_data, + ) + return {"error": f"Server error (500): {error_data}"} + except Exception: + error_text = await response.text() + logger.error( + "Server error (500) for endpoint %s: %s", + endpoint, + error_text, + ) + return {"error": f"Server error (500): {error_text}"} + response.raise_for_status() data = await response.content.read() decoded_data = data.decode("utf-8") return json.loads(decoded_data) except aiohttp.ClientResponseError as e: - logger.debug("HTTP error occurred: %s", e) + logger.error("HTTP error occurred for endpoint %s: %s", endpoint, e) + return {"error": f"HTTP error: {e}"} except Exception as e: - logger.debug("An error occurred: %s", e) + logger.error("An error occurred for endpoint %s: %s", endpoint, e) + return {"error": f"Unexpected error: {e}"} return None async def patch( @@ -116,16 +145,21 @@ async def patch( The response as a JSON object, or None if an error occurred. """ headers = await self._get_headers() + request_kwargs = {"json": req, "params": params, "headers": headers} + if self.proxy: + request_kwargs["proxy"] = self.proxy try: async with self._get_session().patch( - self.base_url + endpoint, json=req, params=params, headers=headers + self.base_url + endpoint, **request_kwargs ) as response: response.raise_for_status() return await response.json() except aiohttp.ClientResponseError as e: - logger.debug("HTTP error occurred: %s", e) + logger.error("HTTP error occurred for endpoint %s: %s", endpoint, e) + return {"error": f"HTTP error: {e}"} except Exception as e: - logger.debug("An error occurred: %s", e) + logger.error("An error occurred for endpoint %s: %s", endpoint, e) + return {"error": f"Unexpected error: {e}"} return None async def close(self): diff --git a/server/secops-soar/secops_soar_mcp/utils/consts.py b/server/secops-soar/secops_soar_mcp/utils/consts.py index 3ff9448c..099535ad 100644 --- a/server/secops-soar/secops_soar_mcp/utils/consts.py +++ b/server/secops-soar/secops_soar_mcp/utils/consts.py @@ -15,6 +15,7 @@ ENV_SOAR_URL = "SOAR_URL" ENV_SOAR_APP_KEY = "SOAR_APP_KEY" +ENV_SOAR_PROXY = "SOAR_PROXY" class Endpoints: From 4d57b193d6faeb7d0c166d4ee18b52aa963bc4a0 Mon Sep 17 00:00:00 2001 From: zion Date: Fri, 5 Sep 2025 06:47:04 +0600 Subject: [PATCH 2/2] update README --- server/secops-soar/README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/server/secops-soar/README.md b/server/secops-soar/README.md index 97e52fb5..944dc367 100644 --- a/server/secops-soar/README.md +++ b/server/secops-soar/README.md @@ -66,7 +66,8 @@ To use this MCP server with Claude Desktop: ], "env": { "SOAR_URL": "${SOAR_URL}", - "SOAR_APP_KEY": "${SOAR_APP_KEY}" + "SOAR_APP_KEY": "${SOAR_APP_KEY}", + "SOAR_PROXY": "${SOAR_PROXY}" }, "disabled": false, "autoApprove": [] @@ -92,7 +93,8 @@ To have the MCP server provide tools for specific marketplace integrations, use ], "env": { "SOAR_URL": "${SOAR_URL}", - "SOAR_APP_KEY": "${SOAR_APP_KEY}" + "SOAR_APP_KEY": "${SOAR_APP_KEY}", + "SOAR_PROXY": "${SOAR_PROXY}" }, "disabled": false, "autoApprove": [] @@ -109,6 +111,7 @@ Set up these environment variables in your system: ```bash export SOAR_URL="your-soar-url" export SOAR_APP_KEY="your-soar-app-key" +export SOAR_PROXY="http://proxy:8080" export SOAR_INTEGRATIONS="ServiceNow,CSV,Siemplify" ``` @@ -116,6 +119,7 @@ export SOAR_INTEGRATIONS="ServiceNow,CSV,Siemplify" ```powershell $Env:SOAR_URL = "your-soar-url" $Env:SOAR_APP_KEY = "your-soar-app-key" +$Env:SOAR_PROXY = "http://proxy:8080" $Env:SOAR_INTEGRATIONS = "ServiceNow,CSV,Siemplify" ```