An Android application that runs as an MCP (Model Context Protocol) server, enabling AI models to fully control an Android device remotely using accessibility services and screenshot capture. The app runs directly on your Android device (or emulator) and exposes an HTTP server (with optional HTTPS) implementing the MCP protocol. AI models like Claude can connect to it and interact with any app on
Add this skill
npx mdskills install danielealbano/android-remote-control-mcpComprehensive MCP server with 54 tools for full Android device control via accessibility APIs
An Android application that runs as an MCP (Model Context Protocol) server, enabling AI models to fully control an Android device remotely using accessibility services and screenshot capture.
The app runs directly on your Android device (or emulator) and exposes an HTTP server (with optional HTTPS) implementing the MCP protocol. AI models like Claude can connect to it and interact with any app on the device — reading UI elements, tapping buttons, typing text, swiping, capturing screenshots, managing files, launching apps, and more.
⚠️ Disclaimer: This software is provided "as-is" without warranty of any kind, for research and educational purposes only. The authors do not condone the use of this tool for any illegal, unauthorized, or unethical activities. Users are solely responsible for ensuring their use complies with all applicable laws and regulations. By using this software, you agree to use it responsibly and at your own risk.
/mcp (MCP specification compliant, JSON-only, no SSE)All tool names use the android_ prefix by default (e.g., android_tap). When a device slug is configured (e.g., pixel7), the prefix becomes android_pixel7_ (e.g., android_pixel7_tap). See docs/MCP_TOOLS.md for the full naming convention.
| Category | Tools | Description |
|---|---|---|
| Screen Introspection (1) | android_get_screen_state | Consolidated screen state: app info, screen dimensions, filtered UI node list (TSV), hierarchy section, optional annotated low-res screenshot with bounding boxes and node ID labels |
| System Actions (6) | android_press_back, android_press_home, android_press_recents, android_open_notifications, android_open_quick_settings, android_get_device_logs | Global device actions and log retrieval |
| Touch Actions (5) | android_tap, android_long_press, android_double_tap, android_swipe, android_scroll | Coordinate-based touch interactions |
| Gestures (2) | android_pinch, android_custom_gesture | Multi-touch and complex gestures |
| Node Actions (5) | android_find_nodes, android_click_node, android_long_click_node, android_tap_node, android_scroll_to_node | Accessibility node-based interactions |
| Text Input (5) | android_type_append_text, android_type_insert_text, android_type_replace_text, android_type_clear_text, android_press_key | Natural text input via InputConnection and key events |
| Utilities (5) | android_get_clipboard, android_set_clipboard, android_wait_for_node, android_wait_for_idle, android_get_node_details | Helper tools for automation and node inspection |
| File Operations (8) | android_list_storage_locations, android_list_files, android_read_file, android_write_file, android_append_file, android_file_replace, android_download_from_url, android_delete_file | File system access via Storage Access Framework (SAF) |
| App Management (3) | android_open_app, android_list_apps, android_close_app | Launch, list, and close applications |
| Camera (6) | android_list_cameras, android_list_camera_photo_resolutions, android_list_camera_video_resolutions, android_take_camera_photo, android_save_camera_photo, android_save_camera_video | Camera photo/video capture via CameraX, list capabilities and resolutions |
| Intent (2) | android_send_intent, android_open_uri | Send explicit/implicit intents and open URIs via the system |
| Notification (6) | android_notification_list, android_notification_open, android_notification_dismiss, android_notification_snooze, android_notification_action, android_notification_reply | Read, interact with, and manage device notifications via NotificationListenerService |
See docs/MCP_TOOLS.md for full tool documentation with input/output schemas and examples.
| Feature | This project | mobile-mcp | Android-MCP | android-mcp-server | adb-mcp | droidrun-mcp |
|---|---|---|---|---|---|---|
| MCP tools | 54 | 21 | 11 | 5 | 10 | 11 |
| Runs on the phone (no ADB) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Action latency | 10-100 ms | 1-4 s | 1-4 s | 1-4 s | 1-4 s | 1-4 s |
| Works over the internet | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Token-efficient screen state | ✅ | ❌ | ✅ | ❌ | ❌ | ✅ |
| Annotated screenshots | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ |
| Configurable screenshot resolution/quality | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Per-tool enable/disable | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Multi-device support | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
| Camera, clipboard, files, downloads | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| iOS support | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
Most alternatives rely on ADB running on a host machine, which means a USB cable or local network connection and a computer sitting next to the phone. This project runs entirely on the device itself, so you can expose the MCP endpoint through a tunnel and control your phone from anywhere.
On the token efficiency side, ADB-based tools typically return raw uiautomator XML dumps which can easily be 10-50x more verbose than the compact representation used here. Combined with numbered screenshot annotations, configurable image quality, and the ability to disable tools you don't need (every tool definition costs tokens on every turn), this significantly reduces the per-interaction cost in agentic loops.
redroid/redroid Android container image)Check all dependencies:
make check-deps
git clone https://github.com/danielealbano/android-remote-control-mcp.git
cd android-remote-control-mcp
make build
# Start an emulator (if no device connected)
make setup-emulator
make start-emulator
# Install the debug APK
make install
takeScreenshot() API (Android 11+).make grant-permissions.Tap the "Start Server" button in the app. The server starts on http://127.0.0.1:8080 by default (HTTPS is disabled by default).
Set up port forwarding (if server is bound to localhost):
make forward-port
Test the connection:
curl -X POST http://localhost:8080/mcp \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"ping"}'
All requests are sent as JSON-RPC 2.0 via POST /mcp (Streamable HTTP transport):
# List available tools
curl -X POST http://localhost:8080/mcp \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
# Get the current screen state (UI nodes + optional screenshot)
curl -X POST http://localhost:8080/mcp \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"android_get_screen_state","arguments":{}}}'
# Tap at coordinates
curl -X POST http://localhost:8080/mcp \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"android_tap","arguments":{"x":540,"y":1200}}}'
The bearer token is displayed in the app's connection info section. You can copy it directly from the app.
make build
# APK: app/build/outputs/apk/debug/app-debug.apk
make build-release
# APK: app/build/outputs/apk/release/app-release.apk
For signed release builds, create keystore.properties in the project root:
storeFile=path/to/your.keystore
storePassword=your_store_password
keyAlias=your_key_alias
keyPassword=your_key_password
make clean
make test-unit
Runs JUnit 5 unit tests with MockK for mocking. Tests cover accessibility tree parsing, node finding, screenshot encoding, settings repository, network utilities, and all 54 MCP tool handlers.
make test-integration
Runs JVM-based integration tests using Ktor testApplication (no device or emulator required). Tests the full HTTP stack: authentication, JSON-RPC protocol, tool dispatch for all 12 tool categories, and error handling.
Note: Some integration tests (e.g.,
NgrokTunnelIntegrationTest) require environment variables. Copy.env.exampleto.envand fill in the required values. The Makefile sources.envautomatically.
make test-e2e
Requires rootful Podman. Starts a redroid Android container via Podman, installs the app, and performs real MCP tool calls. Includes:
make test
make coverage
Generates a Jacoco HTML report at app/build/reports/jacoco/jacocoTestReport/html/index.html. Minimum coverage target: 80%.
The application is a service-based Android app with three main components:
takeScreenshot() on Android 11+)graph TB
Client["MCP Client (AI Model)"]
Client -->|"HTTP/HTTPS POST /mcp + Bearer Token"| McpServer
subgraph Device["Android Device"]
subgraph McpServerService["McpServerService (Foreground Service)"]
McpServer["McpServer (Ktor)"]
McpServer -->|"Streamable HTTP /mcp"| SDK["SDK Server (MCP Kotlin SDK)"]
SDK -->|"54 MCP Tools"| Tools["Tool Handlers"]
TunnelMgr["TunnelManager (optional)"]
TunnelMgr -->|"Cloudflare / ngrok"| PublicURL["Public HTTPS URL"]
end
subgraph Accessibility["McpAccessibilityService"]
TreeParser["AccessibilityTreeParser"]
ElemFinder["ElementFinder"]
ActionExec["ActionExecutor"]
ScreenEnc["ScreenshotEncoder"]
end
subgraph Storage["Storage & App Services"]
StorageProv["StorageLocationProvider"]
FileOps["FileOperationProvider"]
AppMgr["AppManager"]
end
subgraph CameraSvc["Camera Services"]
CamProv["CameraProvider\n(CameraX)"]
end
subgraph IntentSvc["Intent Services"]
IntentDisp["IntentDispatcher"]
end
subgraph NotifSvc["Notification Services"]
NotifProv["NotificationProvider"]
NotifListener["McpNotificationListenerService"]
end
MainActivity["MainActivity (Compose UI)"]
Tools --> Accessibility
Tools --> Storage
Tools --> CameraSvc
Tools --> IntentSvc
Tools --> NotifSvc
MainActivity -->|"StateFlow (status)"| McpServerService
end
See docs/ARCHITECTURE.md for detailed architecture documentation.
| Setting | Default | Description |
|---|---|---|
| Port | 8080 | HTTP/HTTPS server port |
| Binding Address | 127.0.0.1 | 127.0.0.1 (localhost, use with adb port forwarding) or 0.0.0.0 (network, all interfaces) |
| Bearer Token | Auto-generated UUID | Authentication token for MCP requests |
| HTTPS | Disabled | Enable HTTPS with auto-generated self-signed certificate (configurable hostname) or upload custom .p12/.pfx |
| Auto-start on Boot | Disabled | Start MCP server automatically when device boots |
| Device Slug | Empty | Optional device identifier for tool name prefix (e.g., pixel7 makes tools android_pixel7_tap) |
| Remote Access Tunnel | Disabled | Expose server via public HTTPS URL (Cloudflare Quick Tunnels or ngrok) |
| Tool Permissions | All enabled | Per-tool and per-parameter enable/disable (Settings > MCP Tools) |
| File Size Limit | 50 MB | Maximum file size for file operations (range 1-500 MB) |
| Allow HTTP Downloads | Disabled | Allow non-HTTPS downloads via android_download_from_url |
| Download Timeout | 60 seconds | Timeout for file downloads (range 10-300 seconds) |
The app can be fully configured and controlled from the command line without opening the UI. This is useful for automated setups, CI pipelines, or headless devices.
Replace `` with the application ID for your build:
com.danielealbano.androidremotecontrolmcp.debugcom.danielealbano.androidremotecontrolmcp# Enable Accessibility Service (required for UI introspection, actions, and screenshots)
adb shell settings put secure enabled_accessibility_services \
/com.danielealbano.androidremotecontrolmcp.services.accessibility.McpAccessibilityService
# Grant notification permission (Android 13+)
adb shell pm grant android.permission.POST_NOTIFICATIONS
# Grant camera permission
adb shell pm grant android.permission.CAMERA
# Grant microphone permission
adb shell pm grant android.permission.RECORD_AUDIO
All extras are optional — only the ones provided are updated. The app does not need to be open.
adb shell am broadcast \
-a com.danielealbano.androidremotecontrolmcp.ADB_CONFIGURE \
-n /com.danielealbano.androidremotecontrolmcp.services.mcp.AdbConfigReceiver \
--es bearer_token "my-secret-token" \
--es binding_address "0.0.0.0" \
--ei port 8080 \
--ez auto_start_on_boot true \
--ez https_enabled false \
--es certificate_source "AUTO_GENERATED" \
--es certificate_hostname "mcp.local" \
--ez tunnel_enabled false \
--es tunnel_provider "CLOUDFLARE" \
--es ngrok_authtoken "your-ngrok-token" \
--es ngrok_domain "your-domain.ngrok-free.app" \
--ei file_size_limit_mb 50 \
--ez allow_http_downloads false \
--ez allow_unverified_https_certs false \
--ei download_timeout_seconds 60 \
--es device_slug "pixel8" \
--es tool_permissions '{"disabled_tools":["tap"],"disabled_params":{"swipe":["duration_ms"]}}'
| Extra | Type | Description |
|---|---|---|
bearer_token | string | Authentication token for MCP requests |
binding_address | string | 127.0.0.1 (localhost) or 0.0.0.0 (network) |
port | int | HTTP/HTTPS server port (1-65535) |
auto_start_on_boot | boolean | Start MCP server when device boots |
https_enabled | boolean | Enable HTTPS with TLS |
certificate_source | string | AUTO_GENERATED or CUSTOM |
certificate_hostname | string | Hostname for auto-generated certificate |
tunnel_enabled | boolean | Enable remote access tunnel |
tunnel_provider | string | CLOUDFLARE or NGROK |
ngrok_authtoken | string | ngrok authentication token |
ngrok_domain | string | ngrok custom domain (optional) |
file_size_limit_mb | int | Max file size for file operations (1-500) |
allow_http_downloads | boolean | Allow non-HTTPS downloads |
allow_unverified_https_certs | boolean | Allow unverified HTTPS certificates for downloads |
download_timeout_seconds | int | Download timeout (10-300) |
device_slug | string | Device identifier for tool name prefix |
tool_permissions | string (JSON) | Per-tool and per-parameter permissions: {"disabled_tools":["tool_name"],"disabled_params":{"tool_name":["param"]}} |
The server must be started via a trampoline Activity (required on Android 12+ to gain foreground service exemption). This works even when the app is force-stopped.
adb shell am start \
-n /com.danielealbano.androidremotecontrolmcp.services.mcp.AdbServiceTrampolineActivity \
--es action start
adb shell am start \
-n /com.danielealbano.androidremotecontrolmcp.services.mcp.AdbServiceTrampolineActivity \
--es action stop
When the server is bound to 127.0.0.1 (default, most secure):
# Forward device port to host
adb forward tcp:8080 tcp:8080
# Test connection from host
curl -X POST http://localhost:8080/mcp \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"ping"}'
When the server is bound to 0.0.0.0:
POST http://DEVICE_IP:8080/mcp with bearer tokenWarning: Binding to 0.0.0.0 exposes the server to all devices on the same network. Only use on trusted private networks.
For connecting from outside the local network without port forwarding:
*.trycloudflare.com HTTPS URL.Enable the tunnel in the app's "Remote Access" section. The public URL is displayed in the connection info and server logs.
Authorization: Bearer header127.0.0.1: Only accessible via adb port forwarding (most secure)0.0.0.0: Accessible over network (use only on trusted networks)android_list_apps)android_close_app)# Check for issues
make lint
# Auto-fix issues
make lint-fix
Uses ktlint for code style and detekt for static analysis.
git checkout -b feat/your-featuremake lint && make test-unit && make buildfeat: add new MCP tool for ...)See docs/PROJECT.md for the complete project bible.
This project is licensed under the MIT License. See LICENSE.md for details.
Install via CLI
npx mdskills install danielealbano/android-remote-control-mcpAndroid Remote Control MCP is a free, open-source AI agent skill. An Android application that runs as an MCP (Model Context Protocol) server, enabling AI models to fully control an Android device remotely using accessibility services and screenshot capture. The app runs directly on your Android device (or emulator) and exposes an HTTP server (with optional HTTPS) implementing the MCP protocol. AI models like Claude can connect to it and interact with any app on
Install Android Remote Control MCP with a single command:
npx mdskills install danielealbano/android-remote-control-mcpThis downloads the skill files into your project and your AI agent picks them up automatically.
Android Remote Control MCP works with Claude Code, Claude Desktop, Cursor, Vscode Copilot, Windsurf, Continue Dev, Gemini Cli, Amp, Roo Code, Goose. Skills use the open SKILL.md format which is compatible with any AI coding agent that reads markdown instructions.