Add the REA (Reverse Engineer Anything) MCP server
REA 6.8.0 (rea-agents on npm, checked against the registry's integrity hash) runs in its own container from the same image, because some of its tools run the target: workspace only, no display, no capabilities, and only an internal network with no route out. It serves MCP over HTTP and Claude Desktop reaches it through mcp-remote, installed from a committed lockfile. It uses the image's Ghidra 12.1.4 headless and binwalk. run.sh creates the internal network, starts REA before Claude Desktop and stops it when Claude Desktop's container ends. Adds a README with usage and sources. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
# Before launching, verify that a window from the container can actually be
|
||||
# drawn on the host display. Asks (in a folder picker) which host folder to
|
||||
# share with Claude; it is mounted read-write at /workspace in the container.
|
||||
# Also starts the REA MCP server in a second, isolated container and stops it
|
||||
# when Claude Desktop's container ends.
|
||||
#
|
||||
# Usage: ./run.sh [--build] [--check-only] [--workspace DIR]
|
||||
# --build rebuild the image even if it exists
|
||||
@@ -13,6 +15,9 @@ set -euo pipefail
|
||||
|
||||
IMAGE="${IMAGE:-claude-re-setup:latest}"
|
||||
CONTAINER="${CONTAINER:-claude-desktop}"
|
||||
REA_CONTAINER="${REA_CONTAINER:-claude-re-mcp}"
|
||||
# Internal network (no route out) shared by Claude Desktop and REA.
|
||||
NETWORK="${NETWORK:-claude-re-setup-internal}"
|
||||
RUNTIME="${RUNTIME:-docker}"
|
||||
WORKSPACE="${WORKSPACE:-}"
|
||||
|
||||
@@ -29,7 +34,7 @@ while (( $# )); do
|
||||
--workspace) [[ $# -ge 2 ]] || { echo "--workspace needs a directory" >&2; exit 2; }
|
||||
WORKSPACE="$2"; shift ;;
|
||||
--workspace=*) WORKSPACE="${1#*=}" ;;
|
||||
-h|--help) sed -n '2,10p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
|
||||
-h|--help) sed -n '2,12p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
|
||||
*) echo "Unknown argument: $1" >&2; exit 2 ;;
|
||||
esac
|
||||
shift
|
||||
@@ -115,9 +120,11 @@ fi
|
||||
(( check_only )) && { info "Display check passed"; exit 0; }
|
||||
|
||||
# --- Claude Desktop ----------------------------------------------------------
|
||||
if "$RUNTIME" container inspect "$CONTAINER" >/dev/null 2>&1; then
|
||||
die "container '$CONTAINER' already exists; stop it with: $RUNTIME stop $CONTAINER"
|
||||
fi
|
||||
for c in "$CONTAINER" "$REA_CONTAINER"; do
|
||||
if "$RUNTIME" container inspect "$c" >/dev/null 2>&1; then
|
||||
die "container '$c' already exists; stop it with: $RUNTIME stop $c"
|
||||
fi
|
||||
done
|
||||
|
||||
# --- Workspace ---------------------------------------------------------------
|
||||
# The folder Claude can read and write. Chosen in the desktop's own folder
|
||||
@@ -188,13 +195,47 @@ problem="$(workspace_problem "$WORKSPACE")"
|
||||
mkdir -p "$state_dir"
|
||||
printf '%s\n' "$WORKSPACE" > "$last_workspace_file"
|
||||
|
||||
# --- REA MCP server ----------------------------------------------------------
|
||||
# REA (Reverse Engineer Anything) gets its own container from the same image,
|
||||
# because some of its tools run the target: no display, no capabilities, only
|
||||
# the workspace mounted, and only the internal network, which has no route
|
||||
# out. It serves MCP over HTTP as "rea:8765"; Claude Desktop reaches it
|
||||
# through mcp-remote (see claude-desktop/claude_desktop_config.json).
|
||||
if ! "$RUNTIME" network inspect "$NETWORK" >/dev/null 2>&1; then
|
||||
info "Creating internal network $NETWORK"
|
||||
"$RUNTIME" network create --internal "$NETWORK" >/dev/null
|
||||
fi
|
||||
|
||||
ws_mount=(-v "$WORKSPACE:/workspace")
|
||||
info "Starting the REA MCP server in container '$REA_CONTAINER'"
|
||||
"$RUNTIME" run -d --rm --init \
|
||||
--name "$REA_CONTAINER" \
|
||||
--network "$NETWORK" --network-alias rea \
|
||||
--cap-drop ALL --security-opt no-new-privileges \
|
||||
--security-opt label=disable \
|
||||
"${ws_mount[@]}" \
|
||||
"$IMAGE" rea mcp --http --host 0.0.0.0 --port 8765 >/dev/null
|
||||
stop_rea() { "$RUNTIME" stop "$REA_CONTAINER" >/dev/null 2>&1 || true; }
|
||||
|
||||
# --- Claude Desktop ----------------------------------------------------------
|
||||
# No config volume: the container is removed on stop (--rm), so the sign-in
|
||||
# is discarded and Claude asks for a login on every start.
|
||||
# No :z on the workspace mount: label=disable already lets the container use
|
||||
# it, and relabeling an arbitrary host folder could break other apps.
|
||||
# Created first, so it is on the internal network (as well as the default one,
|
||||
# for sign-in) before it starts.
|
||||
info "Starting Claude Desktop and Ghidra in container '$CONTAINER' (workspace: $WORKSPACE -> /workspace)"
|
||||
"$RUNTIME" run -d "${run_args[@]}" \
|
||||
--name "$CONTAINER" \
|
||||
-v "$WORKSPACE:/workspace" \
|
||||
"$IMAGE" >/dev/null
|
||||
info "Running. Logs: $RUNTIME logs -f $CONTAINER Stop: $RUNTIME stop $CONTAINER"
|
||||
if ! { "$RUNTIME" create "${run_args[@]}" --name "$CONTAINER" "${ws_mount[@]}" "$IMAGE" >/dev/null \
|
||||
&& "$RUNTIME" network connect "$NETWORK" "$CONTAINER" \
|
||||
&& "$RUNTIME" start "$CONTAINER" >/dev/null; }; then
|
||||
"$RUNTIME" rm -f "$CONTAINER" >/dev/null 2>&1 || true
|
||||
stop_rea
|
||||
die "could not start Claude Desktop"
|
||||
fi
|
||||
|
||||
# Stop REA once Claude Desktop's container is gone, in the background and
|
||||
# detached from this terminal.
|
||||
setsid nohup bash -c '"$1" wait "$2" >/dev/null 2>&1; "$1" stop "$3" >/dev/null 2>&1' \
|
||||
_ "$RUNTIME" "$CONTAINER" "$REA_CONTAINER" </dev/null >/dev/null 2>&1 &
|
||||
|
||||
info "Running. Logs: $RUNTIME logs -f $CONTAINER (REA: $REA_CONTAINER) Stop: $RUNTIME stop $CONTAINER"
|
||||
|
||||
Reference in New Issue
Block a user