Skip to main content
Glama

IDA Pro MCP

MIT License
2,531
  • Linux
  • Apple
import sys import inspect import logging import argparse import importlib from pathlib import Path import typing_inspection.introspection as intro from mcp.server.fastmcp import FastMCP # idapro must go first to initialize idalib import idapro import ida_auto import ida_hexrays logger = logging.getLogger(__name__) mcp = FastMCP("github.com/mrexodia/ida-pro-mcp#idalib") def fixup_tool_argument_descriptions(mcp: FastMCP): # In our tool definitions within `mcp-plugin.py`, we use `typing.Annotated` on function parameters # to attach documentation. For example: # # def get_function_by_name( # name: Annotated[str, "Name of the function to get"] # ) -> Function: # """Get a function by its name""" # ... # # However, the interpretation of Annotated is left up to static analyzers and other tools. # FastMCP doesn't have any special handling for these comments, so we splice them into the # tool metadata ourselves here. # # Example, before: # # tool.parameter={ # properties: { # name: { # title: "Name", # type: "string" # } # }, # required: ["name"], # title: "get_function_by_nameArguments", # type: "object" # } # # Example, after: # # tool.parameter={ # properties: { # name: { # title: "Name", # type: "string" # description: "Name of the function to get" # } # }, # required: ["name"], # title: "get_function_by_nameArguments", # type: "object" # } # # References: # - https://docs.python.org/3/library/typing.html#typing.Annotated # - https://fastapi.tiangolo.com/python-types/#type-hints-with-metadata-annotations # unfortunately, FastMCP.list_tools() is async, so we break with best practices and reach into `._tool_manager` # rather than spinning up an asyncio runtime just to fetch the (non-async) list of tools. for tool in mcp._tool_manager.list_tools(): sig = inspect.signature(tool.fn) for name, parameter in sig.parameters.items(): # this instance is a raw `typing._AnnotatedAlias` that we can't do anything with directly. # it renders like: # # typing.Annotated[str, 'Name of the function to get'] if not parameter.annotation: continue # this instance will look something like: # # InspectedAnnotation(type=<class 'str'>, qualifiers=set(), metadata=['Name of the function to get']) # annotation = intro.inspect_annotation( parameter.annotation, annotation_source=intro.AnnotationSource.ANY ) # for our use case, where we attach a single string annotation that is meant as documentation, # we extract that string and assign it to "description" in the tool metadata. if annotation.type is not str: continue if len(annotation.metadata) != 1: continue description = annotation.metadata[0] if not isinstance(description, str): continue logger.debug("adding parameter documentation %s(%s='%s')", tool.name, name, description) tool.parameters["properties"][name]["description"] = description def main(): parser = argparse.ArgumentParser(description="MCP server for IDA Pro via idalib") parser.add_argument("--verbose", "-v", action="store_true", help="Show debug messages") parser.add_argument("--host", type=str, default="127.0.0.1", help="Host to listen on, default: 127.0.0.1") parser.add_argument("--port", type=int, default=8745, help="Port to listen on, default: 8745") parser.add_argument("--unsafe", action="store_true", help="Enable unsafe functions (DANGEROUS)") parser.add_argument("input_path", type=Path, help="Path to the input file to analyze.") args = parser.parse_args() if args.verbose: log_level = logging.DEBUG idapro.enable_console_messages(True) else: log_level = logging.INFO idapro.enable_console_messages(False) mcp.settings.log_level = logging.getLevelName(log_level) mcp.settings.host = args.host mcp.settings.port = args.port logging.basicConfig(level=log_level) # reset logging levels that might be initialized in idapythonrc.py # which is evaluated during import of idalib. logging.getLogger().setLevel(log_level) if not args.input_path.exists(): raise FileNotFoundError(f"Input file not found: {args.input_path}") # TODO: add a tool for specifying the idb/input file (sandboxed) logger.info("opening database: %s", args.input_path) if idapro.open_database(str(args.input_path), run_auto_analysis=True): raise RuntimeError("failed to analyze input file") logger.debug("idalib: waiting for analysis...") ida_auto.auto_wait() if not ida_hexrays.init_hexrays_plugin(): raise RuntimeError("failed to initialize Hex-Rays decompiler") plugin = importlib.import_module("ida_pro_mcp.mcp-plugin") logger.debug("adding tools...") for name, callable in plugin.rpc_registry.methods.items(): if args.unsafe or name not in plugin.rpc_registry.unsafe: logger.debug("adding tool: %s: %s", name, callable) mcp.add_tool(callable, name) # NOTE: https://github.com/modelcontextprotocol/python-sdk/issues/466 fixup_tool_argument_descriptions(mcp) # NOTE: npx @modelcontextprotocol/inspector for debugging logger.info("MCP Server availabile at: http://%s:%d/sse", mcp.settings.host, mcp.settings.port) try: mcp.run(transport="sse") except KeyboardInterrupt: pass if __name__ == "__main__": main()

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/mrexodia/ida-pro-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server