
Headless IDA Pro MCP server for AI-assisted binary analysis, powered by idalib
Headless IDA CLI and skill-first toolkit for binary analysis on macOS and
Linux. ida-cli auto-selects a runtime backend, auto-starts a local server
when needed, and exposes the same surface as a flat CLI, a stdio MCP
transport, and a Streamable HTTP MCP transport.
ida-cli binary (client + service in one executable)ida-cli skill (skill/SKILL.md) for agent environmentsThe underlying worker / router service layer is started and shut down by the
CLI automatically. You only need to run serve / serve-http explicitly
when you actually want a long-lived, externally addressable service.
| IDA version | Backend | Notes |
|---|---|---|
< 9.0 | unsupported | — |
9.0 – 9.2 | idat-compat | shells out to idat + IDAPython |
9.3, 9.5 built with the matching SDK | native-linked | links against vendored idalib |
other 9.3+ (incl. 9.4, or a mismatched SDK) | idat-compat | native layer is SDK-version specific |
Backend selection is made at runtime by probe-runtime. native-linked is
only chosen when the IDA runtime matches the SDK the binary was built
against (for example a 9.5 SDK build on IDA 9.5) and that pairing has been
verified; the vendored native layer calls private IDA structures whose
layout changed in 9.4 and 9.5. IDA 9.4 stays on idat-compat: the public
9.4 SDK uses Hex-Rays API magic 5 while the 9.4.260610 build answers 4, so
native-linked would run without a decompiler. Building still
requires an IDA SDK because the vendored native layer is linked against it;
at runtime the CLI opens IDA itself from IDADIR or a normalised common
install path.
Release binaries are built against the IDA 9.5 SDK (releases/9.5 of
HexRaysSA/ida-sdk, matching IDA 9.5.261001), so they use native-linked
on IDA 9.5 and idat-compat elsewhere. Build from source with the matching
SDK to get native-linked on IDA 9.3.
On supported IDA 9.x runtimes, ida-cli can:
.i64 databasesread_* and convert_number helpersrun_scriptOpen items: some write-heavy and advanced type-editing operations are still
partial on idat-compat. See docs/TOOLS.md for the
generated tool list.
The default entrypoint is the ida-cli skill, not a manual CLI install.
# list the skill exposed by this repository
npx -y skills add https://github.com/cpkt9762/ida-cli --list
# install the ida-cli skill for Codex
npx -y skills add https://github.com/cpkt9762/ida-cli --skill ida-cli --agent codex --yes --global
After install, the skill ships its own bootstrap wrapper:
~/.agents/skills/ida-cli/scripts/ida-cli.sh --help
~/.agents/skills/ida-cli/scripts/ida-cli.sh probe-runtime
~/.agents/skills/ida-cli/scripts/ida-cli.sh --path /path/to/binary list-functions --limit 20
If ida-cli is missing, the wrapper installs it through the repository
installer before forwarding the command.
Use this only if you want the standalone CLI without going through the skill.
curl -fsSL https://raw.githubusercontent.com/cpkt9762/ida-cli/master/scripts/install.sh | bash -s -- --add-path
Useful variants:
# install a specific release
curl -fsSL https://raw.githubusercontent.com/cpkt9762/ida-cli/master/scripts/install.sh | bash -s -- --tag v0.9.3 --add-path
# build directly from a branch or ref
curl -fsSL https://raw.githubusercontent.com/cpkt9762/ida-cli/master/scripts/install.sh | bash -s -- --ref master --build-from-source --add-path
Notes:
~/.local/bin/ida-cli by default.--add-path appends that bin directory to your shell rc file.IDASDKDIR nor IDALIB_SDK is set and a local build is
required, the installer clones the open-source HexRaysSA/ida-sdk
automatically.IDADIR explicitly
before installing or running ida-cli.git clone https://github.com/cpkt9762/ida-cli.git
cd ida-cli
export IDADIR="/Applications/IDA Professional 9.5.app/Contents/MacOS" # or a Linux install
export IDASDKDIR="/path/to/ida-sdk" # same version as IDADIR, e.g. HexRaysSA/ida-sdk releases/9.5
cargo build --bin ida-cli
./target/debug/ida-cli --help
ida-cli is client-first. Any client subcommand auto-starts a local
Streamable-HTTP server bound to a random port and writes
/tmp/ida-cli.socket for discovery:
./target/debug/ida-cli --path /path/to/sample.bin list-functions --limit 20
./target/debug/ida-cli --path /path/to/sample.bin decompile --addr 0x140001000
./target/debug/ida-cli --path /path/to/sample.bin raw '{"method":"get_xrefs_to","params":{"address":"0x140001000"}}'
To see which databases the daemon currently holds open, and which clients
hold them, run list-idbs (MCP tool: list_idbs). Run it before rebuilding
or replacing an .i64, for example with idat: a database that is still
listed is held open by the daemon, and idat fails on a database that is
open elsewhere. The answer is {"count": N, "idbs": [...]}; each entry has
the db_handle, the input path and the .i64 path, an opaque id of the
client that opened it, when it was opened, the worker process, whether it is
the active database, and the clients that still hold references. Close
tokens are never returned.
./target/debug/ida-cli list-idbs --json
To get the address behind each line of pseudocode, add --line-addresses to
decompile (MCP and RPC: line_addresses: true on decompile_function). The
answer becomes {"code": ..., "lines": [...]}: code is the usual text and
each lines entry is {"line", "text", "addresses"}. line counts from 1,
text is that line of code, and addresses are the IDA addresses (hex
strings, ascending, no duplicates) of the ctree items Hex-Rays anchored on the
line. A line can map to several non-contiguous addresses, and the list is not
every instruction of the statement. The signature and local declarations have
none; {, }, else and blank lines can carry the address of the block or
if that owns them (the function entry for the outermost braces). The sub_
rename hint is not added in this mode. Hex-Rays can print a very large function
differently from one call to the next, so use code and lines from the same
answer only. Only the native-linked backend can do this; the idat backend
answers NotSupported. Over the CLI socket a large
answer is stored in the response cache like any other (IDA_MCP_MAX_INLINE_BYTES).
{"line": 6, "text": " while ( a1 != 1 )", "addresses": ["0x10000033c", "0x100000340"]}
Commands whose first argument is a service subcommand (serve,
serve-http, serve-worker, probe-runtime) enter service mode instead:
./target/debug/ida-cli serve # stdio MCP transport
./target/debug/ida-cli serve-http --bind 127.0.0.1:8765
./target/debug/ida-cli probe-runtime
Example backend-probe output: