Skip to main content
Glama

BinAssistMCP

by jtang613
plugin.py14.4 kB
""" Binary Ninja plugin integration for BinAssistMCP This module provides the Binary Ninja plugin interface with menu integration, settings management, and automatic server lifecycle management. """ from typing import Optional from .logging import log, disable_external_logging # Disable external library logging early to reduce ScriptingProvider messages disable_external_logging() try: import binaryninja as bn BINJA_AVAILABLE = True except ImportError: BINJA_AVAILABLE = False log.log_warn("Binary Ninja not available") if BINJA_AVAILABLE: try: from .config import BinAssistMCPConfig from .server import BinAssistMCPServer except ImportError as e: log.log_error(f"Failed to import BinAssistMCP modules: {e}") BINJA_AVAILABLE = False class BinAssistMCPPlugin: """Binary Ninja plugin for BinAssistMCP""" def __init__(self): """Initialize the plugin""" self.config: Optional[BinAssistMCPConfig] = None self.server: Optional[BinAssistMCPServer] = None self._settings_registered = False if BINJA_AVAILABLE: self._initialize_plugin() def _initialize_plugin(self): """Initialize the plugin with Binary Ninja""" try: # Load configuration self.config = BinAssistMCPConfig() # Register settings if not already done if not self._settings_registered: self._register_settings() self._settings_registered = True # Update config from Binary Ninja settings self.config.update_from_binja_settings() # Register plugin commands self._register_commands() # Auto-startup is handled via global event registration in __init__.py log.log_info("BinAssistMCP plugin initialized successfully") except Exception as e: log.log_error(f"Failed to initialize plugin: {e}") if self.config and self.config.plugin.show_notifications: log.log_error(f"BinAssistMCP initialization failed: {e}") def _register_settings(self): """Register plugin settings with Binary Ninja""" try: settings = bn.Settings() # Server settings settings.register_setting( "binassistmcp.server.host", '{"description": "BinAssistMCP server host address", "title": "Server Host", "default": "localhost", "type": "string"}' ) settings.register_setting( "binassistmcp.server.port", '{"description": "BinAssistMCP server port", "title": "Server Port", "default": 8000, "type": "number", "minValue": 1024, "maxValue": 65535}' ) settings.register_setting( "binassistmcp.server.transport", '{"description": "MCP transport type", "title": "Transport Type", "default": "both", "type": "string", "enum": ["sse", "stdio", "both"]}' ) # Plugin settings settings.register_setting( "binassistmcp.plugin.auto_startup", '{"description": "Automatically start server when Binary Ninja loads a file", "title": "Auto Startup", "default": true, "type": "boolean"}' ) settings.register_setting( "binassistmcp.plugin.show_notifications", '{"description": "Show status notifications", "title": "Show Notifications", "default": true, "type": "boolean"}' ) # Binary analysis settings settings.register_setting( "binassistmcp.binary.max_binaries", '{"description": "Maximum number of concurrent binaries", "title": "Max Binaries", "default": 10, "type": "number", "minValue": 1, "maxValue": 50}' ) settings.register_setting( "binassistmcp.binary.auto_analysis", '{"description": "Enable automatic binary analysis", "title": "Auto Analysis", "default": true, "type": "boolean"}' ) log.log_info("Registered BinAssistMCP settings") except Exception as e: log.log_error(f"Failed to register settings: {e}") def _register_commands(self): """Register plugin menu commands""" try: # Start server command bn.PluginCommand.register( "BinAssistMCP\\Start Server", "Start the BinAssistMCP server", self._start_server_command ) # Stop server command bn.PluginCommand.register( "BinAssistMCP\\Stop Server", "Stop the BinAssistMCP server", self._stop_server_command ) # Restart server command bn.PluginCommand.register( "BinAssistMCP\\Restart Server", "Restart the BinAssistMCP server", self._restart_server_command ) # Server status command bn.PluginCommand.register( "BinAssistMCP\\Server Status", "Show BinAssistMCP server status", self._server_status_command ) # Configuration command bn.PluginCommand.register( "BinAssistMCP\\Open Settings", "Open BinAssistMCP settings", self._open_settings_command ) log.log_info("Registered BinAssistMCP menu commands") except Exception as e: log.log_error(f"Failed to register commands: {e}") def handle_auto_startup(self, binary_view): """Handle auto-startup when a binary is analyzed""" try: if not self.config or not self.config.plugin.auto_startup: return # Start server if not running if not self.server or not self.server.is_running(): log.log_info("Auto-startup triggered: starting BinAssistMCP server") self._start_server_command(binary_view) else: # Add binary to existing server self.add_binary_to_server(binary_view) log.log_info("Auto-startup triggered: added binary to running server") except Exception as e: log.log_error(f"Error in auto-startup: {e}") def _start_server_command(self, bv): """Start server command handler""" try: if self.server and self.server.is_running(): message = "BinAssistMCP server is already running" if self.config.plugin.show_notifications: log.log_info(message) return # Reload configuration log.log_info("Reloading configuration...") self.config = BinAssistMCPConfig() self.config.update_from_binja_settings() log.log_info("Configuration reloaded successfully") # Create and start server log.log_info("Creating BinAssistMCP server instance...") self.server = BinAssistMCPServer(self.config) log.log_info("Server instance created successfully") # Add current binary if available if bv: self.server.add_initial_binary(bv) # Add detailed logging for server startup log.log_info(f"Attempting to start BinAssistMCP server...") log.log_info(f" Configuration: {self.config.server.host}:{self.config.server.port}") log.log_info(f" Transport: {self.config.server.transport.value}") log.log_info("Calling server.start()...") start_result = self.server.start() log.log_info(f"Server.start() returned: {start_result}") if start_result: message = f"BinAssistMCP server started on {self.config.get_server_url()}" if self.config.plugin.show_notifications: log.log_info(message) # Show transport information if self.config.server.transport.value == "both": log.log_info(f"SSE endpoint: {self.config.get_sse_url()}") log.log_info("STDIO transport: Available via CLI") elif self.config.server.transport.value == "sse": log.log_info(f"SSE endpoint: {self.config.get_sse_url()}") elif self.config.server.transport.value == "stdio": log.log_info("STDIO transport: Available via CLI") # Try to verify server is listening import socket try: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: sock.settimeout(1) result = sock.connect_ex((self.config.server.host, self.config.server.port)) if result == 0: log.log_info("✓ Server is listening and accepting connections") else: log.log_warn(f"⚠ Server started but not accepting connections (connect result: {result})") except Exception as e: log.log_warn(f"⚠ Could not verify server connectivity: {e}") else: message = "Failed to start BinAssistMCP server" log.log_error(message) except Exception as e: error_msg = f"Error starting server: {e}" log.log_error(error_msg) log.log_error(error_msg) # Also log the full traceback for debugging import traceback traceback_msg = traceback.format_exc() log.log_error(f"Server startup traceback: {traceback_msg}") log.log_error(f"Full error details: {traceback_msg}") def _stop_server_command(self, bv): """Stop server command handler""" try: if not self.server or not self.server.is_running(): message = "BinAssistMCP server is not running" if self.config and self.config.plugin.show_notifications: log.log_info(message) return self.server.stop() message = "BinAssistMCP server stopped" if self.config and self.config.plugin.show_notifications: log.log_info(message) except Exception as e: error_msg = f"Error stopping server: {e}" log.log_error(error_msg) log.log_error(error_msg) def _restart_server_command(self, bv): """Restart server command handler""" try: self._stop_server_command(bv) # Small delay to ensure clean shutdown import time time.sleep(0.5) self._start_server_command(bv) except Exception as e: error_msg = f"Error restarting server: {e}" log.log_error(error_msg) log.log_error(error_msg) def _server_status_command(self, bv): """Server status command handler""" try: if not self.server: log.log_info("BinAssistMCP server: Not initialized") return if self.server.is_running(): log.log_info("BinAssistMCP server: Running") if self.config: log.log_info(f" Host: {self.config.server.host}") log.log_info(f" Port: {self.config.server.port}") log.log_info(f" Transport: {self.config.server.transport.value}") from .config import TransportType if self.config.is_transport_enabled(TransportType.SSE): log.log_info(f" SSE URL: {self.config.get_sse_url()}") else: log.log_info("BinAssistMCP server: Stopped") except Exception as e: error_msg = f"Error getting server status: {e}" log.log_error(error_msg) log.log_error(error_msg) def _open_settings_command(self, bv): """Open settings command handler""" try: # This will open the Binary Ninja settings dialog # Users can navigate to the BinAssist section log.log_info("BinAssistMCP settings can be found in Binary Ninja Settings under 'binassist' section") log.log_info("Use Ctrl+, (Cmd+, on Mac) to open settings") except Exception as e: error_msg = f"Error opening settings: {e}" log.log_error(error_msg) log.log_error(error_msg) def add_binary_to_server(self, binary_view): """Add a binary view to the running server""" if self.server and self.server.is_running(): try: # Get the context manager and add the binary context_manager = getattr(self.server.mcp_server, '_context_manager', None) if context_manager: name = context_manager.add_binary(binary_view) if self.config and self.config.plugin.show_notifications: log.log_info(f"Added binary '{name}' to BinAssistMCP server") except Exception as e: log.log_error(f"Failed to add binary to server: {e}") # Global plugin instance reference for callbacks _plugin_instance: Optional[BinAssistMCPPlugin] = None def set_plugin_instance(plugin: BinAssistMCPPlugin): """Set the global plugin instance for callback access""" global _plugin_instance _plugin_instance = plugin def get_plugin_instance() -> Optional[BinAssistMCPPlugin]: """Get the global plugin instance""" return _plugin_instance

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/jtang613/BinAssistMCP'

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