ghidriff-mcp
This server lets an AI agent run and inspect Ghidra-based binary diffing jobs through MCP tools, without needing a GUI or blocking on long analyses.
Start background or blocking diffs between an old binary and one or more newer binaries using ghidriff engines (VersionTrackingDiff, SimpleDiff, StructualGraphDiff).
Configure diff options per job: chained diffs, summary old-vs-newest, symbol lookup on/off, forced analysis/diff, base address, minimum function length, BSIM, side-by-side HTML, output/project directories, and timeouts.
Monitor and manage jobs: check status, read/tail logs, cancel running jobs, and list persisted job history across restarts.
Retrieve structured results: match statistics, added/deleted/modified function names and counts, metadata differences, and artefact paths.
Inspect diffs in detail: page through the generated Markdown report, view one changed function's metadata/code/code diff, and search changed functions by name.
Explore diff outputs on disk, either from run history or arbitrary directories.
Verify the toolchain: check Ghidra, Java, and ghidriff availability, and see effective server configuration/settings.
Operate safely by design: no shell interpolation, agent-controlled targets only, operator-controlled engine arguments, and structured JSON responses instead of console noise.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@ghidriff-mcp diff the old and new versions of the binary and summarize changes"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
ghidriff-mcp
An MCP server that exposes ghidriff — the Ghidra binary diffing engine — as tools an AI agent can call.
Point it at an old and a new build of a binary and it drives Ghidra headlessly through ghidriff, then hands back structured results: match statistics, added/deleted/modified functions, per-function code diffs and the generated Markdown report — as JSON, not as a screenful of console noise.
English | 简体中文
Contents
Related MCP server: GhidraMCP
Why this exists
ghidriff is excellent but it is a batch CLI: you invoke it, wait while
Ghidra imports and analyses every input, and you get a Markdown report plus a
large pdiff JSON on disk. That shape does not fit an agent loop, where you want
to inspect a change, follow up on one function, and keep the reasoning in
context.
This server closes that gap:
Jobs instead of blocking calls. Every diff runs in the background with an id, a log file and a persisted
job.json, so a crash or restart does not lose the result.Structured output. The
pdiffJSON ghidriff writes is the source of truth; the server turns it into compact summaries and per-function details instead of dumping megabytes into the model's context.Late-bound environment. The server does not need ghidriff or Ghidra in its own interpreter — it launches whichever interpreter or command you configure.
Honest errors. Ghidra's failure modes are translated into actionable messages: missing
GHIDRA_INSTALL_DIR, a project path Ghidra refuses, a JVM out of memory, a missingpyghidra.A boundary you control. The agent decides what to diff; you decide how the engine runs. See Security and trust model.
Requirements
Component | Version | Notes |
Python | 3.10+ | for the MCP server itself |
1.x |
| |
11.x / 12.x | set | |
JDK | 21+ | Ghidra 12 requires a modern JDK |
Ghidra and ghidriff do not have to live in the same interpreter as this
server. If your MCP client runs the server from an isolated virtualenv, point
GHIDRIFF_MCP_PYTHON at the interpreter that has ghidriff installed.
Tested against ghidriff 1.0.0, Ghidra 12.1.3 and JDK 21 on Windows, and in CI on Ubuntu, macOS and Windows with Python 3.10 and 3.13.
Install
pip install ghidriff-mcpOr from source:
git clone https://github.com/waterofriver/ghidriff-mcp
cd ghidriff-mcp
pip install -e .Verify the tool chain with the built-in self check:
ghidriff-mcp --checkIt prints a JSON diagnosis of Ghidra, Java and ghidriff, with next steps for anything missing, and exits non-zero when the setup is not usable yet:
// ghidriff-mcp --check (trimmed for length)
{
"ready": true,
"checks": {
"ghidra": { "ok": true, "detail": "Ghidra 12.1.3 at C:\\ghidra\\ghidra_12.1.3_PUBLIC (found via GHIDRA_INSTALL_DIR)" },
"java": { "ok": true, "detail": "Java 21.0.12.1 (C:\\Program Files\\Microsoft\\jdk-21...\\bin\\java.exe)" },
"ghidriff": { "ok": true, "detail": "ghidriff 1.0.0 importable by C:\\Python313\\python.exe" }
},
"problems": [],
"next_steps": []
}The same report is available to agents through the ghidriff_environment tool.
Configuration
Everything is environment driven.
Variable | Default | Purpose |
|
| Root for run directories, Ghidra projects and diff output. |
| auto-discovery | Path to the extracted Ghidra folder. |
| the server's own interpreter | Interpreter that has ghidriff installed. |
| — | Explicit ghidriff command line, e.g. |
|
| Default per-run timeout in seconds. |
|
| Maximum concurrent diffs (each one boots a JVM). |
| — | Raw ghidriff arguments appended to every run. Operator-only: this is where |
|
| ghidriff log level ( |
Ghidra path rule. Ghidra refuses a project location containing a path element that starts with
.(Path element starting with '.' is not permitted). KeepGHIDRIFF_MCP_HOMEdot-free; if it is not, the server relocates the Ghidra project to the system temp directory and reports a warning instead of failing after a multi-minute import.
MCP client configuration
{
"mcpServers": {
"ghidriff": {
"command": "C:\\path\\to\\venv\\Scripts\\python.exe",
"args": ["-m", "ghidriff_mcp"],
"env": {
"GHIDRA_INSTALL_DIR": "C:\\ghidra\\ghidra_12.1.3_PUBLIC",
"GHIDRIFF_MCP_PYTHON": "C:\\Python313\\python.exe",
"GHIDRIFF_MCP_HOME": "C:\\ghidriff-work"
}
}
}
}HTTP transports are available for clients that prefer them:
ghidriff-mcp --transport streamable-http --host 127.0.0.1 --port 8765Command-line flags: --check (self check), --workspace PATH and
--ghidra-install-dir PATH (override the matching environment variables for this
process), --transport, --host, --port, --version.
Tools
Thirteen tools, all prefixed ghidriff_ so an agent can tell them apart from
other Ghidra-related servers.
Tool | What it does |
| Verifies Ghidra, Java and ghidriff, and explains how to fix anything missing. |
| Starts a diff in the background, returns a |
| Blocking convenience wrapper around the above. |
| Status, timing and log tail of one job. |
| Match statistics, function counts, names, artefacts. |
| The raw ghidriff log, tailed or paged. |
| Kills a running diff. |
| Job history (persisted in |
| Diff artefacts on disk, workspace-wide or in one directory. |
| Page through the generated Markdown report. |
| One changed function: metadata, code, and the code diff. |
| Search changed functions by name. |
| Effective configuration and environment variables. |
ghidriff_start_diff accepts old_binary, one or more new_binaries, the
engine (VersionTrackingDiff, SimpleDiff, StructualGraphDiff), output_dir,
project_dir, summary, side_by_side, force_analysis, force_diff, bsim,
bsim_full, min_func_len, max_section_funcs, base_address, no_symbols
and timeout_s. Raw engine pass-through is deliberately not on this list —
that lives in GHIDRIFF_MCP_EXTRA_ARGS.
Pass several newer binaries to get chained diffs (old → v2 → v3), and set
summary=true to also diff old → newest. Use side_by_side=true if you want
the per-function HTML diffs on disk.
How it works
MCP client ──stdio──▶ ghidriff-mcp ──subprocess──▶ ghidriff ──PyGhidra──▶ Ghidra (JVM)
│ │
│ └── writes <name>.ghidriff.md
│ json/<name>.ghidriff.json
└── job.json + ghidriff.log json/<name>.ghidriff.matches.json
(structured summaries, paged reads)The server never imports ghidriff, so a JVM crash cannot take it down, and cancelling a job is a single
kill.One run directory per job:
<workspace>/runs/<job_id>/containingjob.json,ghidriff.log,ghidriff/(output) andghidra_projects/.Child output is redirected into the log file through an inherited handle rather than a pipe, which keeps it streamable and works in environments where pipes are restricted.
pdiffJSON files are parsed lazily, capped in size and cached by mtime.Relative paths in tool calls resolve against the workspace, so an agent never has to know the absolute layout of the machine.
Security and trust model
This server drives ghidriff locally with your own privileges on files you point it at. Before wiring it to an agent, know what it does and does not constrain:
No shell. Every command is an argv list built by the server and passed to
exec-style process creation. Nothing is interpolated into a shell string, so file names and option values cannot become shell syntax.The agent picks the target, you pick the engine. Tool arguments select which binaries to diff, which supported engine to use, and how much output to render. They cannot select the executable, its JVM arguments, or arbitrary extra CLI switches. That matters, because
--jvm-args -javaagent:...would otherwise be a code-execution primitive one model decision away.Option-like file names are neutralised. Binaries are passed after a
--separator, so a file named--force-analysisstays a file.Binaries are untrusted input. Strings, symbol names, decompiled code and report text from a sample end up in the model's context. Treat all of it as data, never as instructions: a crafted binary can try to talk your agent into something. The same holds for anything else you feed the model.
Symbol lookups reach the network by default. ghidriff lets Ghidra resolve PDBs, and for PE files with PDB metadata that can mean a query to a symbol server (Microsoft's among them), which leaks a little about what you are analysing. Pass
no_symbols=true, or setGHIDRIFF_MCP_EXTRA_ARGS=--no-symbols, for offline work.File access is not confined. The server reads and writes wherever you point it, because binaries normally live outside the workspace. Use a dedicated workspace and do not hand an agent paths to files you would not want in a transcript.
Transports. stdio by default. The HTTP transports bind to
127.0.0.1unless you pass--host, and they carry no authentication — treat a non-loopback bind as exposing the server to that network.External tooling is the real attack surface. Ghidra parsing a hostile file is a much bigger risk than this wrapper. Keep ghidriff and Ghidra current, and run samples in a VM you can throw away.
Everything a run produces lives under GHIDRIFF_MCP_HOME, so that directory is
the thing to review — and delete — when you are done.
Troubleshooting
Symptom | Cause and fix |
| Ghidra rejected the project path. Set |
| Set it to the extracted Ghidra folder (the one containing |
| The launched interpreter lacks ghidriff: set |
| Install ghidriff with its dependencies ( |
First diff takes minutes | Expected: Ghidra imports and analyses every binary once. Later diffs reuse the Ghidra project unless |
| You passed |
| Lower |
Job stuck in | Another job holds a slot; |
| The output directory was overridden, e.g. by |
| The pdiff is too large to parse safely. Page through the Markdown report with |
| It starts the configured interpreter next to a possibly running JVM; the probe has a 120s budget and reports a timeout rather than hanging. |
Compared with GhidraMCP
GhidraMCP bridges a running Ghidra
GUI to an agent: interactive decompilation, renaming, commenting. This server
does the opposite job: batch, headless diffing between two builds, with no
GUI and no open project. They complement each other, and the ghidriff_ prefix
means an agent can use both at once.
Project layout
src/ghidriff_mcp/
server.py FastMCP tool surface and CLI entry point
runner.py ghidriff command construction and subprocess execution
jobs.py background job state machine, job.json persistence
reports.py pdiff / Markdown parsing, paging, size limits
ghidra_env.py Ghidra, Java and ghidriff discovery and diagnosis
paths.py workspace path resolution and Ghidra path rules
config.py environment-driven settings
tests/ unit, protocol-level and opt-in integration testsDevelopment
pip install -e ".[dev]"
pytest # 110 tests, no Ghidra needed
ruff check src tests
ruff format --check src testsThe Ghidra-dependent test is opt-in:
GHIDRIFF_MCP_INTEGRATION=1 GHIDRA_INSTALL_DIR=/path/to/ghidra pytest -m integrationIt generates a pair of PE files (a pristine copy of a system binary and one whose entry point is patched) and asserts that at least one function is reported as modified.
License
MIT — see LICENSE.
This project contains no ghidriff source code and does not link against it. It
invokes ghidriff (GPL-3.0) and Ghidra (Apache-2.0) as separate processes that
you install yourself; those programs keep their own licenses.
Available Tools
13 toolsghidriff_environmentA
Check that Ghidra, Java and ghidriff are installed and reachable.
Args: probe: also launch the ghidriff interpreter to confirm it can import ghidriff (adds up to a minute on a cold start).
Inputs: probe. Returns: object
ghidriff_function_detailA
Explain one changed function: metadata, code, and the code diff if modified.
Inputs: name (required), job_id, json_path. Returns: object
ghidriff_job_cancelA
Cancel a running diff job and kill its ghidriff process.
Inputs: job_id (required). Returns: object
ghidriff_job_logA
Read the ghidriff console log of a job, tailed or paged.
Inputs: job_id (required), max_lines, start_line, tail_lines. Returns: object
ghidriff_job_resultA
Return the structured result of a finished (or running) diff job.
Inputs: job_id (required), max_functions, include_metadata_diff. Returns: object
ghidriff_jobsB
List diff jobs known to this server, newest first (survives restarts).
Inputs: limit, status. Returns: object
ghidriff_job_statusA
Report the state of one diff job, including a short log tail.
Inputs: job_id (required), log_tail. Returns: object
ghidriff_list_runsB
List diff outputs on disk, either from the run history or a given directory.
Inputs: limit, directory. Returns: object
ghidriff_read_reportC
Read a page of the generated Markdown diff report.
Inputs: path, job_id, max_lines, start_line. Returns: object
ghidriff_run_diffA
Run a diff and wait for it to finish (blocking convenience wrapper).
Prefer ghidriff_start_diff for large binaries: this call occupies the tool call for the whole analysis.
Inputs: old_binary (required), new_binaries (required), engine, summary, +5 more. Returns: object
ghidriff_search_functionsC
Search the changed functions of a diff by name.
Inputs: query (required), kinds, limit, job_id, +1 more. Returns: object
ghidriff_settingsA
Show the server configuration (workspace, interpreter, limits).
Inputs: no named parameters. Returns: object
ghidriff_start_diffA
Start a binary diff in the background and return a job id immediately.
Raw engine pass-through arguments are deliberately not accepted here: the agent decides what to diff, the operator decides how the engine runs, through GHIDRIFF_MCP_EXTRA_ARGS.
Inputs: old_binary (required), new_binaries (required), bsim, engine, +12 more. Returns: object
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 13 tools
Most tools have clearly distinct purposes: running diffs, managing jobs, and reading reports are separated well. However, ghidriff_start_diff vs ghidriff_run_diff and ghidriff_list_runs vs ghidriff_jobs could cause minor selection hesitation without careful reading.
All tool names share the ghidriff_ prefix and use consistent snake_case, which makes the set feel cohesive. The pattern is mostly verb_noun, but ghidriff_settings, ghidriff_environment, and ghidriff_jobs are noun-only names, so it is not perfectly uniform.
13 tools is well within the ideal range for this domain. The count covers environment checks, diff execution, job lifecycle management, and report exploration without feeling bloated.
The tool surface covers the full workflow: verifying prerequisites, configuring via settings, starting diffs asynchronously or synchronously, monitoring and canceling jobs, and inspecting generated reports and changed functions. There are no obvious dead ends or missing operations for the server's stated purpose.
Maintenance
Related MCP Connectors
Risk-scan a diff, flag AI-generated-code tells, find secrets. 5 of 7 tools need no account.
Ask a codebase what calls what: search, blast radius, paths between symbols, and diffs.
Code intelligence platform for AI agents. 20 tools for architecture, security & impact analysis.
Hunt zero-days by talking to binaries. 40+ tools. Hosted, OAuth + SSO, invite: hi@byteray.ai
Related MCP Servers
- FlicenseAqualityDmaintenanceEnables LLMs to perform binary analysis using Ghidra in headless mode, extracting functions, pseudocode, structs, and enums from binaries for interactive reverse-engineering.95-
- AlicenseNot gradedqualityDmaintenanceEnables LLMs to autonomously reverse engineer binaries using Ghidra's capabilities including decompilation, function analysis, automatic renaming, and BSim integration for function similarity matching.1AGPL 3.0
- AlicenseNot gradedqualityDmaintenanceExposes Ghidra headless binary analysis capabilities via the Model Context Protocol, enabling AI assistants to decompile, analyze, and emulate binaries with 32 tools and multi-binary support.2MIT
- AlicenseNot gradedqualityCmaintenanceEnables reverse-engineering agents to export binaries to BinExport format and compare two builds, returning matched, changed, and build-unique functions with addresses, names, similarity, and confidence scores. Diff results are written as JSON files with overall similarity and confidence summaries.56 PyPI1MIT