JADX MCP Server Setup: Decompile APKs with Claude Code and REA
On this page
REA, the open-source project by morluto, can put a headless JADX decompiler behind your coding agent’s MCP connection. You don’t need to open jadx-gui or keep a plugin running. The agent asks a question about an APK, REA starts JADX in a private workspace, and the answer comes back as Evidence tied to the APK’s SHA-256.
This guide covers the MCP setup only: Java requirements, the JADX JAR, where the environment variables go in Claude Code and Codex, how to word your requests, and what the common errors mean. For the full static Android workflow (resources with Apktool, pulling packages over ADB, Flutter triage), read the companion article Android APK Analysis with REA.
Who this is for: developers who already use Claude Code or Codex and want the agent to read decompiled APK code directly.
What you will learn:
- Which Java and JADX builds REA 6.4.0 accepts, and how it checks them
- How to add
REA_JADX_MCP_JARto the REA server entry in Claude Code and Codex - Which five MCP tools the agent gets, and prompts that use them well
- How to read the error messages REA returns when something is missing
Example target: the F-Droid client APK (GPL-3.0, source at gitlab.com/fdroid/fdroidclient). It is open source, so you can check every answer against the original code.
How the REA JADX setup is put together
There are three pieces, and REA installs none of them for you:
- A full JDK, 17 or newer. REA compiles a small Java “metadata bridge” from source at startup, so it needs the
jdk.compilermodule. A JRE does not include it. - jadx-headless-mcp 0.7.1, the
-all.jarfrom the v0.7.1 release. REA 6.4.0 pins this version: the audited JAR’s SHA-256 is6e5eacf500b64292bfb73c49797c1958f6ee44646e43e868039ae7feb573ff75. - REA 6.4.0, registered with your agent through
npx rea-agents setup(getting started).
When a JADX tool is called, REA checks the host platform, resolves the JAR and java, runs java --list-modules, and reads the JAR’s ZIP directory for the classes its bridge needs. If any check fails, the call returns a specific capability_unavailable error and never starts JADX.
How it differs from jadx-ai-mcp
The other well-known option, jadx-ai-mcp, is a plugin for the JADX GUI paired with a separate Python MCP server. You open the APK in jadx-gui, and the agent works with what the GUI has loaded. That is a good fit if you want to click around while the agent reads along.
REA takes the headless route instead:
| REA + jadx-headless-mcp | jadx-ai-mcp | |
|---|---|---|
| GUI required | No | Yes, jadx-gui with the plugin |
| How the APK is chosen | Each tool call names the APK path | Whatever is open in the GUI |
| Output | Evidence with APK and engine JAR digests | Plugin responses |
| Other targets in the same server | Native, JavaScript, .NET, web, firmware | Android only |
Pick REA if you want repeatable, scriptable answers that also work from CI or the plain rea CLI. Pick the GUI plugin if you want an interactive pairing session.
Prerequisites
- REA 6.4.0, connected to Claude Code or Codex.
- Linux, macOS, or Windows x64. REA 6.4.0 rejects other hosts (for example Windows arm64) with
unsupported_host. On Windows x64 it uses the Windows native component bundled in the package to own Java’s stdio; this path was added for issue #1262. - A JDK 17+ and the jadx-headless-mcp 0.7.1 JAR, installed by you.
- An APK you are allowed to analyze. This guide uses an open-source app.
Step 1: Install and check the JDK
Install any full JDK 17 or newer (Temurin, Zulu, your distribution’s OpenJDK package). Then confirm the two modules REA looks for:
java --list-modules | grep -E '^(java\.base|jdk\.compiler)@'
Why: REA runs this exact command (java --list-modules) and requires a jdk.compiler line and a java.base@ major version of at least 17. If jdk.compiler is missing, you are on a JRE.
If several Java versions are installed, pin one with JAVA_HOME. It must be an absolute path; REA then uses $JAVA_HOME/bin/java (or java.exe on Windows). Without JAVA_HOME, REA uses java from PATH.
Step 2: Download and verify the JADX JAR
Download jadx-headless-mcp-0.7.1-all.jar from the release page and check its digest:
shasum -a 256 jadx-headless-mcp-0.7.1-all.jar
# Windows PowerShell: Get-FileHash .\jadx-headless-mcp-0.7.1-all.jar -Algorithm SHA256
Compare it with 6e5eacf500b64292bfb73c49797c1958f6ee44646e43e868039ae7feb573ff75.
Why: REA records the actual JAR digest in every Evidence result, and it only fills in the upstream source revision when the bytes match the audited release. Use the -all JAR: REA checks that it contains the jadx-headless-mcp session classes, JADX’s JadxDecompiler, Gson and the Kotlin MCP SDK server class. A plain JADX distribution or a thin JAR fails that check.
Put the JAR somewhere stable, such as ~/tools/jadx/. You will reference it by absolute path.
Step 3: Try it once from the CLI
Before touching agent config, check that the pieces work in a shell. Download the F-Droid APK from f-droid.org, record its hash, and run:
export REA_JADX_MCP_JAR="$HOME/tools/jadx/jadx-headless-mcp-0.7.1-all.jar"
export JAVA_HOME=/absolute/path/to/jdk-21 # optional
shasum -a 256 /absolute/path/F-Droid.apk
rea inspect-android-package /absolute/path/F-Droid.apk --json
Why: the CLI and the MCP server share the same workflow. If this works, any later MCP problem is an agent-config issue, not a Java or JAR issue. Keep the APK hash in your notes: the F-Droid download is updated over time, so your results are only comparable to someone else’s when the digests match.
The result includes the decoded manifest, package and SDK declarations, permissions and class/resource counts. It does not verify the APK signature (reported as not_performed) and never runs the app.
REA_JADX_MCP_JAR must be absolute. A relative path is treated as “not configured” and produces the message shown in the troubleshooting section.
Step 4: Add the variables to Claude Code
npx rea-agents setup registers REA, but it does not copy REA_JADX_MCP_JAR into the registration. Your MCP client starts the REA server as a child process with the environment written in its config file, not your shell’s, so add the variables to the rea entry yourself.
For Claude Code, setup writes the entry to ~/.claude.json (or the .claude.json in your configured Claude Code directory). Add an env block to the existing rea server. Keep the command and args that setup wrote. When setup runs through npx, they look like the example below, and the pinned version in args keeps your agent on 6.4.0; a global install writes an absolute path to rea instead:
{
"mcpServers": {
"rea": {
"command": "npx",
"args": ["-y", "[email protected]", "mcp"],
"env": {
"REA_JADX_MCP_JAR": "/Users/you/tools/jadx/jadx-headless-mcp-0.7.1-all.jar",
"JAVA_HOME": "/absolute/path/to/jdk-21"
}
}
}
}
Then quit Claude Code completely and start a new session, and check /mcp to see that rea is connected.
Why the full restart: an already-running client keeps the old server process and its old environment. REA’s setup keeps existing env overrides when you rerun it, so your values survive a later setup refresh.
Step 5: Add the variables to Codex
Codex reads ~/.codex/config.toml (or $CODEX_HOME/config.toml). Setup writes a [mcp_servers.rea] table there. Add an env table under it:
[mcp_servers.rea]
command = "npx"
args = ["-y", "[email protected]", "mcp"]
startup_timeout_sec = 30
[mcp_servers.rea.env]
REA_JADX_MCP_JAR = "/home/you/tools/jadx/jadx-headless-mcp-0.7.1-all.jar"
JAVA_HOME = "/absolute/path/to/jdk-21"
On Windows, use the paths in their native form, for example 'D:\tools\jadx-headless-mcp-0.7.1-all.jar' in a TOML literal string (single quotes, so backslashes are not escapes). Whatever command setup wrote on Windows (a cmd.exe /d /c npx ... wrapper or an absolute Node path), leave it as is.
Start a new Codex session after editing.
Step 6: Optional resource limits
Large APKs can exhaust JADX’s default heap. REA 6.4.0 reads two optional variables and turns them into JVM flags:
| Variable | JVM flag | Example |
|---|---|---|
REA_JADX_HEAP_MIB |
-Xmx<value>m |
8192 |
REA_JADX_ACTIVE_PROCESSOR_COUNT |
-XX:ActiveProcessorCount=<value> |
4 |
Both must be positive integers. Put them in the same env block as the JAR path. Each JADX operation has a 120-second deadline, so a larger heap is usually a better fix for big APKs than retrying.
Step 7: Ask the agent
After reconnecting, the agent has five Android tools:
| MCP tool | CLI command | Purpose |
|---|---|---|
inspect_android_package |
rea inspect-android-package |
Manifest, SDKs, permissions, counts |
search_android_classes |
rea search-android-classes |
Case-sensitive class-name substring search |
inspect_android_class |
rea inspect-android-class |
Fields, methods, overload indices |
inspect_android_method |
rea inspect-android-method |
Decompile one method (smali fallback) |
trace_android_references |
rea trace-android-references |
Incoming static references |
Every call takes the absolute APK path, so there is no “open” step. Good requests state the goal and the evidence you expect, not the tool order. For example:
Using REA's Android tools, inspect /absolute/path/F-Droid.apk.
1. Report the package name, minSdk/targetSdk and the declared permissions.
2. Find the class registered as android:name on <application> in the manifest,
list its methods, and decompile onCreate.
3. Tell me which statements come from decompiled code and which are your
inference. List anything you could not determine.
In /absolute/path/F-Droid.apk, find classes whose names contain "Repo".
Pick the one most likely to parse repository index data, show its methods,
and trace who references it. Cite class and method names for every claim.
Why this shape: the manifest gives the agent real class names to start from instead of guessed ones. Release builds are often shrunk, and classes not kept by the manifest or keep rules may have short, renamed names, so a name search alone can miss them. Asking for “unknowns” makes the agent separate observed facts from inference.
A few behaviors the agent should respect, and that you may need to point out:
- Overloads. If a class has several methods with the same name,
inspect_android_methodrefuses to guess. The agent must callinspect_android_classfirst and passoverload_index. Indices are local to that APK and engine; don’t reuse them across builds. - References are static.
trace_android_referencesreports incoming static references, not observed runtime calls. Reflection and dynamic loading remain unknown. It also cannot target one overload of an overloaded method and reports that as unsupported. - Decompiled Java is a derived view. A complete text result is not proof of complete semantics. Static initializers (
<clinit>) come back as smali for that reason.
Large APKs in 6.4.0
In npm 6.4.0, inspect_android_package takes only path and always returns the complete result, including the decoded manifest. The detail: "summary" option and the Android manifest view through inspect_analysis_view described in android-analysis.md are on repository main, not in 6.4.0; the 6.4.0 inspect-analysis-view covers layout, JavaScript application and native function Evidence. For large apps, keep requests narrow instead: one class search, one class, one method at a time.
For reusable prompt structure across targets, see Claude Code Reverse Engineering Skill.
Common errors and fixes
The messages below are the error texts written into REA 6.4.0. Your paths will differ.
“Set REA_JADX_MCP_JAR to the absolute path of a caller-supplied jadx-headless-mcp 0.7.1 JAR. REA does not download or install it.”
The variable is missing from the server’s environment or is relative. Setting it in your shell does not affect an MCP server started by Claude Code or Codex. Add it to the env block and restart the client.
“… does not provide jdk.compiler … A JRE cannot compile REA’s source bridge.”
java resolves to a JRE. Point JAVA_HOME at a full JDK, or fix PATH.
“… must be JDK 17 or newer and include jdk.compiler”
An older JDK comes first. Set JAVA_HOME to a 17+ JDK.
“JAVA_HOME must select an existing JDK by absolute path; omit it to use java on PATH.”
JAVA_HOME is relative or contains a shell expression the client did not expand (for example ~ in a JSON file). Use a literal absolute path. You will see this text from the CLI; in an MCP env block, REA 6.4.0 also validates JAVA_HOME when the server starts, so a relative value can stop the rea server from starting at all. If /mcp shows rea as failed, check JAVA_HOME first.
“… does not contain the required metadata bridge classes …”
Wrong JAR: usually the non-all artifact or a JADX release. Download the -all.jar for 0.7.1 and check its SHA-256.
“REA_JADX_HEAP_MIB must be a positive safe integer”
Use a plain number such as 8192, not 8g.
unsupported_host on Windows.
REA 6.4.0 supports JADX on Windows x64 only. If the message says the bundled Windows native artifact is missing or lacks protocol stdin, reinstall the matching Windows x64 package (npx [email protected] setup). WSL with a Linux JDK is an alternative.
“Cannot find java.exe on the selected Windows PATH.”
The MCP server did not inherit a PATH with Java. Set JAVA_HOME in the env block instead of relying on PATH.
Timeouts on big APKs.
Raise REA_JADX_HEAP_MIB, narrow the request, and avoid listing all classes (an empty search query) on very large apps.
rea doctor says nothing about JADX.
That is expected: in 6.4.0, rea doctor --provider accepts hopper, ghidra and ida. Check JADX by running rea inspect-android-package once from a shell, or ask the agent to call the binary_session MCP tool, which lists Android tool availability with a remediation message.
You have an .aab file.
App Bundles are not APKs and these tools do not treat them as one. Use the APK the project publishes.
Next steps
- The full static Android workflow, including Apktool resources, ADB and Flutter: /en/android-apk-analysis-rea/
- Prompt templates for agents: /en/claude-code-reverse-engineering-skill/
- How REA compares with single-engine MCP servers: /en/rea-vs-ghidramcp-ida-pro-mcp/
- Save and compare results across app versions: /en/rea-evidence-snapshots-compare/
- Reference: android-analysis.md (repository
main; check it against your installed version)