ghidra-mcp README

The project's README from github.com/bethington/ghidra-mcp. As one markdown file: README.md.

Ghidra MCP Server

MCP Toplist

Tests Release License GitHub Sponsors

Python Java Ghidra MCP

Stars Last commit Discussions Issues OpenSSF Scorecard

If you find this useful, please ⭐ star the repo — it helps others discover it!

If Ghidra MCP saves you time, consider sponsoring the project. One-time and recurring support both help fund compatibility updates, production hardening, docs, and new tooling.

A production-ready Model Context Protocol (MCP) server that bridges Ghidra's powerful reverse engineering capabilities with modern AI tools and automation frameworks. 245 MCP tools, battle-tested AI workflows, and the most comprehensive Ghidra-MCP integration available — now including P-code emulation, live debugger integration, and PCode-graph data flow analysis.

Why Ghidra MCP?

Most Ghidra MCP implementations give you a handful of read-only tools and call it a day. This project is different — it was built by a reverse engineer who uses it daily on real binaries, not as a demo.

Convention Enforcement

You've been there: six months into a project you find ProcessItem, process_items, handleItem, and ItemProc in the same codebase — four functions doing the same thing, named by four different sessions or engineers with no shared contract. Fixing it takes longer than it should, and the problem will happen again.

v5.0 moves conventions from "things to remember" into the tool layer, where they can actually be enforced.

Tier Behavior Example
Auto-fix Applied silently count field on a uint32 → auto-prefixed dwCount on save
Warn Change goes through, warning returned processData → "name should be PascalCase with a verb: ProcessData"
Reject Change blocked with explanation undefined → undefined type change → "no-op rejected, type unchanged"

For AI agents, this means consistent output across every session, every model, every run — without pasting a style guide into every prompt. The tool knows the rules; the model just needs to make the call.

For teams, it eliminates the entire class of review comment that says "that's not our naming convention." Convention arbitration stays in the tool, not in code review.

For solo work at scale, analyze_function_completeness gives you a 0–100% score that measures honestly: structural deductions (unfixable compiler artifacts) are forgiven in your effective score, log-scaling prevents one bad category from burying everything else, and tiered plate comment quality means you know exactly what's missing and why.

🌟 Features

Core MCP Integration

Compatibility note: MCP tool names are normalized for GitHub Copilot CLI and CAPI validation. Exposed tool names use lowercase letters, digits, underscores, and hyphens only; nested HTTP paths such as /debugger/status are advertised as names like debugger_status_2 when needed to avoid collisions with static bridge tools.

Binary Analysis Capabilities

Dynamic Analysis (v5.4.0)

AI-Powered Reverse Engineering Workflows

Development & Automation

🚀 Quick Start

Prerequisites

Shared Ghidra Server users: Ghidra 12.1.3 clients require a Ghidra Server at 12.1, 12.0.5, or a newer compatible version. Upgrade the server before using this plugin from a 12.1 client.

Ghidra 12.1.3 ships Jython as an optional extension. Java scripts work by default, but .py scripts in ghidra_scripts/ require installing the Jython extension from File > Install Extensions and restarting Ghidra.

Installation

Recommended for all platforms: use python -m tools.setup directly.

ensure-prereqs installs runtime Python requirements plus the Ghidra JARs needed in the local Maven repository. deploy copies the build output, installs the user-profile extension, and patches Ghidra user config.

  1. Clone the repository:

    git clone https://github.com/bethington/ghidra-mcp.git
    cd ghidra-mcp
    
  2. Recommended: run environment preflight first:

    python -m tools.setup preflight --ghidra-path "F:\ghidra_12.1.3_PUBLIC"
    
  3. Build and deploy to Ghidra:

    python -m tools.setup ensure-prereqs --ghidra-path "F:\ghidra_12.1.3_PUBLIC"
    python -m tools.setup build
    python -m tools.setup deploy --ghidra-path "F:\ghidra_12.1.3_PUBLIC"
    

    deploy saves/closes an already-running matching Ghidra instance when needed, installs the extension, starts Ghidra, waits for MCP health, and runs schema smoke checks.

    Prefer to click through Ghidra's own dialogs, or installing a release zip on a machine without the repo? Follow the illustrated manual GUI install guide.

  4. Optional strict/manual mode (advanced):

    # Skip automatic prerequisite setup
    python -m tools.setup build
    python -m tools.setup deploy --ghidra-path "F:\ghidra_12.1.3_PUBLIC"
    
  5. Show command help:

    python -m tools.setup --help
    
  6. Optional build-only mode (advanced/troubleshooting):

    python -m tools.setup build
    

    Two Java backends are supported. Gradle is the default for local work — it reads Ghidra's jars straight out of the installation, so there is no install-file step and nothing to install beyond a JDK. CI builds and gates with Maven, so Maven is a maintained peer rather than a fallback.

    # Gradle (default) -- the wrapper is committed, so no Gradle install is needed.
    # -PGHIDRA_INSTALL_DIR or the GHIDRA_INSTALL_DIR env var both work.
    # In Git Bash use forward slashes; a backslash path is mangled before Gradle sees it.
    ./gradlew buildExtension -PGHIDRA_INSTALL_DIR=/path/to/ghidra
    
    # Maven (peer backend; what CI uses). Needs Ghidra's jars in the local .m2 first:
    #   python -m tools.setup ensure-prereqs --ghidra-path /path/to/ghidra
    mvn clean package assembly:single -DskipTests
    

    python -m tools.setup build routes to Maven by default; set TOOLS_SETUP_BACKEND=gradle to route it to Gradle instead.

