mcp: SSETransport discovers messages POST endpoint from /sse and uses it for POSTs

This commit is contained in:
giveen
2026-01-14 16:49:47 -07:00
parent b242dc3031
commit 580fc37614

View File

@@ -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}")