425 lines
27 KiB
Markdown
425 lines
27 KiB
Markdown
# Ghidra MCP comparison for `ghidra-cli`
|
|
|
|
Status: reviewed and adopted into the PRD, 2026-07-28
|
|
|
|
## Scope and source snapshots
|
|
|
|
This note compares the accepted `ghidra-cli` version 0.1 design with two
|
|
primary-source repositories:
|
|
|
|
- LaurieWired/GhidraMCP at commit
|
|
[`27f316f80139e2d5dec882519a1bdf4aa46ac04c`](https://github.com/LaurieWired/GhidraMCP/tree/27f316f80139e2d5dec882519a1bdf4aa46ac04c),
|
|
tagged `1.4`.
|
|
- bethington/ghidra-mcp at commit
|
|
[`8cd2078e10b9ba28b188cb84ce5b9051a904b995`](https://github.com/bethington/ghidra-mcp/tree/8cd2078e10b9ba28b188cb84ce5b9051a904b995),
|
|
tagged `v6.0.0`.
|
|
|
|
The comparison uses repository source, manifests, CI, licenses, changelogs,
|
|
and the repositories' own issue reports. It does not treat README marketing
|
|
claims as independently verified performance evidence.
|
|
|
|
## Executive conclusion
|
|
|
|
Do not change the version 0.1 architecture or widen its command surface because
|
|
of these MCP implementations. The accepted synchronous Rust harness, private
|
|
file protocol, one Sample per command, immutable Analysis, strict JSON, and
|
|
sandboxed short-lived Ghidra worker directly avoid the largest sources of
|
|
complexity visible in both repositories.
|
|
|
|
The repositories nevertheless validate several useful mechanisms:
|
|
|
|
- Relationship queries are the most valuable next capability for an agent.
|
|
Prioritize `strings`, `xrefs`, and direct `calls` after version 0.1, followed
|
|
by targeted disassembly and detailed single-Function inspection.
|
|
- A large agent surface needs categories and capability discovery. Do not build
|
|
that for four commands, but preserve command metadata so a future MCP adapter
|
|
can expose a small, generated catalog without hand-written duplication.
|
|
- Every process/protocol boundary needs independent size validation and
|
|
parity tests. Add a small explicit ceiling for internal `request.json`, and
|
|
test that Rust operation types, Java dispatch, and published schemas contain
|
|
exactly the same operations.
|
|
- Explicit target selection is essential. Keep requiring the Sample and exact
|
|
Function Selector on every Query; never introduce a mutable “current
|
|
program” fallback.
|
|
- An eventual MCP integration should be a thin, separate adapter that invokes
|
|
`ghidr` and forwards its schemas. It should not add an embedded HTTP server,
|
|
persistent Ghidra session, or a second result contract to the core.
|
|
|
|
The principal rejection is breadth. One examined project exposes 272 catalog
|
|
entries, including mutations, arbitrary scripts, project lifecycle, emulation,
|
|
and debugging; its own users subsequently requested filtering because the
|
|
surface was too large for agents ([issue #267](https://github.com/bethington/ghidra-mcp/issues/267))
|
|
and raised broader maintainability and provenance concerns
|
|
([issue #307](https://github.com/bethington/ghidra-mcp/issues/307)). The current
|
|
`ghidra-cli` narrow vertical slice is a feature, not a gap.
|
|
|
|
## LaurieWired/GhidraMCP
|
|
|
|
### Architecture and state
|
|
|
|
The original project is a three-hop interactive topology:
|
|
|
|
```text
|
|
MCP client -> Python FastMCP bridge -> HTTP -> plugin in a running Ghidra GUI
|
|
```
|
|
|
|
The Python process speaks MCP over stdio by default and can expose SSE; every
|
|
tool then makes an HTTP request to a configured Ghidra URL
|
|
([bridge initialization and transports](https://github.com/LaurieWired/GhidraMCP/blob/27f316f80139e2d5dec882519a1bdf4aa46ac04c/bridge_mcp_ghidra.py#L15-L24),
|
|
[CLI transport selection](https://github.com/LaurieWired/GhidraMCP/blob/27f316f80139e2d5dec882519a1bdf4aa46ac04c/bridge_mcp_ghidra.py#L290-L334)).
|
|
The Java extension embeds `com.sun.net.httpserver.HttpServer` in Ghidra and
|
|
operates on the GUI's `ProgramManager.getCurrentProgram()`
|
|
([server construction](https://github.com/LaurieWired/GhidraMCP/blob/27f316f80139e2d5dec882519a1bdf4aa46ac04c/src/main/java/com/lauriewired/GhidraMCPPlugin.java#L95-L114),
|
|
[current Program resolution](https://github.com/LaurieWired/GhidraMCP/blob/27f316f80139e2d5dec882519a1bdf4aa46ac04c/src/main/java/com/lauriewired/GhidraMCPPlugin.java#L1627-L1629)).
|
|
Installation therefore requires installing and enabling a GUI extension and
|
|
opening Ghidra, in addition to Python and the MCP SDK
|
|
([installation prerequisites and workflow](https://github.com/LaurieWired/GhidraMCP/blob/27f316f80139e2d5dec882519a1bdf4aa46ac04c/README.md#L30-L79)).
|
|
|
|
This is session-oriented rather than content-oriented. The Sample is not an
|
|
argument to a tool call; the implicit target is whichever Program is current
|
|
in the GUI. Cursor-dependent tools expose the current address and Function,
|
|
further coupling automation to interactive state
|
|
([current-selection tool wrappers](https://github.com/LaurieWired/GhidraMCP/blob/27f316f80139e2d5dec882519a1bdf4aa46ac04c/bridge_mcp_ghidra.py#L150-L176)).
|
|
|
|
### Tool surface and mutation
|
|
|
|
The snapshot has 27 Python `@mcp.tool` declarations mapped to 27 Java HTTP
|
|
contexts. Read tools cover Functions, classes/namespaces, segments, imports,
|
|
exports, data, strings, disassembly, and cross-references. Write tools rename
|
|
Functions, data, and variables; change prototypes and local types; and add
|
|
comments
|
|
([Python tool declarations](https://github.com/LaurieWired/GhidraMCP/blob/27f316f80139e2d5dec882519a1bdf4aa46ac04c/bridge_mcp_ghidra.py#L60-L288),
|
|
[HTTP context registration](https://github.com/LaurieWired/GhidraMCP/blob/27f316f80139e2d5dec882519a1bdf4aa46ac04c/src/main/java/com/lauriewired/GhidraMCPPlugin.java#L109-L342)).
|
|
Writes run Ghidra transactions on the Swing event thread, so this is not a
|
|
read-only analysis layer
|
|
([rename transaction](https://github.com/LaurieWired/GhidraMCP/blob/27f316f80139e2d5dec882519a1bdf4aa46ac04c/src/main/java/com/lauriewired/GhidraMCPPlugin.java#L512-L540)).
|
|
|
|
### Result contract and bounds
|
|
|
|
The outer MCP parameter schemas are inferred from Python type annotations, but
|
|
the inner HTTP protocol is mostly newline-delimited text. `safe_get` splits
|
|
text into lines, while failures are returned as strings in the same value
|
|
channel rather than typed MCP errors
|
|
([GET behavior](https://github.com/LaurieWired/GhidraMCP/blob/27f316f80139e2d5dec882519a1bdf4aa46ac04c/bridge_mcp_ghidra.py#L26-L43),
|
|
[POST behavior](https://github.com/LaurieWired/GhidraMCP/blob/27f316f80139e2d5dec882519a1bdf4aa46ac04c/bridge_mcp_ghidra.py#L45-L58)).
|
|
The Java server always responds with HTTP 200 and `text/plain`, including
|
|
application errors
|
|
([response writer](https://github.com/LaurieWired/GhidraMCP/blob/27f316f80139e2d5dec882519a1bdf4aa46ac04c/src/main/java/com/lauriewired/GhidraMCPPlugin.java#L1632-L1638)).
|
|
|
|
Many lists have `offset` and `limit`, normally defaulting to 0 and 100, but
|
|
there is no `total`, `returned`, or `has_more` metadata. Some duplicate tools
|
|
are unpaginated, such as `list_functions`
|
|
([list wrapper](https://github.com/LaurieWired/GhidraMCP/blob/27f316f80139e2d5dec882519a1bdf4aa46ac04c/bridge_mcp_ghidra.py#L171-L176)).
|
|
Pagination accepts negative and arbitrarily large values without a public
|
|
validation contract
|
|
([pagination implementation](https://github.com/LaurieWired/GhidraMCP/blob/27f316f80139e2d5dec882519a1bdf4aa46ac04c/src/main/java/com/lauriewired/GhidraMCPPlugin.java#L1582-L1606)).
|
|
There is no whole-response byte bound or spill mechanism.
|
|
|
|
Function selection is ambiguous by construction: name selection returns the
|
|
first exact basename encountered, and address selection accepts an interior
|
|
address by resolving the containing Function
|
|
([name selection](https://github.com/LaurieWired/GhidraMCP/blob/27f316f80139e2d5dec882519a1bdf4aa46ac04c/src/main/java/com/lauriewired/GhidraMCPPlugin.java#L493-L509),
|
|
[interior-address fallback](https://github.com/LaurieWired/GhidraMCP/blob/27f316f80139e2d5dec882519a1bdf4aa46ac04c/src/main/java/com/lauriewired/GhidraMCPPlugin.java#L782-L805)).
|
|
These are precisely the ambiguities the accepted `ghidra-cli` selector
|
|
contract should continue rejecting.
|
|
|
|
The timeout layers are inconsistent. The Python bridge times every HTTP call
|
|
out after five seconds, while Java decompilation can run for 30 or 60 seconds
|
|
([bridge timeouts](https://github.com/LaurieWired/GhidraMCP/blob/27f316f80139e2d5dec882519a1bdf4aa46ac04c/bridge_mcp_ghidra.py#L35-L51),
|
|
[decompiler timeout](https://github.com/LaurieWired/GhidraMCP/blob/27f316f80139e2d5dec882519a1bdf4aa46ac04c/src/main/java/com/lauriewired/GhidraMCPPlugin.java#L1202-L1217)).
|
|
An MCP caller can therefore receive a timeout while Ghidra continues working.
|
|
This validates `ghidra-cli`'s single derived child watchdog and process-group
|
|
termination design.
|
|
|
|
### Security, tests, and dependencies
|
|
|
|
`new InetSocketAddress(port)` binds the embedded server without an explicit
|
|
loopback address, and the request path shown has neither authentication nor a
|
|
body-size cap
|
|
([server bind](https://github.com/LaurieWired/GhidraMCP/blob/27f316f80139e2d5dec882519a1bdf4aa46ac04c/src/main/java/com/lauriewired/GhidraMCPPlugin.java#L95-L108),
|
|
[unbounded form read](https://github.com/LaurieWired/GhidraMCP/blob/27f316f80139e2d5dec882519a1bdf4aa46ac04c/src/main/java/com/lauriewired/GhidraMCPPlugin.java#L1559-L1579)).
|
|
The Ghidra process and plugin are unsandboxed and inherit the GUI user's
|
|
filesystem and network access.
|
|
|
|
Runtime dependencies are Python 3.10+, `mcp`, and `requests`; the Java build
|
|
uses Maven plus manually copied Ghidra JARs
|
|
([Python requirements](https://github.com/LaurieWired/GhidraMCP/blob/27f316f80139e2d5dec882519a1bdf4aa46ac04c/bridge_mcp_ghidra.py#L1-L6),
|
|
[system-scoped Ghidra JARs](https://github.com/LaurieWired/GhidraMCP/blob/27f316f80139e2d5dec882519a1bdf4aa46ac04c/pom.xml#L12-L87)).
|
|
The only committed Java test is the Maven-template `assertTrue(true)` smoke
|
|
test, so no endpoint, schema, timeout, security, or real-Ghidra regression is
|
|
covered
|
|
([test source](https://github.com/LaurieWired/GhidraMCP/blob/27f316f80139e2d5dec882519a1bdf4aa46ac04c/src/test/java/com/lauriewired/AppTest.java#L1-L35)).
|
|
|
|
## bethington/ghidra-mcp
|
|
|
|
### Architecture and state
|
|
|
|
This repository identifies itself as a substantial derivative of the Laurie
|
|
project, not an independent implementation
|
|
([NOTICE](https://github.com/bethington/ghidra-mcp/blob/8cd2078e10b9ba28b188cb84ce5b9051a904b995/NOTICE#L1-L10)).
|
|
It retains the bridge topology but expands it:
|
|
|
|
```text
|
|
MCP client -> Python MCP bridge -> UDS or HTTP -> Ghidra GUI plugin
|
|
-> headless Java server
|
|
-> optional debugger service
|
|
```
|
|
|
|
The bridge supports stdio, Streamable HTTP, and deprecated SSE
|
|
([bridge CLI](https://github.com/bethington/ghidra-mcp/blob/8cd2078e10b9ba28b188cb84ce5b9051a904b995/python/bridge_mcp_ghidra/cli.py#L102-L143)),
|
|
while repository documentation describes separate GUI and headless Java
|
|
servers and a Python protocol-conversion process
|
|
([architecture](https://github.com/bethington/ghidra-mcp/blob/8cd2078e10b9ba28b188cb84ce5b9051a904b995/README.md#L977-L995)).
|
|
|
|
The bridge discovers server instances, remembers a connected project and
|
|
transport, dynamically registers tools, and attempts reconnection after Ghidra
|
|
restarts
|
|
([shared mutable state](https://github.com/bethington/ghidra-mcp/blob/8cd2078e10b9ba28b188cb84ce5b9051a904b995/python/bridge_mcp_ghidra/state.py#L14-L35),
|
|
[reconnection](https://github.com/bethington/ghidra-mcp/blob/8cd2078e10b9ba28b188cb84ce5b9051a904b995/python/bridge_mcp_ghidra/dispatch.py#L67-L128)).
|
|
The cost is visible in the project's own issue history. Concurrent sub-agent
|
|
calls caused intermittent bridge shutdown and forced a serialization lock
|
|
([issue #91](https://github.com/bethington/ghidra-mcp/issues/91),
|
|
[current lock](https://github.com/bethington/ghidra-mcp/blob/8cd2078e10b9ba28b188cb84ce5b9051a904b995/python/bridge_mcp_ghidra/state.py#L18-L25)).
|
|
On macOS, a GUI/agent environment mismatch in `$TMPDIR` prevented Unix-socket
|
|
discovery until the implementation learned to scan multiple candidate
|
|
locations ([issue #170](https://github.com/bethington/ghidra-mcp/issues/170),
|
|
[current discovery rationale](https://github.com/bethington/ghidra-mcp/blob/8cd2078e10b9ba28b188cb84ce5b9051a904b995/python/bridge_mcp_ghidra/discovery.py#L20-L40)).
|
|
|
|
These are real solutions for a persistent multi-instance service, but they are
|
|
states `ghidra-cli` intentionally does not have.
|
|
|
|
### Tool surface, discovery, and mutation
|
|
|
|
The committed endpoint catalog declares 272 tools across 15 categories
|
|
([catalog](https://github.com/bethington/ghidra-mcp/blob/8cd2078e10b9ba28b188cb84ce5b9051a904b995/tests/endpoints.json)).
|
|
The surface includes read-only listing, Functions, cross-references, call
|
|
graphs, strings, data flow, and memory inspection, but also symbol/type/comment
|
|
mutation, project deletion and version control, arbitrary Java script
|
|
execution, P-code emulation, and live debugging
|
|
([feature inventory](https://github.com/bethington/ghidra-mcp/blob/8cd2078e10b9ba28b188cb84ce5b9051a904b995/README.md#L69-L95),
|
|
[API categories](https://github.com/bethington/ghidra-mcp/blob/8cd2078e10b9ba28b188cb84ce5b9051a904b995/README.md#L624-L740)).
|
|
|
|
Its most reusable mechanism is annotation-driven registration. Java service
|
|
methods carry tool/parameter annotations; one scanner creates deterministic
|
|
HTTP endpoint definitions and a machine-readable input schema
|
|
([scanner contract and sorting](https://github.com/bethington/ghidra-mcp/blob/8cd2078e10b9ba28b188cb84ce5b9051a904b995/src/main/java/com/xebyte/core/AnnotationScanner.java#L13-L87),
|
|
[descriptor construction](https://github.com/bethington/ghidra-mcp/blob/8cd2078e10b9ba28b188cb84ce5b9051a904b995/src/main/java/com/xebyte/core/AnnotationScanner.java#L94-L118)).
|
|
The Python bridge fetches that schema and generates callable MCP signatures at
|
|
runtime
|
|
([dynamic handler construction](https://github.com/bethington/ghidra-mcp/blob/8cd2078e10b9ba28b188cb84ce5b9051a904b995/python/bridge_mcp_ghidra/registry.py#L22-L146),
|
|
[schema fetch](https://github.com/bethington/ghidra-mcp/blob/8cd2078e10b9ba28b188cb84ce5b9051a904b995/python/bridge_mcp_ghidra/registry.py#L302-L318)).
|
|
|
|
The current bridge can load categories lazily and exposes `search_tools`,
|
|
`list_tool_groups`, and load/unload/check helpers
|
|
([agent discovery workflow](https://github.com/bethington/ghidra-mcp/blob/8cd2078e10b9ba28b188cb84ce5b9051a904b995/README.md#L377-L393)).
|
|
This directly addresses tool overload, but it also introduces another mutable
|
|
session dimension and depends on clients honoring `tools/list_changed`.
|
|
|
|
### Result contract and bounds
|
|
|
|
The Java response type is an improvement over the original but remains a
|
|
union of structured success, simple `{ "error": "message" }`, and raw text
|
|
passthrough
|
|
([sealed `Response`](https://github.com/bethington/ghidra-mcp/blob/8cd2078e10b9ba28b188cb84ce5b9051a904b995/src/main/java/com/xebyte/core/Response.java#L3-L43)).
|
|
Consequently, some endpoints return JSON while many paginated endpoints still
|
|
return newline-delimited text, and the dynamically generated MCP handlers all
|
|
declare `str` results
|
|
([text pagination](https://github.com/bethington/ghidra-mcp/blob/8cd2078e10b9ba28b188cb84ce5b9051a904b995/src/main/java/com/xebyte/core/ServiceUtils.java#L232-L244),
|
|
[generated return annotation](https://github.com/bethington/ghidra-mcp/blob/8cd2078e10b9ba28b188cb84ce5b9051a904b995/python/bridge_mcp_ghidra/registry.py#L111-L145)).
|
|
The live tool schema describes inputs, not stable versioned output schemas or
|
|
typed error codes
|
|
([schema translation](https://github.com/bethington/ghidra-mcp/blob/8cd2078e10b9ba28b188cb84ce5b9051a904b995/python/bridge_mcp_ghidra/schema.py#L54-L99)).
|
|
|
|
Request hardening is much stronger: version 6.0 applies a 64 MiB body ceiling
|
|
and bounds actual reads rather than trusting `Content-Length`
|
|
([request ceiling](https://github.com/bethington/ghidra-mcp/blob/8cd2078e10b9ba28b188cb84ce5b9051a904b995/src/main/java/com/xebyte/core/SecurityConfig.java#L51-L75),
|
|
[HTTP enforcement](https://github.com/bethington/ghidra-mcp/blob/8cd2078e10b9ba28b188cb84ce5b9051a904b995/src/main/java/com/xebyte/GhidraMCPPlugin.java#L2253-L2263)).
|
|
Response bounding remains endpoint-specific rather than a universal contract.
|
|
For example, the whole-program call graph accepts `limit=0` as unlimited and
|
|
can emit text, DOT, Mermaid, adjacency, or address-edge JSON
|
|
([call-graph parameters and implementation](https://github.com/bethington/ghidra-mcp/blob/8cd2078e10b9ba28b188cb84ce5b9051a904b995/src/main/java/com/xebyte/core/XrefCallGraphService.java#L782-L877)).
|
|
`ghidra-cli`'s one schema, disclosed collection bounds, and universal serialized
|
|
byte cap are materially stronger for an automation agent.
|
|
|
|
The bridge coordinates endpoint-specific HTTP timeouts and retries GET calls;
|
|
write calls deliberately avoid blind retry because completion is ambiguous
|
|
after a connection loss
|
|
([timeout catalog](https://github.com/bethington/ghidra-mcp/blob/8cd2078e10b9ba28b188cb84ce5b9051a904b995/python/bridge_mcp_ghidra/config.py#L7-L35),
|
|
[retry policies](https://github.com/bethington/ghidra-mcp/blob/8cd2078e10b9ba28b188cb84ce5b9051a904b995/python/bridge_mcp_ghidra/dispatch.py#L131-L195)).
|
|
Notably, the bridge's `decompile_function` timeout is 45 seconds while the
|
|
Java endpoint defaults to 60 seconds
|
|
([bridge value](https://github.com/bethington/ghidra-mcp/blob/8cd2078e10b9ba28b188cb84ce5b9051a904b995/python/bridge_mcp_ghidra/config.py#L21-L34),
|
|
[Java value](https://github.com/bethington/ghidra-mcp/blob/8cd2078e10b9ba28b188cb84ce5b9051a904b995/src/main/java/com/xebyte/core/FunctionService.java#L149-L186)).
|
|
This is further evidence that one owning harness should derive and enforce the
|
|
complete child lifetime.
|
|
|
|
The project learned an important target-selection lesson. Calls default to a
|
|
mutable active Program; strict explicit Program selectors exist only behind an
|
|
environment variable because compatibility retained the fallback
|
|
([strict-mode rationale](https://github.com/bethington/ghidra-mcp/blob/8cd2078e10b9ba28b188cb84ce5b9051a904b995/README.md#L350-L375),
|
|
[bridge enforcement](https://github.com/bethington/ghidra-mcp/blob/8cd2078e10b9ba28b188cb84ce5b9051a904b995/python/bridge_mcp_ghidra/registry.py#L26-L87)).
|
|
`ghidra-cli` should keep Sample selection mandatory from its first release and
|
|
never acquire this compatibility burden.
|
|
|
|
### Security, tests, and dependencies
|
|
|
|
Current security controls include loopback defaults, refusal of a non-loopback
|
|
bind without bearer authentication, anti-CSRF/DNS-rebinding checks, script
|
|
execution disabled by default, filesystem-root containment, request-body
|
|
caps, and generic uncaught-error responses
|
|
([security configuration](https://github.com/bethington/ghidra-mcp/blob/8cd2078e10b9ba28b188cb84ce5b9051a904b995/README.md#L440-L493),
|
|
[safe handler](https://github.com/bethington/ghidra-mcp/blob/8cd2078e10b9ba28b188cb84ce5b9051a904b995/src/main/java/com/xebyte/GhidraMCPPlugin.java#L2210-L2279)).
|
|
The change history states that these were hardening changes after earlier
|
|
unauthenticated, ungated, or insufficiently bounded behavior
|
|
([v6.0.0 security history](https://github.com/bethington/ghidra-mcp/blob/8cd2078e10b9ba28b188cb84ce5b9051a904b995/CHANGELOG.md#L7-L73)).
|
|
|
|
These controls are valuable for a network service, but they do not isolate
|
|
Ghidra from the user's account. `ghidra-cli` should retain the stronger design
|
|
of no listener plus a capability-limited worker sandbox. If it ever adds an
|
|
MCP adapter, stdio should remain the default and network transport should be a
|
|
separately threat-modeled feature.
|
|
|
|
The shipped bridge requires Python 3.10+ and the `mcp` package; tests and
|
|
unshipped subsystems add many optional dependencies
|
|
([package manifest](https://github.com/bethington/ghidra-mcp/blob/8cd2078e10b9ba28b188cb84ce5b9051a904b995/pyproject.toml#L1-L24),
|
|
[dependency groups](https://github.com/bethington/ghidra-mcp/blob/8cd2078e10b9ba28b188cb84ce5b9051a904b995/pyproject.toml#L53-L101)).
|
|
The repository has a materially stronger regression practice than the original:
|
|
Java offline tests and coverage, a Python version matrix, Windows-specific
|
|
tests, performance regressions, and Pester setup tests are gating; formatting
|
|
and lint jobs are currently advisory
|
|
([CI gates](https://github.com/bethington/ghidra-mcp/blob/8cd2078e10b9ba28b188cb84ce5b9051a904b995/.github/workflows/tests.yml#L16-L181),
|
|
[quality jobs](https://github.com/bethington/ghidra-mcp/blob/8cd2078e10b9ba28b188cb84ce5b9051a904b995/.github/workflows/tests.yml#L288-L358)).
|
|
Its annotation/schema/catalog parity approach is worth borrowing; its broad
|
|
multi-subsystem matrix is not needed for version 0.1.
|
|
|
|
## Recommendations for `ghidra-cli`
|
|
|
|
### Adopt now
|
|
|
|
1. **Keep one source of truth for operations.** Define every internal operation
|
|
once with its request type, response type, public command kind, and schema
|
|
name. Generate or mechanically verify the Rust schema set and Java dispatch
|
|
set against it. The bethington annotation scanner demonstrates the value of
|
|
preventing hand-maintained endpoint/schema drift, while `ghidra-cli` can do
|
|
this without runtime reflection or dynamic registration.
|
|
|
|
2. **Cap both sides of the private protocol.** The accepted 256 MiB response
|
|
ceiling is good. Add a much smaller fixed serialized `request.json` ceiling,
|
|
suggested at 1 MiB, enforced by Rust before launch and Java before parse.
|
|
Requests contain metadata and tool-owned paths, not Sample bytes, so a
|
|
larger allowance has no known use. This is a protocol-hardening detail, not
|
|
a public CLI flag.
|
|
|
|
3. **Add protocol/catalog parity regressions.** A fixture should fail if a Rust
|
|
operation lacks Java handling, if Java accepts an unpublished operation, or
|
|
if a public schema/golden is missing. Keep the already accepted real-Ghidra
|
|
ELF and PE integration tests and strict lint/format gates.
|
|
|
|
4. **Retain mandatory targets and stable identifiers.** Every Query keeps an
|
|
explicit Sample; Function operations keep exact entry Address or exact name
|
|
selection with ambiguity errors. Do not add GUI cursor, active Program, or
|
|
implicit containing-Function behavior.
|
|
|
|
5. **Preserve extension metadata without exposing it yet.** Command/domain
|
|
definitions may carry a stable category and one-sentence agent description.
|
|
This costs little and lets a later MCP adapter generate a discoverable
|
|
surface, but version 0.1 needs no `search-tools`, runtime group loading, or
|
|
config file.
|
|
|
|
### Defer in priority order
|
|
|
|
1. **`strings` Query.** Return a bounded, stably ordered list with exact Address,
|
|
byte length, decoded representation with explicit encoding, and referring
|
|
Addresses or a separate bounded reference link. Avoid case-folded server-side
|
|
substring filters initially; agents can use `jq`, and raw Sample-derived
|
|
strings may be non-UTF-8 or extremely large.
|
|
|
|
2. **`xrefs` Query.** Model each Reference structurally: source Address,
|
|
destination Address or external target, Ghidra reference type, operand index,
|
|
and containing source Function entry when present. Direction should be an
|
|
explicit enum. Never emit prose such as `From ... in ... [READ]`.
|
|
|
|
3. **Direct `calls` Query.** Start with flat Call Edges containing caller entry,
|
|
callsite, and resolved or unresolved callee. Support pagination and stable
|
|
ordering. Do not begin with recursive whole-program graphs, DOT/Mermaid
|
|
renderers, graph algorithms, or `limit=0` unlimited output; agents can build
|
|
graphs from bounded edge pages.
|
|
|
|
4. **Targeted `disassemble` and `function` detail Queries.** These are more
|
|
useful than widening every `functions` item. A detailed Function result can
|
|
later carry signature, parameters, locals, body ranges, thunk resolution,
|
|
and alternate symbols without making discovery pages expensive.
|
|
|
|
5. **Batch Query execution.** Multiple decompilations in one Ghidra process may
|
|
eventually reduce startup cost, but only add it after measurements. It needs
|
|
explicit per-item success/error objects, deterministic order, aggregate
|
|
byte bounds, cancellation semantics, and no hidden parallelism.
|
|
|
|
6. **Thin MCP adapter.** After schemas and CLI behavior stabilize, a separate
|
|
package can expose one MCP tool per stable `ghidr` Query, invoke the CLI over
|
|
stdio/process execution, and forward its JSON unchanged. If the surface later
|
|
grows, generated categories and a capability-search tool become appropriate.
|
|
The adapter should not call Ghidra directly or own Analysis Store policy.
|
|
|
|
7. **Interactive/mutating analysis.** Renames, types, comments, scripts,
|
|
debugging, and shared-project workflows solve a different problem. If ever
|
|
pursued, put them behind a separate explicit write-capability model and a
|
|
separate domain contract; do not weaken immutable Analysis semantics.
|
|
|
|
### Explicitly reject for version 0.1
|
|
|
|
- Embedded HTTP, TCP, UDS, SSE, Streamable HTTP, discovery scans, reconnect
|
|
logic, persistent daemons, and background JVMs.
|
|
- Mutable current-Program/current-Address/current-Function session state.
|
|
- Any automatic first match for duplicate Function names or interior addresses.
|
|
- Raw text success/error unions, HTTP-status tunneling, untyped addresses, and
|
|
prose records that agents must parse.
|
|
- Hidden retry escalation. A caller may explicitly retry a failed read-only
|
|
Query; `ghidr` must not multiply time budgets behind the contract.
|
|
- Arbitrary Java/Python scripts, mutation, project management, debugger control,
|
|
emulation, binary export, and Ghidra Server administration.
|
|
- Whole-program graph renderers and custom search/query languages. Emit bounded
|
|
facts; let `jq` and downstream tools transform them.
|
|
- Runtime tool-group loading for the initial four-command surface.
|
|
- A database or global mutable index merely to support discovery or MCP state.
|
|
|
|
## Licensing and provenance
|
|
|
|
Both repositories use Apache-2.0
|
|
([LaurieWired license](https://github.com/LaurieWired/GhidraMCP/blob/27f316f80139e2d5dec882519a1bdf4aa46ac04c/LICENSE),
|
|
[bethington package license](https://github.com/bethington/ghidra-mcp/blob/8cd2078e10b9ba28b188cb84ce5b9051a904b995/pyproject.toml#L1-L9)).
|
|
The bethington repository explicitly retains attribution to LaurieWired for
|
|
portions that remain
|
|
([NOTICE](https://github.com/bethington/ghidra-mcp/blob/8cd2078e10b9ba28b188cb84ce5b9051a904b995/NOTICE#L1-L10)).
|
|
|
|
Concepts, public Ghidra API usage patterns, and independently designed schemas
|
|
may be studied without making `ghidra-cli` a derivative. If any source or close
|
|
adaptation is copied, Apache-2.0 requires preserving applicable copyright,
|
|
license, change, and NOTICE obligations. The cleanest course is to keep the
|
|
implementation independent, cite these projects as prior art in research and
|
|
design notes, and record provenance for any deliberately reused code before it
|
|
enters a commit.
|
|
|
|
The open maintainability issue also alleges that some contributor changes were
|
|
recommitted in ways that obscured authorship. This report does not adjudicate
|
|
that allegation, but it reinforces the accepted `ghidra-cli` rule: AI assistance
|
|
does not replace review, attribution, or a traceable change history
|
|
([issue #307](https://github.com/bethington/ghidra-mcp/issues/307)).
|
|
|
|
## Decision impact
|
|
|
|
No accepted version 0.1 product decision should be removed. The research adds
|
|
three concrete implementation-time checks and a clearer post-0.1 order:
|
|
|
|
- internal request-size ceiling;
|
|
- operation/Java/schema parity test;
|
|
- relationship-query order: `strings`, `xrefs`, direct `calls`, then targeted
|
|
disassembly and Function detail.
|
|
|
|
Everything else belongs either in a future thin MCP adapter or in a separately
|
|
designed interactive/mutating product.
|
|
|
|
The PRD now records the request ceiling, operation/dispatch/schema parity,
|
|
no-default-egress regression coverage, ordered relationship Queries, bounded
|
|
batch prerequisites, and thin-adapter MCP boundary. These adoptions do not add
|
|
a public version 0.1 command or widen the Java adapter's authority.
|