When port 8192 was already in use by a non-MCGhidra container (e.g.,
LTspice), docker_start would fail instead of trying the next port.
Now loops through the pool, checking each candidate against Docker's
published ports before using it.
Also includes Docker build retry improvements from earlier session.
- Change from 'except Exception' to bare 'except' to catch Java
exceptions from Ghidra that don't inherit from Python Exception
- Use sys.exc_info() to safely extract error messages when str(e)
might fail on certain Java exception types
- Add null checks after getAddress() since it can return None
instead of throwing for invalid addresses
- Add last-resort response handling to prevent silent connection
drops when exception handling itself fails
These endpoints now return proper JSON error responses instead of
causing "Empty reply from server" errors.
return_all=True on large binaries (1800+ functions) produced 72K char
responses that exceeded the MCP tool result limit. Instead of truncating,
oversized responses now return a structured summary with sample data,
available fields, and actionable instructions for narrowing the query.
Three layers of filtering:
- Server-side grep: Jython HTTP handlers filter during Ghidra iteration
- Field projection: jq-style key selection strips unneeded fields
- Token budget guard: responses exceeding 8k tokens return a summary
New files: core/filtering.py (project_fields, apply_grep, estimate_and_guard)
Modified: config, pagination, base mixin, all 5 domain mixins, headless server
Rewrite GhydraMCPServer.py from 348 to 2138 lines, implementing all 45
routes that the MCP client expects. Previously, most endpoints returned
{"error": "Not found"}, breaking tools like data_list, xrefs_list, and
memory_read.
Key changes:
- Regex-based routing table with method-aware dispatch
- Thread-safe Ghidra transactions via threading.Lock()
- Full read endpoints: functions, data, strings, memory, xrefs, structs
- Full write endpoints: rename, comment, signature, create function/data
- Analysis endpoints: callgraph traversal, dataflow, run analysis
- Jython/Python 2 compatible (no f-strings, type hints, or walrus ops)
Tested with Docker build and curl against all major endpoint groups.
MCP client integration verified working.
- Switch from Java to Python scripts (avoids OSGi bundle issues)
- Update pyproject.toml with proper src layout and ruff config
- Add binaries/ and ghidra-src/ to gitignore
- Clean up Module.manifest
- Add GhydraMCPServer.py with fixed strings endpoint (Jython compatible)
- Fix strings endpoint to iterate through defined data instead of using
DefinedDataIterator.definedStrings() which isn't accessible in Jython
- Add entrypoint.sh for Docker container initialization
Separate Maven dependency resolution from compilation:
- COPY pom.xml first, run dependency:resolve (cached layer)
- COPY src second (only this invalidates on code changes)
- Build step reuses cached dependencies
Result: Code changes rebuild in ~30s instead of 3-5 min
(Ghidra download and Maven deps stay cached)
GhydraMCPServer.java imports Gson but headless scripts run in a
separate OSGi classloader that can't access extension lib JARs.
Fix: Download gson-2.13.1.jar to Framework/Generic/lib/ where it's
available to all scripts regardless of execution mode.
Closes issue documented in BUG_REPORT_HEADLESS_GSON.md