fix: Eliminate blocking HTTP call from instances_use

instances_use previously called register_instance which made a
blocking safe_get (30s timeout) to validate the connection. If the
Ghidra server was slow or unresponsive, this could hang the MCP tool
call indefinitely from the client's perspective.

Now instances_use creates a lazy stub entry and sets the port
immediately — pure in-memory, no network I/O. The first actual tool
call validates the connection naturally.

Also fix background discovery thread using request_timeout (30s)
instead of discovery_timeout (0.5s) per port — worst case went from
300s to 5s per scan cycle.
This commit is contained in:
Ryan Malloy 2026-01-31 20:20:30 -07:00
parent c93abaf86c
commit 458d4fb35b
2 changed files with 36 additions and 18 deletions

View File

@ -166,13 +166,20 @@ class InstancesMixin(GhydraMixinBase):
Returns:
Confirmation message with instance details
"""
# Register lazily without blocking HTTP calls.
# If the instance is unknown, create a stub entry — the first
# actual tool call (functions_list, etc.) will validate the
# connection and fail fast with a clear error if unreachable.
with self._instances_lock:
needs_register = port not in self._instances
if needs_register:
result = self.register_instance(port)
if "Failed" in result or "Error" in result:
return result
if port not in self._instances:
config = get_config()
self._instances[port] = {
"url": f"http://{config.ghidra_host}:{port}",
"project": "",
"file": "",
"registered_at": time.time(),
"lazy": True,
}
self.set_current_port(port)

View File

@ -105,10 +105,14 @@ def create_server(
def _periodic_discovery(interval: int = 30):
"""Background thread for periodic instance discovery.
Uses a short timeout per port so a full scan completes quickly
even when most ports are unreachable.
Args:
interval: Seconds between discovery attempts
"""
from .core.http_client import safe_get
import requests as _requests
from .mixins.base import GhydraMixinBase
config = get_config()
@ -116,15 +120,22 @@ def _periodic_discovery(interval: int = 30):
while True:
time.sleep(interval)
try:
# Quick scan of common ports
# Quick scan — use discovery_timeout (0.5s), NOT request_timeout (30s)
for port in config.quick_discovery_range:
try:
response = safe_get(port, "")
url = f"http://{config.ghidra_host}:{port}/"
resp = _requests.get(
url,
timeout=config.discovery_timeout,
headers={"Accept": "application/json"},
)
if resp.ok:
response = resp.json()
if response.get("success", False):
with GhydraMixinBase._instances_lock:
if port not in GhydraMixinBase._instances:
GhydraMixinBase._instances[port] = {
"url": f"http://{config.ghidra_host}:{port}",
"url": url.rstrip("/"),
"project": response.get("project", ""),
"file": response.get("file", ""),
"discovered_at": time.time(),