chore(mcp): disable MCP auto-start and auto-connect; document manual install

This commit is contained in:
giveen
2026-01-21 09:23:13 -07:00
parent c73745304f
commit c5c6fee8da
7 changed files with 72 additions and 268 deletions

33
PR_BODY.txt Normal file
View File

@@ -0,0 +1,33 @@
Summary:
Fixes runtime and UX bugs that prevented tool execution and caused inconsistent target selection in the TUI. Improves robustness across Textual versions and makes the target visible and authoritative to the LLM.
What was broken:
- TypeError when scheduling Textual workers: asyncio.create_task was given a Textual Worker (not a coroutine).
- LLM-generated flags-only terminal commands (e.g. -p 1-1024 ...) were passed to /bin/sh and caused '/bin/sh: Illegal option -'.
- Active workspace scope checks blocked scans when the target was not in the workspace, while stale/manual targets could persist in conversation and be used by the LLM.
- UI errors on some Textual versions from calling unsupported APIs (e.g. ScrollableContainer.mount_before), and duplicated in-chat target messages cluttered the chat.
What I changed (key files):
- pentestagent/interface/tui.py
- Stop wrapping @work-decorated methods with asyncio.create_task; use the returned Worker correctly.
- Ensure workspace activation/deactivation behavior: clear TUI/agent target on /workspace clear; restore last_target on activation.
- When operator sets a manual target (/target), append a short system AgentMessage so the LLM sees the change; track manual target and remove/supersede it when a workspace restores its saved target.
- Add a persistent header widget to display runtime/mode/target and remove duplicate in-chat target lines.
- Guard mount_before calls with a try/fallback to mount to support Textual versions without mount_before.
- pentestagent/agents/base_agent.py
- If a requested tool name is not found, fall back to the terminal tool and construct a best-effort command string from function-call arguments so semantic tool names (e.g., nmap) execute.
- Preserve workspace-scope validation but return explicit errors that instruct the operator how to proceed.
- pentestagent/tools/terminal/__init__.py
- Detect flags-only command strings and prefix them with a likely binary (nmap, gobuster, rustscan, masscan, curl, wget, etc.) using runtime-detected tools, preventing shell option errors.
- Make terminal execution tolerant to malformed inputs and avoid uncaught exceptions.
Rationale:
Textual workers are Worker objects; scheduling them as coroutines caused runtime errors. Handling Worker objects correctly preserves Textual semantics. LLMs sometimes emit partial or semantic commands; best-effort normalization reduces shell failures and improves task success. Explicit system messages and deterministic workspace restores ensure the LLM uses the intended target. A persistent header provides immediate operator context and avoids losing the active target when the chat scrolls.
Testing performed:
- Reproduced and fixed the Worker scheduling TypeError.
- Verified flags-only commands are now prefixed (nmap test) and no longer produce '/bin/sh: Illegal option -'.
- Walked through workspace/target flows to confirm authoritative target behavior.
- Confirmed mount_before fallback avoids AttributeError on older Textual versions.
Branch: changes pushed to giveen/bug-fix.

View File

