Ghidra BizHawk MCP

io.github.getaniraoPublished by GitHub account getaniraov1.1.0Updated Oct 30, 2026

Unified Ghidra + BizHawk MCP server for static analysis, P-code emulation, and retro ROM triage.

Overview

AI-generated overview

Local MCP server that drives Ghidra headless reverse engineering and BizHawk emulation, letting an assistant triage retro ROMs, decompile code and emulate…

What you can do
  • Triage and load ROMs: detect NES, SNES, GBA, NDS, Switch, PSX, Genesis, SMS or Dreamcast files and open a mapped Ghidra session
  • Manage analysis sessions: import and analyze binaries, list and close workspaces, generate a Markdown workspace report
  • Read and analyze code: decompile functions (paginated), list data types, cross-references, call graphs, bulk-decompile entry points
  • Inspect assembly and bytes: disassemble ranges, dump listing hex/ASCII, search byte patterns, diff two loaded binaries
  • Modify the program database: rename symbols, add comments, create structs, retype variables
  • Emulate and fingerprint: run P-code slices with taint tracking or register breakpoints, compute function…
When to use it
Useful for reverse engineering and retro game or firmware analysis: identifying a ROM's platform, decompiling routines, tracing register values through emulated code, and carrying function names across binary versions. Also fits multi-binary comparison work where several Ghidra sessions stay open at once.
Requirements
Runs locally as a Python package (Python 3.10 or newer) over stdio. Requires a Ghidra 11.x or 12.x installation with JDK 17, set via the required GHIDRA_INSTALL_DIR variable; pyghidra is installed automatically. Live emulation needs BizHawk's EmuHawk, optionally set via BIZHAWK_EXE_PATH. MOCK_MODE=1 runs without Ghidra or BizHawk for testing. A Docker image is also provided.
Before you install
Write tools rename symbols, add comments, create structs and retype variables directly in the Ghidra program database, and signature restore renames matching functions automatically. Live emulation can write emulator memory and drive input. Signature stashes are stored under the user's home directory. The server reads local binary and ROM paths you give it, so point it only at files you are entitled to analyze.

Installation

In SourceWeft

  1. Open Ghidra BizHawk MCP in the dashboard and add it to a workspace.
  2. Enable the server for the chats that should use its tools.

Desktop only via STDIO. STDIO servers start a local process, so they need the SourceWeft desktop host.

Other MCP clients

Follow the launch instructions in the repository.

README

Ghidra BizHawk MCP

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 .gpa files for Ghidra 11.x.

Prerequisites

DependencyVersionRequiredNotes
Python>= 3.10YesRuntime for the MCP server
Ghidra11.x or 12.xYesHeadless or GUI install; GHIDRA_INSTALL_DIR must point here
Java (JDK)>= 17YesBundled with Ghidra; needed for JVM bridge
pyghidra>= 3.0YesPython-to-Ghidra bridge; installed automatically
BizHawk (EmuHawk)Latest stableNoOnly needed for live emulation tools; BIZHAWK_EXE_PATH optional
DockerLatestNoOnly needed for containerized deployment

Architecture

┌─────────────────────────────────────────────────────────────────┐│                        MCP Client (Claude / Cursor)              ││  sends JSON-RPC over stdin/stdout                                │└─────────────────────────────┬───────────────────────────────────┘                              │                              ▼┌──────────────────────────────────────────────────────────────────┐│                    ghidra-bizhawk-mcp                            ││  ┌─────────────────────────────────────────────────────────────┐││  │   MCP Server (server.py) — tool registry, stdio dispatch    │││  └──────────────────────────┬──────────────────────────────────┘││                             │                                   ││              ┌──────────────┴──────────────┐                    ││              ▼                              ▼                   ││  ┌────────────────────┐    ┌──────────────────────────────┐    ││  │   GhidraSession    │    │   BizhawkBridge              │    ││  │   pyghidra → JVM   │    │   TCP client → localhost:8766│    ││  │   decompile, etc.  │    └──────────────┬───────────────┘    ││  └────────────────────┘                   │                     │└───────────────────────────────────────────┼─────────────────────┘                                            │ TCP (newline-delimited JSON)                                            ▼                              ┌──────────────────────────────┐                              │   BizHawk (EmuHawk.exe)       │                              │   built-in socket server      │                              │   ┌────────────────────────┐ │                              │   │  bridge.lua            │ │                              │   │  memory read/write     │ │                              │   │  joypad, savestate     │ │                              │   │  frame advance         │ │                              │   └────────────────────────┘ │                              └──────────────────────────────┘

