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.
10 KiB
mcadb
A Model Context Protocol 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 with a modular mixin architecture. 65 tools across 8 domains. Tested on real hardware.
Quick Start
# 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.):
{
"mcpServers": {
"mcadb": {
"command": "uvx",
"args": ["mcadb"]
}
}
}
For Claude Code:
claude mcp add mcadb -- uvx mcadb
For local development:
claude mcp add mcadb -- uv run --directory /path/to/mcp-adb mcadb
Prerequisites
- Python 3.11+
- ADB installed and on
PATH(adb devicesshould work) - USB debugging enabled on the Android device
- Device connected via USB (or
adb connectfor 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 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 withshlex.quote()before sending to the device shell. This is the default for all tools.run_shell()(string form) is only used by the developer-modeshell_commandtool, where the user intentionally provides a raw command.input_text()rejects special characters ($ ( ) ; | & < >etc.) and directs users toclipboard_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
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:
{
"mcpServers": {
"mcadb": {
"command": "docker",
"args": ["run", "-i", "--privileged", "-v", "/dev/bus/usb:/dev/bus/usb", "mcadb"]
}
}
}
Development
# 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):
{
"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