All-in-one Docker container for reverse engineering β radare2 + Ghidra headless wired as MCP servers, plus BinDiff, angr, AFL++, honggfuzz, and Android tools (apktool, jadx, frida). Usable with Claude Code, Claude Desktop, Cline, Continue, or any MCP client that speaks StreamableHTTP.
Headless only β no GUI, no VNC. Everything runs in the terminal or through MCP.
- Docker 23.0+ (BuildKit required β default since 23.0)
- Docker Compose 2.0+ (
docker compose, notdocker-compose)
Docker 18.09β22.x users: export DOCKER_BUILDKIT=1 before building.
docker compose build
docker compose up -dContainer starts, gateway listens on localhost:3100. Drop binaries in
./workspace β mounted at /workspace inside the container.
docker exec -it toolbox bash # optional β CLI access to all toolsSingle HTTP endpoint. Copy the toolbox entry from .mcp.json into
your MCP client config. The gateway composes all toolbox MCP servers behind one
URL β no Docker socket, no docker exec, no per-server registration.
{
"mcpServers": {
"toolbox": {
"type": "http",
"url": "http://localhost:3100/mcp"
}
}
}Claude Code users: the project's .mcp.json auto-configures this. Other clients:
| MCP client | File |
|---|---|
| Claude Code | .claude/mcp.json (project) or ~/.claude/mcp.json (global) |
| Claude Desktop | claude_desktop_config.json (docs) |
| Cline (VS Code) | cline_mcp_settings.json |
| Continue | ~/.continue/config.json |
| Zed | settings.json β {"context_servers": {...}} |
Restart the client after editing. The gateway runs as a background service inside the container β always up, no cold start.
MCP client (Claude Code, Desktop, etc.)
β HTTP POST /mcp (StreamableHTTP)
βΌ
localhost:3100 ββββββββββββββββββββββββββββββββββββββββ
β β
β docker compose port mapping β
βΌ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β container: toolbox β β
β β β
β gateway.py --transport http :3100 β β
β ββ r2pm -r r2mcp β r2__* β β
β ββ bridge_mcp_ghidra.py :8089 β ghidra__* β β
β ββ shell-mcp.py β shell__* β β
β ββ python3 -m angr.mcp β angr__* β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββ β
Gateway starts all child MCP servers at boot. Tools are namespaced
(r2__*, ghidra__*, shell__*, angr__*) β never collide. Failed children
are logged but don't block the gateway; it degrades with whatever connected.
| Namespace | Server | What it exposes |
|---|---|---|
r2__* |
r2mcp via r2pm |
Disassembly, decompilation (r2ghidra), hexdump, xrefs, symbols, search, emulation |
ghidra__* |
bridge_mcp_ghidra.py β Ghidra headless :8089 | Project mgmt, import, auto-analysis, 200+ tools: decompilation, patching, struct/types, debugger, Bindiff |
shell__* |
shell-mcp.py | Arbitrary shell commands β angr, AFL++, honggfuzz, apktool, jadx, gdb, gcc, python3, etc. |
angr__* |
angr.mcp (built-in) | Binary analysis β project loading, CFG, symbolic execution, data dependency, VFG |
Ghidra tools come in two categories:
| Category | When available | Examples |
|---|---|---|
| Static | Always β no instance needed | import_file, list_instances, connect_instance, list_tool_groups, load_tool_group |
| Instance-scoped | After connecting to a loaded program | decompile_function, list_functions, rename_function, xrefs_to, disassemble_function, all debugger tools |
Lifecycle:
import_file βββ auto-connect βββ schema fetch βββ all 200+ tools available
β
βββ or: connect_instance βββ schema fetch βββ all tools available
Use check_tools to see what's callable right now:
ghidra__check_tools: "decompile_function,list_functions,import_file,bindiff"
β import_file=callable, decompile_function=not_found (no instance yet)
β import_file completes β all four = callable
Static tools are built into the bridge. Instance-scoped tools are fetched
dynamically from the headless server's /mcp/schema after connect. If a tool
shows not_loaded (exists but its group isn't loaded), call load_tool_group
with the category name. Use list_tool_groups to see all categories and their
loaded status.
Run Ghidra GUI on your host with the GhidraMCP plugin on port 8080. Set
GHIDRA_MCP_URL in docker-compose.yml to point the bridge at your host:
environment:
- GHIDRA_MCP_URL=http://host.docker.internal:8080Rebuild and the gateway's ghidra bridge will route to your GUI instance instead of the headless server.
.claude/agents/ ships with specialized RE agents. Anyone cloning the repo gets
them automatically β Claude Code loads agents from the project's .claude/
directory.
| Agent | Model | What it does |
|---|---|---|
binary-triage |
haiku | Fast radare2 + shell first-look at unknown binary |
ghidra-importer |
sonnet | Import binary into Ghidra headless, run auto-analysis |
ghidra-analyst |
sonnet | Static RE β decompile, xrefs, rename, annotate |
ghidra-debugger |
sonnet | Dynamic analysis β attach, breakpoints, trace, memory watch |
re-orchestrator |
opus | Full pipeline: triage β import β static β dynamic β report |
triage this binary: /workspace/suspicious.elf
import /workspace/challenge.exe into Ghidra
decompile the function at 0x401000 and trace its xrefs
find all strings containing "http" in this binary
trace every call to D2Common.ordinal:10624 with arguments
full reverse engineering analysis on /workspace/malware.bin
Add .md files to .claude/agents/. Frontmatter:
---
name: my-agent
description: What it does
model: haiku | sonnet | opus
tools: [Read, Bash, mcp__toolbox__ghidra__*, mcp__toolbox__r2__*, mcp__toolbox__shell__exec]
---tools is optional β omit to inherit all tools from the parent session. MCP
tools use namespaced names: mcp__toolbox__<server>__<tool_name>.
All tools available inside the container. Shell in with:
docker exec -it toolbox bashr2 -A /workspace/chall # open + analyze
r2 -c "pdg @ main" /workspace/chall # decompile main via r2ghidra
r2pm -r r2mcp -t # list MCP tools exposed by r2mcpQuick import via the bundled script:
/opt/tools/scripts/load-ghidra.sh /workspace/my-binary # auto-analyze
/opt/tools/scripts/load-ghidra.sh /workspace/my-binary myproj # named project
/opt/tools/scripts/load-ghidra.sh /workspace/my-binary --no-analyzeChecks MCP server health, imports via analyzeHeadless, calls /load_program
so the bridge sees the binary. Projects land in
/home/ctf/.config/ghidra (persisted via Docker volume ghidra-projects).
Manual import with analyzeHeadless:
/opt/tools/ghidra/support/analyzeHeadless /home/ctf/.config/ghidra myproj \
-import /workspace/chall -overwritecurl http://localhost:8089/check_connection
curl -X POST "http://localhost:8089/load_program" -d "file=/workspace/mybin"
curl "http://localhost:8089/decompile_function?program=mybin&name=main"CLI and MCP. Export .BinExport from Ghidra or IDA, then diff:
bindiff old.BinExport new.BinExportThe ghidra-headless MCP exposes bindiff and bindiff_export_from_ghidra tools
for diffing directly from Ghidra projects.
Pre-installed: angr, pwntools, ropper, ropgadget, capstone, unicorn, keystone, z3, lief, r2pipe, frida-tools, objection.
python3 -c "
import angr
p = angr.Project('/workspace/chall', auto_load_libs=False)
cfg = p.analyses.CFGFast()
print(f'{len(cfg.kb.functions)} functions, entry at {hex(p.entry)}')
"AFL++ and honggfuzz installed under /opt/tools/fuzzing/bin (on PATH).
# AFL++
mkdir -p fuzz-in && echo AAAA > fuzz-in/seed
afl-fuzz -i fuzz-in -o fuzz-out -- /workspace/chall @@
# black-box / no source: add -Q (QEMU mode)
# honggfuzz
mkdir -p hf-in && echo AAAA > hf-in/seed
honggfuzz -i hf-in -o hf-out -- /workspace/chall ___FILE___apktool d app.apk -o app_decoded
jadx app.apk -d app_jadx_out
adb devices
frida -U -f com.example.app -l hook.js --no-pause
objection -g com.example.app exploregdb, lldb, strace, ltrace, nasm, objdump, strings, patchelf,
gcc, clang, and python3 are all on PATH.
.
βββ .mcp.json MCP config β paste into your client (single HTTP entry)
βββ CLAUDE.md Agent reference + quickstart for Claude Code
βββ docker-compose.yml One-command start
βββ docker/
β βββ Dockerfile Multi-stage build, pinned versions
βββ scripts/
β βββ entrypoint.sh Container entrypoint (starts Ghidra MCP + gateway HTTP)
β βββ load-ghidra.sh Import + load binary into Ghidra MCP from CLI
β βββ mcp/
β βββ gateway.py MCP gateway β composes all servers behind one HTTP endpoint
β βββ shell-mcp.py Shell command MCP server
βββ .claude/
β βββ settings.json Auto-enables project MCP servers
β βββ settings.local.json Local overrides (git-ignored)
β βββ agents/ Specialized RE agents (auto-loaded by Claude Code)
β βββ binary-triage.md Fast radare2 first-look
β βββ ghidra-importer.md Binary import + auto-analysis
β βββ ghidra-analyst.md Static RE deep-dive
β βββ ghidra-debugger.md Dynamic analysis / debugger
β βββ re-orchestrator.md Full pipeline coordinator
βββ workspace/ Mounted at /workspace β put binaries here
Compose adds SYS_PTRACE and disables seccomp (unconfined) β required by
gdb, strace, and AFL. Run this container in an isolated VM when analyzing
untrusted binaries.
The MCP gateway listens on 0.0.0.0:3100 inside the container. Only the port
you choose to expose in docker-compose.yml reaches your host. No
authentication on the gateway itself β treat it as a local development tool,
not an internet-facing service.
Builds validated on every push and PR via GitHub Actions
(.github/workflows/build.yml). Push a version tag (v1.0.0, v1.0) to
publish the image to GHCR (ghcr.io/<user>/re-toolbox).