Installation (Linux — Ubuntu/Debian)

  1. Clone the repository:

    git clone https://github.com/bethington/ghidra-mcp.git
    cd ghidra-mcp
    
  2. Install system prerequisites (if not already installed):

    sudo apt update && sudo apt install -y openjdk-21-jdk maven python3 python3-pip python3-venv curl jq unzip
    

    Debian/Kali/Ubuntu 23.04+ note (PEP 668): these distros mark the system Python as externally managed, so a bare pip install fails with error: externally-managed-environment. Don't work around it with --break-system-packages — it can corrupt apt-managed tooling. Instead use uv (recommended — it creates and manages a project-local .venv automatically, and is what this repo's commands use):

    curl -LsSf https://astral.sh/uv/install.sh | sh
    uv run bridge-mcp-ghidra    # resolves deps into .venv and starts the bridge
    

    or a classic virtual environment:

    python3 -m venv .venv && source .venv/bin/activate
    pip install -e .
    bridge-mcp-ghidra
    
  3. Run environment preflight:

    python -m tools.setup preflight --ghidra-path ~/ghidra_12.1.3_PUBLIC
    
  4. Build and deploy to Ghidra (single command):

    python -m tools.setup ensure-prereqs --ghidra-path ~/ghidra_12.1.3_PUBLIC
    python -m tools.setup build
    python -m tools.setup deploy --ghidra-path ~/ghidra_12.1.3_PUBLIC
    

    This will:

    • Install Ghidra JAR dependencies into your local ~/.m2/repository
    • Build GhidraMCP-<version>.zip with Maven
    • Extract the extension to ~/.config/ghidra/ghidra_<version>_PUBLIC/Extensions/
    • Update preferences with LastExtensionImportDirectory
    • Install Python requirements
  5. Optional: setup only Maven dependencies:

    python -m tools.setup install-ghidra-deps --ghidra-path ~/ghidra_12.1.3_PUBLIC
    
  6. Show command help:

    python -m tools.setup --help
    

Linux paths: The extension is installed to $HOME/.config/ghidra/ghidra_<version>_PUBLIC/Extensions/GhidraMCP/. Ghidra config files are in $HOME/.config/ghidra/ghidra_<version>_PUBLIC/.

Installation (macOS — Homebrew)

  1. Install prerequisites:

    brew install openjdk@21 maven python ghidra
    
  2. Clone the repository:

    git clone https://github.com/bethington/ghidra-mcp.git
    cd ghidra-mcp
    
  3. Install Ghidra JARs into local Maven:

     python -m tools.setup install-ghidra-deps \
        --ghidra-path /opt/homebrew/opt/ghidra/libexec
    
  4. Build and deploy:

     python -m tools.setup ensure-prereqs \
        --ghidra-path /opt/homebrew/opt/ghidra/libexec
     python -m tools.setup build
     python -m tools.setup deploy \
        --ghidra-path /opt/homebrew/opt/ghidra/libexec
    

    The extension is installed to ~/Library/ghidra/ghidra_12.1.3_PUBLIC/Extensions/GhidraMCP/.

    Note: --ghidra-version is required when using the Homebrew path because the path contains no version string.

  5. Start Ghidra and enable the plugin:

    /opt/homebrew/opt/ghidra/libexec/ghidraRun
    

    The server starts with the plugin. Check it from the project window: Tools > GhidraMCP > Server Status

  6. Configure Cursor/Claude MCP (~/.cursor/mcp.json) — use the absolute path to uv (which uv), not the bare name; GUI-launched clients do not inherit your shell's PATH (#441):

    {
      "mcpServers": {
        "ghidra": {
          "command": "/opt/homebrew/bin/uv",
          "args": ["run", "--directory", "/path/to/ghidra-mcp", "bridge-mcp-ghidra"]
        }
      }
    }
    

    macOS is the sharpest case: apps launched from Finder/Dock get launchd's PATH, which never contains ~/.local/bin or /opt/homebrew/bin.

Installation (Arch Linux — AUR)

@Pandoriaantje maintains community AUR packages:

Install with your AUR helper of choice, e.g.:

yay -S ghidra-mcp        # or ghidra-mcp-git

Basic Usage

uv run bridge-mcp-ghidra          # or: python -m bridge_mcp_ghidra

MCP client config (.mcp.json, ~/.cursor/mcp.json, Claude Desktop config, …). Use the absolute path to uv — see the note below for why:

{
  "mcpServers": {
    "ghidra-mcp": {
      "command": "/home/<you>/.local/bin/uv",
      "args": ["run", "--directory", "/path/to/ghidra-mcp", "bridge-mcp-ghidra", "--transport", "stdio"],
      "env": { "GHIDRA_MCP_URL": "http://127.0.0.1:8089" }
    }
  }
}

On Windows the same config points at uv.exe, e.g. "command": "C:\\Users\\<you>\\.local\\bin\\uv.exe". Find your own path with which uv (POSIX) or where.exe uv (Windows), or just run python -m tools.setup preflight, which prints the resolved absolute path and a ready-to-paste snippet.

