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.
Warning: 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.
- HTTP server running directly on Android (Ktor + Netty), with optional HTTPS
- Streamable HTTP transport at
/mcp(MCP specification compliant, JSON-only, no SSE) - Bearer token authentication (global, all requests)
- Auto-generated self-signed TLS certificates (or custom certificate upload)
- Configurable binding: localhost (127.0.0.1) or network (0.0.0.0)
- Auto-start on boot
- Remote access tunnels via Cloudflare Quick Tunnels or ngrok (public HTTPS URL)
Screen introspection, system actions, touch actions, gestures, node actions, text input, utilities, file operations, app management, camera, intents, notifications, and location.
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 tool reference with input/output schemas and examples.
- Material Design 3 UI with tabbed layout (Server / Settings / About) and dark mode
- Server status monitoring (running/stopped) with permission warning banner
- Connection info display (IP, port, token, tunnel URL)
- Per-tool and per-parameter permissions (enable/disable individual MCP tools)
- Permission management (Accessibility, Notifications, Camera, Microphone)
- Remote access tunnel configuration (Cloudflare / ngrok)
- Storage location management (automatic locations + SAF authorization for file tools)
- Server log viewer (MCP tool calls, tunnel events)
- Headless setup via ADB (configure, grant permissions, start/stop server without UI)
| Feature | This project | mobile-mcp | Android-MCP | android-mcp-server | adb-mcp | droidrun-mcp |
|---|---|---|---|---|---|---|
| MCP tools | 55 | 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.
- Open the Releases page on your phone's browser
- Download the APK from the latest release
- Open the downloaded APK and follow the prompts to install it (you may need to allow installation from unknown sources)
- Download the APK from the Releases page
- Connect your phone via USB (with USB Debugging enabled)
- Install the APK:
adb install app-release.apkgit clone https://github.com/danielealbano/android-remote-control-mcp.git
cd android-remote-control-mcp
make build
make install # installs on connected device/emulatorSee CONTRIBUTING.md for full build requirements and instructions.
- Open the app and go to Settings > Permissions
- Enable Accessibility Service (required for UI introspection, actions, and screenshots)
- Grant additional permissions as needed (Camera, Microphone, Location, Notifications)
- Configure storage locations in Settings > Storage if you plan to use file tools (includes automatic locations like Downloads, plus custom locations via SAF)
- Go back to the Server tab and tap Start to start the MCP server
The server starts on http://127.0.0.1:8080 by default. The connection info (IP, port, token, URL) is displayed on the Server tab.
Note:
127.0.0.1refers to the phone's localhost, not your computer. To connect from your computer, use adb port forwarding, bind to0.0.0.0(network mode), or enable a remote access tunnel.
Add the server to your .mcp.json configuration file:
{
"mcpServers": {
"android-phone": {
"type": "http",
"url": "http://DEVICE_IP:PORT/mcp",
"headers": {
"Authorization": "Bearer YOUR_TOKEN"
}
}
}
}Replace DEVICE_IP, PORT, and YOUR_TOKEN with the values shown in the app's Server tab. If the server is bound to localhost (default), you'll need adb port forwarding or a remote access tunnel to connect.
Note for clients without custom-header support (e.g. Claude Desktop): if your MCP client cannot send an
Authorizationheader, you can clear the bearer token in the app (Settings → General → Bearer Token → Clear). When the token is empty, the server skips authentication entirely. Only do this on a network you trust — anyone who can reach the server will be able to use it.
The MCP server exposes a standard Streamable HTTP endpoint at /mcp with bearer token authentication. Any MCP-compatible client can connect to it — refer to your client's documentation for the specific configuration format.
MCP Inspector is the official visual testing tool for MCP servers. It provides a browser-based UI to connect, list tools, fill parameters, and see responses:
npx @modelcontextprotocol/inspectorThis opens a UI at http://localhost:6274 where you can connect to your server and test tools interactively.
All requests are sent as JSON-RPC 2.0 via POST /mcp (Streamable HTTP transport). The server requires a session initialization handshake before tool calls:
# Initialize a session (required before any tool call)
# Capture the mcp-session-id from the response headers
curl -s -D- -X POST http://localhost:8080/mcp \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"curl-client","version":"1.0.0"}}}'
# Use the mcp-session-id from the response headers in all subsequent requests
# List available tools
curl -X POST http://localhost:8080/mcp \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "mcp-session-id: SESSION_ID" \
-d '{"jsonrpc":"2.0","id":2,"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" \
-H "Accept: application/json, text/event-stream" \
-H "mcp-session-id: SESSION_ID" \
-d '{"jsonrpc":"2.0","id":3,"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" \
-H "Accept: application/json, text/event-stream" \
-H "mcp-session-id: SESSION_ID" \
-d '{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"android_tap","arguments":{"x":540,"y":1200}}}'Replace SESSION_ID with the mcp-session-id value from the initialize response headers.
When configured (non-empty), the bearer token is displayed in the app's connection info section and can be copied directly from the app. When the token is cleared, the row is hidden and the server accepts unauthenticated requests.
| 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 (one-shot, clearable) | Authentication token for MCP requests. Cleared = authentication disabled. |
| 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) |
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" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"ping"}'When the server is bound to 0.0.0.0:
- Find the device's IP address (shown in the app's connection info)
- Connect directly via
POST http://DEVICE_IP:8080/mcpwith bearer token
Warning: 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:
- Cloudflare Quick Tunnels (default, no account required): Creates a temporary tunnel with a random
*.trycloudflare.comHTTPS URL. - ngrok (account required): Supports optional custom domains. Requires an ngrok authtoken (free tier available). Only available on ARM64 devices.
Enable the tunnel in the app's "Remote Access" section. The public URL is displayed in the connection info and server logs.
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 <app-id> with the application ID for your build:
- Debug:
com.danielealbano.androidremotecontrolmcp.debug - Release:
com.danielealbano.androidremotecontrolmcp
# Enable Accessibility Service (required for UI introspection, actions, and screenshots)
adb shell settings put secure enabled_accessibility_services \
<app-id>/com.danielealbano.androidremotecontrolmcp.services.accessibility.McpAccessibilityService
# Enable Notification Listener Service (required for notification tools)
adb shell cmd notification allow_listener \
<app-id>/com.danielealbano.androidremotecontrolmcp.services.notifications.McpNotificationListenerService
# Grant notification permission (Android 13+)
adb shell pm grant <app-id> android.permission.POST_NOTIFICATIONS
# Grant camera permission
adb shell pm grant <app-id> android.permission.CAMERA
# Grant microphone permission
adb shell pm grant <app-id> android.permission.RECORD_AUDIO
# Grant location permissions
adb shell pm grant <app-id> android.permission.ACCESS_FINE_LOCATION
adb shell pm grant <app-id> android.permission.ACCESS_COARSE_LOCATION
adb shell pm grant <app-id> android.permission.ACCESS_BACKGROUND_LOCATION
# Grant media read permissions (Android 13+)
adb shell pm grant <app-id> android.permission.READ_MEDIA_IMAGES
adb shell pm grant <app-id> android.permission.READ_MEDIA_VIDEO
adb shell pm grant <app-id> android.permission.READ_MEDIA_AUDIOAll 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 <app-id>/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"]}}'# Clear the bearer token (disables authentication; see Security section)
adb shell am broadcast \
-a com.danielealbano.androidremotecontrolmcp.ADB_CONFIGURE \
-n <app-id>/com.danielealbano.androidremotecontrolmcp.services.mcp.AdbConfigReceiver \
--es bearer_token ""Passing --es bearer_token "" clears the stored token. The MCP server skips authentication when the token is empty — use only on trusted networks.
| Extra | Type | Description |
|---|---|---|
bearer_token |
string | Authentication token for MCP requests (empty string clears the token and disables auth) |
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 <app-id>/com.danielealbano.androidremotecontrolmcp.services.mcp.AdbServiceTrampolineActivity \
--es action startadb shell am start \
-n <app-id>/com.danielealbano.androidremotecontrolmcp.services.mcp.AdbServiceTrampolineActivity \
--es action stopThe application runs entirely within Android's standard permission model. No root access, no unlocked bootloader, no custom ROM required.
When the bearer token is configured (non-empty), every MCP request must carry an Authorization: Bearer <token> header. The token is auto-generated once on first launch (UUID, preserved across app upgrades) and can be viewed, copied, regenerated, or cleared in the app. When the token is empty, the MCP server skips authentication entirely — see the security note in the Connect section.
- Default binding
127.0.0.1: Only accessible via adb port forwarding or tunnels (most secure) - Optional binding
0.0.0.0: Accessible over network (use only on trusted networks; security warning displayed when enabling) - HTTPS: Optional, disabled by default. When enabled, uses auto-generated self-signed certificates or upload your own CA-signed certificate. Certificate is stored in app-private storage.
All permissions are managed from the app's Settings > Permissions tab. Every sensitive permission requires explicit user action — nothing is granted silently.
Storage locations are configured in Settings > Storage, which includes automatic locations (e.g., Downloads) and custom locations authorized via the system file picker.
See CONTRIBUTING.md for build requirements, testing, architecture, and development conventions.
This project is licensed under the MIT License. See LICENSE.md for details.