(9-2-01)智能编程助手(IDA Pro+VS Code+MCP):MCP服务器

(9)下面代码定义了函数generate_readme,用于生成README文档内容,包括函数列表、描述和MCP配置示例。它遍历安全函数和不安全函数,生成相应的描述,并打印MCP配置示例。
def generate_readme():
"""生成README文档内容(函数列表及描述)"""
print("README:")
print(f"- `check_connection()`: 检查IDA插件是否正在运行。")
def get_description(name: str):
"""获取函数的签名和描述"""
function = visitor.functions[name]
signature = function.name + "("
for i, arg in enumerate(function.args.args):
if i > 0:
signature += ", "
signature += arg.arg
signature += ")"
description = visitor.descriptions.get(function.name, "<无描述>").strip()
if description[-1] != ".":
description += "."
return f"- `{signature}`: {description}"
# 输出安全函数
for safe_function in SAFE_FUNCTIONS:
print(get_description(safe_function))
# 输出不安全函数
print("
不安全函数(需--unsafe标志):
")
for unsafe_function in UNSAFE_FUNCTIONS:
print(get_description(unsafe_function))
# 输出MCP配置示例
print("
MCP配置:")
mcp_config = {
"mcpServers": {
"github.com/mrexodia/ida-pro-mcp": {
"command": "uv",
"args": [
"--directory",
"c:MCPida-pro-mcp",
"run",
"server.py",
"--install-plugin"
],
"timeout": 1800,
"disabled": False,
}
}
}
print(json.dumps(mcp_config, indent=2))
(10)下面代码定义了函数get_python_executable,用于获取Python可执行文件的路径。它首先检查虚拟环境变量,然后从系统路径中查找Python可执行文件。
def get_python_executable():
"""获取Python可执行文件路径"""
venv = os.environ.get("VIRTUAL_ENV")
if venv:
if sys.platform == "win32":
python = os.path.join(venv, "Scripts", "python.exe")
else:
python = os.path.join(venv, "bin", "python3")
if os.path.exists(python):
return python
# 从系统路径中查找Python
for path in sys.path:
if sys.platform == "win32":
path = path.replace("/", "")
split = path.split(os.sep)
if split[-1].endswith(".zip"):
path = os.path.dirname(path)
if sys.platform == "win32":
python_executable = os.path.join(path, "python.exe")
else:
python_executable = os.path.join(path, "..", "bin", "python3")
python_executable = os.path.abspath(python_executable)
if os.path.exists(python_executable):
return python_executable
return sys.executable # 默认返回当前Python可执行文件
(11)下面代码定义了函数print_mcp_config,用于打印MCP服务器的配置JSON。它包含命令、参数、超时时间等信息,以便于用户配置MCP客户端。
def print_mcp_config():
"""打印MCP服务器配置JSON"""
print(json.dumps({
"mcpServers": {
mcp.name: {
"command": get_python_executable(),
"args": [
__file__,
],
"timeout": 1800,
"disabled": False,
}
}
}, indent=2)
)
(12)下面代码定义了函数install_mcp_servers,用于安装或卸载MCP客户端配置。它支持不同的平台和客户端工具,例如Cline、Roo Code等。函数install_mcp_servers能够根据操作系统确定配置文件路径,并执行安装或卸载操作。
def install_mcp_servers(*, uninstall=False, quiet=False, env={}):
"""安装或卸载MCP客户端配置(适配不同工具如Cline、Roo Code等)"""
# 不同平台的MCP客户端配置路径
if sys.platform == "win32":
configs = {
"Cline": (os.path.join(os.getenv("APPDATA"), "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings"), "cline_mcp_settings.json"),
"Roo Code": (os.path.join(os.getenv("APPDATA"), "Code", "User", "globalStorage", "rooveterinaryinc.roo-cline", "settings"), "mcp_settings.json"),
"Claude": (os.path.join(os.getenv("APPDATA"), "Claude"), "claude_desktop_config.json"),
"Cursor": (os.path.join(os.path.expanduser("~"), ".cursor"), "mcp.json"),
"Windsurf": (os.path.join(os.path.expanduser("~"), ".codeium", "windsurf"), "mcp_config.json"),
"LM Studio": (os.path.join(os.path.expanduser("~"), ".lmstudio"), "mcp.json"),
}
elif sys.platform == "darwin": # macOS
configs = {
"Cline": (os.path.join(os.path.expanduser("~"), "Library", "Application Support", "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings"), "cline_mcp_settings.json"),
"Roo Code": (os.path.join(os.path.expanduser("~"), "Library", "Application Support", "Code", "User", "globalStorage", "rooveterinaryinc.roo-cline", "settings"), "mcp_settings.json"),
"Claude": (os.path.join(os.path.expanduser("~"), "Library", "Application Support", "Claude"), "claude_desktop_config.json"),
"Cursor": (os.path.join(os.path.expanduser("~"), ".cursor"), "mcp.json"),
"Windsurf": (os.path.join(os.path.expanduser("~"), ".codeium", "windsurf"), "mcp_config.json"),
"Claude Code": (os.path.join(os.path.expanduser("~")), ".claude.json"),
"LM Studio": (os.path.join(os.path.expanduser("~"), ".lmstudio"), "mcp.json"),
}
elif sys.platform == "linux": # Linux
configs = {
"Cline": (os.path.join(os.path.expanduser("~"), ".config", "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings"), "cline_mcp_settings.json"),
"Roo Code": (os.path.join(os.path.expanduser("~"), ".config", "Code", "User", "globalStorage", "rooveterinaryinc.roo-cline", "settings"), "mcp_settings.json"),
"Cursor": (os.path.join(os.path.expanduser("~"), ".cursor"), "mcp.json"),
"Windsurf": (os.path.join(os.path.expanduser("~"), ".codeium", "windsurf"), "mcp_config.json"),
"Claude Code": (os.path.join(os.path.expanduser("~")), ".claude.json"),
"LM Studio": (os.path.join(os.path.expanduser("~"), ".lmstudio"), "mcp.json"),
}
else:
print(f"不支持的平台:{sys.platform}")
return
installed = 0
for name, (config_dir, config_file) in configs.items():
config_path = os.path.join(config_dir, config_file)
if not os.path.exists(config_dir):
action = "卸载" if uninstall else "安装"
if not quiet:
print(f"跳过{name}的{action}
配置路径:{config_path}(不存在)")
continue
# 读取现有配置
if not os.path.exists(config_path):
config = {}
else:
with open(config_path, "r") as f:
data = f.read().strip()
if len(data) == 0:
config = {}
else:
try:
config = json.loads(data)
except json.decoder.JSONDecodeError:
if not quiet:
print(f"跳过{name}的卸载
配置路径:{config_path}(无效JSON)")
continue
# 初始化mcpServers配置
if "mcpServers" not in config:
config["mcpServers"] = {}
mcp_servers = config["mcpServers"]
if uninstall:
# 卸载:移除当前MCP服务器配置
if mcp.name not in mcp_servers:
if not quiet:
print(f"跳过{name}的卸载
配置路径:{config_path}(未安装)")
continue
del mcp_servers[mcp.name]
else:
# 安装:添加当前MCP服务器配置
if mcp.name in mcp_servers:
# 保留现有环境变量
for key, value in mcp_servers[mcp.name].get("env", {}):
env[key] = value
mcp_servers[mcp.name] = {
"command": get_python_executable(),
"args": [__file__],
"timeout": 1800,
"disabled": False,
"autoApprove": SAFE_FUNCTIONS, # 自动允许安全函数
"alwaysAllow": SAFE_FUNCTIONS, # 始终允许安全函数
}
if env:
mcp_servers[mcp.name]["env"] = env # 添加环境变量
# 写入更新后的配置
with open(config_path, "w") as f:
json.dump(config, f, indent=2)
if not quiet:
action = "已卸载" if uninstall else "已安装"
print(f"{action}{name}的MCP服务器(需重启)
配置路径:{config_path}")
installed += 1
# 若未安装任何配置,输出通用配置
if not uninstall and installed == 0:
print("未安装任何MCP服务器。对于不支持的客户端,请使用以下配置:
")
print_mcp_config()
(13)下面代码定义了函数install_ida_plugin,用于安装或卸载IDA Pro插件。函数install_ida_plugin能够根据操作系统确定插件目录,并创建软链接或复制插件文件。
def install_ida_plugin(*, uninstall: bool = False, quiet: bool = False):
"""安装或卸载IDA插件"""
# 确定IDA插件目录
if sys.platform == "win32":
ida_plugin_folder = os.path.join(os.getenv("APPDATA"), "Hex-Rays", "IDA Pro", "plugins")
else:
ida_plugin_folder = os.path.join(os.path.expanduser("~"), ".idapro", "plugins")
plugin_destination = os.path.join(ida_plugin_folder, "mcp-plugin.py") # 插件目标路径
if uninstall:
# 卸载:删除插件
if not os.path.exists(plugin_destination):
print(f"跳过IDA插件卸载
路径:{plugin_destination}(不存在)")
return
os.remove(plugin_destination)
if not quiet:
print(f"已卸载IDA插件
路径:{plugin_destination}")
else:
# 安装:创建插件目录并添加插件(优先软链接,失败则复制)
if not os.path.exists(ida_plugin_folder):
os.makedirs(ida_plugin_folder)
# 检查软链接是否已存在且有效
realpath = os.path.realpath(plugin_destination)
if realpath == IDA_PLUGIN_PY:
if not quiet:
print(f"跳过IDA插件安装(软链接已更新)
插件路径:{realpath}")
else:
# 移除现有插件(文件或无效链接)
if os.path.lexists(plugin_destination):
os.remove(plugin_destination)
# 尝试创建软链接,失败则复制文件
try:
os.symlink(IDA_PLUGIN_PY, plugin_destination)
except OSError:
shutil.copy(IDA_PLUGIN_PY, plugin_destination)
if not quiet:
print(f"已安装IDA Pro插件(需重启IDA)
插件路径:{plugin_destination}")
(14)下面代码定义了主函数main,用于处理命令行参数,并执行相应的操作,如安装、卸载、生成文档等。函数main还会启动MCP服务器,支持stdio或SSE传输协议,并根据用户输入启用或禁用不安全函数。
def main():
"""主函数:处理命令行参数并执行对应操作"""
global ida_host, ida_port
parser = argparse.ArgumentParser(description="IDA Pro MCP服务器")
parser.add_argument("--install", action="store_true", help="安装MCP服务器和IDA插件")
parser.add_argument("--uninstall", action="store_true", help="卸载MCP服务器和IDA插件")
parser.add_argument("--generate-docs", action="store_true", help=argparse.SUPPRESS) # 生成文档(隐藏参数)
parser.add_argument("--install-plugin", action="store_true", help=argparse.SUPPRESS) # 仅安装插件(隐藏参数)
parser.add_argument("--transport", type=str, default="stdio", help="MCP传输协议(stdio或http://127.0.0.1:8744)")
parser.add_argument("--ida-rpc", type=str, default=f"http://{ida_host}:{ida_port}", help=f"IDA RPC服务器地址(默认:http://{ida_host}:{ida_port})")
parser.add_argument("--unsafe", action="store_true", help="启用不安全函数(危险)")
parser.add_argument("--config", action="store_true", help="生成MCP配置JSON")
args = parser.parse_args()
# 校验参数冲突
if args.install and args.uninstall:
print("无法同时安装和卸载")
return
# 执行安装/卸载操作
if args.install:
install_mcp_servers()
install_ida_plugin()
return
if args.uninstall:
install_mcp_servers(uninstall=True)
install_ida_plugin(uninstall=True)
return
# 生成文档(开发者用)
if args.generate_docs:
generate_readme()
return
# 静默安装插件(用于自动化安装)
if args.install_plugin:
install_ida_plugin(quiet=True)
# 输出MCP配置
if args.config:
print_mcp_config()
return
# 解析IDA RPC服务器地址
ida_rpc = urlparse(args.ida_rpc)
if ida_rpc.hostname is None or ida_rpc.port is None:
raise Exception(f"无效的IDA RPC服务器地址:{args.ida_rpc}")
ida_host = ida_rpc.hostname
ida_port = ida_rpc.port
# 过滤不安全函数(未启用--unsafe时)
if not args.unsafe:
mcp_tools = mcp._tool_manager._tools
for unsafe in UNSAFE_FUNCTIONS:
if unsafe in mcp_tools:
del mcp_tools[unsafe]
# 启动MCP服务器(支持stdio或SSE传输)
try:
if args.transport == "stdio":
mcp.run(transport="stdio")
else:
url = urlparse(args.transport)
if url.hostname is None or url.port is None:
raise Exception(f"无效的传输协议URL:{args.transport}")
mcp.settings.host = url.hostname
mcp.settings.port = url.port
# 注意:使用npx @modelcontextprotocol/inspector进行调试
print(f"MCP服务器可访问:http://{mcp.settings.host}:{mcp.settings.port}/sse")
mcp.settings.log_level = "INFO"
mcp.run(transport="sse")
except KeyboardInterrupt:
pass # 捕获Ctrl+C,优雅退出
if __name__ == "__main__":
main()
注意:文件server.py 和前面介绍的文件idalib_server.py 都是 MCP (Model Context Protocol) 服务器,但它们在架构中扮演不同的角色,主要区别如下:
1. 核心区别
- server.py:作为主服务器,负责与外部 MCP 客户端(如 Cline、Cursor、Claude 等)通信,通过动态生成的工具函数转发请求到 IDA Pro 插件。
- idalib_server.py:作为 IDA Pro 内部的服务器,使用 idalib(IDA Pro 的 Python 接口)直接操作 IDB 数据库,处理来自 server.py 的请求并返回结果。
2. 架构关系
在本项目中,文件server.py 和文件idalib_server.py的架构关系如下:
外部工具(Cline、Cursor等)→server.py→IDA Pro插件→idalib_server.py→IDA Pro内部API
(1)server.py:是用户直接启动的进程,负责:
- 与MCP客户端建立连接
- 动态解析mcp-plugin.py生成工具函数
- 管理安全/不安全函数的权限控制
- 安装/卸载插件和配置文件
(2)idalib_server.py:是 IDA Pro 内部运行的进程,负责:
- 加载和分析二进制文件
- 提供 IDA Pro 的核心功能(反编译、获取函数信息等)
- 通过 JSON-RPC 协议与 server.py 通信
3. 协作流程
(1)用户启动 server.py时,它会动态生成工具函数(基于 mcp-plugin.py)、监听 MCP 客户端连接、安装 IDA Pro 插件(如果需要)。
(2)用户在 IDA Pro中启动MCP插件时,插件会加载 idalib_server.py启动内部服务器,监听来自 server.py 的请求。
(3)当 MCP 客户端发送请求时,server.py 接收请求并转发给 IDA Pro 插件。idalib_server.py 处理请求,调用 IDA Pro API。最终的结果通过 server.py 返回给客户端。
4. 典型场景
(1)文件server.py
- 开发人员在终端启动服务器
- 配置多个 MCP 客户端(如 Cline、Cursor)
- 管理插件的安装和卸载工作
(2)文件idalib_server.py
- 在IDA Pro中分析二进制文件时提供实时数据
- 执行反编译、获取函数信息等逆向工程操作
- 通过插件菜单(如Edit→Plugins→MCP)启动