Security Model

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.

Hardware & Retro Ecosystem Integration

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:

  • Nintendo Entertainment System (NES) via GhidraNes
  • Super Nintendo Entertainment System (SNES) via native 65816 memory maps
  • Game Boy Advance (GBA) via gba-ghidra-loader
  • Nintendo DS (NDS) via NTRGhidra
  • Nintendo Switch via ghidra-switch-loader
  • PlayStation 1 (PSX) via ghidra_psx_ldr
  • Sega Genesis / Mega Drive via native 68000 memory maps
  • Sega Master System / Game Gear via Ghidra-SegaMasterSystem-Loader
  • Sega Dreamcast via native SuperH4 memory maps

Zero-Input Triage — Worked Example (GBA)

The primary entry point is triage_and_load_retro_rom. Call it with any ROM path and the server handles the rest:

python
# Auto-detect platform, map language, provision sessiontriage_and_load_retro_rom(rom_path="/data/game.gba")# → platform: "Game Boy Advance (GBA)"# → loader:   "GBA ROM Loader"# → arch:     "ARM:LE:32:v4t"
# Decompile the main entry point on the same sessiondecompile_function(address="0x00001c2c")# → decompiled C code for the GBA ROM entry routine
# Search for a known pattern (e.g. 32-bit ARM store-multiple)search_bytes(pattern="09 08 00 01")# → matching addresses labelled "gba_ram_start"

Execution Chaining Flow

