Add Ghidra 12.2 and start it alongside Claude Desktop

Install Ghidra 12.2 from the official GitHub release into /opt/ghidra,
verified against the SHA-256 in the release notes, with Ubuntu's
openjdk-25-jdk (Ghidra 12.2 requires Java 25). Version, build date and
hash are build args.

The entrypoint, renamed from launch-claude.sh to launch-apps.sh, now
starts both apps and stops both, ending the container, when either is
closed: Ghidra when its process exits (closing the project window),
Claude Desktop when none of its windows is open or minimized. Claude is
stopped via its main process so it can shut down its own helpers.

CLAUDE.md now records that Claude Desktop and Ghidra share one
container, at the user's request.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-02 12:38:38 +01:00
co-authored by Claude Opus 5.5
parent 16dbf25c9b
commit 4baf0208e1
5 changed files with 95 additions and 53 deletions
+3 -3
View File
@@ -13,7 +13,7 @@ The goal is that `docker compose up` gives you a working setup where Claude Desk
## Status
The repo is at an early stage (no code yet). The layout below is the **intended** structure. Update this file when the real structure differs.
Early stage. What exists: a single image (`Dockerfile`, `launch-apps.sh`) with Claude Desktop and Ghidra, started by `run.sh`; no compose setup yet. The layout below is the **intended** structure. Update this file when the real structure differs.
## Intended layout
@@ -37,7 +37,7 @@ The repo is at an early stage (no code yet). The layout below is the **intended*
## Architecture
- **One service per container.** Claude Desktop, Ghidra and the RE MCP server each get their own image, connected on one compose network.
- **Claude Desktop and Ghidra share one container** (the root `Dockerfile`, started by `run.sh`), at the user's request. Ghidra is the official GitHub release (version, build date and SHA-256 are build args), runs on Ubuntu's `openjdk-25-jdk`, lives in `/opt/ghidra` and starts together with Claude Desktop. Further services such as the RE MCP server may still get their own containers.
- **Shared workspace volume.** Every container mounts the workspace at `/workspace`, so a file path means the same thing to Claude, Ghidra and the RE MCP. `run.sh` asks for the host folder on each start in the desktop's folder picker (`zenity`, or `kdialog` on KDE; terminal prompt if neither exists), opening at the last folder used, else `./workspace`. Skip it with `--workspace DIR` or `WORKSPACE=DIR`. It refuses `/` and `$HOME`. The image symlinks `~/workspace` to `/workspace` for Claude Desktop's file picker.
- **GUI forwarding.** Claude Desktop and Ghidra are GUI apps. Default to X11 forwarding (`/tmp/.X11-unix` + `DISPLAY`). Support Wayland through XWayland. Keep a noVNC/VNC fallback in mind for headless hosts. Never solve display access with a blanket `xhost +`. Scope it (`xhost +local:docker` or `+SI:localuser:...`).
- **GhidraMCP wiring.** GhidraMCP has two parts:
@@ -45,7 +45,7 @@ The repo is at an early stage (no code yet). The layout below is the **intended*
- a Python MCP bridge (`bridge_mcp_ghidra.py`) that speaks MCP to Claude and forwards calls to that HTTP server.
The bridge is configured with the Ghidra server URL (e.g. `http://ghidra:8080/`). The plugin must be enabled in the CodeBrowser and a program must be open, or the bridge has nothing to talk to.
- **Container lifetime follows the window.** Closing Claude Desktop's window only hides the app (GNOME shows no tray icon to bring it back). The image's entrypoint `launch-claude.sh` watches the app's X windows: when none is open or minimized for ~3 s, it quits the app and the container exits. `run.sh` uses `--init` so signals and zombie processes are handled.
- **Container lifetime follows the apps.** The entrypoint `launch-apps.sh` starts Claude Desktop and Ghidra and stops both, ending the container, when either is closed. Ghidra counts as closed when its process exits (closing the project window quits it; CodeBrowser or Help windows don't). Claude Desktop's window only hides the app on close (GNOME shows no tray icon), so its X windows are watched: none open or minimized for ~3 s counts as closed. `run.sh` uses `--init` so signals and zombie processes are handled.
- **MCP transport between containers.** Claude Desktop starts local MCP servers as stdio subprocesses. Because the servers live in other containers, pick one approach and apply it consistently:
- *Preferred:* run the MCP servers with an HTTP/SSE transport in their own containers. Bridge them into Claude Desktop's stdio config with a small proxy (e.g. `mcp-remote` or `mcp-proxy`) installed in the Claude Desktop image.
- *Alternative:* install the bridges inside the Claude Desktop image and point them at the other containers over the network. This works well for the GhidraMCP bridge, which is just Python + HTTP.