diff --git a/adbdevicemanager.py b/adbdevicemanager.py index b6c7b79..cfaccc8 100644 --- a/adbdevicemanager.py +++ b/adbdevicemanager.py @@ -7,7 +7,13 @@ class AdbDeviceManager: - def __init__(self, device_name: str | None = None, exit_on_error: bool = True) -> None: + def __init__( + self, + device_name: str | None = None, + exit_on_error: bool = True, + adb_host: str = "127.0.0.1", + adb_port: int = 5037, + ) -> None: """ Initialize the ADB Device Manager @@ -15,7 +21,12 @@ def __init__(self, device_name: str | None = None, exit_on_error: bool = True) - device_name: Optional name/serial of the device to manage. If None, attempts to auto-select if only one device is available. exit_on_error: Whether to exit the program if device initialization fails + adb_host: Host address of the ADB server (default: 127.0.0.1) + adb_port: Port of the ADB server (default: 5037) """ + self.adb_host = adb_host + self.adb_port = adb_port + if not self.check_adb_installed(): error_msg = "adb is not installed or not in PATH. Please install adb and ensure it is in your PATH." if exit_on_error: @@ -60,7 +71,7 @@ def __init__(self, device_name: str | None = None, exit_on_error: bool = True) - # At this point, selected_device_name should always be set due to the logic above # Initialize the device - self.device = AdbClient().device(selected_device_name) + self.device = AdbClient(host=self.adb_host, port=self.adb_port).device(selected_device_name) @staticmethod def check_adb_installed() -> bool: @@ -72,10 +83,9 @@ def check_adb_installed() -> bool: except (subprocess.CalledProcessError, FileNotFoundError): return False - @staticmethod - def get_available_devices() -> list[str]: + def get_available_devices(self) -> list[str]: """Get a list of available devices.""" - return [device.serial for device in AdbClient().devices()] + return [device.serial for device in AdbClient(host=self.adb_host, port=self.adb_port).devices()] def get_packages(self) -> str: command = "pm list packages" diff --git a/config.yaml.example b/config.yaml.example index f5707e4..a53868f 100644 --- a/config.yaml.example +++ b/config.yaml.example @@ -1,6 +1,12 @@ # Android MCP Server Configuration # All sections are optional +# ADB server configuration (optional) +# If not specified, defaults to localhost:5037 +adb: + host: "127.0.0.1" # ADB server host (default: 127.0.0.1) + port: 5037 # ADB server port (default: 5037) + # Device configuration (optional) # If not specified or name is empty, will auto-select device when only one is connected device: diff --git a/server.py b/server.py index fe1998c..6f0d973 100644 --- a/server.py +++ b/server.py @@ -12,11 +12,20 @@ # Load config (make config file optional) config = {} device_name = None +adb_host = "127.0.0.1" +adb_port = 5037 if os.path.exists(CONFIG_FILE): try: with open(CONFIG_FILE) as f: config = yaml.safe_load(f.read()) or {} + + # Load ADB server configuration + adb_config = config.get("adb", {}) + if adb_config: + adb_host = adb_config.get("host", adb_host) + adb_port = adb_config.get("port", adb_port) + device_config = config.get("device", {}) configured_device_name = device_config.get( "name") if device_config else None @@ -28,9 +37,11 @@ if configured_device_name and configured_device_name.strip(): device_name = configured_device_name.strip() print(f"Loaded config from {CONFIG_FILE}") + print(f"ADB server: {adb_host}:{adb_port}") print(f"Configured device: {device_name}") else: print(f"Loaded config from {CONFIG_FILE}") + print(f"ADB server: {adb_host}:{adb_port}") print( "No device specified in config, will auto-select if only one device connected") except Exception as e: @@ -45,7 +56,7 @@ # Initialize MCP and device manager # AdbDeviceManager will handle auto-selection if device_name is None mcp = FastMCP("android") -deviceManager = AdbDeviceManager(device_name) +deviceManager = AdbDeviceManager(device_name, adb_host=adb_host, adb_port=adb_port) @mcp.tool() diff --git a/tests/test_adb_device_manager.py b/tests/test_adb_device_manager.py index 9323e6e..6ebada9 100644 --- a/tests/test_adb_device_manager.py +++ b/tests/test_adb_device_manager.py @@ -138,10 +138,13 @@ def test_check_adb_installed_failure(self, mock_run): assert result is False + @patch('adbdevicemanager.AdbDeviceManager.check_adb_installed') @patch('adbdevicemanager.AdbClient') - def test_get_available_devices(self, mock_adb_client): + def test_get_available_devices(self, mock_adb_client, mock_check_adb): """Test getting available devices""" - # Setup mock devices + # Setup mocks + mock_check_adb.return_value = True + mock_device1 = MagicMock() mock_device1.serial = "device123" mock_device2 = MagicMock() @@ -149,8 +152,13 @@ def test_get_available_devices(self, mock_adb_client): mock_adb_client.return_value.devices.return_value = [ mock_device1, mock_device2] + mock_adb_client.return_value.device.return_value = MagicMock() + + # Create manager with first device to test get_available_devices + with patch('builtins.print'): + manager = AdbDeviceManager(device_name="device123", exit_on_error=False) - devices = AdbDeviceManager.get_available_devices() + devices = manager.get_available_devices() assert devices == ["device123", "device456"] @@ -169,3 +177,33 @@ def test_exit_on_error_true(self, mock_adb_client, mock_get_devices, mock_check_ AdbDeviceManager(device_name=None, exit_on_error=True) mock_exit.assert_called_once_with(1) + + @patch('adbdevicemanager.AdbDeviceManager.check_adb_installed') + @patch('adbdevicemanager.AdbClient') + def test_custom_adb_host_port(self, mock_adb_client, mock_check_adb): + """Test that custom ADB host and port are used""" + # Setup mocks + mock_check_adb.return_value = True + mock_device1 = MagicMock() + mock_device1.serial = "device123" + mock_adb_client.return_value.devices.return_value = [mock_device1] + mock_adb_client.return_value.device.return_value = MagicMock() + + # Test with custom host and port + custom_host = "192.168.1.100" + custom_port = 5038 + + with patch('builtins.print'): + manager = AdbDeviceManager( + device_name=None, + exit_on_error=False, + adb_host=custom_host, + adb_port=custom_port + ) + + # Verify custom host and port are stored + assert manager.adb_host == custom_host + assert manager.adb_port == custom_port + + # Verify AdbClient was called with custom host and port + mock_adb_client.assert_called_with(host=custom_host, port=custom_port)