Instead of forcing your AI agent to spend cycles manually identifying architecture maps, register layouts, or memory segments, chain the automated ingestion pipeline:

  1. Invoke triage_and_load_retro_rom with a target file path.
  2. The server headlessly parses the binary file structure (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.
  3. Use the integrated emulate_slice or emulate_slice_with_taint tools to analyze localized console loops — no physical console hardware or open GDB networking ports needed.

Triage Tool

ToolDescription
triage_and_load_retro_romReads 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.

Quick Start

1. Install

bash
pip install ghidra-bizhawk-mcp

Or from source:

bash
git clone https://github.com/getanirao/ghidra-bizhawk-mcp.gitcd ghidra-bizhawk-mcppip install -e .

2. Set environment

bash
# Required: point to your Ghidra installationexport GHIDRA_INSTALL_DIR=/opt/ghidra_11.2    # Linux / macOSset GHIDRA_INSTALL_DIR=C:\Program Files\ghidra_11.2   # Windows
# Optional: enable live BizHawk emulationexport BIZHAWK_EXE_PATH=/path/to/EmuHawk.exe
# Optional: run in mock mode (no Ghidra/BizHawk needed)export MOCK_MODE=1

3. Run

bash
ghidra-bizhawk-mcp

The server listens on stdin/stdout — pipe it to any MCP-compatible client.

Docker

bash
docker build -t ghidra-bizhawk-mcp .docker run -i --rm -v /path/to/binaries:/data ghidra-bizhawk-mcp

The container bundles JDK 17, Ghidra 11.2, and the server — no host dependencies beyond Docker.

Configuration

Environment Variables

VariableRequiredDefaultDescription
GHIDRA_INSTALL_DIRYes—Path to Ghidra installation (e.g. /opt/ghidra_11.2)
BIZHAWK_EXE_PATHNo—Path to EmuHawk.exe for live emulation features
MOCK_MODENo0Set to 1 to run without Ghidra/BizHawk (for testing/CI)

Claude Desktop

Add to your claude_desktop_config.json:

json
{  "mcpServers": {    "ghidra-bizhawk": {      "command": "ghidra-bizhawk-mcp",      "env": {        "GHIDRA_INSTALL_DIR": "/opt/ghidra_11.2"      }    }  }}

Cursor

Add to your Cursor MCP configuration:

json
{  "mcpServers": {    "ghidra-bizhawk": {      "command": "ghidra-bizhawk-mcp",      "env": {        "GHIDRA_INSTALL_DIR": "/opt/ghidra_11.2"      }    }  }}

Tools

Session management

ToolDescription
analyze_binaryImport + analyze a binary, returns a session_id. Reuses the ID if provided, otherwise auto-generates.
list_sessionsList all active workspaces with their session IDs, binary paths, and load times.
close_sessionClose 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.

Read / Analysis

ToolDescription
decompile_functionDecompile a function by name or address.
decompile_function_paginatedDecompile with line_start, line_end, max_tokens (token-budget truncation), and summarize (strips boilerplate locals + collapsing blank lines). Prevents context-window exhaustion.
get_data_typesList all data types defined in the program.
get_cross_referencesCross-references to/from an address.
get_call_graphRecursive call graph + callers for a function.
analyze_and_decompile_entrypointsComposite — bulk decompile all entry points (program entry, exports, main, _start, etc.) in one call.
generate_workspace_reportProduce a Markdown summary of the active workspace — entry points, function count, custom symbols, recovered structures, renamed functions, comments. Replaces a GUI CodeBrowser window.

Write / Mutation

ToolDescription
rename_symbolRename a function or label. Stored in the Ghidra project DB.
add_commentAttach a comment (plate, pre, post, eol, repeatable).
create_structCreate a custom structured data type from a JSON member layout [{offset, name, type}, ...]. Offsets are optional.
retype_variableRe-type a local variable or function parameter (e.g. undefined4* → MyStruct*).

Assembly-level

ToolDescription
disassemble_rangeDisassemble N raw instructions at an address — returns mnemonic, operands, hex bytes, and length for precise lower-level inspection.
get_listing_rangeRaw hex + ASCII dump for a byte range, equivalent to Ghidra's Listing panel. Complements disassemble_range for data regions.

Byte-sequence search

ToolDescription
search_bytesSearch 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.

Binary diffing

ToolDescription
diff_binariesCompare two loaded sessions by function name and body size. Returns functions unique to each side and changed functions.

Workspace Sessions

Each analyze_binary call creates a named session. Sessions keep their Ghidra project open independently, so multiple binaries can be loaded concurrently:

python
# Load two binaries into separate sessionss1 = analyze_binary(binary_path="/bin/a.out")        # auto session_ids2 = analyze_binary(binary_path="/bin/b.out", session_id="my_session")
# Operate on a specific sessiondecompile_function(function_name="main", session_id=s1.session_id)
# Diff themdiff_binaries(session_a=s1.session_id, session_b="my_session")

Deployment

Docker (multi-user / CI)

bash
docker build -t ghidra-bizhawk-mcp .
# Run as an MCP subprocessdocker run -i --rm \  -v /data/binaries:/data \  ghidra-bizhawk-mcp \  --ghidra-dir /opt/ghidra

The Dockerfile bundles Ghidra 11.2 and JDK 17 in a slim Python 3.11 image. Bind-mount your binaries directory at runtime.

MCP Bundle (MCPB — Claude Desktop / Smithery)

Package as a portable .mcpb bundle for one-click install in Claude Desktop or publishing on Smithery.

Prerequisites: Install the MCPB CLI:

bash
npm install -g @anthropic-ai/mcpb

Build the bundle:

bash
# From the repo root — also builds the Smithery publish variantscripts/build-mcpb.ps1

Or manually with mcpb:

bash
mcpb pack

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:

Smithery's publish API requires MCP-style tool schemas (inputSchema) and object-form prompt arguments, which the MCPB schema forbids in manifest.json. The build script therefore emits a second bundle whose manifest is patched from mcpb/tool-schemas.json:

bash
python scripts/build_smithery_bundle.pysmithery mcp publish ./dist/ghidra-bizhawk-mcp.smithery.mcpb -n getanirao/ghidra-bizhawk-mcp

P-code micro-emulation

ToolDescription
emulate_sliceHeadlessly execute N instructions. Seed register state and get a step-by-step trace of register mutations.
emulate_slice_with_taintSame 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_breakpointsExecute 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.

Worked example — breaking on a register condition

Suppose you're reversing a GBA ROM and want to find the first time r0 becomes zero inside a loop at 0x08000100:

python
# Step until r0 == 0, stop before the matching instructionresult = emulate_slice_with_breakpoints(    session_id="gba_v1",    start_address="0x08000100",    max_instructions=5000,    stop_condition="R0==0",    stop_mode="before")# result.exit_reason → "R0==0"# result.instructions_executed → 312# result.trace → [step 311: r0 goes 4→2, step 312: r0 goes 2→0]
# Check if a specific address was reached after a branchresult = emulate_slice_with_breakpoints(    session_id="gba_v1",    start_address="0x08000100",    max_instructions=5000,    stop_condition="PC==0x08001234")# result.exit_reason → "PC==0x08001234"
# Use inequalities to catch bounds checksresult = emulate_slice_with_breakpoints(    session_id="gba_v1",    start_address="0x08000100",    max_instructions=5000,    stop_condition="R1>0xFF")# result.exit_reason → "R1>0xFF"# result.last_step["r1"] → 0x100

This is especially powerful for identifying copy-loop bounds (R3 >= R4), null-pointer paths (R0==0), or switch-table targets (PC==0x).

Function fingerprinting / signature transfer

ToolDescription
calculate_function_fingerprintGenerate a structural hash for a function (vars, params, body size, branches, called funcs, embedded strings, numeric constants). Survives compiler reordering.
export_signature_mapBuild a complete {hash → name} map for every function in the current binary. Save this JSON to reuse across versions.
apply_signature_mapPass a previously exported signature map; the server sweeps the binary and renames every matching function automatically.

Persistent signature stash (server-side cache)

ToolDescription
save_active_binary_signatureFingerprint 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_stashLoad a stashed map by lineage_group_id and auto-rename every matching function.
auto_stash_current_binaryZero-input auto-stash — hashes the binary's first 4 KB, saves a map under that hash. Just analyze and call.
auto_restore_current_binaryZero-input auto-restore — hashes the binary, looks up a previous stash, renames matches. No group ID needed.
list_stashed_signature_groupsList all stashed groups currently in the local cache.

Workflow — fully automated persistence:

python
# Analyze v1 — stashes automatically under binary content hashs1 = analyze_binary(binary_path="/bin/v1.bin")auto_stash_current_binary(session_id=s1.session_id)
# Later, analyze v2 — restores automaticallys2 = analyze_binary(binary_path="/bin/v2.bin")auto_restore_current_binary(session_id=s2.session_id)# → 142 functions renamed, zero manual JSON handling

Try these prompts

After configuring your MCP client (see Configuration), ask your AI agent:

  • "Load this GBA ROM and decompile the entry point."
  • "What functions call 0x8001234 in this NDS binary?"
  • "Triage this PSX EXE and trace r0 through the first 20 instructions."
  • "Diff the two sessions I have open and show me changed functions."

Project Structure

ghidra-bizhawk-mcp/├── Dockerfile├── pyproject.toml├── README.md└── src/ghidra_bizhawk_mcp/    ├── __init__.py    ├── server.py          # MCP server, tool registry, stdio transport    ├── ghidra_bridge.py   # GhidraSession — pyghidra wrapper, all Ghidra logic    ├── lua/    │   └── bridge.lua     # BizHawk-side Lua bridge for live emulation    └── tools/        ├── __init__.py        ├── bizhawk_bridge.py  # TCP client connecting MCP ↔ BizHawk        └── ...

How it works

  1. pyhidra.start() boots Ghidra's JVM once at server startup
  2. Each analyze_binary call opens a new Ghidra project in its own named session
  3. Read/write tools route to the requested session via session_id (or the active default)
  4. Write tools apply changes directly to the Ghidra program database
  5. Sessions persist until explicitly closed — enabling multi-binary workflows and diffing

Source: README.md at commit 8892856

Tools

0
SourceWeft has not started this package in its sandbox yet, so its tools are not known.

Version history

1
  1. v1.1.0LatestOct 16, 2026

Score

50% of the applicable checks met

How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.

  • Reached in the last 30 days25 pts

    Keep the endpoint answering the MCP handshake; SourceWeft probes it every 30 days.

  • Auth declared10 pts

    Declare the credential headers in the registry entry, or keep the endpoint answering the probe so its auth can be classified.

  • Tools listed15 pts

    Answer tools/list without a credential, or ship a package that starts in a sandbox without one.

  • DescriptionRequired15 pts
  • Logo10 pts
  • Documentation15 pts
  • License10 pts