ida-codex-mcp

February 27, 2026 · View on GitHub

Local MCP (Model Context Protocol) server plus an IDA 9.2 plugin to expose common reverse engineering capabilities to MCP clients.

  • ida_bridge.py: an IDA plugin that exposes core analysis features (function list, call graph, Hex-Rays pseudocode, disassembly, imports/exports, xrefs, strings, memory reads, etc.) over a local TCP JSON interface (default 127.0.0.1:31337).
  • mcp_ida_server.py: a local MCP server that talks to the IDA plugin, then re-exposes features as MCP tools and resources over STDIN/STDOUT so MCP clients (e.g., Codex CLI) can use them directly.

Features

  • Functions and calls
    • List functions: ida_list_functions
    • Call graph: ida_call_graph(name, max_depth)
    • Analyze and optionally rename: ida_analyze_function(name, max_depth, rename, rename_locals)
  • Decompile/Disassemble
    • Pseudocode (Hex-Rays): ida_get_pseudocode(name|ea, offset, limit)
    • Disassembly: ida_get_disassembly(name|ea, offset, limit)
    • Rename function: ida_rename_function(old_name, new_name)
  • Program info
    • Imports: ida_get_imports
    • Exports: ida_get_exports
    • Cross-references: ida_get_xrefs(target) (name or address)
    • Globals: ida_list_globals(offset, count)
    • Memory read: ida_read_memory(address, size)
    • Strings: ida_get_strings(min_length, offset, count)
  • Multi-target routing (NEW)
    • Actively discover running IDA instances: ida_discover_targets
    • List known targets (configured + discovered): ida_list_targets
    • Set active target for subsequent calls: ida_set_active_target
    • Find an API across all targets: ida_find_function_across_targets
    • All ida_* tools support optional ida_target argument
    • Route one MCP server to multiple IDA instances (for example A.dll and B.dll)
  • Function Pointer Operations (NEW - FIXED v0.2.1)
    • Jump to address: ida_jump_to_address(address)
    • Set data type: ida_set_data_type(address, data_type) (byte, word, dword, qword, etc.)
    • Set function pointer type: ida_set_function_pointer_type(address, function_signature)
    • Set name: ida_set_name(address, name)
    • Complete workflow: ida_create_function_pointer(address, name, function_signature) - converts MEMORY[address] calls to named function calls in Hex-Rays
    • Recent fixes: Resolved syntax errors in function signature parsing, added signature normalization, improved fallback types
  • MCP resources
    • resources/list: export top functions as resources (first 500)
    • resources/read: fetch pseudocode (or fallback to disassembly) via ida://function/{ea}/{name}
    • resources/templates/list: provide ida_function template

Repository Layout

ida-codex-mcp/
├─ ida_bridge.py        # IDA plugin: local TCP JSON bridge
├─ mcp_ida_server.py    # MCP local process server: bridge MCP <-> IDA
└─ __pycache__/         # cache generated at runtime

Requirements

  • IDA Pro 9.2 (Hex-Rays recommended for pseudocode features)
  • Python 3.9+
  • Windows/Linux/macOS

Installation & Startup

  1. IDA side (plugin)
  • Copy ida_bridge.py into IDA's plugins directory and restart IDA.
  • When a target is open, the plugin spawns a local listener at 127.0.0.1:31337 (auto-increments port if taken).
  1. MCP side (local process)
  • From this repo folder, run:
python mcp_ida_server.py
  • To override the IDA bridge address/port (single target), set environment variables:
# Default: 127.0.0.1:31337
set IDA_HOST=127.0.0.1
set IDA_PORT=31337
  • To connect multiple IDA instances from one MCP server, use:
# alias=host:port;alias=host:port
set IDA_TARGETS=A=127.0.0.1:31337;B=127.0.0.1:31338
# optional, default is "default"
set IDA_DEFAULT_TARGET=A
  • Then call tools with ida_target:
{"jsonrpc":"2.0","id":"10","method":"tools/call","params":{"name":"ida_list_functions","arguments":{"ida_target":"A"}}}
{"jsonrpc":"2.0","id":"11","method":"tools/call","params":{"name":"ida_list_functions","arguments":{"ida_target":"B"}}}
  • Optional auto-discovery range overrides:
set IDA_DISCOVERY_HOST=127.0.0.1
set IDA_DISCOVERY_PORT_START=31337
set IDA_DISCOVERY_PORT_END=31437
set IDA_AUTO_DISCOVER_ON_START=1
# If MCP process may restart frequently, skip repeated startup scan in this interval (seconds)
set IDA_STARTUP_DISCOVERY_MIN_INTERVAL_SEC=30
# Hard budget for startup discovery (seconds). Discovery stops early when budget is reached.
set IDA_STARTUP_DISCOVERY_TIMEOUT_SEC=4.0
# Fast TCP connect timeout used for per-port probe (milliseconds, localhost can be very small)
set IDA_DISCOVERY_CONNECT_TIMEOUT_MS=15
# Retry timeout for busy IDA UI thread (milliseconds)
set IDA_DISCOVERY_RETRY_TIMEOUT_MS=1200
# Optional cache file for discovered targets (restored on next start)
set IDA_DISCOVERY_CACHE_FILE=C:\temp\ida_mcp_targets_cache.json
  • Discovery + locate workflow:
{"jsonrpc":"2.0","id":"20","method":"tools/call","params":{"name":"ida_discover_targets","arguments":{}}}
{"jsonrpc":"2.0","id":"21","method":"tools/call","params":{"name":"ida_find_function_across_targets","arguments":{"name":"CreateFileW","match_mode":"exact"}}}
{"jsonrpc":"2.0","id":"22","method":"tools/call","params":{"name":"ida_set_active_target","arguments":{"ida_target":"B"}}}
  • The process communicates over STDIN/STDOUT. Register it in your MCP client (e.g., Codex CLI) to use tools and resources.

