Skip to main content
Glama

borer is a reverse-engineering engine whose user is an AI agent. One Rust binary: an MCP server when run with no arguments, a command-line tool with them. It owns its loader, lifters, emulator and decompiler, and it has no GUI.

Everything below is real output of borer 0.2.12 (release build, commit 7d9bb42c) on two files of Windows 11: notepad.exe (sha256 2d0b3948...2efb158f) and ntdll.dll (sha256 b072efae...ce5aed21). Lines cut for length are marked .... Addresses differ on another build: borer symbols ntdll.dll RtlComputeCrc32 finds the export, and its decompile names the two functions it forwards to.

WARNING

borer is early-stage. Tool names, output and resource URIs still change between releases. Please report anything that looks wrong.

What it does

One call says what a file is and where to read

$ borer triage notepad.exe
# PE x86-64, 360448 bytes, entry 0x1400019c0, 8 sections, 340 imports, 0 exports, 523 functions
# built: MSVC, cl 19.38 build 33145, Visual Studio 2022 17.8 (high: Rich header linker entry 0x102.33145; ...)
# protection: plain: no packer marks
# dynamic imports: 13 functions resolved at runtime, 1 library loaded, from 16 call site groups
# capabilities: 6 of 16 capabilities present across 340 imports
#   filesystem                CreateFileW, DeleteFileW, FindFirstFileW, ReadFile, WriteFile
#   persistence               RegCreateKeyExW, RegCreateKeyW, RegSetKeyValueW, RegSetValueExW
...
# blind spot: 4 of 18 GetProcAddress calls pass a name the decompiler cannot read (computed, hashed or decrypted): the imports resolved there stay unseen
# read first: no protection signal fired, so the ranking is by the strings and imports each function references
0x140013150  sub_140013150                references 13 strings and 47 imports
0x140012a28  sub_140012a28                references 38 strings and 3 imports
...
# next: function_summary(addr), then decompile(addr); emulate/<addr> runs one

The whole answer is 2,408 bytes. It ends with what to call next, and it names its own blind spot instead of hiding it.

The decompiler checks its own work

On x86, x86-64, arm64 and 32-bit ARM, every function's header says how much of the text was checked against the machine code it came from:

$ borer decompile ntdll.dll 0x180102184
// sub_180102184 @ 0x180102184: 6 instructions, 3 blocks
// checked vs binary: statements as emitted, 3/3 blocks: 3 proved vs lift; unchecked: C spelling, later text passes
// signature: uint64_t sub_180102184(uint64_t a1, uint8_t *a2, uint32_t a3)
...
if (a2 == nullptr) {
  return 0;
}
return sub_1800ed890(a1, a2, a3);

certify goes one step further: it runs the final text as C beside the binary on the same inputs and grades the result.

$ borer certify ntdll.dll 0x180102184
# certify for sub_180102184 (0x180102184): what of its decompilation was checked against the binary; agreement is evidence, not proof
lift	statements as emitted, 3/3 blocks: 3 proved vs lift; unchecked: C spelling, later text passes
text	20 of 64 inputs agree as ISO C, 44 unchecked, 0 differ (seed 1, 20000 steps each)
unchecked	44	the binary did not return: fault at 0x500000010050
reached	2 of 3 blocks; never reached: 0x180102191
grade	agrees	every input checked agrees (they reached 2 of 3 blocks) and no lifted block is contradicted

The block check covers x86, x86-64, arm64 and 32-bit ARM; RISC-V output has no check header yet. certify runs on x86 and x86-64. The header always says what was not checked (here the C spelling and the later text passes). An instruction borer cannot model stays in the text as /* asm: ... */. It is never replaced by a guess.

It runs code without running the file

borer has its own emulator, so an agent can call a function on typed inputs and read the result. Here the function RtlComputeCrc32 forwards to gets the standard CRC-32 test string, and answers the published check value 0xcbf43926. The last column says the decompiled text, run on the same inputs, gave the same results:

