IDA-instances-MCP
README.md
# IDA-instances-MCP
Custom build of [ida-pro-mcp](https://github.com/mrexodia/ida-pro-mcp): an
[MCP Server](https://modelcontextprotocol.io/introduction) that lets MCP clients
drive IDA Pro — reworked with a stability pass aimed at hosting **multiple
headless idalib instances** (the Ida-Instances setup).
This is a fork of mrexodia's `ida-pro-mcp` (MIT). All upstream credit goes to
[Duncan Ogilvie](https://github.com/mrexodia) and contributors; the custom
changes below are maintained in this repository.
## What's custom
Stability hardening on top of upstream v2.0.0:
| Area | Change |
|------|--------|
| Data safety | Partial-database cleanup is ownership-aware: it never deletes `.id0/.id1/.id2/.nam/.til` files claimed by another live instance, and fails safe when discovery is unavailable |
| Save reliability | `idb_save` runs with a 600s budget (was 60s); `idb_close` refuses to kill a worker after a failed save instead of silently discarding changes |
| Concurrency | Supervisor lock no longer spans worker spawn (~120s), health probes (~2.5s/session) or stale-worker termination — one slow open can't freeze every session |
| Robustness | Malformed JSON-RPC over stdio returns `-32700`/`-32600` instead of crashing the proxy |
| Timeouts | Proxy timeout raised 30s → 900s and made configurable (`IDA_MCP_PROXY_TIMEOUT`) so long decompiles don't "fail" client-side while still running server-side |
| Networking | Worker port TOCTOU now retries; SSE writes and teardown are lock-guarded; CORS policy reads are cached off the IDA main thread |
| Resource limits | IDB trace log capped at 64 MB (`IDA_MCP_TRACE_MAX_MB`) with oversized arguments/results clipped; flush-failure queue bounded |
| Authentication | Every HTTP request requires an instance API key; issued **exactly once** via `GET /key`, then burned. The key persists on disk so it survives restarts and reboots |
| Hosting endpoints | `GET /health` (unauthenticated readiness probe), `GET /sessions` (open databases + last-accessed), `POST /upload` (direct binary upload with size cap) |
| Idle reaping | Sessions untouched for `IDA_MCP_IDLE_TIMEOUT` minutes are auto-closed (saved first) |
| Graceful shutdown | On `SIGTERM`/`SIGINT` every open database is saved before exit — no more lost work on `pct stop` |
| Self-update | Startup checks GitHub releases; interactive sessions are offered a one-keypress auto-update |
| Hosting defaults | Headless supervisor listens on **0.0.0.0:9999**; the GUI plugin also binds **0.0.0.0** and now requires the same API key |
## Requirements
- Linux, macOS or Windows
- [Python](https://www.python.org/downloads/) **3.11+** (use `idapyswitch` inside IDA to match)
- [IDA Pro](https://hex-rays.com/ida-pro) **8.3+** (9.x recommended). **IDA Free is not supported**
- [uv](https://astral.sh/uv)
- An MCP client (Claude Code, Cursor, Codex, Opencode, VS Code, ...)
## Setup
Activate idalib for your uv environment:
```bash
# linux (server deployment)
uv run "/opt/idapro-9.x/idalib/python/py-activate-idalib.py"
# macos
uv run "/Applications/IDA Professional 9.x.app/Contents/MacOS/idalib/python/py-activate-idalib.py"
# windows
uv run "C:\Program Files\IDA Professional 9.x\idalib\python\py-activate-idalib.py"
```
Headless runs additionally need the IDA install directory in `IDADIR`.
### Run the headless supervisor (hosting mode)
```bash
uv run idalib-mcp # serves http://0.0.0.0:9999/mcp (new default)
```
On startup an **API key** is generated (or loaded from disk) and every request
without it gets `401 Unauthorized` — no exceptions.
Fetch the key once, from the machine/network that owns the instance:
```bash
curl http://your-host:9999/key
# {"key": "3f2b..."} <- save it; this endpoint is now burned until restart
```
- The key persists at `~/.idapro/mcp/api_key` (mode `0600`) and survives
reboots; `GET /key` re-issues it once per server start
- Override with `IDA_MCP_API_KEY` (or relocate via `IDA_MCP_API_KEY_FILE`)
- All other endpoints require it on every call:
```
Authorization: Bearer <key>
# or equivalently
X-API-Key: <key>
```
The key is never logged.
#### Hosting endpoints
| Endpoint | Auth | Purpose |
|----------|------|---------|
| `GET /health` | none | Readiness probe: `{"status":"ok","uptime":N,"sessions":N}` — poll instead of sleeping |
| `GET /sessions` | key | Open databases incl. last-accessed timestamps |
| `POST /upload?filename=NAME` | key | Raw-body binary upload, streamed to `IDA_MCP_UPLOAD_DIR`; returns the path to feed `idb_open` |
```bash
# provisioning flow
while ! curl -sf http://10.0.0.4:9999/health >/dev/null; do sleep 1; done
KEY=$(curl -s http://10.0.0.4:9999/key | jq -r .key)
curl -H "Authorization: Bearer $KEY" --data-binary @sample.elf \
"http://10.0.0.4:9999/upload?filename=sample.elf"
```
Useful flags:
```bash
uv run idalib-mcp --host 127.0.0.1 --port 9999 # override bind address
uv run idalib-mcp --max-workers 8 # concurrent databases (default 4, 0 = unlimited)
uv run idalib-mcp --unsafe # enable destructive/debugger tools (DANGEROUS)
uv run idalib-mcp --profile tools.txt # restrict worker tools to a profile file
uv run idalib-mcp --stdio # stdio transport instead of HTTP
uv run idalib-mcp path/to/binary # open a binary at startup
```
Session lifecycle tools: `idb_open`, `idb_list`, `idb_close` — open one database
per session; each session gets its own isolated worker process.
### Run the GUI proxy (interactive IDA)
Start the plugin inside IDA (`Edit -> Plugins -> MCP`, or `Ctrl+Alt+M`), then:
```bash
uv run ida-pro-mcp # stdio proxy, auto-discovers IDA
uv run ida-pro-mcp --transport http://127.0.0.1:9999 # serve HTTP instead
```
### Connect an MCP client
Generic JSON config (HTTP transport):
```json
{
"mcpServers": {
"ida-instances": {
"url": "http://your-host:9999/mcp",
"headers": {
"Authorization": "Bearer <key-from-/key>"
}
}
}
}
```
Claude Code:
```bash
claude mcp add --transport http ida-instances http://your-host:9999/mcp \
--header "Authorization: Bearer <key-from-/key>"
```
For the GUI proxy over stdio, run `uv run ida-pro-mcp --config` and paste the
JSON into your client.
## Configuration (environment variables)
| Variable | Default | Meaning |
|----------|---------|---------|
| `IDA_MCP_OPEN_TIMEOUT` | `1800` | Max seconds for open + auto-analysis before reap (`0` = unlimited) |
| `IDA_MCP_WORKER_CALL_TIMEOUT` | `900` | Backstop per forwarded tool call |
| `IDA_MCP_PROXY_TIMEOUT` | `900` | GUI-proxy → IDA upstream timeout (`0` = unlimited) |
| `IDA_MCP_TRACE_MAX_MB` | `64` | Trace log cap stored inside the IDB |
| `IDA_MCP_MAX_WORKERS` | `4` | Default `--max-workers` |
| `IDA_MCP_HEALTH_TCP_TIMEOUT` / `IDA_MCP_HEALTH_RPC_TIMEOUT` | `2.0` / `10.0` | Health probe budgets |
| `IDA_MCP_IDLE_TIMEOUT` | `60` | Minutes before an untouched session is auto-closed (`0` disables) |
| `IDA_MCP_MAX_UPLOAD_MB` | `100` | Cap for `POST /upload` bodies |
| `IDA_MCP_UPLOAD_DIR` | `~/.idapro/mcp/uploads` | Where uploads are stored |
| `IDA_MCP_API_KEY` / `IDA_MCP_API_KEY_FILE` | – / `~/.idapro/mcp/api_key` | Inject or relocate the instance key |
| `IDA_MCP_SHUTDOWN_SAVE_TIMEOUT` / `IDA_MCP_SHUTDOWN_SAVE_BUDGET` | `30` / `120` | Per-database / total save budget on SIGTERM |
| `IDA_MCP_NO_UPDATE_CHECK` | unset | Set `1` to disable the GitHub release check |
> **Security notes:** the API key gates every HTTP endpoint on both the
> supervisor and the GUI plugin (the plugin can execute arbitrary code via
> `py_eval`, so it is keyed even on loopback). It is a bearer token — anyone
> who obtains it owns the instance; add a TLS reverse proxy if it leaves your
> trusted network. Browsers can pass the key as `?key=<key>` (handy for
> `/config.html`).
## Testing
```bash
IDADIR=/path/to/ida uv run ida-mcp-test tests/crackme03.elf -q
IDADIR=/path/to/ida uv run ida-mcp-test tests/typed_fixture.elf -q
IDADIR=/path/to/ida uv run ida-mcp-test tests/crackme03.elf -c api_analysis # one category
IDADIR=/path/to/ida uv run coverage run -m ida_pro_mcp.test tests/crackme03.elf -q
```
## License
MIT — inherited from upstream [`ida-pro-mcp`](https://github.com/mrexodia/ida-pro-mcp).
Upstream product names (IDA Pro, Hex-Rays) are trademarks of Hex-Rays SA; this
project requires your own valid IDA Pro license.
This server cannot be deployed
Maintenance
ActivityActive
ResponsivenessNo issues