@@ -146,7 +146,11 @@ PentestAgent includes built-in tools and supports MCP (Model Context Protocol) f
### MCP Integration
Add external tools via MCP servers in `pentestagent/mcp/mcp_servers.json`:
PentestAgent supports MCP (Model Context Protocol) servers, but automatic
installation and auto-start of vendored MCP adapters has been removed. Operators
should run the installers and setup scripts under `third_party/` manually and
then configure `mcp_servers.json` for any MCP servers they intend to use. Example
config (place under `mcp_servers.json`):
```json
{
@@ -217,7 +221,7 @@ This branch vendors an optional integration with HexStrike (a powerful MCP-enabl
Special thanks and credit to the HexStrike project and its author: https://github.com/0x4m4/hexstrike-ai
Notes:
- HexStrike is vendored under `third_party/hexstrike` and is opt-in; follow `scripts/install_hexstrike_deps.sh` to install its Python dependencies.
- Auto-start of the vendored HexStrike adapter is controlled via the `.env` flag `LAUNCH_HEXTRIKE` and can be enabled per-user.
- Notes:
- HexStrike is vendored under `third_party/hexstrike` and is opt-in; follow `scripts/install_hexstrike_deps.sh` or the vendor README to install its dependencies and start the service manually.
- Automatic background install/start of vendored MCP adapters has been removed; operators should use the provided third-party scripts and then update `mcp_servers.json`.
- This update also includes several TUI fixes (improved background worker handling and safer task cancellation) to stabilize the terminal UI while using long-running MCP tools.

View File

@@ -88,23 +88,12 @@ async def run_cli(
except Exception:
pass
# Initialize MCP if config exists (silently skip failures)
# MCP auto-connect/install has been disabled. Operators should run the
# installation scripts under `third_party/` manually and configure
# `mcp_servers.json` for any MCP servers they intend to use. No automatic
# background installs or starts will be performed by the CLI.
mcp_manager = None
mcp_count = 0
try:
from ..mcp import MCPManager
from ..tools import register_tool_instance
mcp_manager = MCPManager()
if mcp_manager.config_path.exists():
mcp_tools = await mcp_manager.connect_all()
for tool in mcp_tools:
register_tool_instance(tool)
mcp_count = len(mcp_tools)
if mcp_count > 0:
console.print(f"[{PA_DIM}]Loaded {mcp_count} MCP tools[/]")
except Exception:
pass # MCP is optional, continue without it
# Initialize runtime - Docker or Local
if use_docker:

View File

@@ -1313,24 +1313,13 @@ class PentestAgentTUI(App):
self._add_system(f"[!] RAG: {e}")
self.rag_engine = None
# MCP - auto-load only if enabled in environment
# MCP: automatic install/start has been removed. Operators should
# run the scripts in `third_party/` manually to install and start
# any vendored MCP servers (e.g., HexStrike, MetasploitMCP) and
# then configure `mcp_servers.json` accordingly. No automatic
# background installs or starts will be performed.
self.mcp_manager = None
mcp_server_count = 0
import os
launch_hexstrike = os.getenv("LAUNCH_HEXTRIKE", "false").lower() == "true"
launch_metasploit = os.getenv("LAUNCH_METASPLOIT_MCP", "false").lower() == "true"
if launch_hexstrike or launch_metasploit:
try:
self.mcp_manager = MCPManager()
if self.mcp_manager.config_path.exists():
mcp_tools = await self.mcp_manager.connect_all()
for tool in mcp_tools:
register_tool_instance(tool)
mcp_server_count = len(self.mcp_manager.servers)
except Exception as e:
self._add_system(f"[!] MCP: {e}")
else:
self.mcp_manager = None
mcp_server_count = 0
# Runtime - Docker or Local
if self.use_docker:

View File

@@ -122,44 +122,13 @@ class MCPManager:
# - Metasploit: LAUNCH_METASPLOIT_MCP
# If set to a truthy value (1,true,y), force-enable auto-start for matching vendored server.
# If set to a falsy value (0,false,n), force-disable auto-start for matching vendored server.
def _apply_launch_override(env_names, match_fn):
launch_env = None
for e in env_names:
launch_env = os.environ.get(e)
if launch_env is not None:
break
if launch_env is None:
return
v = str(launch_env).strip().lower()
enable = v in ("1", "true", "yes", "y")
disable = v in ("0", "false", "no", "n")
for name, cfg in servers.items():
try:
if not match_fn(name, cfg):
continue
if enable:
cfg.start_on_launch = True
elif disable:
cfg.start_on_launch = False
except Exception:
continue
# Hexstrike override
_apply_launch_override(["LAUNCH_HEXTRIKE", "LAUNCH_HEXSTRIKE"],
lambda name, cfg: (
(name or "").lower().find("hexstrike") != -1
or (cfg.command and "third_party/hexstrike" in str(cfg.command))
or any("third_party/hexstrike" in str(a) for a in (cfg.args or []))
))
# Metasploit override
_apply_launch_override(["LAUNCH_METASPLOIT_MCP"],
lambda name, cfg: (
(name or "").lower().find("metasploit") != -1
or (cfg.command and "third_party/MetasploitMCP" in str(cfg.command))
or any("third_party/MetasploitMCP" in str(a) for a in (cfg.args or []))
))
# NOTE: Removed automatic LAUNCH_* overrides. MCP vendored adapters and
# auto-start behavior is intentionally disabled. MCP servers should be
# installed and configured manually via scripts in `third_party/` and
# `mcp_servers.json` should be prepared by the operator. Automatic
# start-on-launch logic previously controlled by environment variables
# is deprecated to simplify setup and avoid unexpected background
# processes.
return servers
except json.JSONDecodeError as e:
@@ -308,81 +277,12 @@ class MCPManager:
async def connect_all(self) -> List[Any]:
servers_config = self._load_config()
# Respect explicit LAUNCH_* env overrides for vendored MCP servers.
# If set to a falsy value (0/false/no/n) we will skip connecting to matching vendored servers.
launch_hex_env = os.environ.get("LAUNCH_HEXTRIKE") or os.environ.get("LAUNCH_HEXSTRIKE")
launch_hex_disabled = False
if launch_hex_env is not None:
v = str(launch_hex_env).strip().lower()
if v in ("0", "false", "no", "n"):
launch_hex_disabled = True
launch_msf_env = os.environ.get("LAUNCH_METASPLOIT_MCP")
launch_msf_disabled = False
if launch_msf_env is not None:
v = str(launch_msf_env).strip().lower()
if v in ("0", "false", "no", "n"):
launch_msf_disabled = True
all_tools = []
# Connect to any configured servers but DO NOT auto-start vendored adapters.
# Operators should install and start vendored MCPs manually (see third_party/).
for name, config in servers_config.items():
if not config.enabled:
continue
# If the user explicitly disabled launching HexStrike, skip hexstrike entries entirely
lowered = name.lower() if name else ""
is_hex = (
"hexstrike" in lowered
or (config.command and "third_party/hexstrike" in str(config.command))
or any("third_party/hexstrike" in str(a) for a in (config.args or []))
)
if launch_hex_disabled and is_hex:
print(f"[MCP] Skipping auto-connection for {name} due to LAUNCH_HEXTRIKE={launch_hex_env}")
continue
# Optionally auto-start vendored servers (e.g., HexStrike subtree or MetasploitMCP)
if getattr(config, "start_on_launch", False):
try:
args_joined = " ".join(config.args or [])
cmd_str = config.command or ""
# Hexstrike auto-start
if "third_party/hexstrike" in args_joined or (cmd_str and "third_party/hexstrike" in cmd_str):
if not launch_hex_disabled:
try:
from .hexstrike_adapter import HexstrikeAdapter
adapter = HexstrikeAdapter()
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 as e:
print(f"[MCP] Failed to auto-start vendored server {name}: {e}")
else:
print(f"[MCP] Skipping auto-start for {name} due to LAUNCH_HEXTRIKE override")
# Metasploit auto-start
if "third_party/MetasploitMCP" in args_joined or (cmd_str and "third_party/MetasploitMCP" in cmd_str) or (name and "metasploit" in name.lower()):
if not launch_msf_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 as e:
print(f"[MCP] Failed to auto-start vendored server {name}: {e}")
else:
print(f"[MCP] Skipping auto-start for {name} due to LAUNCH_METASPLOIT_MCP override")
except Exception:
pass
server = await self._connect_server(config)
if server:
self.servers[name] = server

View File

@@ -129,71 +129,9 @@ if (Test-Path -Path ".env") {
New-Item -ItemType Directory -Force -Path "loot" | Out-Null
Write-Host "[OK] Loot directory created"
# Install vendored HexStrike dependencies automatically if present
$hexReq = Join-Path -Path (Get-Location) -ChildPath "third_party/hexstrike/requirements.txt"
if (Test-Path -Path $hexReq) {
Write-Host "Installing vendored HexStrike dependencies..."
try {
& .\scripts\install_hexstrike_deps.ps1
} catch {
Write-Host "Warning: Failed to install HexStrike deps: $($_.Exception.Message)" -ForegroundColor Yellow
}
}
# Attempt to vendor MetasploitMCP via bundled script if not already present
$msDir = Join-Path -Path (Get-Location) -ChildPath "third_party/MetasploitMCP"
$addScript = Join-Path -Path (Get-Location) -ChildPath "scripts/add_metasploit_subtree.sh"
if (-not (Test-Path -Path $msDir) -and (Test-Path -Path $addScript)) {
Write-Host "Vendoring MetasploitMCP into third_party (requires bash)..."
if (Get-Command bash -ErrorAction SilentlyContinue) {
try {
& bash -c "scripts/add_metasploit_subtree.sh"
} catch {
Write-Host "Warning: Failed to vendor MetasploitMCP via bash: $($_.Exception.Message)" -ForegroundColor Yellow
}
} else {
Write-Host "Warning: 'bash' not available; please run scripts/add_metasploit_subtree.sh manually." -ForegroundColor Yellow
}
}
# Install vendored MetasploitMCP dependencies automatically if present
$msReq = Join-Path -Path (Get-Location) -ChildPath "third_party/MetasploitMCP/requirements.txt"
$installMsScript = Join-Path -Path (Get-Location) -ChildPath "scripts/install_metasploit_deps.sh"
if (Test-Path -Path $msReq) {
Write-Host "Installing vendored MetasploitMCP dependencies..."
if (Test-Path -Path $installMsScript -and (Get-Command bash -ErrorAction SilentlyContinue)) {
try {
& bash -c "scripts/install_metasploit_deps.sh"
} catch {
Write-Host "Warning: Failed to install MetasploitMCP deps via bash: $($_.Exception.Message)" -ForegroundColor Yellow
}
} else {
Write-Host "Warning: Could not run install script automatically; run scripts/install_metasploit_deps.sh manually." -ForegroundColor Yellow
}
}
# Optionally auto-start msfrpcd if configured in .env
if (($env:LAUNCH_METASPLOIT_MCP -eq 'true') -and ($env:MSF_PASSWORD)) {
$msfUser = if ($env:MSF_USER) { $env:MSF_USER } else { 'msf' }
$msfServer = if ($env:MSF_SERVER) { $env:MSF_SERVER } else { '127.0.0.1' }
$msfPort = if ($env:MSF_PORT) { $env:MSF_PORT } else { '55553' }
Write-Host "Starting msfrpcd (user=$msfUser, host=$msfServer, port=$msfPort) without sudo (background)..."
# Start msfrpcd without sudo; if it's already running the cmd will fail harmlessly.
if (Get-Command msfrpcd -ErrorAction SilentlyContinue) {
try {
if ($env:MSF_SSL -eq 'true' -or $env:MSF_SSL -eq '1') {
Start-Process -FilePath msfrpcd -ArgumentList "-U", $msfUser, "-P", $env:MSF_PASSWORD, "-a", $msfServer, "-p", $msfPort, "-S" -NoNewWindow -WindowStyle Hidden
} else {
Start-Process -FilePath msfrpcd -ArgumentList "-U", $msfUser, "-P", $env:MSF_PASSWORD, "-a", $msfServer, "-p", $msfPort -NoNewWindow -WindowStyle Hidden
}
Write-Host "msfrpcd start requested; check with: netstat -an | Select-String $msfPort"
} catch {
Write-Host "Warning: Failed to start msfrpcd: $($_.Exception.Message)" -ForegroundColor Yellow
}
} else {
Write-Host "msfrpcd not found; please install Metasploit Framework to enable Metasploit RPC." -ForegroundColor Yellow
}
}
# NOTE: Automatic vendored MCP installation/start has been removed.
# Operators should run `scripts/*` helpers manually when they want to
# install or vendor third-party MCP adapters and their dependencies.
Write-Host ""
Write-Host "Setup complete!"

View File

@@ -120,64 +120,15 @@ fi
mkdir -p loot
echo "[OK] Loot directory created"
# Install vendored HexStrike dependencies automatically if present
if [ -f "third_party/hexstrike/requirements.txt" ]; then
echo "Installing vendored HexStrike dependencies..."
bash scripts/install_hexstrike_deps.sh
fi
# Vendor MetasploitMCP via git-subtree if not already vendored
if [ ! -d "third_party/MetasploitMCP" ] && [ -f "scripts/add_metasploit_subtree.sh" ]; then
echo "Vendoring MetasploitMCP into third_party..."
bash scripts/add_metasploit_subtree.sh || echo "Warning: failed to vendor MetasploitMCP; you can run scripts/add_metasploit_subtree.sh manually."
fi
# Install vendored MetasploitMCP dependencies automatically if present
if [ -f "third_party/MetasploitMCP/requirements.txt" ]; then
echo "Installing vendored MetasploitMCP dependencies..."
bash scripts/install_metasploit_deps.sh || echo "Warning: failed to install MetasploitMCP dependencies."
fi
# Optionally auto-start Metasploit RPC daemon if configured
# Start `msfrpcd` without sudo if LAUNCH_METASPLOIT_MCP=true and MSF_PASSWORD is set.
if [ "${LAUNCH_METASPLOIT_MCP,,}" = "true" ] && [ -n "${MSF_PASSWORD:-}" ]; then
if command -v msfrpcd >/dev/null 2>&1; then
MSF_USER="${MSF_USER:-msf}"
MSF_SERVER="${MSF_SERVER:-127.0.0.1}"
MSF_PORT="${MSF_PORT:-55553}"
MSF_SSL="${MSF_SSL:-false}"
echo "Starting msfrpcd (user=${MSF_USER}, host=${MSF_SERVER}, port=${MSF_PORT})..."
# Start msfrpcd as a background process without sudo. The daemon will bind to the loopback
# interface and does not require root privileges on modern systems for ephemeral ports.
msfrpcd_cmd=$(command -v msfrpcd || true)
if [ -n "$msfrpcd_cmd" ]; then
LOG_DIR="loot/artifacts"
mkdir -p "$LOG_DIR"
MSF_LOG="$LOG_DIR/metasploit_msfrpcd.log"
# For safety, bind msfrpcd to loopback by default. To intentionally expose RPC to the host
# set EXPOSE_MSF_RPC=true in your environment (not recommended on shared hosts).
if [ "${EXPOSE_MSF_RPC,,}" != "true" ]; then
if [ "$MSF_SERVER" != "127.0.0.1" ] && [ "$MSF_SERVER" != "localhost" ]; then
echo "Warning: MSF_SERVER is set to '$MSF_SERVER' but EXPOSE_MSF_RPC is not true. Overriding to 127.0.0.1 for safety."
fi
MSF_SERVER=127.0.0.1
else
echo "EXPOSE_MSF_RPC=true: msfrpcd will bind to $MSF_SERVER and may be reachable from the host network. Ensure you know the risks."
fi
if [ "${MSF_SSL,,}" = "true" ] || [ "${MSF_SSL}" = "1" ]; then
"$msfrpcd_cmd" -U "$MSF_USER" -P "$MSF_PASSWORD" -a "$MSF_SERVER" -p "$MSF_PORT" -S >"$MSF_LOG" 2>&1 &
else
"$msfrpcd_cmd" -U "$MSF_USER" -P "$MSF_PASSWORD" -a "$MSF_SERVER" -p "$MSF_PORT" >"$MSF_LOG" 2>&1 &
fi
echo "msfrpcd started (logs: $MSF_LOG)"
else
echo "msfrpcd not found; please install Metasploit Framework to enable Metasploit RPC."
fi
else
echo "msfrpcd not found; please install Metasploit Framework to enable Metasploit RPC."
fi
fi
# NOTE: Automatic vendored MCP installation/start has been removed.
# If you need vendored MCP servers (e.g., HexStrike, MetasploitMCP), run
# the helper scripts under `third_party/` or the `scripts/` helpers manually.
# Example manual steps:
# bash scripts/install_hexstrike_deps.sh
# bash scripts/add_metasploit_subtree.sh
# bash scripts/install_metasploit_deps.sh
# Starting msfrpcd or other networked services should be done explicitly by
# the operator in a controlled environment.
echo ""
echo "=================================================================="