Codex CLI Integration (config.toml)

Add this server to Codex CLI's config.toml so the tools appear in the client. The exact location of the config file depends on your setup, for example:

  • Windows: %AppData%\\Codex\\config.toml
  • macOS: ~/Library/Application Support/Codex/config.toml
  • Linux: ~/.config/codex/config.toml

Example configuration using stdio transport:

# Register an MCP server named "ida"
[mcp.servers.ida]
type = "stdio"
command = "python"
# Use an absolute path to the server script; single quotes are safe for Windows paths
args = ['D:\\Code\\ida-codex-mcp\\mcp_ida_server.py']

# Optional environment overrides:
# - Single target: IDA_HOST + IDA_PORT
# - Multi target: IDA_TARGETS + IDA_DEFAULT_TARGET
env = { IDA_TARGETS = "A=127.0.0.1:31337;B=127.0.0.1:31338", IDA_DEFAULT_TARGET = "A" }

# Optional stability knobs
enabled = true
restart_on_exit = true
timeout_ms = 15000

Unix-like path variant:

[mcp.servers.ida]
type = "stdio"
command = "python3"
args = ["/path/to/ida-codex-mcp/mcp_ida_server.py"]
env = { IDA_HOST = "127.0.0.1", IDA_PORT = "31337" }
# or:
# env = { IDA_TARGETS = "A=127.0.0.1:31337;B=127.0.0.1:31338", IDA_DEFAULT_TARGET = "A" }
enabled = true
restart_on_exit = true
timeout_ms = 15000

Verification steps:

  • Ensure IDA is running with ida_bridge.py loaded and a database open.
  • Restart Codex CLI so it picks up the new server.
  • In Codex CLI, list tools and confirm ida_* tools (e.g., ida_list_functions, ida_get_pseudocode) are available.

Quick Check (two ways)

Option A: Direct TCP to the IDA plugin (simple self-test)

# quick_test.py
import json, socket

HOST, PORT = "127.0.0.1", 31337
req = {"method": "list_functions", "params": {}}

s = socket.create_connection((HOST, PORT), timeout=5)
s.sendall((json.dumps(req) + "\n").encode("utf-8"))
data = s.recv(65536)
print("IDA reply:", data.decode("utf-8", "ignore"))
python quick_test.py

Option B: Through MCP (recommended via an MCP client)

  • Initialize:
{"jsonrpc":"2.0","id":"1","method":"initialize","params":{"protocolVersion":"2024-11-05"}}
  • List tools:
{"jsonrpc":"2.0","id":"2","method":"tools/list"}
  • Call example (list functions):
{"jsonrpc":"2.0","id":"3","method":"tools/call","params":{"name":"ida_list_functions","arguments":{}}}

Tip: Sending single JSON lines via a pipe is only for smoke tests—register this process in an MCP client for the best experience.

Function Pointer Workflow Example

The new function pointer tools help convert Hex-Rays pseudocode from this:

v10 = MEMORY[0xFFFFF803799C3010](a1, a4, 48, 1, 538997579);

To this:

v10 = SomeApiCall(a1, a4, 48, 1, 538997579);

Method 1: Manual steps

  1. ida_jump_to_address(address=0xFFFFF803799C3010) - Jump to the address
  2. ida_set_data_type(address=0xFFFFF803799C3010, data_type="qword") - Set as 8-byte pointer
  3. ida_set_function_pointer_type(address=0xFFFFF803799C3010, function_signature="NTSTATUS (__fastcall *)(void *, void *, int, int, int)") - Set function pointer type
  4. ida_set_name(address=0xFFFFF803799C3010, name="SomeApiCall") - Give it a name

Method 2: One-step workflow

ida_create_function_pointer(
  address=0xFFFFF803799C3010,
  name="SomeApiCall", 
  function_signature="NTSTATUS (__fastcall *)(void *, void *, int, int, int)"
)

This performs all steps automatically and refreshes the Hex-Rays view.

FAQ

  • If Hex-Rays is not installed, ida_get_pseudocode will error; use ida_get_disassembly instead.
  • To add pseudocode comments, use ida_py_exec to call IDA's Python API (e.g., ida_hexrays / idc.set_cmt) directly.
  • Function pointer operations require the address to be mapped in IDA's address space.
  • Function signatures should follow standard C function pointer syntax: return_type (__calling_convention *)(param_types)
  • If port 31337 is occupied, the plugin increases the port number automatically; set IDA_PORT accordingly on the MCP side to match.
  • Transport encoding is UTF-8; each request/response is a single JSON line ending with a newline.

License

TBD. Contributions welcome—please mention license intentions in PRs.

Demo

IDA reverse engineering demo