rea
CLI Toolmorluto/rea
MCP server and CLI that lets AI agents reverse engineer apps and binaries.
Overview
REA connects AI coding agents to reverse engineering tools over MCP, analyzing native binaries, JavaScript/Electron apps, .NET assemblies, and websites without source code. It integrates with Hopper and Ghidra for deep static analysis, runs locally, and returns evidence-backed findings with stated limitations. It can also be used directly from the terminal as a CLI.
README Preview
\n\n**English** · [简体中文](README_zh.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [العربية](README_ar.md)\n\n# REA: Reverse Engineer Anything\n\n### One MCP for reverse engineering across binaries, applications, and runtime behavior.\n\n**See a feature you like. Understand how it works, down to the binary level.**\n\n[](https://www.npmjs.com/package/rea-agents)\n[](https://github.com/morluto/rea/actions/workflows/ci.yml)\n[](#tool-catalog-for-investigation)\n[](https://nodejs.org/)\n[](LICENSE)\n[](https://discord.gg/GkcryMnJDM)\n\n\n\n[Quick start](#quick-start) · [Current status](#current-status) · [Investigation model](#the-investigation-model) · [Tool catalog](#tool-catalog-for-investigation) · [Roadmap](#roadmap) · [How it works](#how-it-works)\n\nnpx rea-agents setup\n\n\n\n\n\n\n\n\n\n\n \n \n Join the Reverse Engineering Community\n \n Discord · Q&A · Show and Tell\n\n\n\n\n\n\n\n\n---\n\nSee a feature in an app that you want in your own product? Ask your agent to investigate it with REA. It can inspect the app without its source code, explain how the feature works, show the evidence, and build a version for your project.\n\nREA connects your agent to tools for inspecting native binaries, JavaScript and Electron apps, .NET assemblies, and websites. You can also use the same tools from your terminal. Analysis runs locally, and results include the evidence and limitations behind each conclusion.\n\nSetup registers REA with your agent and installs matching workflow instructions. Native analysis can use an existing Hopper or Ghidra installation; setup can optionally install Hopper with approval. Static JavaScript analysis needs neither engine.\n\n## Quick start\n\n### Run setup (recommended)\n\nSet up REA with your agent:\n\n```bash\nnpx rea-agents setup\n```\n\nChoose which supported agents should use REA, then review the exact paths and changes before approving. Existing REA registrations are selected by default; newly detected agents are avail
FAQ (5)
TroubleshootingWhy do Ghidra MCP responses (e.g. list_procedures) hit the 10 MiB resource_constraint limit at only ~20k rows, and how to fix duplicate raw_result data?
This was a bug in REA ≤6.7.0: the Ghidra adapter (GhidraProviderClient.ts projectExecution) unconditionally copied the bridge reply into raw_result, even though it was byte-identical to normalized_result. Each Evidence record then carried the result twice, and since MCP responses embed Evidence in both text and structured content, replies held ~4 copies — halving the inventory that fits in the 10 MiB response budget (large inventories failed with resource_constraint at ~20k instead of ~40k rows). Fix: upgrade to a build containing PR #5355, which sets raw_result to null whenever normalized_result and raw_result are byte-identical (per the contract in AnalysisProvider.ts and docs/mcp-contracts.md, raw_result should only hold a provider representation distinct from result). No data is lost because the wrapper's limitations are already merged into Evidence limitations. Breaking note: clients that parsed analyze_function output via raw_result.value must switch to normalized_result, since raw_result is now null for these operations.
TroubleshootingWhy does rizin_debug_session_status retain more than the 16KB recent output budget for CJK or emoji output?
The recent-frame history was truncated by code-point count instead of UTF-8 bytes, so 3-byte CJK characters like 雪 tripled the retained size (49,152 bytes instead of 16,384), and cuts could split an emoji's surrogate pair, leaving ill-formed UTF-8. This is fixed in version 6.8.0 via PR #5246: upgrade to 6.8.0+. The fix measures byte length (e.g. TextEncoder().encode) and only cuts on code-point boundaries, keeping CJK output within the 16,384-byte budget and emoji prefixes well-formed.
TroubleshootingWhy does Rizin recent_output exceed its 16 KB frame limit with CJK or emoji output, and how to fix the truncation retaining 3x the UTF-8 byte budget?
Root cause: in src/rizin/RizinDebugSessionManager.ts (lines 1068-1078) the recent-frame truncation slices frame.text — a decoded JS string — so String.prototype.slice counts UTF-16 code units, not bytes. With MAX_RECENT_FRAME_BYTES = 16 * 1024, a 3-byte CJK character is charged at only 1 code unit, so one frame retains up to 49,152 bytes (3x budget) and the 64-frame ring balloons from the intended 1 MiB to 3 MiB.
Fix: slice on the byte side with a Buffer and walk back UTF-8 continuation bytes, the same approach already used by #startupCapturedOutput at lines 1046-1052:
const bytes = Buffer.from(frame.text, "utf8");
let end = Math.min(bytes.length, MAX_RECENT_FRAME_BYTES);
// avoid splitting a multi-byte sequence: back off continuation bytes (0b10xxxxxx)
while (end > 0 && (bytes[end] & 0xc0) === 0x80) end--;
const kept = bytes.subarray(0, end).toString("utf8");
const historyFrame =
frameBytes > MAX_RECENT_FRAME_BYTES || frame.truncated
? `${kept}[recent frame truncated]`
: frame.text;
Note: the existing test only covers ASCII (where code units == bytes), which is why the regression went unnoticed — add CJK/emoji truncation tests. This issue (#5242) is a duplicate of #5091; track the fix there.
TroubleshootingHow to fix 'Owned JavaScript worker exhausted its heap' error in rea when analyzing React bundles?
This is a known regression in rea's JavaScript member-mutation pass introduced by PR #4243 (commit 42fc6c53a). Cycle detection moved from bindings to individual initializers, so results inside alias cycles are never memoized and the analysis enumerates initializer orders, exhausting the worker heap (even at 4 GiB) on React-style bundles (playwright-core, Notion renderer, Next.js framework-*.js chunks).
Fixes:
1. Upgrade past the npm release. The bug is present in rea-agents@6.7.0 (current npm latest) but fixed on main in commit e4c3d7239. Install a build from main (verified working at 58d6043) instead of the npm package.
2. Workaround: pin/downgrade to a build from before #4243 (commit 1bd5c5c87), which analyzes the same files in ~1–2 s.
Notes: Raising the heap limit does not help — the search grows exponentially and exhausts 4 GiB in ~17 s. After the fix, the same files complete in ~10 s at ~430 MB RSS with no resource-constraint unknowns.
TroubleshootingHow to fix 'AnalysisResourceConstraintError' (96 MiB allowance) when match_native_functions refuses large function inventories?
match_native_functions (main only, not in the 6.7.0 release) charges a fixed 96 MiB representation allowance per call, refusing Ghidra-shaped inventories above ~10k rows per side (plain address+name rows above ~19.5k). Fixes: (1) PR #7108 corrects the accounting so the shared inventory Evidence ID is charged once instead of per function, raising the limit to ~22k Ghidra-shaped rows per side under the unchanged 96 MiB default. (2) PR #7112 adds an override: set native.matching_representation_bytes in config (copied by rea setup) or the REA_NATIVE_MATCHING_REPRESENTATION_BYTES environment variable — default 96 MiB, maximum 1 GiB. At 512 MiB, 50,000 rows per side matched successfully (CLI: 8.7 s, 1.34 GB peak memory; MCP: 2.7 s). Refusals remain non-retryable and name the limit and override in details.limit.