$ borer run ntdll.dll '0x1800ed890/0,"123456789",9;0,"",0;0,"a",1'
# run uint32_t sub_1800ed890(uint32_t a1, uint8_t *a2, uint64_t a3): 3 inputs, at most 1000000 steps each; the text agrees on 3, differs on 0, unchecked on 0
# args	returns	stop	steps	writes	text
0 "123456789" 9	0xcbf43926	returned	126	-	agrees
0 "" 0	0	returned	38	-	agrees
0 "a" 1	0xe8b7be43	returned	47	-	agrees

The same emulator drives input solving (solve), unpacking (unpack), devirtualization (devirt), taint (taint) and struct recovery (layout). The file itself is never executed and never modified.

It says what it does not know

Ask the exported wrapper the same question and borer does not round its answer up:

$ borer run ntdll.dll 'RtlComputeCrc32/0,"123456789",9'
# run uint64_t RtlComputeCrc32(uint64_t a1, uint64_t a2, uint64_t a3): 1 input, at most 1000000 steps each; the text agrees on 0, differs on 0, unchecked on 1
...
0 "123456789" 9	0xcbf43926	returned	131	-	unchecked
# input 1 unchecked at line 6 `return sub_180102184(a1, a2, a3);`: the text states only the low 8 bits of the result; the bits above are the machine's own

A heuristic answer names its evidence. borer why <address> lists where each fact about an address came from:

$ borer why ntdll.dll 0x1800ed890
# 0x1800ed890 is the entry of sub_1800ed890: 864 bytes, 36 blocks, 215 instructions
# section .text at 0x180001000, r-x and code
# name: nothing names it, so the name is the address
# discovered by: an entry in the exception or symbol table, then 1 call site(s)
# referenced from 1 site(s): 0x18010218c in sub_180102184

It finds code by what it is

$ borer crypto ntdll.dll
# 17 crypto/hash constant hits: 6 algorithm(s) across 16 function(s)
# algorithm        sites  functions  where
CRC-32            8      3          data in .rdata, read by sub_18015431c, sub_180154538, crc32_fn
MD5               3      9          data in .rdata, read by sha1_or_ripemd160_fn, MD5Init, MD5Final, ...
SHA-256           2      2          data in .rdata, read by sha256_fn, sha256_or_blake2s_fn
...
$ borer sig ntdll.dll 0x180102184
# signature of sub_180102184 at 0x180102184: 8 bytes, 1 wildcarded, unique in the image
pattern  48 85 D2 74 ?? 45 8B C0
...

Classes, vtables, message formats, opcode dispatchers, decoded and generated strings, IOCs and YARA rules come from the same engine: borer help lists every command, and borer://help every resource.

Answers sized for a context window

Every list is filtered and paged on the server, and says how much it left out:

$ borer functions notepad.exe "" 0 3
# showing 3 of 481 functions; 39 import thunks left out, filter=thunk lists them; 3 exception funclets left out, filter=funclet lists them
# entry size blocks instrs callers callees src name
0x140001008 133 3 26 5 2 - sub_140001008
0x140001094 288 12 67 2 2 - sub_140001094
0x1400011bc 149 3 27 5 2 - sub_1400011bc

Over MCP an answer can also be asked again as a diff: see "Ask again, get only what changed" below. Every answer ends with an evidence id; the session keeps the text behind the last 256 ids, so a recent answer can be fetched again exactly.

Same question, same bytes

The same binary and the same query give the same bytes, on one core or all of them, in any order. That is a tested contract, not a habit: the gate decompiles a corpus twice and in parallel and compares.

$ borer decompile notepad.exe 0x140013150 full 0 100000 | sha256sum
b45d12089ef2280f...
$ borer decompile notepad.exe 0x140013150 full 0 100000 | sha256sum
b45d12089ef2280f...

A hostile file gets an error, not a crash

