Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/mrexodia/ida-pro-mcp/llms.txt

Use this file to discover all available pages before exploring further.

IDA Pro MCP uses a modular API system where every exported tool is a plain Python function decorated with @tool and @idasync. Adding a new tool requires no registration, routing, or boilerplate beyond the function itself — the decorators handle everything automatically.

Tool decorator pattern

Every tool function must use two decorators: @tool registers it with the MCP server, and @idasync routes the call to the IDA main thread where all SDK operations must run.
from .rpc import tool
from .sync import idasync

@tool
@idasync
def my_tool(...):
    ...
The order is fixed: @tool must be the outermost decorator and @idasync immediately below it. Reversing the order will break tool registration.
All IDA SDK calls must run on the main thread. Never call IDA APIs from a background thread. The @idasync decorator handles this by scheduling the function body through idaapi.execute_sync in write mode.

A complete example

The following example shows a fully idiomatic tool function with type hints, docstring, batch input, and address parsing:
from typing import Annotated
from .rpc import tool
from .sync import idasync
from .utils import normalize_list_input, parse_address

@tool
@idasync
def get_func_flags(
    addrs: Annotated[str | list, "Function addresses (0x401000, main) or list"],
) -> list[dict]:
    """Return the flags word for each function address."""
    results = []
    for addr_str in normalize_list_input(addrs):
        try:
            ea = parse_address(addr_str)
            flags = idc.get_full_flags(ea)
            results.append({"addr": addr_str, "flags": hex(flags), "error": None})
        except Exception as exc:
            results.append({"addr": addr_str, "flags": None, "error": str(exc)})
    return results

API conventions

Following these conventions keeps new tools consistent with the rest of the codebase. Batch-first design. Accept a list or a comma-separated string for any multi-item input. Use normalize_list_input() to handle both forms uniformly. Annotated[...] descriptions. Every parameter should carry a human-readable description inside Annotated[...]. This string becomes part of the MCP schema that LLM clients see.
addrs: Annotated[str | list, "Addresses (0x401000, main) or list"]
Docstring as tool description. The function’s docstring is used verbatim as the MCP tool description. Write a single, clear sentence. Return list[dict] for batch results. Every item in the list should have a consistent shape. Include an "error" key (string or None) so callers can distinguish per-item failures without raising an exception.

Common helpers

Import these from utils.py rather than reimplementing them:
HelperPurpose
parse_address(s)Convert a hex string, decimal string, or symbol name to an integer address
normalize_list_input(v)Accept either a comma-separated string or a Python list; always returns a list
normalize_dict_list(v)Same as above but for TypedDict items
paginate(items, query)Apply offset/count pagination to a list, returning a Page result
pattern_filter(items, pat)Filter a list of strings by a glob pattern
from .utils import parse_address, normalize_list_input, normalize_dict_list, paginate

Unsafe tools

Debugger controls and destructive operations must be marked with @unsafe. This hides them from the default tool list and prevents accidental use:
from .rpc import tool, unsafe
from .sync import idasync

@unsafe
@tool
@idasync
def dangerous_op(...):
    """Perform a destructive operation."""
    ...
The decorator order for unsafe tools is @unsafe outermost, then @tool, then @idasync. The unsafe decorator only adds the function name to the MCP_UNSAFE set; it does not wrap the function.

Choosing the right file

Place new tools in the api_*.py file whose scope best matches the operation:

api_core.py

IDB metadata, function listing, string search, imports, integer conversion

api_analysis.py

Decompilation, disassembly, xrefs, call graphs, pattern search, basic blocks

api_memory.py

Raw byte reads, integer reads and writes, string reads, memory patching

api_types.py

Struct definitions, type inference, type application, type export

api_modify.py

Comments, renaming functions/globals/locals, assembly patching

api_stack.py

Stack frame inspection and variable declaration

api_debug.py

Debugger control — use @unsafe for all tools in this file

api_python.py

Arbitrary Python execution in the IDA context
If your tool does not fit any existing module, create a new api_<name>.py file in src/ida_pro_mcp/ida_mcp/. It will be loaded automatically.

Testing with MCP inspector

After adding a tool, launch the MCP inspector to verify it appears and behaves correctly:
1

Start the inspector

uv run mcp dev src/ida_pro_mcp/server.py
This opens a web UI at http://localhost:5173.
2

Locate your tool

Navigate to the Tools tab. Your new function should appear with the docstring as its description and the Annotated texts as parameter descriptions.
3

Call the tool

Fill in test arguments and click Run. Confirm the return value matches your expected shape and that errors are surfaced as per-item "error" fields rather than exceptions.
4

Write a test

Add a @test() function immediately after your tool definition. See Testing IDA Pro MCP tools for the full workflow.

Build docs developers (and LLMs) love