Ghidra MCP Server
A Ghidra headless server exposed via the Model Context Protocol (MCP), designed for binary analysis and malware reverse engineering with AI assistants. Runs locally or in Docker.
Features
- 32 MCP tools for binary analysis: decompilation, function listing, string search, cross-references, byte pattern search, malware-focused analysis, advanced RE tools (CFG, call graphs, instruction search), and emulation
- 3 emulation tools — emulate functions with automatic calling convention handling, single-step through code, read registers and memory
- 5 MCP resources for browsing binary metadata, functions, strings, and imports
- Multi-binary support — analyze multiple binaries simultaneously in a single Ghidra project
- Malware analysis tools — entropy analysis (packing detection), suspicious API categorization, section anomaly detection
- Code Mode — token-saving operating mode that exposes only 2 tools (
search+execute) instead of all 32, for LLM-efficient usage - Ghidra Server — connect to a shared Ghidra server for collaborative reverse engineering with checkout/checkin workflow
- SSE transport — run as an HTTP server with
--transport ssefor network-accessible deployments - Docker or local — runs in an isolated container or directly on your machine via stdio transport
- PyGhidra 3.0 — direct Ghidra Java API access via in-process JVM (no Ghidra scripts needed)
Quick Start
Docker (recommended)
# Build the image
docker compose build
# Run in full mode (all 32 tools)
docker compose run --rm -i ghidra-mcp
# Run in code mode (2 tools: search + execute, saves tokens)
docker compose run --rm -i ghidra-mcp --mode code
See Client Configuration for setup with Claude Desktop, Claude Code, OpenCode, and Continue.dev.
Apple Silicon
The Docker image builds natively on arm64. Since Ghidra releases don't include pre-built linux_arm_64 decompiler binaries, the Dockerfile automatically builds the decompiler from source during docker compose build (adds ~2 min to build time, requires no extra configuration). If you encounter issues, you can force x86_64 emulation by uncommenting platform: linux/amd64 in docker-compose.yml (slower due to Rosetta/QEMU).
Local (no Docker)
Prerequisites
- Python 3.11+ — python.org/downloads. On Windows, check "Add Python to PATH" during installation.
- Java 21+ (e.g., OpenJDK 21) —
JAVA_HOMEmust be set andjavamust be on your PATH. - Linux/macOS:
export JAVA_HOME=/path/to/jdk-21(add to~/.bashrcor~/.zshrc) - Windows: Set
JAVA_HOMEvia System Properties > Environment Variables (e.g.,C:\Program Files\Eclipse Adoptium\jdk-21) - Ghidra 12.0.4 — download and extract (no installer needed).
GHIDRA_INSTALL_DIRenvironment variable pointing to your Ghidra installation.- Linux/macOS:
export GHIDRA_INSTALL_DIR=/path/to/ghidra_12.0.4_PUBLIC(add to~/.bashrcor~/.zshrc) - Windows: Set via System Properties > Environment Variables, or
set GHIDRA_INSTALL_DIR=C:\ghidra\ghidra_12.0.4_PUBLICin Command Prompt
Installation
Quick install (checks prerequisites, creates venv, installs):
# Linux/macOS
./scripts/install.sh
# Windows (PowerShell)
.\scripts\install.ps1
Manual install:
python -m venv venv
# Linux/macOS:
source venv/bin/activate
# Windows (Command Prompt):
venv\Scripts\activate
# Windows (PowerShell):
venv\Scripts\Activate.ps1
pip install -e .
Usage
# Activate the virtual environment first (see Installation for Windows commands)
source venv/bin/activate
# Full mode (default) — all 32 tools
ghidra-mcp
# Code mode — 2 meta-tools (search + execute)
ghidra-mcp --mode code
# Custom project directory and name
ghidra-mcp --project-dir ~/my-projects --project-name my_project
# Windows: ghidra-mcp --project-dir C:\Users\you\my-projects --project-name my_project
# SSE transport (HTTP server on localhost:8080)
pip install -e ".[sse]"
ghidra-mcp --transport sse
# SSE with custom host and port
ghidra-mcp --transport sse --host 0.0.0.0 --port 3000
See Client Configuration for setup with Claude Desktop, Claude Code, OpenCode, and Continue.dev.
Client Configuration
All examples show full mode. For code mode, append
--mode code(Docker: add"--mode", "code"to the args array; Local: add"--mode", "code"to args or the command).
Claude Desktop
Config file: claude_desktop_config.json
Docker:
{
"mcpServers": {
"ghidra": {
"command": "docker",
"args": ["compose", "-f", "/path/to/docker-compose.yml", "run", "--rm", "-i", "ghidra-mcp"]
}
}
}
Local:
{
"mcpServers": {
"ghidra": {
"command": "/path/to/venv/bin/ghidra-mcp",
"env": {
"GHIDRA_INSTALL_DIR": "/path/to/ghidra_12.0.4_PUBLIC"
}
}
}
}
Note: Use the full path to
ghidra-mcpinside your virtual environment. IfGHIDRA_INSTALL_DIRis already set in your shell profile, you can omit theenvblock. Windows: UseC:\path\to\venv\Scripts\ghidra-mcp.exeas the command and Windows-style paths forGHIDRA_INSTALL_DIR.
Claude Code
Docker — via CLI:
claude mcp add ghidra -- docker compose -f /path/to/docker-compose.yml run --rm -i ghidra-mcp
Docker — via project config (.mcp.json in project root):
{
"mcpServers": {
"ghidra": {
"command": "docker",
"args": ["compose", "-f", "/path/to/docker-compose.yml", "run", "--rm", "-i", "ghidra-mcp"]
}
}
}
Local — via CLI:
claude mcp add ghidra --env GHIDRA_INSTALL_DIR=/path/to/ghidra_12.0.4_PUBLIC -- /path/to/venv/bin/ghidra-mcp
Local — via project config (.mcp.json):
{
"mcpServers": {
"ghidra": {
"command": "/path/to/venv/bin/ghidra-mcp",
"env": {
"GHIDRA_INSTALL_DIR": "/path/to/ghidra_12.0.4_PUBLIC"
}
}
}
}
Note: Use the full path to
ghidra-mcpinside your virtual environment. IfGHIDRA_INSTALL_DIRis already set in your shell profile, you can omit theenvblock and--envflag. Windows: UseC:\path\to\venv\Scripts\ghidra-mcp.exeas the command and Windows-style paths forGHIDRA_INSTALL_DIR.
OpenCode
Config file: opencode.json (project root or ~/.config/opencode/opencode.json)
Docker:
{
"mcp": {
"ghidra": {
"type": "local",
"command": ["docker", "compose", "-f", "/path/to/docker-compose.yml", "run", "--rm", "-i", "ghidra-mcp"],
"enabled": true
}
}
}
Local:
{
"mcp": {
"ghidra": {
"type": "local",
"command": ["/path/to/venv/bin/ghidra-mcp"],
"environment": {
"GHIDRA_INSTALL_DIR": "/path/to/ghidra_12.0.4_PUBLIC"
},
"enabled": true
}
}
}
Note: Use the full path to
ghidra-mcpinside your virtual environment. IfGHIDRA_INSTALL_DIRis already set in your shell profile, you can omit the"environment"block. Windows: UseC:\path\to\venv\Scripts\ghidra-mcp.exein the command array and Windows-style paths forGHIDRA_INSTALL_DIR.
Continue.dev
Config file: .continue/mcpServers/ghidra.json
Note: MCP tools are only available in Continue's Agent mode, not Chat mode.
Docker:
{
"mcpServers": [
{
"name": "Ghidra",
"type": "stdio",
"command": "docker",
"args": ["compose", "-f", "/path/to/docker-compose.yml", "run", "--rm", "-i", "ghidra-mcp"]
}
]
}
Local:
{
"mcpServers": [
{
"name": "Ghidra",
"type": "stdio",
"command": "/path/to/venv/bin/ghidra-mcp",
"env": {
"GHIDRA_INSTALL_DIR": "/path/to/ghidra_12.0.4_PUBLIC"
}
}
]
}
Note: Use the full path to
ghidra-mcpinside your virtual environment. IfGHIDRA_INSTALL_DIRis already set in your shell profile, you can omit theenvblock. Windows: UseC:\path\to\venv\Scripts\ghidra-mcp.exeas the command and Windows-style paths forGHIDRA_INSTALL_DIR.
Server Modes
The server supports three operating modes, selectable via the --mode flag:
| Mode | Flag | Tools Registered | Use Case | |------|------|-----------------|----------| | Full | --mode full (default) | All 32 tools + 5 resources | Direct tool access, best for exploration and interactive use | | Code | --mode code | 2 tools (search + execute) | Token-efficient, best for automated pipelines and cost-sensitive usage | | Script | --mode script | 6 tools (search_api + get_class_info + execute_script + binary management) | Direct Ghidra Java API access via live reflection and Python code execution |
Full and Code modes provide identical analytical capabilities — Code Mode routes calls through a dynamic dispatcher instead of registering each tool individually. Script Mode goes further by exposing the raw Ghidra Java API for custom analysis not covered by the built-in tools.
MCP Tools (Full Mode)
Project Management
| Tool | Description | |------|-------------| | import_binary | Import a binary file for analysis | | upload_binary | Upload a binary via base64-encoded data | | list_binaries | List all imported binaries | | delete_binary | Remove a binary from the project |
Analysis
| Tool | Description | |------|-------------| | list_functions | List functions with pagination and name filtering | | decompile_function | Decompile a function to C pseudocode | | rename_function | Rename a function | | rename_variable | Rename a variable (parameter or local) within a function | | rename_label | Rename a symbol/label in the program | | list_strings | List defined strings | | search_strings | Search strings by substring or regex | | list_imports | List imported symbols | | list_exports | List exported symbols | | get_xrefs | Get cross-references to/from an address | | search_bytes | Search for hex byte patterns with wildcards | | get_memory_bytes | Read raw bytes from an address | | search_instructions | Regex search over disassembly mnemonics/operands |
Malware Analysis
| Tool | Description | |------|-------------| | get_entropy | Per-section Shannon entropy, packing detection | | detect_suspicious_apis | Categorized suspicious imports (injection, persistence, crypto, network, anti-debug) | | get_sections | Sections with permissions, entropy, and anomaly flags (W+X, unusual names) |
Advanced Analysis
| Tool | Description | |------|-------------| | get_function_summary | Rich function metadata (params, callees, callers, strings, complexity) without decompilation | | get_basic_blocks | Control-flow graph basic blocks with instructions and edges | | get_call_graph | Function call graph with BFS depth control (callees/callers/both) |
Emulation
Emulate functions using Ghidra's EmulatorHelper API. The emulator automatically handles calling conventions — arguments are placed in the correct registers or stack locations based on the function's parameter metadata, and return values are extracted from the appropriate return register.
| Tool | Description | |------|-------------| | emulate_function | Emulate a function with optional integer arguments, get return value | | emulate_step | Single-step an existing emulator session, read registers and memory | | emulate_session_destroy | Destroy an emulator session and free its resources |
Server Connectivity
Connect to a shared Ghidra server for collaborative reverse engineering. Programs opened from the server are available to all analysis tools. Supports checkout/checkin workflow with exclusive locking.
| Tool | Description | |------|-------------| | connect_server | Connect to a Ghidra server (host, port, username, optional password) | | disconnect_server | Disconnect from server, release checkouts, clean up | | list_repositories | List available repositories on the connected server | | list_server_files | List files and subfolders in a server repository | | open_from_server | Open a program from the server for analysis (with optional checkout) | | checkin_file | Check in changes back to the Ghidra server |
Server Workflow
# 1. Connect to the Ghidra server
connect_server(host="ghidra.example.com", port=13100, username="analyst")
# 2. Browse available repositories and files
list_repositories()
list_server_files(repository_name="malware-lab")
# 3. Open a program (checkout for editing)
open_from_server(repository_name="malware-lab", file_path="/samples/trojan.exe", checkout=True)
# 4. Analyze with any tool — decompile, rename functions, etc.
decompile_function(binary_name="trojan.exe", name_or_addr="main")
rename_function(binary_name="trojan.exe", old_name="FUN_00401000", new_name="decrypt_payload")
# 5. Save changes back to the server
checkin_file(binary_name="trojan.exe", comment="Identified decryption routine")
# 6. Disconnect when done
disconnect_server()
Emulation Workflow
The typical emulation workflow is:
- Start emulation with
emulate_function— sets up the emulator, places arguments, runs until the function returns or the step limit is reached, and returns the result including the return value.
- Inspect interactively (optional) with
emulate_step— afteremulate_functioncreates a session, you can single-step through the remaining execution, reading specific registers and memory regions at each step.
- Clean up with
emulate_session_destroy— disposes the emulator and frees resources. Sessions are also cleaned up automatically when a binary is deleted or the server shuts down.
Emulation Example
# Step 1: Emulate a function with arguments
emulate_function(binary_name="malware.exe", name_or_addr="decrypt_string", args=[0x00402000, 16])
# Returns: {
# "session_key": "malware.exe:decrypt_string",
# "return_value": 4198400,
# "steps_executed": 847,
# "hit_breakpoint": true,
# ...
# }
# Step 2: Inspect registers and memory after execution
emulate_step(binary_name="malware.exe", name_or_addr="decrypt_string",
count=0, read_registers=["RAX", "RCX"],
read_memory=[{"address": "0x00402000", "size": 32}])
# Returns: {
# "registers": {"RAX": "0x401000", "RCX": "0x0"},
# "memory": [{"address": "0x00402000", "hex": "48656c6c6f..."}],
# ...
# }
# Step 3: Clean up
emulate_session_destroy(binary_name="malware.exe", name_or_addr="decrypt_string")
Emulation Limitations
- External calls: If the emulated function calls imported/external functions (e.g.,
printf,malloc), emulation will stop or produce undefined behavior. Only self-contained functions emulate correctly without additional stubbing. - Architecture support: x86/x86-64 (push sentinel return address to stack) and ARM/AARCH64 (set LR register) are supported. Other architectures will raise an error.
- Integer arguments only: The current implementation handles integer arguments via register/stack writes. Floating-point and struct arguments are not supported.
- Step limit: A
max_stepsparameter (default: 10,000) prevents runaway emulation.
MCP Resources
| URI | Description | |-----|-------------| | ghidra://binaries | All binaries in the project | | ghidra://binary/{name}/info | Binary metadata (arch, format, hashes, entry point) | | ghidra://binary/{name}/functions | Full function list | | ghidra://binary/{name}/strings | All defined strings | | ghidra://binary/{name}/imports | All imported symbols |
Code Mode
Code Mode is a token-efficient operating mode that replaces all 32 individual tool registrations with just 2 meta-tools: search and execute. This dramatically reduces the number of tool schemas sent to the LLM on every request, saving tokens and cost while preserving full analytical capability.
Activation
# Docker
docker compose run --rm -i ghidra-mcp --mode code
# Local
ghidra-mcp --mode code
Tools
search(query?)
Search the tool catalog. Returns tool names, descriptions, and full parameter signatures.
| Parameter | Type | Required | Description | |-----------|------|----------|-------------| | query | string | No | Substring to filter tool names and descriptions. Returns all 32 tools if omitted. |
Example — find emulation tools: ``` search(query="emulate")
Returns:
[
{"tool": "emulate_function", "description": "Emulate a function with optional arguments...",
"parameters": [{"name": "binary_name", "type": "string", "required": true}, ...]},
{"tool": "emulate_step", ...},
{"tool": "emulate_session_destroy", ...}
]
**Example — find tools related to entropy:**
search(query="entropy")
Returns tools whose name or description contains "entropy"
#### `execute(method, params?)`
Execute any Ghidra analysis tool by name with the given parameters.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `method` | string | Yes | Tool name from the catalog (use `search` to discover available tools). |
| `params` | object | No | Keyword arguments as a dictionary. Omit for tools with no required params. |
**Example — list functions:**
execute(method="list_functions", params={"binary_name": "test.elf", "filter": "main", "limit": 10}) ```
Example — decompile a function: `` execute(method="decompile_function", params={"binary_name": "test.elf", "name_or_addr": "main"}) ``
Example — emulate a function: `` execute(method="emulate_function", params={"binary_name": "test.elf", "name_or_addr": "decrypt", "args": [1, 2]}) ``
Example — list all binaries (no params needed): `` execute(method="list_binaries") ``
Error Handling
- Unknown method: Raises
ValueErrorwith the list of all available method names. - Missing required parameter: Raises
TypeErrorfrom the underlying Python method call. - Bridge errors:
KeyError(binary not found),RuntimeError(decompilation failure), etc. propagate directly with descriptive messages.
When to Use Code Mode
| Scenario | Recommended Mode | |----------|-----------------| | Interactive exploration with Claude Desktop | Full | | Automated analysis pipelines | Code | | Cost-sensitive / high-volume usage | Code | | First time using the server | Full | | LLM with small context window | Code | | Custom analysis beyond built-in tools | Script | | Exploring Ghidra API for scripting | Script |
Script Mode
Script Mode provides direct access to the Ghidra Java API via live reflection and Python code execution. Instead of using pre-built tools, the LLM can search API classes, inspect method signatures, and execute arbitrary Python code with full Ghidra API access — like a programmable Ghidra scripting environment.
Activation
# Docker
docker compose run --rm -i ghidra-mcp --mode script
# Local
ghidra-mcp --mode script
Tools
search_api(query, package?)
Search Ghidra Java API classes and methods by keyword using live Java reflection.
search_api(query="FunctionManager")
# Returns class info with all method signatures
search_api(query="getParameters", package="ghidra.program.model.listing")
# Returns classes in that package with matching methods
get_class_info(class_name)
Get full reflection info for a specific class — methods, parameter types, return types, interfaces.
get_class_info(class_name="ghidra.program.model.listing.Function")
# Returns: {"class": "...", "methods": [{"name": "getName", "params": [], "returns": "String"}, ...]}
get_class_info(class_name="Function") # Short name also works
execute_script(code, binary_name?)
Execute a Python code snippet with full Ghidra Java API access.
execute_script(
binary_name="malware.exe",
code="""
fm = program.getFunctionManager()
funcs = list(fm.getFunctions(True))
result = []
for f in funcs[:5]:
result.append({
'name': f.getName(),
'addr': str(f.getEntryPoint()),
'cc': f.getCallingConventionName(),
})
return result
"""
)
Pre-defined variables in script scope:
bridge— GhidraBridge instanceprogram/currentProgram— Program object (ifbinary_nameprovided)monitor— ConsoleTaskMonitor- All
ghidra.*packages importable via normal Python imports
Configuration
CLI Flags
| Flag | Default | Description | |------|---------|-------------| | --mode | full | Server mode: full, code, or script | | --transport | stdio | Transport protocol: stdio or sse | | --host | localhost | Host to bind for SSE transport | | --port | 8080 | Port to bind for SSE transport | | --project-dir | ./ghidra-projects | Directory for Ghidra projects | | --project-name | mcp_project | Ghidra project name |
Environment Variables
| Variable | Default | Description | |----------|---------|-------------| | GHIDRA_INSTALL_DIR | (auto-detect) | Path to Ghidra installation directory (required if PyGhidra auto-detection fails) | | GHIDRA_ANALYSIS_TIMEOUT_SECONDS | 300 | Analysis timeout per binary | | GHIDRA_MAX_HEAP | 2g | JVM max heap size |
Docker Volumes
| Path | Purpose | |------|---------| | /home/ghidra/binaries | Input binaries (mounted read-only) | | /home/ghidra/projects | Persistent Ghidra project data |
Development
# Install dev dependencies
pip install -e ".[dev]"
# Run tests (uses mocked GhidraBridge, no Ghidra needed)
pytest tests/ -v
# 192 tests covering full mode, emulation, server, code mode, script mode, CLI, and validation
Architecture
MCP Client (Claude Desktop / Claude Code / OpenCode / Continue.dev / ...)
↕ stdio or SSE (HTTP)
FastMCP Server (server.py)
├── Full Mode: 32 @mcp.tool() + 5 @mcp.resource()
├── Code Mode: search + execute → _dispatch()
└── Script Mode: search_api + get_class_info + execute_script
↕
GhidraBridge (ghidra_bridge.py)
├── Programs cache (dict[str, Program])
├── Decompilers cache (dict[str, DecompInterface])
└── Emulators cache (dict[str, EmulatorHelper])
↕
PyGhidra / JPype / JVM
↕
Ghidra Java API
- FastMCP handles MCP protocol over stdio or SSE
- GhidraBridge manages the JVM lifecycle, Ghidra project, cached program handles, decompiler instances, and emulator sessions
- PyGhidra provides in-process access to Ghidra's Java API via JPype (no separate Ghidra process needed)
- Code Mode dispatcher (
_dispatch) translatesexecute(method, params)calls into the appropriate bridge method invocations, handling parameter renaming and response wrapping
License
MIT











