RE-MCP
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@RE-MCPRun the Bakugan DS quality suite"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
RE-MCP
A safety-first local Model Context Protocol server for ROM reverse-engineering workflows.
RE-MCP uses stdio and exposes narrow, tested tools rather than an unrestricted shell.
Current capabilities
General
Project Git status
Allowlisted npm verification
SHA-256 file verification
Capability and policy reporting
Bakugan DS
Compile, Ruff, mypy, pytest, or full quality suite
Regenerate Milestone 6E contracts
Run the Milestone 6E installer in dry-run mode
Generate the Milestone 6E roster analysis
Nintendo DS static analysis
Parse one canonical ROM identity using SHA-256
Read ARM9 and ARM7 executable metadata
Parse FAT physical file ranges
Reconstruct FNT/NitroFS paths while retaining unnamed FAT entries
Parse ARM9 and ARM7 overlay tables, including initialized range, BSS, file backing, compression metadata, and flags
Resolve ARM9/ARM7 runtime addresses against main executables and static overlay candidates
Reverse-map ROM offsets to structural, NitroFS, executable, and overlay relationships
Decode bounded ARM/Thumb instruction windows from deterministic file-backed NDS code sources
Build bounded direct-control-flow graphs across deterministic same-processor branch targets without recursively traversing calls
Classify deterministic single-instruction direct branch/call, literal-pool-slot, and PC-relative address references
Find bounded reverse cross-references through proven code seeds with explicit component coverage and truncation status
Discover bounded ARM9/ARM7 function-entry call graphs using only program-entry and deterministic resolved direct-call proof
Prove one requested function entry and distinguish complete negative evidence from incomplete proof coverage before analyzing its CFG
Search validated NDS bytes for exact/wildcard signatures, typed integers, ASCII strings, and UTF-16LE strings using canonical component or explicit whole-ROM scope
Extract validated ARM9, ARM7, overlay, or NitroFS components to a deterministic generated-analysis tree
Build a transactional static-analysis bundle without dumping every NitroFS asset
Optionally bootstrap and inspect a full-ROM-SHA-scoped Ghidra project through a configured local Ghidra 12.x installation
The source ROM is read-only. Static-analysis extraction artifacts are restricted to analysis/generated/nds/<sha-prefix>/ under the configured workspace. The static-analysis tools do not accept generic binary inputs, caller-selected output paths, arbitrary ROM offset/length extraction requests, or caller-defined raw search ranges.
Controlled NDS Mutation — Milestone 1
RE-MCP now exposes a narrow manifest-driven write/build surface for exact Nintendo DS ROM revisions:
nds_mutation_validatevalidates the strict mutation manifest, exact source SHA-256, canonical component selectors, original-byte/component guards, replacement artifacts, conflicts, and deterministic build identity without publishing output.nds_mutation_buildapplies the validated plan only to a temporary copy of the source ROM, reparses and verifies the result, attributes every changed byte to an approved operation, and atomically publishes the deterministic build.nds_mutation_verifyfreshly revalidates an existing deterministic build and its evidence; it never silently repairs or overwrites a divergent/tampered build.
The source ROM remains immutable. Milestone 1 supports only same-size guarded byte replacements and exact-size whole-component replacements selected through the canonical NDS model. Callers cannot provide an arbitrary ROM offset or caller-selected output paths.
Successful builds are published beneath:
output/nds/<source-sha-prefix>/<build-id>/alongside the rebuilt .nds file and deterministic evidence:
mutation-manifest.json
resolved-plan.json
verification.json
changed-components.json
output.sha256Verification requires the rebuilt ROM to parse through the canonical NDS model, preserves immutable structural geometry, revalidates compressed-overlay runtime images when applicable, checks every requested operation, and requires zero unexpected changed bytes. Re-running the same exact build may reuse it only after fresh verification; a mismatched or tampered deterministic output fails closed as a publish collision.
Milestone 1 deliberately does not provide variable-size rebuilding or relocation, FAT/FNT mutation, decoded compressed-overlay editing, BLZ recompression, generic source-ROM writes, arbitrary ROM offset writes, or caller-selected output paths. Those capabilities remain future controlled-build work rather than being inferred from this narrow mutation surface.
DeSmuME and ARM9 GDB
Start, inspect, and stop one server-owned DeSmuME process
Probe and wait for the owned ARM9 GDB port
Read the raw ARM9 register packet
Read up to 4096 bytes of ARM9 memory
Derive the main ARM9 executable range from the NDS ROM header before launch
Maintain an allowlist of the main ARM9 range plus up to 64 explicit or overlay executable ranges
Add, remove, and list controlled ARM9 software breakpoints
Continue execution, wait for a stop, interrupt/pause, and single-step up to 100 instructions
Decode DeSmuME ARM9 registers into
r0-r12,sp,lr,pc, andcpsrCapture structured stop context with bounded PC, stack, and optional memory windows
Match breakpoint hits, track hit counts, and retain ARM/Thumb execution history
Atomically capture raw registers plus labeled memory regions
Reset debugger state automatically when the owned emulator exits or its process generation changes
Correlate the exact stopped ARM9 PC/CPSR mode with the launch-time ROM SHA-256, canonical NDS ownership, bounded static instructions/references, and exact function-entry proof
RE-MCP does not expose register writes, general memory writes, watchpoints, or an arbitrary GDB-command tool.
Related MCP server: local-code-mcp
NDS Static Analysis
The canonical static-analysis surface consists of fourteen MCP tools:
nds_inspect_romnds_list_filesnds_list_overlaysnds_resolve_runtime_addressnds_resolve_rom_offsetnds_extract_componentnds_extract_analysis_bundlends_disassemble_rangends_analyze_control_flownds_list_referencesnds_find_xrefsnds_search_patternnds_discover_functionsnds_analyze_function
These canonical static tools are native-independent and have no DeSmuME, GDB, or Ghidra dependency. The optional Ghidra bridge described below consumes their canonical evidence but does not change their proof rules.
Canonical ROM model
nds_inspect_rom parses the ROM into one validated model containing:
full source SHA-256 and file size
game title, game code, maker code, unit code, capacity, and ROM version
ARM9 and ARM7 ROM offsets, entry addresses, RAM/load addresses, sizes, and runtime ranges
FNT and FAT regions
ARM9 and ARM7 overlay-table regions
NitroFS file count
ARM9 and ARM7 overlay counts
validated static executable/runtime candidate ranges
FAT remains authoritative for physical file byte ranges. FNT remains authoritative for names and directory hierarchy. Overlay records keep file-backed bytes, initialized runtime bytes, and BSS/runtime-only bytes distinct.
Address-resolution rules
nds_resolve_runtime_address does not guess when static overlay ranges overlap. If more than one main/overlay candidate contains an address, every candidate is returned with an ambiguity status.
BSS has no source ROM bytes, so BSS results return no ROM offset.
Compressed overlay bytes require special provenance handling. Stored FAT-backed bytes and decoded runtime bytes remain distinct: a decoded runtime byte never receives a fabricated direct ROM-byte offset. The resolver reports overlay/file/runtime/backing metadata, while validated BLZ-derived runtime images may be consumed by the later disassembly, reference, function, Ghidra, and runtime-correlation layers with romOffset: null.
nds_resolve_rom_offset performs the reverse classification and may return multiple valid relationships for one ROM byte, such as a NitroFS file plus an ARM9 overlay backing file. Compressed overlay backing bytes do not receive fabricated runtime addresses.
ARM/Thumb static disassembly
nds_disassemble_range and nds_analyze_control_flow use @alexaltea/capstone-js 5.0.9 through a narrow RE-MCP-owned ARM decoder interface. The backend is JavaScript + WebAssembly and is bundled with RE-MCP; no external Capstone, Ghidra, or radare2 executable is required.
Both tools accept only Nintendo DS sources resolved through the canonical ROM model. A request identifies arm9 or arm7, exactly one runtime address or ROM offset, an optional overlay ID used only as a static disambiguator, and an ARM/Thumb mode.
Supported modes are:
armthumbconservative
auto
Initial auto mode succeeds only when the resolved source is the matching ARM9 or ARM7 main header entry point, which is an ARM seed. Merely being in an executable range or overlay is not sufficient evidence. During CFG traversal, a deterministic direct edge may propagate its statically proven target mode. RE-MCP never decodes both modes and chooses the more plausible stream, and it does not use address bit 0 as a general-purpose mode guess.
ARM starts and deterministic ARM targets must be 4-byte aligned. Thumb starts and deterministic Thumb targets must be 2-byte aligned. Invalid alignment is rejected rather than rounded.
Decodable code sources are limited to validated initialized executable representations:
ARM9 main
ARM7 main
uncompressed ARM9 overlays
uncompressed ARM7 overlays
validated decoded BLZ runtime images for compressed overlays, represented as derived code with
romOffset: null
BSS remains runtime-only and is never fabricated into an instruction stream. If an uncompressed overlay's runtime initialized extent is larger than its physical backing file, only the exact file-backed prefix is eligible.
If multiple static code mappings contain a requested address or branch target, RE-MCP returns or records ambiguous-code-source rather than guessing which overlay is loaded. Supplying overlayId can select one starting static source, but it never claims that overlay is loaded at runtime. A deterministic branch that stays within that already selected component preserves its static component identity; a cross-component branch is re-resolved and traversed only when the same processor, source bytes, and target mode are all deterministic.
The ROM SHA-256 used to construct the canonical map is checked immediately before and after each top-level linear or CFG operation. A modified ROM invalidates the operation even if a decode callback also fails.
Linear disassembly limits
nds_disassemble_range decodes sequentially and classifies control flow without changing linear traversal based on branch instructions.
Limit | Default | Maximum |
Instructions | 32 | 256 |
Source bytes | 128 | 1,024 |
Decoding stops at the first instruction limit, byte limit, component boundary, instruction that would cross a component boundary, or undecodable instruction. A local decode failure returns the successfully decoded prefix with decode-stopped; RE-MCP never skips bytes and silently resumes. complete means the requested bounded window completed, not that a whole function or component was discovered.
Direct-control-flow limits and semantics
nds_analyze_control_flow builds basic blocks using a deterministic FIFO worklist. Block identity includes processor, component, overlay ID, runtime address, and mode, preventing cycles from repeatedly decoding the same block identity.
Limit | Default | Maximum |
Basic blocks | 64 | 256 |
Total instructions | 512 | 4,096 |
Total decoded source bytes | 2 KiB | 16 KiB |
Traversal edges | 128 | 1,024 |
All limits apply simultaneously. If any cap prevents further exploration, the graph returns status: "truncated" with explicit reasons chosen from block-limit, instruction-limit, byte-limit, and edge-limit. A truncated graph is a valid partial result and is never presented as complete.
Deterministic non-call direct branches may be traversed. Conditional branches may create both taken and valid same-component fall-through edges. Direct calls are fully annotated but their callees are not queued as CFG blocks. Indirect call targets are recorded as unresolved rather than guessed; caller-side sequential decoding can continue at the valid fall-through. Indirect branches and returns terminate the current block. Register-indirect targets never receive invented addresses or modes.
Static disassembly is independent of physical Catalina/DeSmuME Dynamic Debugging acceptance. Passing the Capstone.js tests or package smoke check does not constitute native emulator-debugger acceptance.
Proven reference discovery
Reference discovery is deliberately narrower than generic pattern or pointer searching. RE-MCP emits only deterministic single-instruction references in four classes:
direct-branchdirect-callliteral-poolpc-relative-address
Direct branch/call references also retain the canonical ARM/Thumb target mode when control-flow decoding proves it. Data/address references such as literal-pool slots do not receive an invented target mode.
literal-pool means the architecturally computed literal-pool slot address. The word stored in that slot is not automatically interpreted as another pointer or reference. Ordinary immediates are not references merely because their numeric value looks like a ROM/RAM address, and this milestone performs no register-value or broader data-flow inference.
nds_list_references is source → reference analysis. It decodes one bounded sequential ARM/Thumb window using the same source policy as nds_disassemble_range, classifies each decoded instruction, and does not follow branches or calls. Its bounds are therefore the same:
Limit | Default | Maximum |
Instructions | 32 | 256 |
Source bytes | 128 | 1,024 |
nds_find_xrefs is target → cross-reference analysis. It scans only caller-selected static scope for one processor, using deterministic FIFO traversal from proven code seeds. Main code has one implicit ARM seed at the processor's NDS header entry point. Overlays are scanned only when the caller supplies an explicit aligned ARM/Thumb seed for that uncompressed overlay or a proven direct branch/call from already scanned code reaches it. Selecting an overlay does not imply that it is loaded at runtime.
A direct call may expand xref search coverage because the purpose of this tool is to discover references in proven reachable code. This does not change nds_analyze_control_flow: the CFG tool still records direct calls without traversing their callees.
Reverse-xref search bounds are:
Limit | Default | Maximum |
Components | 32 | 128 |
Basic blocks | 128 | 512 |
Instructions | 2,048 | 16,384 |
Decoded source bytes | 8 KiB | 64 KiB |
Traversal edges | 512 | 4,096 |
Returned xrefs | 256 | 2,048 |
The result status is one of:
complete: all selected/considered components had proven seeds and their bounded reachable work completed;partial-coverage: at least one selected component could not be proven/scanned, but no global scan limit truncated explored work;truncated: one or more scan/result limits prevented complete bounded exploration.
Per-component coverage is explicit:
scannedno-proven-seedcompressed-overlay-not-decodableout-of-limit
A result containing zero xrefs is definitive for the selected static scope only when status === "complete". A zero-result partial-coverage or truncated response is intentionally not presented as proof that no xref exists.
Runtime targets may preserve resolved, ambiguous-overlay, BSS, compressed-overlay, or unmapped ownership metadata; reference matching still uses the exact requested runtime address. A ROM-offset target is accepted only when that offset maps to exactly one runtime address for the selected processor. Structural/NitroFS-only bytes are not reverse-xref targets in this milestone.
Reference searches are on-demand only. RE-MCP does not create a persistent whole-ROM xref database or index. Raw pattern search is a separate exact byte-level facility and does not change or broaden the deterministic reference classifier. Heuristic pointer discovery and arbitrary immediate-pointer inference remain deferred.
Proven function-entry discovery
nds_discover_functions and nds_analyze_function add a higher-level static layer without broadening the evidence model. A function entry is proven only by one of two sources:
the selected processor's NDS main executable entry address in ARM mode (
program-entry); ora deterministic resolved direct call whose target address, target ARM/Thumb mode, processor, component, and overlay ownership are exact (
direct-call).
The following are explicitly not function proof: direct or conditional branch targets, indirect calls, returns, alignment, prologue-looking bytes, pointer-like constants, selected overlay IDs, or caller-supplied seeds. Explicit seeds provide bounded code-search coverage only.
A proven function identity is deterministic across:
processor + component + overlay ID + runtime address + ARM/Thumb modends_discover_functions starts from the selected main program entry plus any validated coverage-only seeds, analyzes bounded CFGs, and follows deterministic resolved direct calls as function-to-function proof. Recursion and mutual recursion terminate through canonical function identity. Distinct direct call sites remain distinct evidence, while duplicate observations of the same site/target are deduplicated.
Direct branches remain intrafunction CFG edges and do not create functions. Indirect calls remain unresolved. The tool does not infer tail calls, shared epilogues, function ends, or exclusive byte ownership.
Whole-operation discovery bounds are:
Limit | Default | Maximum |
Components considered | 32 | 128 |
Proven functions | 128 | 1,024 |
Direct call sites | 512 | 8,192 |
Total basic blocks | 512 | 4,096 |
Total instructions | 4,096 | 32,768 |
Total decoded source bytes | 32 KiB | 256 KiB |
Total traversal edges | 2,048 | 16,384 |
Each individual function CFG is also capped independently:
Per-function CFG limit | Default | Maximum |
Basic blocks | 64 | 256 |
Instructions | 512 | 4,096 |
Decoded bytes | 2 KiB | 16 KiB |
Traversal edges | 128 | 1,024 |
Aggregate budgets always dominate. Before a CFG is analyzed, its local limits are clipped to the remaining whole-operation budget so one function cannot overshoot a global cap before returning control.
Discovery status is complete, partial-coverage, or truncated. Component coverage uses the same explicit vocabulary as xref search: scanned, no-proven-seed, compressed-overlay-not-decodable, and out-of-limit. Selecting an overlay does not disambiguate overlapping runtime ownership by itself. A call target becomes a proven function only when the canonical control-flow resolver actually produces one exact source.
nds_analyze_function focuses on one requested processor/address/mode/optional overlay identity. It first requires that identity to resolve uniquely to exact initialized, uncompressed file-backed code. It then returns one proof status:
proven: program-entry or at least one exact direct-call proof exists;not-proven-function-entry: the selected proof search completed with no qualifying proof;proof-inconclusive: no proof was found, but truncation or a coverage gap means a negative conclusion would be unsafe.
A positive proof remains proven even when unrelated selected coverage is incomplete; the coverage metadata still reports that incompleteness.
Focused proof-search bounds are:
Limit | Default | Maximum |
Components considered | 32 | 128 |
Blocks decoded | 128 | 512 |
Instructions decoded | 2,048 | 16,384 |
Decoded bytes | 8 KiB | 64 KiB |
Traversal edges | 512 | 4,096 |
Direct-call proof sites | 256 | 2,048 |
A full target CFG is returned only when the entry is proven, using the standard CFG bounds of 64/256 blocks, 512/4,096 instructions, 2 KiB/16 KiB decoded bytes, and 128/1,024 traversal edges.
Neither function tool claims an end address. Multiple returns, shared epilogues, jump tables, tail branches, interleaved data, and unreachable code make such a claim unsafe under this milestone. Heuristic function discovery and function-boundary ownership inference remain deferred.
This function layer is fully static and does not depend on physical Catalina/DeSmuME Dynamic Debugging acceptance.
Raw pattern and signature discovery
nds_search_pattern searches one validated .nds ROM for one deterministic byte-level pattern. It accepts exactly four pattern kinds:
byte-signatureintegerasciiutf16le
Byte signatures use whitespace-separated exact bytes plus the whole-byte wildcard ??:
12 34 56 78
12 34 ?? 78
AA ?? ?? FFConcrete bytes must contain exactly two hexadecimal digits. ?? is the only wildcard syntax. Nibble wildcards such as A?, regular expressions, alternation, repetition, fuzzy matching, and all-wildcard signatures are rejected.
Typed integers require an explicit width of 8, 16, or 32 bits, explicit little- or big-endian encoding, and explicit signedness. Alignment defaults to 1 byte and may be set explicitly to 1, 2, or 4 bytes. Alignment is checked against the absolute ROM offset; width never silently implies alignment.
ASCII search is exact and case-sensitive and rejects non-ASCII input. UTF-16LE search is also exact and case-sensitive. Neither string mode appends a null terminator, performs Unicode normalization, folds case, or tries alternate encodings.
Every encoded pattern must contain between 1 and 4,096 bytes.
The search scope is either:
whole-rom, which treats the validated ROM file as one physical matching domain; orcomponents, selecting any bounded combination of ARM9 main, ARM7 main, explicit ARM9/ARM7 overlay IDs, NitroFS file IDs, and exact NitroFS paths.
Component selections retain their canonical boundaries even when physical ranges overlap or are adjacent. Overlapping selected physical bytes are scanned once, but a component-scoped match is valid only when its complete byte span lies inside at least one selected canonical component. A signature cannot begin in one adjacent component and finish in another unless one selected component contains the entire span. whole-rom is the explicit mode that permits matches across structural/component boundaries.
Compressed overlays are searchable because this tool operates on physical ROM bytes. RE-MCP searches the exact stored FAT-backed compressed representation and marks the overlay ownership as compressed; it never decompresses the overlay or fabricates a decompressed runtime mapping.
Each physical hit is emitted once in ascending ROM-offset order and preserves every deterministic canonical owner known for the complete hit span. Ownership may include main executable, overlay storage, NitroFS/FAT file, parsed header metadata, FNT, FAT, overlay tables, or unmapped. An owner receives a runtime address only when the entire hit has a deterministic direct file-backed runtime mapping. For an uncompressed overlay this mapping is limited to the initialized prefix min(ramSize, romSize). Compressed overlay storage never receives a runtime address. bannerOffset alone does not define a validated banner extent, so the search tool does not invent banner ownership.
Overlapping matches are preserved. For example, searching AA AA in AA AA AA returns starts at offsets 0 and 1 relative to that region.
Search limits are:
Limit | Default | Maximum |
Returned page size | 100 | 1,000 |
Match-index | 0 | 99,999 |
Physical bytes scanned | 64 MiB | 512 MiB |
Context bytes per side | 0 | 64 |
Encoded pattern bytes | — | 4,096 |
Discovered matches | — | 100,000 |
offset is a match index, not a ROM-byte offset and not a scan-resume cursor. Increasing offset does not extend coverage after a maxScanBytes boundary. To inspect beyond a maxScanBytes boundary, raise the scan budget or narrow/change the selected scope.
A result is complete only when the selected physical scope was fully examined. Otherwise it is truncated, with explicit reasons chosen from:
scan-byte-limitmatch-count-limit
discoveredMatches counts only matches actually established before completion or truncation. nextOffset is non-null only when the current scan has already discovered later matches beyond the returned page; it does not speculate about unscanned bytes. Therefore a zero-hit result is definitive only when status === "complete".
Optional context bytes are informational only and do not affect matching or the physical scan-byte counter. Whole-ROM context is clipped only to ROM bounds. Component-scoped context remains inside a deterministic selected component that fully contains the hit, so it never leaks across an adjacent component boundary.
Pattern hits are byte-level facts only. RE-MCP does not promote a matching integer or byte sequence into a pointer, reference, function, table, or other semantic claim. There is no generic binary search input, caller-supplied byte buffer, arbitrary caller-defined ROM range, output path, persistent signature database, decompression path, or ROM mutation surface.
Controlled extraction
nds_extract_component accepts only canonical component selectors:
ARM9 main
ARM7 main
ARM9 overlay ID
ARM7 overlay ID
NitroFS file ID or exact parsed NitroFS path
The caller cannot provide a raw ROM offset, byte length, or output destination. RE-MCP chooses the deterministic location below:
analysis/generated/nds/<first-16-sha256-hex>/Before extraction, RE-MCP verifies that the source ROM still matches the SHA-256 used to construct the canonical map. Extracted artifacts record both the source ROM SHA-256 and their own SHA-256. Compressed overlays are extracted exactly as their stored FAT-backed bytes and remain compressed.
nds_extract_analysis_bundle builds the complete static-analysis package transactionally:
analysis/generated/nds/<sha-prefix>/
├── manifest.json
├── address-map.json
├── filesystem.json
├── overlays.json
├── arm9.bin
├── arm7.bin
└── overlays/
├── arm9/
└── arm7/The bundle is assembled in a temporary sibling directory and promoted only when complete. If replacement of an existing completed bundle fails, RE-MCP attempts to restore the previous complete bundle. The bundle intentionally does not extract every NitroFS asset; individual assets remain opt-in through nds_extract_component.
Example static-analysis workflow
Call
nds_inspect_romto validate the ROM and obtain the canonical structural summary.Use
nds_list_files,nds_list_overlays, and the address resolvers to identify deterministic code/file relationships.Call
nds_search_patternto locate an exact/wildcard byte signature, typed constant, or exact string within explicit canonical components or the whole validated ROM.Call
nds_disassemble_rangefor a bounded ARM/Thumb instruction window at a validated runtime address or ROM offset.Call
nds_list_referenceswhen you want deterministic references from a bounded sequential source window without traversal.Call
nds_analyze_control_flowwhen deterministic non-call direct branch traversal is useful.Call
nds_find_xrefsto search for references to one runtime target within an explicit same-processor static scope; inspectstatusand component coverage before treating a negative result as definitive.Call
nds_discover_functionsto turn program-entry/direct-call evidence into a bounded proven-function call graph, ornds_analyze_functionto prove and inspect one exact entry.Extract a specific validated component with
nds_extract_component, or generate the executable/metadata bundle withnds_extract_analysis_bundle, when an external artifact is actually needed.
The canonical static layer still does not implement heuristic function discovery, function-end or exclusive-boundary ownership inference, heuristic pointer discovery, persistent pattern/xref/function indexing, symbol recovery, generic binary disassembly/search, broad code/data heuristics, generic recompression/rebuilding, graphics decoding, runtime overlay-loaded-state detection, Ghidra-to-RE-MCP evidence promotion, watchpoints, ROM mutation, NitroFS rebuilding, or patch generation.
Controlled Ghidra Integration
Ghidra support is optional and deliberately sits on top of the canonical static-analysis layer. The controlled Ghidra MCP surface includes project bootstrap/status plus bounded read-only inspection:
nds_ghidra_bootstrapnds_ghidra_statusnds_ghidra_inspect_functionnds_ghidra_decompile_functionnds_ghidra_search_symbolsnds_ghidra_list_referencesnds_ghidra_list_calls
There is no generic Ghidra command, arbitrary script runner, caller-selected project path, loader/language selector, raw Ghidra argument list, arbitrary environment map, or caller-selected output path.
Configuration
RE_MCP_GHIDRA_HOME points to a supported local Ghidra 12.x installation. The reference acceptance release is Ghidra 12.1.2. RE_MCP_GHIDRA_TIMEOUT_MS defaults to 900000 ms (15 minutes) and is capped at 3600000 ms (60 minutes) per headless invocation.
These settings are optional at server startup. All non-Ghidra tools continue to work without them. Calling nds_ghidra_bootstrap without RE_MCP_GHIDRA_HOME returns ghidra-not-configured; nds_ghidra_status only reads deterministic ROM/project state and does not invoke Ghidra.
Example:
RE_MCP_WORKSPACE_ROOT=/absolute/path/to/rom-modding
RE_MCP_GHIDRA_HOME=/absolute/path/to/ghidra_12.1.2_PUBLIC
RE_MCP_GHIDRA_TIMEOUT_MS=900000RE-MCP derives support/analyzeHeadless beneath RE_MCP_GHIDRA_HOME, requires the installation to expose both ARM:LE:32:v5t and ARM:LE:32:v4t, invokes with an argument array and shell: false, and terminates a headless process if the timeout or RE_MCP_MAX_OUTPUT_BYTES bound is exceeded.
Project and bridge layout
Every full ROM SHA-256 receives an isolated persistent project/state root:
analysis/ghidra/nds/<full-sha256>/
├── project/
└── state/Replaceable bridge inputs stay separate:
analysis/generated/nds/<sha-prefix>/ghidra-bridge/
├── manifest.json
├── evidence/
├── results/
└── scripts/The ARM9 program uses ARM:LE:32:v5t; ARM7 uses ARM:LE:32:v4t. NDS overlays are represented as distinct Ghidra overlay address spaces at their canonical runtime offsets, so overlapping overlay addresses remain distinct. Validated decoded compressed-overlay runtime artifacts are imported as derived overlay code while retaining distinct stored-byte provenance; BSS remains runtime-only.
Evidence and analyst-work rules
RE-MCP imports only facts it has already established: canonical mappings, exact ARM/Thumb proven entries, program-entry/direct-call proof, and deterministic direct-call evidence. It does not invent function-body or function-end boundaries for Ghidra. Normal Ghidra auto-analysis runs after RE-MCP evidence is installed; functions, labels, strings, types, references, switch recovery, decompiler output, and other analysis that Ghidra derives remain non-authoritative to RE-MCP.
Reruns reconcile only RE-MCP-owned metadata and evidence. Analyst-created labels, comments, bookmarks, types, namespaces, function names/signatures, and Ghidra-only discoveries are preserved. If project ownership/state cannot be reconciled safely, RE-MCP returns project-state-mismatch instead of overwriting the project.
nds_ghidra_status is non-mutating: it does not validate/install Ghidra, regenerate the bridge, run analyzeHeadless, or modify project state. Read-only inspection tools require an already-current SHA-scoped project and disable auto-analysis during inspection.
The packaged RE-MCP bundle includes its Ghidra Java resources, but it does not bundle Ghidra itself. Normal CI/package smoke verifies the bridge, resources, runner, state model, and tool registration without downloading Ghidra. Real Ghidra 12.1.2 acceptance is a separate workflow and is also separate from the physical Intel Catalina/DeSmuME debugger acceptance gate. See docs/nds-ghidra-integration.md for the focused integration contract.
Current-stop NDS runtime correlation
nds_correlate_stop_context connects the server-owned stopped DeSmuME ARM9 session to the canonical static-analysis stack without resuming execution. The ROM path comes only from the owned process metadata, and every DeSmuME generation is bound to the full launch-time ROM SHA-256. Correlation reparses/revalidates that exact ROM, uses the observed PC and CPSR ARM/Thumb mode without breakpoint rewind heuristics, and revalidates the source SHA before returning.
Inputs are deliberately narrow:
timeoutMs: 100–30000, default 3000nearbyInstructions: 1–32, default 8referenceLimit: 0–64, default 16includeGhidra: boolean, defaultfalsedecompileGhidraFunction: boolean, defaultfalse; requiresincludeGhidra: true
By default, correlation performs no Ghidra work. With includeGhidra: true, each decodable canonical candidate may be enriched from an already-current Ghidra project that is scoped to the same full ROM SHA-256. Runtime correlation does not bootstrap Ghidra, reconcile/migrate projects, run auto-analysis, or mutate persistent analyst state. decompileGhidraFunction: true requests bounded decompilation only after the exact candidate function is found by the controlled read-only inspection path.
Overlapping overlay candidates remain separate canonical candidates. Each candidate may receive candidate-specific static/Ghidra interpretation using its exact overlay ID, but Ghidra output is never used to claim which overlay is loaded. Compressed overlays use the validated decoded derived runtime image and retain romOffset: null; BSS/runtime-only candidates never receive fabricated instructions.
The result keeps authority classes separate: observed runtime facts, canonical NDS ownership, RE-MCP static evidence, and ghidraDerived inference. A missing/stale Ghidra project is reported as not-ready for enrichment without invalidating an otherwise valid canonical/static correlation result.
Real Ghidra 12.1.2/JDK 21 acceptance covers both ARM9 main code and a compressed-overlay candidate and verifies that the ROM and persistent Ghidra project remain byte-for-byte unchanged. Physical Catalina/DeSmuME debugger acceptance remains a separate gate.
Dynamic-debugging tools
The controlled debugger surface consists of nine MCP tools:
desmume_breakpoint_adddesmume_breakpoint_removedesmume_breakpoint_listdesmume_continuedesmume_step_instructiondesmume_pausedesmume_wait_for_stopdesmume_capture_stop_contextdesmume_executable_ranges_replace
The existing desmume_read_register_packet, desmume_read_memory, desmume_probe_gdb, and desmume_wait_for_gdb tools share the same owned debugger session rather than opening a competing GDB connection.
Dynamic-debugging limits
GDB host is fixed to
127.0.0.1and the ARM9 port recorded for the current owned DeSmuME process.At most 32 active breakpoints are allowed.
Breakpoints must resolve inside the main ARM9 executable range or an explicitly allowlisted executable range.
ARM breakpoints must be 4-byte aligned; Thumb breakpoints must be 2-byte aligned.
autoexecution mode fails when ARM versus Thumb remains ambiguous.Continue and stop-wait requests are bounded to at most 30000 ms.
Single-step requests allow 1 through 100 instructions, with a bounded wait for every step.
Stop context captures 64 bytes around PC and up to 64 bytes from SP, clamped at address-space boundaries.
A stop-context request may add at most eight labeled regions, each from 1 through 4096 bytes.
Additional executable ranges are capped at 64.
Stop-context output is bounded by the configured
maxOutputBytesvalue.Emulator exit, explicit stop, or a new process generation invalidates the old debugger session and session-scoped state.
Example debugger workflow
Call
desmume_startwith a verified launcher, the intended.ndsROM, and an ARM9 GDB port. RE-MCP parses the ROM header before launch and initializes the debugger with the derived main ARM9 range.Use
desmume_wait_for_gdbordesmume_probe_gdbto confirm the owned stub is reachable.Add a validated breakpoint with
desmume_breakpoint_add. Specifyarmorthumbwhen mode is not already unambiguous.Call
desmume_continue, optionally supplyingexpectedBreakpointId. Context capture is enabled by default.Inspect the returned stop reason, decoded
pc/cpsr, matched breakpoint, hit count, and bounded memory windows.Call
nds_correlate_stop_contextwhile stopped to map the exact live PC/mode back to canonical code and bounded static evidence; opt into already-current Ghidra enrichment only when needed.Use
desmume_step_instructionfor a bounded instruction sequence while stopped.If execution is running after a timeout, use
desmume_wait_for_stopordesmume_pauserather than issuing a stopped-state command.Remove the breakpoint with
desmume_breakpoint_removewhen finished.Call
desmume_stopor restart the emulator. Session-scoped breakpoints, executable ranges, stop state, and the old GDB connection are invalidated.
Requirements
Node.js 20 or newer
An MCP host that can launch local stdio servers
A dedicated workspace containing the intended repositories and private ROM-development inputs
For Ghidra bootstrap/inspection or optional runtime-correlation enrichment, a supported local Ghidra 12.x installation; Ghidra 12.1.2 is the reference acceptance release
For emulator tools, a verified DeSmuME debug bundle
Downloadable RE-MCP bundle
The Package GitHub Actions workflow publishes a re-mcp-downloadable-bundle artifact containing:
Compiled JavaScript
Production dependencies, including the pinned Capstone.js WebAssembly backend
RE-MCP-owned Ghidra Java bridge/inspection resources
Configuration template
Installation self-check
SHA-256 checksum
Before publishing the artifact, the package workflow performs a production-only install inside the assembled bundle, verifies the packaged Ghidra resources and controlled tool registration, requires the runtime-correlation service/Ghidra adapter/tool modules, initializes the packaged Capstone.js runtime, decodes known ARM and Thumb instructions, smoke-classifies an ARM direct call plus a Thumb PC-relative literal-slot reference, smoke-searches a temporary valid NDS ROM through the compiled pattern-search service to verify wildcard overlap and canonical ARM9 ownership, and runs a packaged ARM9 BL fixture through proven-function discovery to verify program-entry/direct-call proof and call-edge construction. The package check does not require a Ghidra installation or external disassembler download.
After downloading and extracting the archive:
cd re-mcp-0.6.0
node scripts/check-install.mjs .The same self-check verifies the required package files, assembled function/Ghidra/runtime-correlation tool registration, Ghidra resources, ARM/Thumb decoder fixtures, deterministic reference classifier, packaged NDS pattern-search path, and packaged proven-function discovery path before reporting ok: true.
Copy mcp-config.example.json, replace the required workspace/server paths, and either set the optional Ghidra paths for Ghidra bootstrap/inspection/runtime enrichment or remove those optional environment entries when Ghidra tools are not needed.
Build the Catalina-native DeSmuME debugger bundle
The manual Build Catalina-Native DeSmuME Debug Bundle workflow builds the DeSmuME 0.9.13 Cocoa dev+ application for Intel x86_64 Macs with a macOS 10.15 deployment target. It applies a narrow patch that starts the existing ARM9 GDB stub when RE_MCP_ARM9_GDB_PORT is supplied.
To run it:
Open the repository's Actions tab.
Select Build Catalina-Native DeSmuME Debug Bundle.
Select branch
feature/catalina-native-desmume.Choose Run workflow.
Download
desmume-catalina-native-debug-bundleafter the job finishes.Verify
desmume-catalina-native-debug.zipagainst the accompanying.zip.sha256file before extraction.
After extraction on the Catalina Mac:
xattr -dr com.apple.quarantine desmume-catalina-native-debug
chmod +x desmume-catalina-native-debug/run-desmume-debug.command
./desmume-catalina-native-debug/run-desmume-debug.command \
/absolute/path/to/Bakugan.nds 20000Only remove quarantine after verifying the checksum and confirming that the artifact came from the expected workflow run.
Catalina dynamic-debugging acceptance
Automated CI verifies packet framing, breakpoint lifecycle, execution state, timeout behavior, register decoding, context capture, lifecycle reset, and MCP validation. Final acceptance still requires the verified native DeSmuME bundle on the target Intel macOS Catalina system.
Follow docs/dynamic-debugging-catalina-acceptance.md to verify breakpoint installation, continue/stop, PC and CPSR capture, single stepping, pause, breakpoint removal, debugger-state reset after emulator restart, and the final stopped nds_correlate_stop_context check. That final check must confirm the launch SHA-256, observed PC/CPSR, canonical candidates, and bounded static interpretation against the real stop.
Build RE-MCP from source
npm install
npm run check
npm run buildRun directly without Ghidra integration:
RE_MCP_WORKSPACE_ROOT=/absolute/path/to/rom-modding \
node dist/index.jsRun with the optional controlled Ghidra integration enabled:
RE_MCP_WORKSPACE_ROOT=/absolute/path/to/rom-modding \
RE_MCP_GHIDRA_HOME=/absolute/path/to/ghidra_12.1.2_PUBLIC \
RE_MCP_GHIDRA_TIMEOUT_MS=900000 \
node dist/index.jsThe server refuses to start without an explicit workspace root. A Ghidra home is not required unless a Ghidra operation is requested.
DeSmuME launcher contract
The currently verified Linux launcher contract is:
run-desmume-debug.sh --arm9gdb=20000 /path/to/game.ndsThe Catalina-native bundle uses:
run-desmume-debug.command /path/to/game.nds 20000RE-MCP owns at most one emulator child process per server instance. It rejects duplicate starts, captures bounded logs, resets session-scoped debugger state when that process exits, and terminates the owned emulator during MCP shutdown.
Security model
No arbitrary shell tool
No shell interpolation
Fixed executable and argument construction
Workspace path containment
Process timeouts and bounded output
Minimal child-process environment
Milestone 6E installation restricted to dry-run mode
One server-owned DeSmuME process
GDB restricted to the owned localhost ARM9 port
Breakpoints restricted to validated executable ranges
Maximum 32 active breakpoints and 100 instructions per single-step request
Bounded continue, wait, pause, register, memory, and stop-context operations
Controlled GDB packets only: software breakpoint insert/remove, continue, single-step, interrupt, register read, bounded memory read, and stop-status query
No arbitrary GDB packet tool
No register writes, general memory writes, or watchpoints
Runtime evidence restricted to project
analysis/generatedRuntime correlation derives ROM identity, stopped PC/mode, and process generation only from the owned session; callers cannot supply a ROM path, PC/registers, processor, overlay selector, arbitrary GDB memory range, or arbitrary Ghidra project/program/script path
Runtime correlation defaults to zero Ghidra work; optional Ghidra enrichment requires an already-current full-SHA-scoped project and never bootstraps/reconciles/mutates it
Runtime correlation preserves overlapping overlay candidates and never turns candidate-specific static/Ghidra interpretation into a loaded-overlay claim
NDS source ROMs are read-only; generated static-analysis artifacts are restricted to
analysis/generated/nds/<sha-prefix>/NDS extraction accepts canonical component selectors only; no raw offset/length extraction or caller-controlled output path
NDS disassembly and reference listing accept canonical NDS code mappings only; no generic binary path, caller-provided byte buffer, arbitrary base address, or arbitrary raw byte range
nds_search_patternaccepts only a validated NDS ROM plus canonical component scope or explicit whole-ROM scope; no generic binary path, caller-supplied byte buffer, caller-defined start/end range, runtime-memory target, or output pathPattern search is bounded to 4,096 encoded pattern bytes, 512 MiB scanned bytes, 1,000 returned hits per page, 100,000 discovered matches, and 64 context bytes per side
Component-scoped pattern hits require full-span containment in at least one selected canonical component; physical overlap is deduplicated and adjacent components do not authorize cross-boundary matches
Compressed overlays are pattern-searched only as exact stored FAT-backed bytes; pattern search performs no decompression and fabricates no compressed-runtime address mapping
Pattern hits remain byte-level facts and are not promoted into pointers, references, functions, or tables
ARM/Thumb linear decoding and source-reference listing are bounded to 256 instructions and 1,024 bytes per request
CFG traversal is bounded to 256 blocks, 4,096 instructions, 16 KiB decoded bytes, and 1,024 traversal edges
Reverse-xref traversal is bounded to 128 components, 512 blocks, 16,384 instructions, 64 KiB decoded bytes, 4,096 traversal edges, and 2,048 returned xrefs
Only deterministic single-instruction direct branch/call, literal-pool-slot, and PC-relative address-construction references are emitted
Literal-pool contents and pointer-looking ordinary immediates are not interpreted as references
nds_find_xrefsmay follow proven direct calls for search coverage, whilends_analyze_control_flowcontinues to annotate calls without traversing themReverse-xref coverage gaps and truncation are explicit; a zero-xref result is definitive for selected scope only when status is
completeProven function discovery is bounded to 128 selected components, 1,024 functions, 8,192 direct call sites, 4,096 blocks, 32,768 instructions, 256 KiB decoded bytes, and 16,384 traversal edges
Focused function proof is bounded to 128 components, 512 blocks, 16,384 instructions, 64 KiB decoded bytes, 4,096 traversal edges, and 2,048 retained direct-call proof sites
Function entries are proven only by NDS program-entry or exact deterministic direct-call evidence; direct branches, indirect calls, returns, explicit seeds, alignment, and prologue-like bytes do not prove functions
Function tools do not infer end addresses, tail calls, shared-epilogue ownership, or exclusive function byte ranges
Function proof preserves exact processor/component/overlay/address/mode identity; scope selection never turns ambiguous overlay ownership into proof
Explicit function seeds provide coverage only; unseeded components remain explicit coverage gaps and incomplete negative proof returns
proof-inconclusiveNo persistent pattern/xref/function index or heuristic pointer/function discovery
Indirect targets are never guessed
Compressed overlay runtime code is consumed only from validated decoded derived artifacts and never receives a fabricated direct ROM offset; BSS remains non-decodable runtime-only memory
Overlapping static overlay ranges are reported as ambiguous candidates rather than guessed
Static overlay selection/disassembly/reference/function search never claims that an overlay is loaded at runtime
Static operations revalidate the source ROM SHA-256 before and after decoding/searching
Ghidra bootstrap derives one
analyzeHeadlessexecutable fromRE_MCP_GHIDRA_HOME; callers cannot provide executable paths, project paths, loaders, languages, scripts, raw Ghidra arguments, environment maps, or output pathsGhidra projects are isolated by full source ROM SHA-256; generated bridge inputs remain separate from persistent analyst state
NDS overlays use distinct Ghidra overlay address spaces; validated compressed-overlay runtime images remain derived and separate from stored-byte provenance
RE-MCP imports proven entries/modes/direct-call evidence only and does not promote Ghidra-derived functions, bodies, types, or other heuristics into canonical RE-MCP evidence
Ghidra reruns preserve analyst-created state and fail
project-state-mismatchinstead of destructively repairing unrecognized ownershipGhidra headless execution is shell-free, timeout-bounded, output-bounded, and source-ROM identity is revalidated during bootstrap
nds_ghidra_statusis non-mutating and does not invoke GhidraControlled Ghidra inspection and correlation enrichment are read-only and disable auto-analysis; correlation does not create or repair projects
Debugger session, breakpoint registry, executable ranges, and stop state reset with emulator lifecycle
No attachment to unrelated emulator processes
Do not use your general home directory as RE_MCP_WORKSPACE_ROOT. Create a dedicated directory containing only the repositories and private inputs intended for RE-MCP.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseBqualityDmaintenanceAn MCP server providing direct access to gdb-multiarch for debugging Nintendo Switch executables on Yuzu or hardware via a GDB stub. It features specialized tools for offset-based breakpoints, instruction patching, and frame-pointer backtraces relative to the game's base address.333MIT
- Alicense-qualityBmaintenanceSafe local MCP server for Windows to list, read, search, patch, backup, and verify code files in allowed folders, with Git integration and dry-run diffs.1MIT
- Alicense-qualityAmaintenanceMCP server for reverse engineering Windows executables and related binary formats, offering static analysis, Ghidra-assisted function recovery, plugin-driven tooling, and optional isolated Windows runtime execution.5238MIT
- Alicense-qualityDmaintenanceAn MCP server that exposes Python debugging tools backed by debugpy, providing a focused debugging surface for local scripts.MIT
Related MCP Connectors
A MCP server built for developers enabling Git based project management with project and personal…
Scans MCP servers for tool poisoning, prompt injection and supply chain risks.
An MCP server for deep research or task groups
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/79cbd8hmgj-wq/RE-MCP'
If you have feedback or need assistance with the MCP directory API, please join our Discord server