From 580fc37614257d9a2ac6200856d3a638c4bdec6f Mon Sep 17 00:00:00 2001 From: giveen Date: Wed, 14 Jan 2026 16:49:47 -0700 Subject: [PATCH] mcp: SSETransport discovers messages POST endpoint from /sse and uses it for POSTs --- pentestagent/mcp/transport.py | 46 ++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/pentestagent/mcp/transport.py b/pentestagent/mcp/transport.py index 52e0883..d9468dc 100644 --- a/pentestagent/mcp/transport.py +++ b/pentestagent/mcp/transport.py @@ -174,6 +174,7 @@ class SSETransport(MCPTransport): self.url = url self.session: Optional[Any] = None # aiohttp.ClientSession self._connected = False + self._post_url: Optional[str] = None @property def is_connected(self) -> bool: @@ -186,6 +187,48 @@ class SSETransport(MCPTransport): import aiohttp self.session = aiohttp.ClientSession() + + # Try to discover the POST endpoint for sending messages. + # Many MCP SSE servers (including MetasploitMCP) expose an SSE + # endpoint (e.g. /sse) which returns an initial `endpoint` event + # with a `data: /messages/?session_id=...` value. We perform a + # short GET to read that event and extract the messages POST URL. + try: + async with self.session.get(self.url, timeout=5) as resp: + if resp.status != 200: + # Still consider connected (we may only need POST), + # but leave discovery to send() which will give clearer + # errors if the server isn't compatible. + self._connected = True + return + + # Read a few lines to find `data:` + for _ in range(20): + line = await resp.content.readline() + if not line: + break + try: + text = line.decode(errors="ignore").strip() + except Exception: + continue + if text.startswith("data:"): + endpoint = text.split("data:", 1)[1].strip() + # Build absolute POST URL from the discovered endpoint + from urllib.parse import urlparse + + p = urlparse(self.url) + if endpoint.startswith("http"): + self._post_url = endpoint + elif endpoint.startswith("/"): + self._post_url = f"{p.scheme}://{p.netloc}{endpoint}" + else: + self._post_url = f"{p.scheme}://{p.netloc}/{endpoint.lstrip('/') }" + break + + except Exception: + # Discovery failed — still create session and let send() report errors. + pass + self._connected = True except ImportError as e: raise RuntimeError( @@ -206,8 +249,9 @@ class SSETransport(MCPTransport): raise RuntimeError("Transport not connected") try: + post_target = self._post_url or self.url async with self.session.post( - self.url, json=message, headers={"Content-Type": "application/json"} + post_target, json=message, headers={"Content-Type": "application/json"} ) as response: if response.status != 200: raise RuntimeError(f"HTTP error: {response.status}")