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.
284 lines
10 KiB
Markdown
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
|