mcp-adb/README.md
Ryan Malloy e0c05dc72a Add connectivity and settings mixins (50 → 65 tools)
New mixins:
- connectivity.py: adb_connect, adb_disconnect, adb_tcpip, adb_pair,
  device_properties (batch getprop)
- settings.py: settings_get/put, wifi/bluetooth/airplane toggles,
  screen_brightness, screen_timeout, notification_list, clipboard_get,
  media_control

Also fixes clipboard_set false-positive on devices where
cmd clipboard returns exit 0 but has no implementation.
2026-02-11 03:05:27 -07:00

284 lines
10 KiB
Markdown

# mcadb
A [Model Context Protocol](https://modelcontextprotocol.io/) server that gives AI assistants direct control over Android devices through ADB. Point any MCP-compatible client at a phone plugged into USB, and it can take screenshots, tap buttons, launch apps, inspect UI elements, transfer files, and run shell commands — all through structured, type-safe tool calls.
Built on [FastMCP](https://gofastmcp.com/) with a modular mixin architecture. 65 tools across 8 domains. Tested on real hardware.
## Quick Start
```bash
# Run directly (no install)
uvx mcadb
# Or install and run
uv add mcadb
mcadb
```
### MCP Client Configuration
Add to your MCP client's config (Claude Desktop, Claude Code, etc.):
```json
{
"mcpServers": {
"mcadb": {
"command": "uvx",
"args": ["mcadb"]
}
}
}
```
For Claude Code:
```bash
claude mcp add mcadb -- uvx mcadb
```
For local development:
```bash
claude mcp add mcadb -- uv run --directory /path/to/mcp-adb mcadb
```
## Prerequisites
- **Python 3.11+**
- **ADB** installed and on `PATH` (`adb devices` should work)
- **USB debugging** enabled on the Android device
- Device connected via USB (or `adb connect` for network)
## What Can It Do?
### Standard Tools (always available)
| Domain | Tool | What it does |
|--------|------|-------------|
| **Devices** | `devices_list` | Discover connected devices (USB + network) |
| | `devices_use` | Set active device for multi-device setups |
| | `devices_current` | Show which device is selected |
| | `device_info` | Battery, WiFi, storage, Android version, model |
| **Connectivity** | `adb_connect` | Connect to device over TCP/IP |
| | `adb_disconnect` | Disconnect a network device |
| | `adb_pair` | Wireless debugging pairing (Android 11+) |
| | `device_properties` | Batch getprop (model, SoC, versions, serial, ABI) |
| **Input** | `input_tap` | Tap at screen coordinates |
| | `input_swipe` | Swipe between two points |
| | `input_scroll_down` | Scroll down (auto-detects screen size) |
| | `input_scroll_up` | Scroll up (auto-detects screen size) |
| | `input_back` | Press Back |
| | `input_home` | Press Home |
| | `input_recent_apps` | Open app switcher |
| | `input_key` | Send any key event (`VOLUME_UP`, `ENTER`, etc.) |
| | `input_text` | Type text into focused field |
| | `clipboard_set` | Set clipboard (handles special chars), optional auto-paste |
| **Apps** | `app_launch` | Launch app by package name |
| | `app_open_url` | Open URL in default browser |
| | `app_close` | Force stop an app |
| | `app_current` | Get the foreground app and activity |
| **Screen** | `screenshot` | Capture screen as PNG |
| | `screen_size` | Get display resolution |
| | `screen_density` | Get display DPI |
| | `screen_on` / `screen_off` | Wake or sleep the display |
| **UI** | `ui_dump` | Dump accessibility tree (all visible elements) |
| | `ui_find_element` | Search for elements by text, ID, class, or description |
| | `wait_for_text` | Poll until text appears on screen |
| | `wait_for_text_gone` | Poll until text disappears |
| | `tap_text` | Find an element by text and tap it |
| **Settings** | `settings_get` | Read any system/global/secure setting |
| | `notification_list` | List recent notifications (title, text, package) |
| | `clipboard_get` | Read clipboard contents |
| | `media_control` | Play/pause/next/previous/stop/volume |
| **Config** | `config_status` | Show current settings |
| | `config_set_developer_mode` | Toggle developer tools |
| | `config_set_screenshot_dir` | Set where screenshots are saved |
### Developer Mode Tools
Enable with `config_set_developer_mode(true)` to unlock power-user tools. Destructive operations (uninstall, clear data, reboot, delete) require user confirmation via MCP elicitation.
| Domain | Tool | What it does |
|--------|------|-------------|
| **Shell** | `shell_command` | Run any shell command on device |
| **Input** | `input_long_press` | Press and hold gesture |
| **Apps** | `app_list_packages` | List installed packages (with filters) |
| | `app_install` | Install APK from host |
| | `app_uninstall` | Remove an app (with confirmation) |
| | `app_clear_data` | Wipe app data (with confirmation) |
| | `activity_start` | Launch activity with full intent control |
| | `broadcast_send` | Send broadcast intents |
| **Screen** | `screen_record` | Record screen to MP4 |
| | `screen_set_size` | Override display resolution |
| | `screen_reset_size` | Restore original resolution |
| **Device** | `device_reboot` | Reboot device (with confirmation) |
| | `logcat_capture` | Capture system logs |
| | `logcat_clear` | Clear log buffer |
| **Files** | `file_push` | Transfer file to device |
| | `file_pull` | Transfer file from device |
| | `file_list` | List directory contents |
| | `file_delete` | Delete file (with confirmation) |
| | `file_exists` | Check if file exists |
| **Connectivity** | `adb_tcpip` | Switch USB device to TCP/IP mode |
| **Settings** | `settings_put` | Write system/global/secure setting |
| | `wifi_toggle` | Enable/disable WiFi |
| | `bluetooth_toggle` | Enable/disable Bluetooth |
| | `airplane_mode_toggle` | Toggle airplane mode (with confirmation) |
| | `screen_brightness` | Set brightness 0-255 |
| | `screen_timeout` | Set screen timeout duration |
### Resources
| URI | Description |
|-----|-------------|
| `adb://devices` | Connected device list |
| `adb://device/{id}` | Detailed device properties |
| `adb://apps/current` | Currently focused app |
| `adb://screen/info` | Screen resolution and DPI |
| `adb://help` | Tool reference and tips |
## Usage Examples
**Screenshot + UI inspection loop** (how an AI assistant typically navigates):
```
1. screenshot() → See what's on screen
2. ui_dump() → Get element tree with tap coordinates
3. tap_text("Settings") → Tap the "Settings" element
4. wait_for_text("Wi-Fi") → Wait for the screen to load
5. screenshot() → Verify the result
```
**Open a URL and check what loaded:**
```
1. app_open_url("https://example.com")
2. wait_for_text("Example Domain")
3. screenshot()
```
**Install and launch an APK** (developer mode):
```
1. config_set_developer_mode(true)
2. app_install("/path/to/app.apk")
3. app_launch("com.example.myapp")
4. logcat_capture(filter_spec="MyApp:D *:S")
```
**Connect to a WiFi device:**
```
1. adb_connect("10.20.0.25") → Connect to network device
2. devices_list() → Verify it appears
3. screenshot() → Capture from network device
```
**Multi-device workflow:**
```
1. devices_list() → See all connected devices
2. devices_use("SERIAL_NUMBER") → Select target device
3. device_info() → Check battery, WiFi, storage
4. screenshot() → Capture from selected device
```
**Read settings and control media:**
```
1. settings_get("global", "wifi_on") → Check WiFi state
2. notification_list() → See recent notifications
3. media_control("pause") → Pause media playback
4. clipboard_get() → Read clipboard contents
```
## Architecture
The server uses FastMCP's [MCPMixin](https://gofastmcp.com/) pattern to organize 65 tools into focused, single-responsibility modules:
```
src/
server.py ← FastMCP app, ADBServer (thin orchestrator)
config.py ← Persistent config (~/.config/adb-mcp/config.json)
models.py ← Pydantic models (DeviceInfo, CommandResult, ScreenshotResult)
mixins/
base.py ← ADB command execution, injection-safe shell quoting
devices.py ← Device discovery, info, logcat, reboot
connectivity.py ← TCP/IP connect/disconnect, wireless pairing, properties
input.py ← Tap, swipe, scroll, keys, text, clipboard, shell
apps.py ← Launch, close, install, intents, broadcasts
screenshot.py ← Capture, recording, display settings
ui.py ← Accessibility tree, element search, text polling
files.py ← Push, pull, list, delete, exists
settings.py ← System settings, radios, brightness, notifications, media
```
`ADBServer` inherits all eight mixins. Each mixin calls `run_shell_args()` (injection-safe) or `run_adb()` on the base class. The base handles device targeting, subprocess execution, and timeouts.
## Security Model
All tools that accept user-provided values use **injection-safe command execution**:
- **`run_shell_args()`** quotes every argument with `shlex.quote()` before sending to the device shell. This is the default for all tools.
- **`run_shell()`** (string form) is only used by the developer-mode `shell_command` tool, where the user intentionally provides a raw command.
- **`input_text()`** rejects special characters (`$ ( ) ; | & < >` etc.) and directs users to `clipboard_set()` instead.
- **`input_key()`** strips non-alphanumeric characters from key codes.
- **Destructive operations** (uninstall, clear data, delete, reboot) require user confirmation via MCP elicitation.
- **Developer mode** is off by default and must be explicitly enabled. Settings persist at `~/.config/adb-mcp/config.json`.
## Docker
```bash
docker build -t mcadb .
docker run --privileged -v /dev/bus/usb:/dev/bus/usb mcadb
```
The `--privileged` flag and USB volume mount are required for ADB to detect physical devices.
MCP client config for Docker:
```json
{
"mcpServers": {
"mcadb": {
"command": "docker",
"args": ["run", "-i", "--privileged", "-v", "/dev/bus/usb:/dev/bus/usb", "mcadb"]
}
}
}
```
## Development
```bash
# Clone and install
git clone https://git.supported.systems/MCP/mcp-adb.git
cd mcp-adb
uv sync --group dev
# Run locally
uv run mcadb
# Lint
uv run ruff check src/
# Format
uv run ruff format src/
# Type check
uv run mypy src/
```
## Configuration
Settings are stored at `~/.config/adb-mcp/config.json` (override with `ADB_MCP_CONFIG_DIR` env var):
```json
{
"developer_mode": false,
"default_screenshot_dir": null,
"auto_select_single_device": true
}
```
| Setting | Default | Description |
|---------|---------|-------------|
| `developer_mode` | `false` | Unlock advanced tools (shell, install, reboot, etc.) |
| `default_screenshot_dir` | `null` | Directory for screenshots/recordings (null = cwd) |
| `auto_select_single_device` | `true` | Skip device selection when only one is connected |
## License
MIT