From 567404b513e8c8f1bf20e485e9e0be67e56648bf Mon Sep 17 00:00:00 2001 From: giveen Date: Wed, 14 Jan 2026 13:58:18 -0700 Subject: [PATCH] mcp: select SSE transport for HTTP servers; auto-start Metasploit adapter on single-server connect --- pentestagent/mcp/manager.py | 81 +++++++++++++++++++++++++++++++++++-- 1 file changed, 77 insertions(+), 4 deletions(-) diff --git a/pentestagent/mcp/manager.py b/pentestagent/mcp/manager.py index 30e57ad..a3b800b 100644 --- a/pentestagent/mcp/manager.py +++ b/pentestagent/mcp/manager.py @@ -388,6 +388,40 @@ class MCPManager: if name not in servers_config: return None config = servers_config[name] + # If this appears to be a vendored Metasploit MCP entry, attempt to auto-start + # the vendored adapter so `pentestagent mcp test metasploit-local` works + try: + args_joined = " ".join(config.args or []) + cmd_str = config.command or "" + is_msf = ( + (name and "metasploit" in name.lower()) + or ("third_party/MetasploitMCP" in cmd_str) + or ("third_party/MetasploitMCP" in args_joined) + ) + if is_msf: + launch_msf_env = os.environ.get("LAUNCH_METASPLOIT_MCP") + launch_disabled = False + if launch_msf_env is not None: + v = str(launch_msf_env).strip().lower() + if v in ("0", "false", "no", "n"): + launch_disabled = True + if not launch_disabled: + try: + from .metasploit_adapter import MetasploitAdapter + + adapter = MetasploitAdapter() + started = await adapter.start() + if started: + try: + self._started_adapters[name] = adapter + except Exception: + pass + print(f"[MCP] Auto-started vendored server for {name}") + except Exception: + pass + except Exception: + pass + server = await self._connect_server(config) if server: self.servers[name] = server @@ -397,10 +431,49 @@ class MCPManager: transport = None try: env = {**os.environ, **config.env} - transport = StdioTransport( - command=config.command, args=config.args, env=env - ) - await transport.connect() + + # Decide transport type: + # - If args contain a http/sse transport or a --server http:// URL, use SSETransport + # - Otherwise default to StdioTransport (spawn process and use stdio JSON-RPC) + use_http = False + http_url = None + args_joined = " ".join(config.args or []) + if "--transport http" in args_joined or "--transport sse" in args_joined: + # Try to extract host/port from args + try: + # naive parsing: look for --host and --port + host = None + port = None + for i, a in enumerate(config.args or []): + if a == "--host" and i + 1 < len(config.args): + host = config.args[i + 1] + if a == "--port" and i + 1 < len(config.args): + port = config.args[i + 1] + if host and port: + http_url = f"http://{host}:{port}" + except Exception: + http_url = None + use_http = True + # If args specify a --server URL, prefer that + if not http_url: + for i, a in enumerate(config.args or []): + if a == "--server" and i + 1 < len(config.args): + candidate = config.args[i + 1] + if isinstance(candidate, str) and candidate.startswith("http"): + http_url = candidate + use_http = True + break + + if use_http and http_url: + from .transport import SSETransport + + transport = SSETransport(url=http_url) + await transport.connect() + else: + transport = StdioTransport( + command=config.command, args=config.args, env=env + ) + await transport.connect() await transport.send( {