Why absolute? "command": "uv" fails under service and GUI launchers. The MCP client resolves command with its own PATH, not your shell's. A client started from a systemd user service, a .desktop entry, or any other GUI session inherits that launcher's environment, which routinely lacks ~/.local/bin and ~/.cargo/bin — the very directories uv installs into. The failure lands at process-spawn time as spawn uv ENOENT, before any bridge code runs, so there is nothing in any log to read. An absolute path makes the client's PATH irrelevant and works on the first try. The same applies to python, python3, and the bridge-mcp-ghidra console script. (#441)

To add the bridge to Autohand Code from a cloned checkout:

autohand mcp add ghidra /home/<you>/.local/bin/uv run --directory /path/to/ghidra-mcp bridge-mcp-ghidra

Add --scope project before ghidra to save the server in the current project's .autohand configuration instead of your user configuration.

uv run bridge-mcp-ghidra --transport streamable-http --mcp-host 127.0.0.1 --mcp-port 8081

MCP client config for the HTTP transport (add to your client's MCP config file):

{
  "mcpServers": {
    "ghidra-mcp-http": {
      "url": "http://127.0.0.1:8081/mcp"
    }
  }
}

Browser-based clients (e.g. MCP Inspector) work out of the box: the HTTP transports answer CORS preflight (OPTIONS) requests and expose the mcp-session-id / mcp-protocol-version headers to scripts. Allowed origins mirror the Host-header policy — loopback on any port is always permitted, plus the bind host and any hosts listed in GHIDRA_MCP_ALLOWED_HOSTS.

GHIDRA_MCP_ALLOWED_HOSTS also supports clients that route a loopback-bound bridge through another network namespace. For example, a container can address the host as host.containers.internal without exposing the bridge on a LAN interface:

GHIDRA_MCP_ALLOWED_HOSTS=host.containers.internal \
  uv run bridge-mcp-ghidra --transport streamable-http \
  --mcp-host 127.0.0.1 --mcp-port 8081

The setting extends DNS-rebinding Host/Origin validation only; it does not change the bind address or make the listener reachable on additional interfaces.

Option 3: SSE Transport (Deprecated — use streamable-http instead)

uv run bridge-mcp-ghidra --transport sse --mcp-host 127.0.0.1 --mcp-port 8081

Bridge advanced flags

Flag Default Description
--transport stdio stdio (AI tools), streamable-http (web clients), sse (deprecated)
--mcp-host 127.0.0.1 Bind host for HTTP transports
--mcp-port — Port for HTTP transports
--lazy (default) Load only the default tool groups on connect, and let the model pull in the rest with search_tools/load_tool_group.
--no-lazy off Load all tool groups immediately on connect. Needed only by MCP clients that ignore tools/list_changed; rejected outright by the Gemini API (see below).
--default-groups listing,function,program Comma-separated groups loaded on connect under --lazy.

Lazy tool loading is the default (issue #440)

Advertising all 245 endpoints in a single tools/list is over a hard limit for at least one major provider. Gemini compiles function declarations into a constrained-decoding state machine and rejects the whole request before any tool is ever called:

400 INVALID_ARGUMENT
The specified schema produces a constraint that has too many states for serving

That is not a degradation, it is an outright break, and no client-side setting could work around a server that only ever offered the full set. So the bridge now loads listing,function,program (84 endpoints plus the 8 static tools) on connect and registers the rest on demand.

If your client ignores tools/list_changed it will not notice tools that are registered later, and should turn lazy loading off:

uv run bridge-mcp-ghidra --no-lazy          # when you control the command line
export GHIDRA_MCP_LAZY=0                    # when you don't (Docker, uvx, some client configs)

GHIDRA_MCP_LAZY accepts 0/false/no/off and 1/true/yes/on; an explicit --lazy/--no-lazy on the command line wins over it. Startup logs which mode is in effect.

Strict program routing (multi-program safety)

Set GHIDRA_MCP_REQUIRE_PROGRAM_SELECTORS=1 to make the bridge refuse any program-scoped call that omits a program selector, returning a clear error instead of letting the call ride the server's shared "current program" (the one switch_program and the active GUI tab move).

export GHIDRA_MCP_REQUIRE_PROGRAM_SELECTORS=1
uv run bridge-mcp-ghidra

Without this, a call that leaves program= out runs against whichever program is current, which is fine for a single-program workflow but a hazard once several programs are open: the call can read or edit the wrong binary with no error. The hazard is worse when more than one client shares a server, since each one moves that current-program global out from under the others.

With strict mode on, every program-scoped call must name its target. This covers every selector that picks an open program: plain program= and the cross-program tools' source_program/target_program or program_a/program_b (declared required, but the server still falls back to the current program when one arrives empty). A forgotten selector surfaces as a loud error on the first bad call instead of a silent write to the wrong binary. Tools with no program selector (open_program and close_program take path/name) are unaffected. Off by default: with the variable unset the bridge sends calls unchanged.

Reducing tool-context overhead

The bridge exposes a large catalog, so it loads only listing,function,program on connect (see above) and lets the model discover the rest on demand instead of registering everything:

search_tools works in both lazy and --no-lazy modes, so agents that honor tools/list_changed get full discovery without the upfront context cost.

Minimal read-only allowlist

Some MCP clients gate tools through an explicit allowlist. Cut it too far and the agent loses discovery — it cannot find entry points or enumerate functions through MCP, so it works around the allowlist by curl-ing the HTTP API on 127.0.0.1:8089 directly, which defeats the point of having one. The allowlist has to be small and self-sufficient.

Minimum viable read-only set (4 tools):

Tool Group What it buys you
get_metadata program Which binary is loaded — name, architecture, image base, function count. Orientation, and it confirms the bridge reached Ghidra at all.
list_methods listing Paginated function-name enumeration (offset, limit). This is the discovery tool — without it the agent cannot answer "what is in this binary".
get_entry_points listing Where execution starts, so analysis has a root to work down from.
get_functions function The payload. Takes function= (a name or an address) or functions= (comma-separated names or addresses), so one call can pull several bodies; fields= picks what comes back (decompiled code, callers, callees, comments, tags…).

That set is genuinely closed: get_entry_points and list_methods supply the addresses and names that get_functions consumes, and a decompiled body names its callees, which feed straight back into get_functions.

The three tools suggested in #441 — get_metadata, get_entry_points, decompile_function — were a workable floor. decompile_function is now get_functions (which also took over eight other function readers). list_methods is the one addition worth making: without it the agent can only reach code that is reachable by name from something it already decompiled, so anything not referenced from an entry point is invisible.

Useful next additions, in order:

Tool Group Why
get_functions with fields=callers,callees function Walk the call graph without decompiling every body to find edges (no decompile happens unless decompiled_code is requested).
get_xrefs_to xref Who touches this address — the standard question about a global.
list_strings listing Strings are the cheapest orientation signal in an unknown binary.
search_functions listing Name search, once the agent knows what it is hunting for.
list_imports / list_exports listing The binary's external surface.

Every tool above is a GET; none of them writes to the Ghidra database.

Two things to check when your allowlist is narrow:

Verify any allowlist against the running server rather than against this table: curl http://127.0.0.1:8089/mcp/schema lists every tool with the category the bridge groups it by.

Optional: Connect a standalone debugger server

The debugger server itself moved to the d2-game-exe repository on 2026-08-11 (its D2 calling-convention layer made it game-specific). Start it there, then point this bridge at it:

export GHIDRA_DEBUGGER_URL=http://127.0.0.1:8099

The bridge's 22 debugger_* proxy tools register only when that variable is set, so leaving it unset costs nothing — the tools simply do not appear rather than appearing and failing.

Debugger server flags:

Flag Default Description
--port 8099 HTTP server port
--host 127.0.0.1 Bind address (0.0.0.0 to expose on LAN)
--exports-dir — Path to a dll_exports/ directory for ordinal-to-name resolution
--log-level INFO DEBUG, INFO, WARNING, or ERROR

Set GHIDRA_DEBUGGER_URL in .env if you change the default port or host so the bridge can find it.

In Ghidra

  1. Start Ghidra and open a CodeBrowser window
  2. In CodeBrowser, enable the plugin via File > Configure > Utility > Configure > GhidraMCPPlugin
  3. Optional: configure custom port via CodeBrowser > Edit > Tool Options > GhidraMCP HTTP Server
  4. The server starts with the plugin; check it via Tools > GhidraMCP > Server Status in the project window
  5. The server runs on http://127.0.0.1:8089/ by default

Screenshots of every step: docs/INSTALL_GUI.md.

Verify It's Working

# Quick health check
curl http://127.0.0.1:8089/check_connection
# Expected: "Connected: GhidraMCP plugin running with program '<name>'"

# Get version info
curl http://127.0.0.1:8089/get_version

Support This Project

If Ghidra MCP saves you engineering or reverse-engineering time, consider sponsoring the project.

🔒 Security

GhidraMCP is designed for localhost-only development. The default configuration — HTTP server bound to 127.0.0.1, no authentication — is safe on a trusted single-user workstation and matches pre-v5.4.1 behavior.

If you expose the server beyond loopback, configure these three environment variables first. The server refuses to start on a non-loopback bind without a token.

Env var Effect
GHIDRA_MCP_AUTH_TOKEN When set, every HTTP request must carry Authorization: Bearer <token>. Timing-safe comparison. /mcp/health, /health, /check_connection are exempt.
GHIDRA_MCP_ALLOW_SCRIPTS Set to 1, true, or yes to enable /run_script_inline and /run_ghidra_script. Off by default as of v5.4.1 — these endpoints execute arbitrary Java against the Ghidra process. In headless mode this also triggers OSGi BundleHost initialization at server startup (Felix framework, ~hundreds of ms); leave it off if you don't need script execution.
GHIDRA_MCP_FILE_ROOT When set to a directory path, filesystem-path endpoints (/load_program, /import_file, /open_project, /delete_file, etc.) canonicalize the input and require it to fall under this root. Prevents path-traversal.

Name-quality enforcement is separate from security. By default, rename_function and global write endpoints reject names that fail the built-in quality gates, and struct field writes apply the built-in field prefix convention. Disable the built-in convention layer with Edit > Tool Options > GhidraMCP HTTP Server > Strict Naming Enforcement. The same Tool Options checkbox covers rename_symbol (all symbol kinds), set_global, the apply_data_type prefix/type guard, and struct-field Hungarian prefix auto-fixes in create_struct, add_struct_field, and modify_struct_field. The setting is read when the MCP server starts or restarts. Function/global convention warnings are still returned when enforcement is disabled.

Example: exposing to a private LAN with auth

export GHIDRA_MCP_AUTH_TOKEN=$(openssl rand -hex 32)
export GHIDRA_MCP_ALLOW_SCRIPTS=1     # only if your workflow needs it
export GHIDRA_MCP_FILE_ROOT=/srv/ghidra/inputs

java -jar GhidraMCPHeadless.jar --bind 0.0.0.0 --port 8089

Ghidra Server authentication

When connecting to a shared Ghidra Server, GhidraMCP can suppress the password dialog automatically. It resolves credentials in this order (first non-empty value wins):

Compatibility note: Ghidra 12.1.3 clients require Ghidra Server 12.1.2, 12.0.5, or a newer compatible server. Older shared servers are not safe targets for a 12.1 client upgrade.

  1. GHIDRA_SERVER_PASSWORD environment variable (or .env file in the Ghidra install directory or ~)
  2. ~/.ghidra-cred — single-line password file in your home directory
  3. <ghidra-install-dir>/.ghidra-cred

Username resolves similarly: GHIDRA_SERVER_USER env var → user.name system property.

If no password is found, Ghidra shows its normal GUI prompt. Set these in .env (see .env.template for the full block) to enable silent auth.

Migration from v5.4.0 → v5.4.1

❓ Troubleshooting

spawn uv ENOENT / spawn python ENOENT when the client starts the server

Cause: the MCP client could not find the command on its own PATH. This happens before any bridge code runs, so there is nothing in the Ghidra log or the bridge log to look at — the process was never created.

It shows up when the client is launched by something other than a login shell:

None of those contain ~/.local/bin (uv's default install location) or ~/.cargo/bin, so "command": "uv" cannot resolve even though uv works perfectly in your terminal.

Solution: use an absolute path in the client config.

// before — resolves against the client's PATH, fails under a service/GUI launcher
"command": "uv"
// after — no PATH lookup at all
"command": "/home/<you>/.local/bin/uv"

Find yours with which uv (POSIX) or where.exe uv (Windows). Or run:

python -m tools.setup preflight

which prints the resolved absolute path, the PATH entries it searched when a launcher is missing, and a ready-to-paste config snippet. Note what that check can and cannot tell you: it resolves against your shell's PATH, not the client's, so a pass there is evidence, not proof — the absolute path is what actually removes the failure mode. (#441)

If you must keep a bare command name, give the service manager the PATH instead — e.g. Environment="PATH=%h/.local/bin:/usr/local/bin:/usr/bin:/bin" in the unit file, or systemctl --user import-environment PATH — but the absolute path is the smaller and more portable fix.

"GhidraMCP" menu not appearing in Tools

Cause: Plugin not enabled or installed incorrectly.

Solution:

  1. Verify extension is installed: File > Install Extensions — GhidraMCP should be listed
  2. Enable the plugin: File > Configure > Utility > Configure > GhidraMCPPlugin (check the box)
  3. Restart Ghidra after installation/enabling

Illustrated walkthrough: docs/INSTALL_GUI.md.

Server not responding / Connection refused

Cause: Server not started or wrong port.

Solution:

  1. Check the server state: Tools > GhidraMCP > Server Status (it starts with the plugin; use Restart Server if it is stopped)

  2. Check configured port: Edit > Tool Options > GhidraMCP HTTP Server

  3. Check if port is in use:

    # Linux/macOS
    lsof -i :8089
    # Windows
    netstat -ano | findstr :8089
    
  4. Look for errors in Ghidra console: Window > Console

pip install fails with error: externally-managed-environment

Cause: PEP 668. Debian-family distros (Debian 12+, Kali, Ubuntu 23.04+) mark the system Python as externally managed, so global pip install is blocked to protect apt-managed packages.

Solution: Use a virtual environment — never --break-system-packages. The recommended path is uv, which manages a project-local .venv automatically:

curl -LsSf https://astral.sh/uv/install.sh | sh
cd ghidra-mcp
uv run bridge-mcp-ghidra

Or a classic venv:

python3 -m venv .venv && source .venv/bin/activate
pip install -e .
bridge-mcp-ghidra

The debugger_* tools do not appear

Cause: They are registered only when GHIDRA_DEBUGGER_URL is set, and only on Windows. The debugger server they proxy to lives in the d2-game-exe repository since 2026-08-11 — this repo ships the proxies, not the server.

Solution: start the debugger server from that repo, then set the URL before launching the bridge:

export GHIDRA_DEBUGGER_URL=http://127.0.0.1:8099

A ModuleNotFoundError for pybag or comtypes while starting that server is a missing optional dependency on its side; install its Windows-only extras from that repo, and make sure you install into and run from the same interpreter.

500 Internal Server Errors

Cause: Server-side exception, often due to missing program data.

Solution:

  1. Ensure a binary is loaded in CodeBrowser
  2. Run auto-analysis first: Analysis > Auto Analyze
  3. Check Ghidra console (Window > Console) for Java exceptions
  4. Some operations require fully analyzed binaries

404 Not Found Errors

Cause: Endpoint doesn't exist or wrong URL.

Solution:

  1. Verify endpoint exists: curl http://127.0.0.1:8089/get_version
  2. Check for typos in endpoint name
  3. Ensure you're using correct HTTP method (GET vs POST)

Python Ghidra scripts fail with "No script provider found"

Cause: In Ghidra 12.1.3, Jython support is no longer enabled by default. .py scripts need the bundled Jython extension; Python 3 scripts should use PyGhidra instead of the Ghidra Script Manager.

Solution:

  1. In the Ghidra Front End, open File > Install Extensions.
  2. Check Jython, restart Ghidra, then refresh Script Manager.
  3. For new automation, prefer Java Ghidra scripts or PyGhidra.

Extension not appearing in Install Extensions

Cause: JAR file in wrong location.

Solution:

  1. Manual install location: ~/.config/ghidra/ghidra_12.1.3_PUBLIC/Extensions/GhidraMCP/lib/GhidraMCP.jar (%APPDATA%\ghidra\... on Windows, ~/Library/ghidra/... on macOS)
  2. Or use: File > Install Extensions > Add and select the ZIP file — see the illustrated guide
  3. Ensure JAR/ZIP was built for your Ghidra version

Build fails with "Ghidra dependencies not found"

Cause: Ghidra JARs not installed in local Maven repository.

Solution:

# Windows (recommended)
python -m tools.setup install-ghidra-deps --ghidra-path "C:\ghidra_12.1.3_PUBLIC"

📊 Production Performance

🛠️ API Reference

245 MCP tools backed by HTTP endpoints, grouped by catalog category. Generated from tests/endpoints.json by python -m tools.gen_readme_api_reference --write; the live schema at /mcp/schema is authoritative at runtime. Usage patterns: docs/prompts/TOOL_USAGE_GUIDE.md.

204 of these are served by both the GUI plugin and the standalone headless server. The rest are marked (GUI only) (27) or (headless only) (14) — calling one against the other server returns a 404, not an error message. See python -m tools.audit_server_scope for how the split is derived.

Program & Session Management

Project Organization

Headless Project & Program Lifecycle

Available on the standalone headless server (GhidraMCPHeadlessServer).

Listing & Enumeration

Current GUI Context

Functions: Decompile, Rename, Prototypes & Variables

Symbols, Labels & Globals

Cross-References & Call Graphs

Data Types & Structures

Comments

Analysis

Malware & Anti-Analysis

Cross-Binary Documentation & Archive

Health, Schema & Tool Control

Emulation

Ghidra Server & Version Control

Debugger (Ghidra TraceRmi)

On Windows hosts where the bridge's WinDbg debugger proxy is active (GHIDRA_DEBUGGER_URL), colliding names get a _2 suffix (e.g. debugger_status_2).

System

Bridge Static Tools

Defined in the Python bridge itself (instance discovery, tool-group management); always available even before a Ghidra connection. The bridge also proxies 22 debugger_* WinDbg tools when GHIDRA_DEBUGGER_URL points at the standalone debugger server.

See CHANGELOG.md for version history.

🏗️ Architecture

┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   AI/Automation │◄──►│   MCP Bridge    │◄──►│  Ghidra Plugin  │
│     Tools       │    │ (bridge_mcp_    │    │ (GhidraMCP.jar) │
│  (Claude, etc.) │    │  ghidra/)       │    │                 │
└─────────────────┘    └─────────────────┘    └─────────────────┘
        │                       │                       │
   MCP Protocol            HTTP REST              Ghidra API
(stdio/streamable-http) (localhost:8089)      (Program, Listing)

Components

🔧 Development

Building from Source

# Recommended: direct Python-first workflow
python -m tools.setup ensure-prereqs --ghidra-path "C:\ghidra_12.1.3_PUBLIC"
python -m tools.setup build
python -m tools.setup deploy --ghidra-path "C:\ghidra_12.1.3_PUBLIC"

# Version bump (updates all maintained version references atomically)
python -m tools.setup bump-version --new X.Y.Z

The authoritative build system today is Maven. tools.setup, the VS Code tasks, and the documented deploy flow all build through pom.xml and write artifacts to target/. build.gradle remains in the repo as a manual fallback for direct Ghidra/Gradle users, but it is not the primary path.

Command Reference

Command What it does
ensure-prereqs Install Python deps + Ghidra Maven JARs in one shot. Start here on a new machine.
preflight Validate Python, build tool, Ghidra path, and JAR availability without making changes. Add --strict to also check network reachability.
build Build the plugin JAR and extension ZIP via Maven (or Gradle when TOOLS_SETUP_BACKEND=gradle).
deploy Copy the built extension into the Ghidra profile and patch FrontEndTool.xml for auto-activation.
start-ghidra Launch the configured Ghidra installation.
clean Remove Maven/Gradle build outputs (target/, build/).
clean-all Remove build outputs plus local cache artifacts (.m2 Ghidra JARs, etc.).
install-ghidra-deps Install only the Ghidra JARs into ~/.m2. Useful when the build environment changes.
install-python-deps Install the Python dependency groups via uv sync.
run-tests Run the Java offline test suite (no live Ghidra needed).
verify-version Check that version strings are consistent across pom.xml, CHANGELOG.md, and README.md.
bump-version --new X.Y.Z Atomically update all version references. Pass --tag to create a git tag.

Common flags accepted by most commands:

Flag Description
--ghidra-path PATH Ghidra installation directory. Defaults to GHIDRA_PATH from .env.
--dry-run Print actions without executing them.
--force Reinstall Ghidra JARs even if already present (install-ghidra-deps, ensure-prereqs).
--with-debugger Force-install debugger Python requirements (Windows only).
--use-debugger-toggle Read INSTALL_DEBUGGER_DEPS from .env to decide whether to install debugger deps.
--test TIER (deploy only) Opt into live deploy regression tiers such as release or debugger-live.
--strict (preflight only) Also check network reachability for Maven Central and PyPI.

Deploy test tiers are opt-in because benchmark tiers can import/reset Benchmark.dll and BenchmarkDebug.exe in the active Ghidra project. Use --test release before cutting releases, or set GHIDRA_MCP_DEPLOY_TESTS=release in a local .env when you want every deploy on your machine to run the live benchmark regression. The value is validated against the same tier list --test accepts, and an unknown tier is refused rather than skipped. See Testing and Release Regression.

# Standard first-time setup and deploy
python -m tools.setup ensure-prereqs --ghidra-path "C:\ghidra_12.1.3_PUBLIC"
python -m tools.setup build
python -m tools.setup deploy --ghidra-path "C:\ghidra_12.1.3_PUBLIC"

# Preflight check before deploying
python -m tools.setup preflight --strict --ghidra-path "C:\ghidra_12.1.3_PUBLIC"

# Version bump and tag
python -m tools.setup bump-version --new X.Y.Z --tag

# Run offline Java tests
python -m tools.setup run-tests

# Show full help
python -m tools.setup --help

Project Structure

ghidra-mcp/
├── pyproject.toml           # uv project (ghidra-mcp-bridge wheel + dependency groups)
├── python/bridge_mcp_ghidra/ # MCP server package (Python, 245 catalog entries)
├── src/main/java/           # Ghidra plugin + headless server (Java)
│   └── com/xebyte/
│       ├── GhidraMCPPlugin.java         # GUI plugin (231 endpoints)
│       ├── headless/                    # Headless server (218 endpoints)
│       └── core/                        # Shared service layer (14 services)
├── ghidra_scripts/          # Automation scripts for batch workflows
├── tests/                   # Python unit tests + endpoint catalog
│   ├── unit/               # Catalog consistency, schema, tool function tests
│   └── endpoints.json      # Endpoint specification (225 entries)
├── docs/                    # Documentation
│   ├── prompts/            # AI workflow prompts (V5 documentation workflows)
│   ├── releases/           # Version release notes
│   └── project-management/ # Contributor planning docs (Gradle migration, etc.)
├── tools/setup/             # Build and deployment CLI (python -m tools.setup)
├── fun-doc/                 # Internal RE curation tool — not part of the MCP plugin
│                            #   Priority-queue worker, LLM scoring, web dashboard.
│                            #   See fun-doc/README.md for details.
└── .github/workflows/      # CI/CD pipelines

Library Dependencies

Ghidra JARs must be installed into your local Maven repository (~/.m2/repository) before compilation. This is a one-time setup per machine, and again when your Ghidra version changes. -Deploy now installs these automatically by default.

The tool enforces version consistency between:

If these do not match, deployment fails fast with a clear error.

Troubleshooting: Version Mismatch

If you see a version mismatch error, align both values:

  1. pom.xml → ghidra.version
  2. --ghidra-path version segment (ghidra_X.Y.Z_PUBLIC)

Then rerun:

python -m tools.setup preflight --ghidra-path "C:\ghidra_12.1.3_PUBLIC"
# Windows
python -m tools.setup install-ghidra-deps --ghidra-path "C:\path\to\ghidra_12.1.3_PUBLIC"

Required Libraries (14 JARs, ~37MB):

Library Source Path Purpose
Base.jar Features/Base/lib/ Core Ghidra functionality
Decompiler.jar Features/Decompiler/lib/ Decompilation engine
PDB.jar Features/PDB/lib/ Microsoft PDB symbol support
FunctionID.jar Features/FunctionID/lib/ Function identification
SoftwareModeling.jar Framework/SoftwareModeling/lib/ Program model API
Project.jar Framework/Project/lib/ Project management
Docking.jar Framework/Docking/lib/ UI docking framework
Generic.jar Framework/Generic/lib/ Generic utilities
Utility.jar Framework/Utility/lib/ Core utilities
Gui.jar Framework/Gui/lib/ GUI components
FileSystem.jar Framework/FileSystem/lib/ File system support
Graph.jar Framework/Graph/lib/ Graph/call graph analysis
DB.jar Framework/DB/lib/ Database operations
Emulation.jar Framework/Emulation/lib/ P-code emulation

Note: Libraries are NOT included in the repository (see .gitignore). You must install them from your Ghidra installation before building.

Automation entry point:

  • python -m tools.setup is the supported setup/build/deploy/versioning interface
  • use ensure-prereqs, build, deploy, preflight, clean-all, and bump-version directly
  • these commands currently use Maven as the canonical Java build backend

Development Features

📚 Documentation

Core Documentation

AI Workflow Prompts

Release History

🐳 Headless Server (Docker)

GhidraMCP includes a headless server mode for automated analysis without the Ghidra GUI.

Quick Start with Docker

# Build and run
docker-compose up -d ghidra-mcp

# Test connection
curl http://localhost:8089/check_connection
# Connection OK - GhidraMCP Headless Server v7.0.0

Headless API Workflow

# 1. Load a binary
curl -X POST -d "file=/data/program.exe" http://localhost:8089/load_program

# 2. Run auto-analysis (identifies functions, strings, data types)
curl -X POST http://localhost:8089/run_analysis

# 3. List discovered functions
curl "http://localhost:8089/list_functions?limit=20"

# 4. Decompile a function
curl "http://localhost:8089/get_functions?function=0x401000&fields=decompiled_code"

# 5. Get metadata
curl http://localhost:8089/get_metadata

Key Headless Endpoints

Endpoint Method Description
/load_program POST Load binary file for analysis
/run_analysis POST Run Ghidra auto-analysis
/list_functions GET List all discovered functions
/list_exports GET List exported symbols
/list_imports GET List imported symbols
/get_functions GET One or many functions: decompiled code, signature, callers, callees, comments, tags
/create_function POST Create function at address
/get_metadata GET Get program metadata
/create_project POST Create a Ghidra project
/list_analyzers GET List available analyzers
/server/status GET Check Ghidra Server connection

Configuration

Environment variables for Docker:

🤝 Contributing

See CONTRIBUTING.md for detailed contribution guidelines.

Quick Start

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Build and test your changes (./gradlew buildExtension -PGHIDRA_INSTALL_DIR=/path/to/ghidra, or mvn clean package assembly:single -DskipTests under the Maven backend)
  4. Update documentation as needed
  5. Commit your changes (git commit -m 'Add amazing feature')
  6. Push to the branch (git push origin feature/amazing-feature)
  7. Open a Pull Request

📄 License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

🏆 Production Status

Metric Value
Version 7.0.0
MCP Tools 245 fully implemented
GUI Endpoints 231 (GhidraMCPPlugin)
Headless Endpoints 218 (GhidraMCPHeadlessServer)
Compilation ✅ 100% success
Batch Efficiency 93% API call reduction
AI Workflows 7 proven documentation workflows
Ghidra Scripts Automation scripts included
Documentation Comprehensive with AI prompts

See CHANGELOG.md for version history and release notes.

🙏 Acknowledgments

This project was originally derived from LaurieWired/GhidraMCP in August 2025 and has since been substantially rewritten and extended. We acknowledge LaurieWired's original work as the starting point. See NOTICE for license attribution.

Powered by

JetBrains logo.

Tooling provided by JetBrains through their Open Source Support Program.

👥 Contributors

This project has benefited from the work of dedicated contributors:

Core Contributors

@heeen — Significant contributions including:

@huehuehuehueing — Significant contributions including:



Ready for production deployment with enterprise-grade reliability and comprehensive binary analysis capabilities.

ghidra-mcp (bethington/ghidra-mcp)

Searching Database…

What is this?This is an MCP (Model Context Protocol) server that bridges Ghidra's reverse engineering engine with AI tools like Claude and Cursor, giving AI agents 245 tools to analyze, rename, document, and even emulate functions in binaries.
Is this a Ghidra plugin, a standalone server, or both?Both: a Java extension (GUI plugin) installs into Ghidra, paired with a Python bridge that exposes Ghidra's analysis engine over MCP, so it can also run headless without the Ghidra GUI.
How do I get it running?Clone the repo, run `python -m tools.setup ensure-prereqs` to install dependencies, then `python -m tools.setup deploy` to build the extension, install it into Ghidra, and start the MCP server; point your MCP client at the bridge using the absolute path to uv.
What do I need installed before this works?Java 21 LTS, Apache Maven 3.9+, Ghidra 12.1.3 (or a compatible version), and Python 3.10+ with uv (or pip + venv).
Any install pitfalls I should watch for?Yes — MCP clients launched from Finder, Dock, or other GUI launchers don't inherit your shell's PATH, so uv must be referenced by its absolute path or the connection fails silently at process-spawn time with no log output at all.
Does it work on Linux and Windows, or just macOS?It supports Windows, macOS (with a Homebrew install path), and Linux (Ubuntu/Debian plus Arch AUR packages); the live debugger backend uses dbgeng on Windows and gdb/lldb elsewhere.
What is convention enforcement and why does it matter?It's a tiered system (auto-fix, warn, reject) that enforces naming and type conventions in the tool layer itself, so AI agents and engineers produce consistently named functions and types instead of drifting into different naming styles across sessions.
What can the dynamic analysis features do?P-code emulation runs any function in isolation via Ghidra's EmulatorHelper to brute-force things like API hash resolution in milliseconds, and the live debugger integration lets the AI attach, step, set breakpoints, and read memory/registers during actual execution.
How is this different from other Ghidra MCP servers?The author built it from daily use on real binaries rather than as a demo: it exposes 245 tools (roughly 3x more than competing implementations), supports full write access including renaming, typing, and script execution, and propagates documentation across binary versions via SHA-256 function hash matching.
Does it support team or shared Ghidra setups?Yes, it integrates with Ghidra Server for shared repositories, version control, and checkout/checkin workflows for multi-user collaboration, though client-side Ghidra 12.1.3 requires a Server at 12.1, 12.0.5, or newer.
Creator
bethington
Size
15.2 MB
Stars
5.7K
Age
1y
Commits · 90 Days