Install
$ agentstack add mcp-wklwkl115-ida-pilot ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
IDA Pilot
[](https://github.com/wklwkl115/ida-pilot/actions/workflows/verify.yml) [](LICENSE)
> Drive IDA Pro's decompiler and disassembler from an AI agent over the Model Context Protocol — headless, fast, and token-frugal.
Headless IDA Pro analysis server exposing MCP tools (28 with py_eval enabled, 27 without). Designed for AI agents — tiered tool loading, multi-layer caching, batch operations, and compact payloads minimize token cost per turn.
Go orchestrates sessions and caching. Python workers run IDA via idalib. Communication uses Connect RPC over TCP.
Why IDA Pilot?
- Built for agents, not humans. Every response is compact — hex addresses, sparse fields,
[address, type]xref tuples,"ok"instead of{"success": true}— to spend the fewest tokens per turn. - One call, not ten. Composite tools like
survey_binaryandanalyze_functionreturn decompilation + metadata + xrefs + comments in a single round-trip;analyze_functionsbatches up to 10 at once. - Stays responsive on huge binaries. Loading and auto-analysis run detached with a live progress heartbeat, so a 200 MB binary won't stall or time out your MCP client — you poll until
ready=trueand keep working. - Tiered tool surface. Agents boot with 3 tools; read/analysis and write tools promote in as the workflow advances, keeping the per-turn schema small.
- Multi-binary. Open several databases and
cross_reference/cross_searchacross them. - Safe by default. Loopback-only bind,
py_evaloff,Origin/Hostguard, optional path allowlist (details below).
⚠️ Security model — read before exposing the port
IDA Pilot has no built-in authentication. The defaults are tuned for a single trusted MCP client on the same machine:
- Bound to
127.0.0.1by default. Only callers on the local host can reach it. Override with--bind 0.0.0.0(orIDA_PILOT_BIND) only if you are putting an authenticated reverse proxy in front. Origin/Hostvalidation on every request when bound to loopback — blocks DNS-rebinding and browser cross-origin attacks.py_evalis OFF by default. It runs arbitrary Python inside the IDA worker (full host filesystem + network access). Enable explicitly with--enable-py-eval(orIDA_PILOT_ENABLE_PY_EVAL=1) and treat the resulting endpoint as an RCE primitive — anyone who can reach the port can execute code.- Filesystem allowlist (opt-in). By default a client can open any file the server user can read (
open_binary,import_metadata). Set--allowed-roots(orIDA_PILOT_ALLOWED_ROOTS) to confine those paths to specific directories — recommended whenever you expose the port. Paths are symlink-resolved before the check, so a link inside a root can't escape it.
The truncated-output cache (get_cached_output) is process-global with no per-session ACL — the server has no client identity to bind entries to — so access rests on unguessable 128-bit cache IDs plus the loopback/Origin guards above, not on session ownership.
In short: a fresh ./bin/ida-pilot run is safe to leave on; opening the port to the network or enabling py_eval is an explicit choice and your responsibility to protect.
Architecture
MCP Client (Claude, Cursor, etc.)
│
│ Streamable HTTP / SSE
▼
Go Server (:17300)
│ session registry, tool tiers, caching, output pagination
│
│ Connect RPC (TCP)
▼
Python Worker (one per binary)
│ idalib + Hex-Rays decompiler
▼
IDA database (.i64)
Quick Start
Prerequisites
- IDA Pro 9.0+ with idalib activated (docs)
- Go 1.25+
- Python 3.10+ (the supported floor; CI and
misepin 3.12)
Build
git clone https://github.com/wklwkl115/ida-pilot.git
cd ida-pilot
pip install -r python/requirements.txt
go build -o bin/ida-pilot ./cmd/ida-pilot
Run
./bin/ida-pilot --debug
The server listens on http://localhost:17300/ (Streamable HTTP) and http://localhost:17300/sse (SSE fallback).
Connect an MCP Client
Add to your MCP client configuration:
{
"mcpServers": {
"ida-pilot": {
"type": "http",
"url": "http://127.0.0.1:17300/"
}
}
}
Claude Desktop: ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows)
Claude Code: .mcp.json in project root or ~/.claude/settings.json
Cursor: .cursor/mcp.json in project root
Agent-Optimized Design
IDA Pilot is built for agents, not humans. Every design decision targets lower token cost and fewer round-trips.
Tiered Tool Loading
Agents start with 3 tools. More appear as needed — the schema payload per turn stays small.
| Tier | Trigger | Tools | Purpose | |------|---------|-------|---------| | 0 | Server boot | 3 | open_binary, list_sessions, get_cached_output | | 1 | Binary opened | +19 = 22 | Read, query, composite, analysis, context & cross-session tools | | 2 | First analysis | +5 = 27 | Write, annotate, and import tools |
When the last session closes, tools demote back to Tier 0. Adding --enable-py-eval registers one more Tier-1 tool, taking the full surface to 28.
Composite Tools
One call replaces many. These are the primary entry points for analysis:
survey_binary— segments, function/import/export/string counts with top entries, entry point, decompiler status. One call to orient.analyze_function— pseudocode + metadata + xrefs + comments for a single function. All cached.analyze_functions— batch-analyze up to 10 functions in parallel.annotate_function— batch rename, retype, and comment in a single call per function.
Multi-Layer Caching
| Cache | Scope | Capacity | Invalidation | |-------|-------|----------|--------------| | List (functions, imports, exports, strings) | Session | Unbounded | rename / makefunction | | Decompilation (pseudocode) | Per-function | 200 entries | targeted: function renames invalidate only that function; data-label renames search and evict only entries containing the old name; lvar/comments invalidate that function | | Xrefs (to + from) | Per-address | Unbounded | makefunction | | Segments | Session | Static | Never | | Output (truncated responses) | Global | 200 entries, 1h TTL | FIFO eviction | | Analysis context (visited + notes) | Session | Unbounded | Never |
Background cache warming starts automatically when a binary is opened — survey_binary returns instantly on its first call.
Compact Payloads
- Addresses are hex (
0x...) on both input and output, so the agent pastes back exactly what it sees in disassembly/listings (decimal is also accepted on input) - Sparse fields: zero/false/empty values omitted
- Xrefs as
[address, type]tuples instead of objects - Write operations return
"ok"instead of{"success": true} - Responses over 8KB auto-truncated with pagination via
get_cached_output - Session ID auto-detected when only one session is active
Analysis Context
Agents lose context on long conversations. IDA Pilot tracks it server-side:
set_analysis_note— attach notes to addresses during analysisget_analysis_context— retrieve all visited functions and notes to recover state
Tools Reference
The surface is intentionally small: related operations are consolidated behind a few dispatcher tools that take a discriminator field (category, mode, action, target, format). 28 tools across three tiers.
Session & lifecycle (Tier 0–1)
| Tool | Description | |------|-------------| | open_binary | Open a binary. Returns immediately; loads + auto-analyzes in the background | | close_binary | Close session and save database | | save_database | Save without closing | | list_sessions | List active sessions | | get_session_progress | Load/analysis progress and readiness — poll until ready=true | | get_cached_output | Paginate a truncated response via its _cache_id |
open_binary does not block: loading and auto-analysis run in a background worker thread so the call returns at once (large binaries take minutes to analyze). Poll get_session_progress until ready=true before issuing analysis tools — they are gated and return a not ready error until then. Status RPCs stay responsive throughout because the worker serializes IDA access on a single thread while answering progress queries from cached state.
Composite (Tier 1)
| Tool | Description | |------|-------------| | survey_binary | Full binary overview in one call | | analyze_function | Decompile + metadata + xrefs + comments for one function | | analyze_functions | Batch-analyze up to 10 functions; per-item errors so one bad address won't fail the batch |
Read & query (Tier 1)
| Tool | Description | |------|-------------| | query | Browse by category: functions, imports, exports, strings, globals, segments, structs, enums, entry_point. Regex, pagination, and category filters (named_only, module, name) | | get_references | Cross-references. mode=code (direction=to/from/both), data, or string | | search | mode=text (needle) or binary (IDA pattern), over a start/end range | | inspect | Name + type + comment + function bounds + instruction length at an address | | get_disasm | Disassembly at an address, or the whole function | | read_memory | Raw memory: format=bytes/dword/qword/byte/string |
Cross-session (Tier 1)
| Tool | Description | |------|-------------| | cross_reference | Look up a symbol from one binary (source_session_id + address) and find where it appears in another (target_session_id) — searches imports, exports, function names, and strings | | cross_search | Search across multiple sessions at once. mode=functions/imports/exports/strings, regex or exact needle. Omit session_ids to search all active sessions |
Analysis control & context (Tier 1)
| Tool | Description | |------|-------------| | run_auto_analysis | Trigger IDA auto-analysis (for binaries opened with skip_analysis). Returns once analysis finishes, or — for a long analysis that outlasts the call — promptly with status:"analyzing" while it continues in the background; poll get_session_progress until ready=true | | watch_auto_analysis | Observe auto-analysis progress (does not drive or cancel it) | | set_analysis_note / get_analysis_context | Server-side context tracking for state recovery | | prune_context | Clear cached outputs + noted-function caches to free memory | | py_eval (opt-in) | Execute Python in IDA — registered only with --enable-py-eval. Invalidates caches after each call (read_only=true to keep them warm). See [Security model](#%EF%B8%8F-security-model--read-before-exposing-the-port). |
Write & import (Tier 2)
| Tool | Description | |------|-------------| | annotate_function | Batch rename + retype + comment for a function → returns updated pseudocode | | set_metadata | action=setname, deletename, or setcomment (scope=address/function/decompiler) | | set_type | target=function (prototype), global (C type), or lvar (C type + function_address + lvar_name) | | make_function | Create a function at an address | | import_metadata | format=il2cpp (script.json + il2cpp.h) or flutter (fluttermeta.json) |
Configuration
CLI Flags
--bind Bind interface (default 127.0.0.1; use 0.0.0.0 only behind an auth proxy)
--port HTTP port (default: from config.json)
--config Config file path (default: config.json)
--worker Python worker script path
--max-sessions Max concurrent sessions
--session-timeout Session idle timeout (e.g. 4h)
--debug Verbose logging
--enable-py-eval Register the py_eval tool (arbitrary Python in the IDA worker — RCE primitive)
--allowed-roots Restrict agent-supplied paths to these dirs (OS-path-list separated; empty = unrestricted)
Environment Variables
IDA_PILOT_BIND=127.0.0.1
IDA_PILOT_PORT=17300
IDA_PILOT_SESSION_TIMEOUT_MIN=240
IDA_PILOT_MAX_SESSIONS=10
IDA_PILOT_WORKER=/path/to/worker.py
IDA_PILOT_DEBUG=1
IDA_PILOT_ENABLE_PY_EVAL=0
IDA_PILOT_ALLOWED_ROOTS=/srv/samples:/data/binaries # OS-path-list separated; empty = unrestricted
config.json
{
"bind": "127.0.0.1",
"port": 17300,
"session_timeout_minutes": 240,
"max_concurrent_sessions": 4,
"database_directory": "./databases",
"python_worker_path": "python/worker/server.py",
"debug": false,
"enable_py_eval": false,
"allowed_roots": []
}
Development
make build # Build server
make test # Run tests
make proto # Regenerate protobuf (requires protoc)
make clean # Clean artifacts
make inspector # Launch MCP Inspector at localhost:5173
Project Structure
ida-pilot/
├── cmd/ida-pilot/ # Server entry point
├── internal/
│ ├── server/ # MCP tool handlers, caching, tiers
│ ├── session/ # Session registry + persistence
│ └── worker/ # Python worker process manager
├── proto/ # Protobuf service definitions
└── python/worker/ # idalib wrapper + Connect RPC server
Contributing
Contributions are welcome — see [CONTRIBUTING.md](CONTRIBUTING.md) for setup, the local test gates, and conventions. To report a security issue privately, see [SECURITY.md](SECURITY.md).
License
MIT
Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: wklwkl115
- Source: wklwkl115/ida-pilot
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.