Reads are bounds-checked, scans capped and walks fuel-limited, and the loaders are fuzzed in CI. The first 300 bytes of notepad.exe:

$ borer triage cut.exe
Error: loading cut.exe

Caused by:
    the file starts like a PE but its headers do not parse, even leniently: it is truncated or damaged (300 bytes; ...)

Fast enough to read everything

On notepad.exe (523 functions), borer triage loads, discovers and ranks the whole file in about 2.3 seconds (release build 7d9bb42c, three runs: 2.24, 2.41, 2.29 s). A cold decompile takes under 7 ms per function at the 95th percentile (tests/bench.rs, release build 70b5db2c, three runs: 6.64, 6.48, 6.49 ms). Both on one desktop machine. The time bars, 25 ms at p95 and 5 s at worst per function, are part of the gate. An agent can read every function instead of guessing where to look.

Related MCP server: kuna_mcp

Platforms

Architecture

Disassemble

Decompile

Emulation tools

x86, x86-64

yes

yes

yes

arm64

yes

yes

yes

32-bit ARM (A32, Thumb-2)

yes

yes

no

RISC-V (RV32, RV64 GC)

yes

yes

no

Formats: PE, ELF, Mach-O, raw code and memory dumps.

Install

macOS and Linux:

curl -fsSL https://borer.lol/install | sh

Windows (PowerShell):

irm https://borer.lol/install.ps1 | iex

The installer downloads the latest release, checks it against the release's SHA256SUMS and registers borer with every MCP client it finds: Claude Code, Claude Desktop, Cursor, VS Code, Windsurf, Zed, Codex CLI, Gemini CLI and 20 more. Restart the clients it names afterwards.

Package managers install the same release binary:

cargo binstall borer                  # or cargo install borer
brew install imjustprism/tap/borer    # macOS, Linux x64
npm install -g @imjustprism/borer     # or npx -y @imjustprism/borer
pipx install borer                    # or uvx borer

On Windows with Scoop: scoop bucket add imjustprism https://github.com/imjustprism/scoop-bucket, then scoop install imjustprism/borer. In Claude Code: /plugin marketplace add imjustprism/borer, then /plugin install borer@borer. From source, with the toolchain rust-toolchain.toml pins: cargo build --release.

