The full upstream README, mirrored here for reference. Install config, tool schemas, adoption signals, and an original overview live on the Ghidra Retro MCP listing page.
A unified MCP (Model Context Protocol) server bridging Ghidra's headless static analysis with BizHawk's live emulation — switch between decompiling a ROM and running it on real hardware in the same session.
GBA ROMs: If analyzing Game Boy Advance ROMs, install pudii/gba-ghidra-loader in your Ghidra installation for proper ROM header parsing, mirrored memory regions, and I/O register maps. The loader repository has pre-built
.gpafiles for Ghidra 11.x.
| Dependency | Version | Required | Notes |
|---|---|---|---|
| Python | >= 3.10 | Yes | Runtime for the MCP server |
| Ghidra | 11.x or 12.x | Yes | Headless or GUI install; GHIDRA_INSTALL_DIR must point here |
| Java (JDK) | >= 17 | Yes | Bundled with Ghidra; needed for JVM bridge |
| pyghidra | >= 3.0 | Yes | Python-to-Ghidra bridge; installed automatically |
| BizHawk (EmuHawk) | Latest stable | No | Only needed for live emulation tools; BIZHAWK_EXE_PATH optional |
| Docker | Latest | No | Only needed for containerized deployment |
The MCP server communicates with the MCP client exclusively over stdin/stdout — no HTTP or network listener. The only local TCP socket is a loopback-only connection (127.0.0.1:8766) between the server and BizHawk's built-in Lua socket server. This is used solely for live-emulation features and is not exposed to the network.
ghidra-bizhawk-mcp includes native out-of-the-box support for retro-reversing automation pipelines via Ghidra's static analysis, plus live emulation via BizHawk's multi-system emulator. The server bundles:
GhidraNesgba-ghidra-loaderNTRGhidraghidra-switch-loaderghidra_psx_ldrGhidra-SegaMasterSystem-LoaderThe primary entry point is triage_and_load_retro_rom. Call it with any ROM path and the server handles the rest:
Instead of forcing your AI agent to spend cycles manually identifying architecture maps, register layouts, or memory segments, chain the automated ingestion pipeline:
triage_and_load_retro_rom with a target file path.NES\x1a, NTR, NSO0, GBA, SNES title vectors, PS-X EXE, SEGA, TMR SEGA, SEGA ENTERPRISES), binds the matching Ghidra language module (6502:LE:16, ARM:LE:32:v4t, AARCH64:LE:64, 65816:LE:24, MIPS:LE:32, 68000:BE:32, Z80:16, SuperH4:LE:32), loads standard address memory blocks, and links automated signature cache arrays.emulate_slice or emulate_slice_with_taint tools to analyze localized console loops — no physical console hardware or open GDB networking ports needed.| Tool | Description |
|---|---|
triage_and_load_retro_rom | Reads raw file magic bytes to detect NES, SNES, GBA, NDS, Switch, PSX, Genesis, SMS, or Dreamcast ROMs. Provisions a correctly-language-mapped Ghidra session and auto-restores cached function signatures. Returns platform, loader, architecture tag, and mapped memory blocks. |
Or from source:
The server listens on stdin/stdout — pipe it to any MCP-compatible client.
The container bundles JDK 17, Ghidra 11.2, and the server — no host dependencies beyond Docker.
| Variable | Required | Default | Description |
|---|---|---|---|
GHIDRA_INSTALL_DIR | Yes | — | Path to Ghidra installation (e.g. /opt/ghidra_11.2) |
BIZHAWK_EXE_PATH | No | — | Path to EmuHawk.exe for live emulation features |
MOCK_MODE | No | 0 | Set to 1 to run without Ghidra/BizHawk (for testing/CI) |
Add to your claude_desktop_config.json:
Add to your Cursor MCP configuration:
| Tool | Description |
|---|---|
analyze_binary | Import + analyze a binary, returns a session_id. Reuses the ID if provided, otherwise auto-generates. |
list_sessions | List all active workspaces with their session IDs, binary paths, and load times. |
close_session | Close a session and free its Ghidra project resources. |
Most tools accept an optional session_id parameter — omit it to use the most recently loaded session.
| Tool | Description |
|---|---|
decompile_function | Decompile a function by name or address. |
decompile_function_paginated | Decompile with line_start, line_end, max_tokens (token-budget truncation), and summarize (strips boilerplate locals + collapsing blank lines). Prevents context-window exhaustion. |
get_data_types | List all data types defined in the program. |
get_cross_references | Cross-references to/from an address. |
get_call_graph | Recursive call graph + callers for a function. |
analyze_and_decompile_entrypoints | Composite — bulk decompile all entry points (program entry, exports, main, _start, etc.) in one call. |
generate_workspace_report | Produce a Markdown summary of the active workspace — entry points, function count, custom symbols, recovered structures, renamed functions, comments. Replaces a GUI CodeBrowser window. |
| Tool | Description |
|---|---|
rename_symbol | Rename a function or label. Stored in the Ghidra project DB. |
add_comment | Attach a comment (plate, pre, post, eol, repeatable). |
create_struct | Create a custom structured data type from a JSON member layout [{offset, name, type}, ...]. Offsets are optional. |
retype_variable | Re-type a local variable or function parameter (e.g. undefined4* → MyStruct*). |
| Tool | Description |
|---|---|
disassemble_range | Disassemble N raw instructions at an address — returns mnemonic, operands, hex bytes, and length for precise lower-level inspection. |
get_listing_range | Raw hex + ASCII dump for a byte range, equivalent to Ghidra's Listing panel. Complements disassemble_range for data regions. |
| Tool | Description |
|---|---|
search_bytes | Search the entire binary for a hex byte pattern (e.g. 09 08 00 01 or F86D0003). Returns matching addresses with context bytes and any string label at the hit. |
| Tool | Description |
|---|---|
diff_binaries | Compare two loaded sessions by function name and body size. Returns functions unique to each side and changed functions. |
Each analyze_binary call creates a named session. Sessions keep their Ghidra project open independently, so multiple binaries can be loaded concurrently:
The Dockerfile bundles Ghidra 11.2 and JDK 17 in a slim Python 3.11 image. Bind-mount your binaries directory at runtime.
Package as a portable .mcpb bundle for one-click install in Claude Desktop or publishing on Smithery.
Prerequisites: Install the MCPB CLI:
Build the bundle:
Or manually with mcpb:
The output ghidra-bizhawk-mcp.mcpb wraps the server with a manifest.json that prompts for GHIDRA_INSTALL_DIR (required) and optionally BIZHAWK_EXE_PATH at install time — no manual JSON editing.
Publishing to Smithery:
| Tool | Description |
|---|---|
emulate_slice | Headlessly execute N instructions. Seed register state and get a step-by-step trace of register mutations. |
emulate_slice_with_taint | Same as emulate_slice but with automated taint tracking — specify a taint register (e.g. r0) and the tool flags exactly when its value is modified or propagates to other registers. |
emulate_slice_with_breakpoints | Execute until a condition is met or the count expires. Condition syntax: R0==0, R1>0xFF, R2!=R3, PC==0x1234. Stops before or after the matching instruction. |
All run inside the pyhidra process via Ghidra's EmulatorHelper — no GDB/LLDB, no network ports, no debugger stubs. Works on ARM, x86, MIPS, and any Ghidra-supported architecture.
Suppose you're reversing a GBA ROM and want to find the first time r0 becomes zero inside a loop at 0x08000100:
This is especially powerful for identifying copy-loop bounds (R3 >= R4), null-pointer paths (R0==0), or switch-table targets (PC==0x).
| Tool | Description |
|---|---|
calculate_function_fingerprint | Generate a structural hash for a function (vars, params, body size, branches, called funcs, embedded strings, numeric constants). Survives compiler reordering. |
export_signature_map | Build a complete {hash → name} map for every function in the current binary. Save this JSON to reuse across versions. |
apply_signature_map | Pass a previously exported signature map; the server sweeps the binary and renames every matching function automatically. |
| Tool | Description |
|---|---|
save_active_binary_signature | Fingerprint all functions and stash the map under a lineage_group_id (e.g. "my_firmware_v1"). Stored in ~/.ghidra_bizhawk_mcp/signatures/ — no JSON files to manage. |
auto_restore_signatures_from_stash | Load a stashed map by lineage_group_id and auto-rename every matching function. |
auto_stash_current_binary | Zero-input auto-stash — hashes the binary's first 4 KB, saves a map under that hash. Just analyze and call. |
auto_restore_current_binary | Zero-input auto-restore — hashes the binary, looks up a previous stash, renames matches. No group ID needed. |
list_stashed_signature_groups | List all stashed groups currently in the local cache. |
Workflow — fully automated persistence:
After configuring your MCP client (see Configuration), ask your AI agent:
pyhidra.start() boots Ghidra's JVM once at server startupanalyze_binary call opens a new Ghidra project in its own named sessionsession_id (or the active default)