GUI instance discovery file deleted moments after registration, even though instance is alive and reachable
Summary
discover_instances() in src/ida_pro_mcp/ida_mcp/discovery.py deletes a GUI instance's instance_<port>.json registration file almost immediately after it's written — even though the instance is fully alive and reachable at the time. This breaks idb_open(mode="prefer_gui"|"force_gui") and idb_list() for adopting an already-running GUI session via idalib-mcp.
Environment
- OS: Windows 11
- IDA Pro 9.3
- Installed via the Claude Code plugin marketplace (
claude plugin install ida-pro-mcp@mrexodia), invoked asuv run --project <plugin-cache> idalib-mcp --stdio idalibactivated viapy-activate-idalib.pyagainst the IDA 9.3 install
Repro
- Load the GUI plugin in IDA (
ida_mcp.py, from this repo'sida-plugin.jsonentry point). Confirms in the IDA log:[MCP] Registered instance: ZDefend (pid=25252, port=13337) Discovery file: ...\Hex-Rays\IDA Pro\mcp\instances\instance_13337.json - Immediately (within a few seconds) call
idb_listviaidalib-mcp --stdio:Result:{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"idb_list","arguments":{}}}{"sessions":[],"count":0} - Check the instances directory — the file is gone:
ls "$APPDATA/Hex-Rays/IDA Pro/mcp/instances/" # empty - At the same moment, the instance is still perfectly reachable:
curl -X POST http://127.0.0.1:13337/mcp ...→ valid JSON-RPC response- Calling
server_healthdirectly against that port (via the non-idalibida-pro-mcpstdio proxy, which doesn't go through this discovery/prune logic) returns real data (module,idb_path,hexrays_ready: true, etc.)
Reproduced this identically twice in one session (once against the original instance, once after re-triggering registration via Ctrl+Alt+M).
What I ruled out
Directly isolated both liveness checks used by discover_instances() against the live PID/port right after a fresh registration, and both passed on their own:
import discovery
discovery.is_pid_alive(25252) # -> True
discovery.probe_instance("127.0.0.1", 13337, 2.0) # -> TrueSo it's not a static/permission issue with OpenProcess (same user, non-elevated) or a fundamentally wrong port. This points to a timing/race condition: the plugin's HTTP server (http.server-based, effectively single-threaded) may be momentarily busy servicing another request when discover_instances()'s probe runs, causing a transient connect failure that gets treated as "instance is dead" and triggers os.unlink(file_path) — permanently, since there's no periodic re-registration to recover from a false-negative prune.
Impact
idb_open(..., mode="prefer_gui")can't find/adopt the already-open GUI session, silently falls back toward a headless open of the same.i64file, and fails with"Failed to open database"since the GUI process already holds an exclusive lock on it.idb_list()undercounts/misses live GUI sessions entirely once the race hits once.- Headless-only multi-session (
idb_open(mode="force_headless")for independent binaries) is unaffected and works correctly — verified opening two separate binaries concurrently, each with distinct session IDs, both listed correctly byidb_list.
Suggested directions
- Don't delete the registration file on a single failed probe — require N consecutive failures (or a debounce window) before pruning, since
is_pid_alive+probe_instanceare both point-in-time checks against a possibly-momentarily-busy single-threaded server. - Alternatively/additionally, have the GUI plugin periodically refresh (touch/rewrite) its registration file so a wrongly-pruned entry gets restored on the next heartbeat instead of staying gone until the plugin is reloaded.
Source: mrexodia/ida-pro-mcp