Register an installed binary with borer setup --apply (borer setup alone shows each file and entry it would write, in each client's own format). Most clients (Claude Desktop, Cursor, Windsurf, Gemini CLI and others) read mcpServers, with the binary's absolute path:

{ "mcpServers": { "borer": { "command": "/path/to/borer", "args": [] } } }

VS Code reads servers (with "type": "stdio"), Zed context_servers, and Codex CLI [mcp_servers.borer] in its TOML; borer setup writes each in its own format.

To uninstall, run borer setup --apply --remove, then delete the binary and its cache (%LOCALAPPDATA%\borer or ~/.cache/borer). The installers do all three with --uninstall (-Uninstall in PowerShell).

Quick start

From an agent:

  1. load_binary

  2. read borer://triage: what the file is, how it is protected, where to start

  3. find_function_by_string, strings or symbols

  4. function_summary, then decompile

  5. xrefs, cfg or callgraph to go wider

Any address argument takes 0x hex, decimal or a name.

Area

Tools

Load

load_binary, binary_info, fetch_pdb, reload

Find

functions, function, symbols, strings, search, find_function_by_string, find_crypto_constants, entropy

Read code

function_summary, function_strings, decompile, decompile_batch, disassemble, disassemble_function, cfg, callgraph, reaches, xrefs, class_layout

Read data

read, hexdump

Annotate

set_name, set_comment, set_type, types, annotations, save_project, load_project

Live process (Windows)

live_attach, live_detach, live_modules, live_read, live_object, live_instances, live_watch, live_scan

Anything else

query, script

Deeper analyses are borer:// resources: taint, struct layout, emulation, input solving, unpacking, deobfuscation, binary diff, decoded strings, IOCs, signatures and YARA rules. borer://help lists them all, and a client without resource support passes the same URI to query.

From the command line:

borer triage    sample.exe
borer decompile sample.exe <address|name>
borer xrefs     sample.exe <address|name>
borer certify   sample.exe <address|name>   # how far the decompiled text was checked
borer help                                  # every command

--pack <file> loads target knowledge, --no-pdb skips the companion PDB, and --arch x86|x64|arm64 with --base and --entry maps raw code or a dump.

More of what only borer does

Ask again, get only what changed

An agent renames a function and asks for the same decompile again. Instead of the whole text a second time, borer sends the lines that differ. These three calls are one MCP session on borer 0.2.12:

> query borer://decompile/0x1800ed890!limit=12
# showing 12 of 113 from 0
// sub_1800ed890 @ 0x1800ed890: 215 instructions, 36 blocks
...
extern uint64_t sub_1800eddf4(uint64_t, uint64_t, uint64_t);
...
# since=8704869d
# evidence: 5-c9635f3d

> set_name address=0x1800eddf4 name=crc32_fold_wide dry_run=false
renamed function 0x1800eddf4: sub_1800eddf4 -> crc32_fold_wide

> query borer://decompile/0x1800ed890!limit=12!since=8704869d
# changed since the answer tagged 8704869d: 15 lines, 1141 bytes now; the lines that differ from that answer follow, ...
@@ -13,1 +13,1 @@
-extern uint64_t sub_1800eddf4(uint64_t, uint64_t, uint64_t);
+extern uint64_t crc32_fold_wide(uint64_t, uint64_t, uint64_t);
# since=0a3dc285

Ask once more with the new tag and the answer is one line:

> query borer://decompile/0x1800ed890!limit=12!since=0a3dc285
# unchanged since the answer tagged 0a3dc285: 15 lines, 1141 bytes; this note stands in for that text, ...

Names, comments and types live in a session overlay, never in the binary, and every change is a dry run until dry_run=false.

A struct from what the code does through a pointer

RtlInitUnicodeString fills a UNICODE_STRING. borer recovers the three fields from the accesses alone, and the decompile carries the layout and the function's effects in its header:

$ borer layout ntdll.dll RtlInitUnicodeString
# structure through rcx of RtlInitUnicodeString, from 4 access site(s)
# 3 field(s) over 0x10 bytes
# offset  size  access        seen
0x0      2/8   written       2x
0x2      2     written       1x
0x8      8     written       1x

$ borer decompile ntdll.dll RtlInitUnicodeString
// RtlInitUnicodeString @ 0x1800993c0: 19 instructions, 4 blocks
// checked vs binary: statements as emitted, 4/4 blocks: 2 proved vs lift, 2 tested x3 states; unchecked: C spelling, later text passes
// signature: void RtlInitUnicodeString(PUNICODE_STRING a1, PCWSTR a2)
// effects: writes a1, a1+8, a1+2
// layout a1: 0x2 u16, 0x8 u64
uint64_t var1;

*(uint64_t*)a1 = 0;
a1->field_0x8 = a2;
if (a2 != 0) {
  var1 = wcslen(a2);
  *(uint16_t*)a1 = (var1 * 2 >= 0xfffe ? 0xfffc : var1 * 2);
  a1->field_0x2 = (var1 * 2 >= 0xfffe ? 0xfffc : var1 * 2) + 2;
}

Reading borer://layout/<fn>,apply=<Name> plans the struct and names the set_type calls that state it; after them every decompile names the fields.

A class borer recovered takes one field at a time, and keeps the rest of its layout as recovered: set_type(name=<class>, fields=["0x270:m_pipeline"]), or set_type(address=<method>, var="field_0x270", name="m_pipeline") at a method that reads the field through its own object. Every method that takes the class as this then prints this->m_pipeline, and class_layout marks the row stated. Fields merge by offset into a user struct too; replace=true drops the ones not restated, and with no fields every field stated of a class. A project's stated fields are held to the code again when it loads: those of a class the code contradicts are skipped, with the reason.

One function, any inputs, no process

$ borer emulate ntdll.dll iswdigit,a1=0x35
# ran iswdigit from 0x18012aa90 with a1=0x35: returned after 11 steps, 0 bytes written
# registers: rax=0x4 rcx=0x4 rdx=0x4 r8=0x0 r9=0x0 ...

The same spec takes registers, memory writes, a stop address, a step budget, a trace and a dump (borer help shows the grammar). borer://diverge runs a function on two inputs and names the first instruction where the paths part.

What borer does not do yet is in docs/limits.md. It has no telemetry; the few things that reach the network are in docs/network.md.

Contributing

python scripts/gate.py runs what CI runs. The rules for people and coding agents are in borer.rules, and the terms for contributions in CONTRIBUTING.md.

Crate

Role

borer-engine

The analysis core: loading, lifting, emulation, decompilation. Pure and synchronous.

borer

The MCP server and CLI over the engine.

borer-live

Read-only access to a running process.

borer-fixtures

Programs the tests decompile and check against their source.

License

borer is free software under the GNU Affero General Public License v3.0 (AGPL-3.0-only). If you distribute borer or a modified version, or let users interact with a modified version over a network, you must offer its complete source under the same license. Versions 0.1.0 and 0.1.1 were released under MIT. A commercial license is available for uses the AGPL does not fit: open an issue or reach out on GitHub.

Third-party notices are in NOTICE, and each release archive carries the licenses of everything the binary links. Ghidra is a project of the NSA. IDA and Hex-Rays are trademarks of Hex-Rays SA, Binary Ninja is a trademark of Vector 35, and Claude is a trademark of Anthropic. borer is not affiliated with or endorsed by any of them.

Available Tools

34 tools
annotationsA
Read-only

List the names, findings and open questions (comments opening with ?) added this session, paged. Use it to resume work; not for binary symbols (use symbols) or user types (use types).

Inputs: limit, filter, offset, count_only.

binary_infoA
Read-only

Summarize the loaded program: format, arch, entry, sections with entropy, imports, exports. load_binary already returns it; for protection and where to start, query borer://triage. Fails if nothing is loaded.

Inputs: no named parameters.

callgraphA
Read-only

Draw a function's call graph as mermaid: callees, or callers with callers=true; imports are leaves and a %% line counts nodes cut by max_nodes. Not for one path (use reaches) or a caller list (use xrefs).

Inputs: address (required), callers, max_nodes.

cfgA
Read-only

Draw a function's control-flow graph as mermaid: blocks and successor edges, 300 blocks a page, a %% line when cut. Not for reading logic (use decompile) or a block table (use function).

Inputs: address (required), offset.

class_layoutA
Idempotent

Infer a C++ class's fields, methods and vftable from how its methods use the object: offset, width, reads, writes. Use it on an unknown class; for a known layout use set_type. apply=Name saves it as a struct (dry run until dry_run=false).

Inputs: class (required), apply, dry_run.

decompileA
Read-only

Decompile one function to pseudo-C. Locals are local_N/arg_N (local_N is N hex bytes below the frame base: on x86 one word below the entry sp, so rbp-N where the prologue pushes rbp and sets it there; on arm the entry sp; higher N, lower address; local_mN is N bytes above the frame base); /* asm */ marks unmodeled code. Line 2 says what was checked, // warn: what is wrong. Not for a first look (use function_summary).

Inputs: address (required), limit, detail, filter, +1 more.

decompile_batchA
Destructive

Decompile many functions matching a name filter, in parallel, 6000 chars each and 40000 in all; out=<file.c> writes all uncapped to a file under the output root, kept unless overwrite=true. For one use decompile.

Inputs: out, filter, library, overwrite, +1 more.

disassembleA
Read-only

Disassemble count instructions from any address as va text rows. Use it for a few instructions or code outside functions; for a whole function use disassemble_function. Fails if unmapped.

Inputs: address (required), bytes, count.

disassemble_functionA
Read-only

Disassemble a whole function by blocks, with L_<addr>: labels and named call targets, CIL for .NET. Use it when exact instructions matter; for C use decompile, for a few instructions disassemble.

Inputs: address (required), bytes, max_instrs.

entropyA
Read-only

Find packed, compressed or encrypted data: chunks at or above threshold bits/byte, as off va len entropy rows, at most 200, paged. To name the packer query borer://protection instead.

Inputs: limit, offset, window, threshold.

fetch_pdbA
Idempotent

Download the loaded PE's missing PDB over https into the cache, kept only if its GUID and age match. Use it when load_binary reports no PDB; dry run until dry_run=false, then load_binary again.

Inputs: dry_run.

find_crypto_constantsA
Read-only

Find crypto constants and tables (AES, SHA, MD5, CRC-32, ChaCha, Blowfish, FNV, Base64) and hashed API names as va section algorithm rows, paged. Use it to locate crypto code, then xrefs a hit; not for strings (use strings).

Inputs: limit, offset.

find_function_by_stringA
Read-only

Find the functions whose strings contain a text, case-insensitively: a numbered string legend, then entry name: 1 2 rows, paged. Not for one function's strings (use function_strings).

Inputs: filter (required), limit, offset.

functionA
Read-only

Show one function's raw facts: range, counts, callers, callees and block table (start size term successors), 300 blocks a page. Not for a readable triage (use function_summary).

Inputs: address (required), offset.

functionsA
Read-only

List discovered functions as entry size blocks instrs callers callees src name rows, paged; filter on name or entry hex, sort=size for the largest. Not for imports (use symbols).

Inputs: sort, limit, filter, offset, +1 more.

function_stringsA
Read-only

List the string literals one function references as site string_va text rows, at most 100. Not for finding a text's functions (use find_function_by_string).

Inputs: address (required).

function_summaryA
Read-only

Triage one function in one call: shape, loops, jump tables, callers, callees, strings and data, then # next: lines. Use it before decompile; for the raw block table use function.

Inputs: address (required).

hexdumpA
Read-only

Hexdump raw bytes at an address as address hex |ascii| rows; a range past mapped bytes shows what is there. Not for typed values (use read), text (use strings) or a live process (live_read).

Inputs: address (required), length.

load_binaryA
DestructiveIdempotent

Load a PE, ELF or Mach-O file (or raw bytes with arch) as the program all tools read, returning its summary. Clears the session's names: save_project first. A refused load changes nothing.

Inputs: path (required), pdb, arch, base, +3 more.

load_projectA
DestructiveIdempotent

Replace the session's names, comments and types with a saved .json project. Another binary's project (by SHA-256) is refused unless force=true; for a rebuild use borer://carry.

Inputs: path (required), force.

queryA
Read-only

Read a borer:// resource (triage, taint, emulate, solve, grep...) for hosts without MCP resources; same answer as resources/read. Use it for analyses no tool has; borer://help lists them, !offset=N!limit=N pages.

Inputs: uri (required).

reachesA
Read-only

Find the shortest call chain from one function to another or to an import, or say none exists within depth. Not for the whole neighbourhood (use callgraph) or direct callers (use xrefs).

Inputs: to (required), from (required), depth.

readA
Read-only

Read typed values (kind) as va value rows, or a user type (type) one row per field, pointers named; stops at the first unmapped byte. Not for raw bytes (use hexdump) or a live process (live_read).

Inputs: address (required), kind, type, count.

reloadA

Restart the borer server on its rebuilt binary, keeping the session, the loaded file and its annotations. Use it after rebuilding borer instead of restarting the client; live attaches end.

Inputs: no named parameters.

save_projectA
DestructiveIdempotent

Save the session's names, comments and types to a .json project under the output root, keyed by SHA-256. Restore with load_project; an existing file is kept unless overwrite=true.

Inputs: path (required), overwrite.

scriptA

Run a sandboxed Rhai script on a snapshot of the program: query(uri) and rows(uri) read any resource. Use it when no single tool answers. Proposed names apply only with dry_run=false; a failed script applies nothing.

Inputs: code (required), dry_run.

set_commentA
Idempotent

Comment an address in the session overlay, never the binary; a comment opening with ? is an open question. To name it use set_name. Dry run until dry_run=false; save_project keeps it.

Inputs: address (required), comment (required), dry_run.

set_nameA
Idempotent

Name a function or data global in the session overlay, never the binary; names=[addr=name,...] renames many, checked first. A taken name needs force=true. Dry run until dry_run=false.

Inputs: name, force, names, address, +1 more.

set_typeA
Idempotent

Define a struct, enum or typedef, or apply a type at an address so decompile names its fields; at a function, type sets the prototype. To infer a class use class_layout. Checked first; dry run until dry_run=false.

Inputs: var, name, size, type, +4 more.

stringsA
Read-only

List printable ASCII and UTF-16LE strings as va len enc text rows, paged, skipping bytes of code. Not for the code using one (use find_function_by_string).

Inputs: limit, filter, offset, encoding, +2 more.

symbolsA
Read-only

List imports, exports, forwards, locals and data as demangled kind addr name rows by module, paged. Not for discovered code (use functions) or call sites (use xrefs).

Inputs: kind, limit, filter, offset, +1 more.

typesA
Read-only

List user-defined types and where each is applied, paged. Not for defining one (use set_type) or names and comments (use annotations).

Inputs: limit, filter, offset, count_only.

xrefsA
Read-only

List references to an address or name, one row per function, paged; an import lists calls through its slot, Class+0x24 who touches that field. Not for call paths (use reaches).

Inputs: address (required), limit, offset, count_only.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4.1/5.0

    Scored across 34 tools

    Disambiguation5/5

    Descriptions explicitly scope each tool and cross-reference alternatives ('Not for X, use Y'), so close pairs such as decompile/disassemble, function/function_summary, and the string-focused tools are distinguishable. Minor overlap remains between query/script and binary_info/borer://triage, but not enough to cause misselection.

    Naming Consistency4/5

    All names are lower_snake_case and mostly follow a query-by-resource-noun or action-verb_noun pattern, with no casing inconsistencies. There is some mixing of noun-style query tools (`functions`, `strings`) and verb-style operations (`load_binary`, `set_name`), but the convention remains logical and readable.

    Tool Count3/5

    The server exposes 34 tools, which is heavy compared with the typical 3-15 range and feels overloaded. The broad reverse-engineering domain justifies many primitives, but several closely related variants could be parameterized or consolidated.

    Completeness4/5

    The static-analysis lifecycle is well covered: loading, inspecting, navigating, decompiling/disassembling, graphing, annotating, typing, saving/restoring and reloading. Gaps include no actual live-process tool despite descriptions referencing `live_read`, and no byte-patching or assembly operation.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      D
      maintenance
      MCP server for targeted binary analysis with Kuna, enabling listing and decompiling specific functions via natural language.
      2
      MIT
    • F
      license
      A
      quality
      B
      maintenance
      Enables MCP clients to execute Python snippets and interact with Ghidra through its PyGhidra scripting environment via a local Java plugin.
      24
      1
      -
    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables agents to reverse-engineer software across native binaries, managed code, Electron apps, websites, and live runtime behavior, returning findings linked to the evidence and limitations behind each conclusion. It also maintains a durable workspace where those behaviors can be inspected, questioned, compared against real execution, and exported as runnable handoffs.
      371,123 npm
      MIT