Skip to main content
Glama
shuao-pro

Android MCP Server

by shuao-pro
README.md
# Android MCP Server

**AI-powered Android device automation via MCP (Model Context Protocol).**

Control an Android phone with natural language β€” through Claude Desktop, Cherry Studio, Kai 9000, or the built-in Web GUI with AI chat.

<p align="center">
  <b>πŸ‡ΊπŸ‡Έ English</b> &nbsp;|&nbsp; <a href="./README_zh.md">πŸ‡¨πŸ‡³ δΈ­ζ–‡</a>
</p>

<p align="center">
  <a href="https://github.com/shuao-pro/android-mcp"><img src="https://img.shields.io/github/stars/shuao-pro/android-mcp?style=social" alt="Stars"></a>
  <a href="https://github.com/shuao-pro/android-mcp"><img src="https://img.shields.io/badge/GitHub-shuao--pro%2Fandroid--mcp-181717?logo=github" alt="GitHub"></a>
  <img src="https://img.shields.io/badge/python-3.10+-blue" alt="Python">
  <img src="https://img.shields.io/badge/license-MIT-green" alt="License">
  <img src="https://img.shields.io/badge/MCP-1.8+-purple" alt="MCP">
  <img src="https://img.shields.io/badge/version-2.1.0-orange" alt="v2.1.0">
</p>

---

## ✨ Features

### Device Control (37 MCP Tools)

| Category | Tools |
|----------|-------|
| **Device** | `health_check`, `get_device_info`, `get_battery_info` |
| **Shell** | `shell` β€” any ADB-level command |
| **Input** | `click`, `long_click`, `swipe`, `drag`, `type_text`, `press_key` |
| **Apps** | `open_app`, `close_app`, `clear_app_data`, `install_app`, `uninstall_app`, `get_current_app`, `list_installed_apps` |
| **Screen** | `take_screenshot`, `get_ui_hierarchy` |
| **Files** | `read_file`, `write_file` (including `/data/data`) |
| **System** | `get_system_setting`, `put_system_setting`, `set_clipboard`, `get_clipboard`, `get_notifications`, `start_activity` |
| **Privilege** | `get_privilege_mode`, `set_privilege_mode` β€” switch device execution backend (auto / shizuku / root) |
| **Tasks** | `submit_task`, `get_task_status`, `get_task_result`, `cancel_task`, `list_tasks`, `run_task_and_wait` β€” long-running commands as background tasks |
| **Vision** | `find_element` β€” AI locates UI elements, `click_element` β€” find + click in one step |

### πŸ›‘οΈ Safety Guard

High-risk device operations are gated behind user confirmation:

- Destructive shell commands (`rm -rf`, `dd`, `mkfs`, `mount`, `reboot`, `su`, `pm uninstall/clear`, …)
- Writes to protected paths (`/system`, `/data`, `/vendor`, …)
- App install / uninstall / data clear, and system settings mutation

`SAFETY_MODE` controls the policy: `confirm` (default) prompts the user via MCP elicitation, `permissive` allows everything, and `strict` blocks risky operations outright.

### πŸ”“ Privilege Mode (Root / Shizuku)

The Android app executes commands through a unified **PrivilegeExecutor** that supports three modes:

| Mode | Backend | Use |
|------|---------|-----|
| `root` | `su` (uid 0) | Rooted devices (Magisk / KernelSU / APatch / SuperSU) |
| `shizuku` | Shizuku binder (uid 2000) | Non-root devices with Shizuku installed |
| `auto` (default) | root β†’ shizuku fallback | Use root when available, otherwise Shizuku |

Selectable from the Android app UI, the Web Dashboard, or the `set_privilege_mode` MCP tool.

### ⏳ Long-running Tasks

Commands that may exceed the 30s HTTP timeout run as asynchronous background tasks on the device:

- `submit_task` / `run_task_and_wait` β€” run a command in the background and poll until it finishes
- `get_task_status` / `get_task_result` / `cancel_task` / `list_tasks` β€” monitor and control tasks
- On-device `TaskManager` runs commands on a dedicated thread pool (10 concurrent) with output truncation and automatic cleanup

### πŸ€– Multi-step Agent

The Web Dashboard AI chat uses a closed-loop agent: it chains multiple tool calls (with screenshots + vision verification, long-running tasks, and retries) until the goal is done β€” up to 10 steps.

### AI Vision

- AI-powered screen element recognition via Claude Vision / GPT-4o / custom API
- Natural language β†’ pixel coordinates β†’ automated click
- Example: `find_element("the login button")` β†’ `{center_x: 540, center_y: 960, confidence: 0.95}`

### Web Dashboard

- **AI Chat** β€” multi-step agent: describe a goal and it chains tool calls (screenshots + vision, long-running tasks, retries) until done
- **Live Screen** β€” 10fps WebSocket stream with click-to-touch
- **scrcpy** β€” native low-latency mirroring (one-click launch)
- **Setup Wizard** β€” guided 5-step setup with auto-detection + MCP SSE endpoint display
- **Settings Panel** β€” configure API providers + ADB device manager with .env sync
- **Privilege Mode** β€” switch Auto / Shizuku / Root from the sidebar
- **δΈ­/English** β€” full i18n support
- **Shell Terminal** β€” live ADB shell in the browser

### MCP Clients

Connect any MCP-compatible client to the server:

| Client | Transport | Endpoint | Platform |
|--------|-----------|----------|----------|
| **Kai 9000** | Streamable HTTP | `:9000/mcp` | Android (F-Droid) |
| **Cherry Studio** | Streamable HTTP | `:9000/mcp` | Windows / macOS / Linux |
| **Claude Desktop** | SSE / stdio | `:9000/sse` or `stdio` | Windows / macOS / Linux |
| **Termux + curl** | SSE | `:9000/sse` | Android (Termux) |

> **Cherry Studio config:** Set MCP type to `streamableHttp`, URL `http://<lan_ip>:9000/mcp`.
> Or import `cherry-studio-mcp.json` from the project root.

### MCP Transport

| Mode | Endpoint | Use Case |
|------|----------|----------|
| `stdio` | (local pipe) | Claude Desktop local integration |
| SSE | `:9000/sse` | Claude Desktop remote, web frontends |
| Streamable HTTP | `:9000/mcp` | Kai 9000, modern MCP clients |
| **Combined** (default) | **both on `:9000`** | **SSE + Streamable HTTP simultaneously** |

---

## πŸš€ Quick Start

### Prerequisites

- Python 3.10+
- Android device with **Shizuku** installed (or a rooted device β€” either works)
- ADB (Android SDK Platform Tools)
- scrcpy (optional, for native mirroring)

### 1. Install

```bash
git clone https://github.com/shuao-pro/android-mcp.git
cd android-mcp
pip install -e .
```

### 2. Setup

```bash
# First-time setup (configures .env)
bash scripts/setup.sh
```

Or manually:
```bash
cp .env.example .env
```

Install the Android APK to your phone:
```bash
# Pre-built APK (recommended) ιˆ₯?from the android/ project
adb install "android/app/build/outputs/apk/debug/app-debug.apk"

# Or build from source
cd android && .\gradlew assembleDebug
adb install app/build/outputs/apk/debug/app-debug.apk
```

### 3. On Your Phone

1. Start **Shizuku** (grant root or wireless debugging permission), or use a rooted device
2. Open **Android MCP** app β†’ pick a mode (Auto / Shizuku / Root) β†’ grant permission β†’ tap **Start**
3. Notification shows "MCP service running" on port 18080
4. Copy the **auth token** shown in the app into `.env` β†’ `ANDROID_TOKEN=`

### 4. Start Server

```bash
# One-click (SSE + Web GUI + ADB forward)
./start.sh

# Windows
start.bat
```

Opens browser at `http://127.0.0.1:8080`.

### 5. Connect MCP Client

In the Web GUI, open **Menu β†’ Setup** to see your MCP addresses:

| Client | Endpoint |
|--------|----------|
| **Kai 9000** (phone) | `http://192.168.x.x:9000/mcp` |
| **Claude Desktop** (remote) | `http://192.168.x.x:9000/sse` |
| **Same device** (Termux) | `http://127.0.0.1:9000/sse` or `/mcp` |

Add the address in Kai 9000 (Settings β†’ MCP Servers β†’ Add) or Claude Desktop:

```json
{
  "mcpServers": {
    "android": {
      "command": "python",
      "args": ["-m", "android_mcp.main", "--mode", "mcp"]
    }
  }
}
```

Now chat with the AI to control your phone β€” "open settings", "take a screenshot", "click the search button".

---

## βš™οΈ Configuration

Edit `.env`:

```env
# Device connection
ANDROID_HOST=127.0.0.1
ANDROID_PORT=18080

# Android bridge auth token (shown in the app UI β€” copy it here)
ANDROID_TOKEN=

# Safety guard β€” gate high-risk device operations (default: confirm)
SAFETY_MODE=confirm           # permissive | confirm | strict

# Web GUI
WEB_HOST=127.0.0.1
WEB_PORT=8080

# MCP Server (SSE + Streamable HTTP) β€” for Kai 9000 & other clients
# Defaults to 127.0.0.1 (local-only, secure); set 0.0.0.0 for WiFi/phone clients
MCP_HOST=127.0.0.1
MCP_PORT=9000

# AI Vision (optional β€” enables AI chat + element recognition)
VISION_PROVIDER=anthropic       # anthropic | openai | custom
VISION_API_KEY=sk-ant-api03-xxxxx
VISION_MODEL=                   # leave empty for default
VISION_API_BASE=                # only for custom provider
```

---

## πŸ—οΈ Architecture

Three layers cooperate to turn a natural-language request into system-level actions on the device:

```mermaid
flowchart LR
    subgraph CLIENTS["πŸ€– MCP Clients"]
        direction TB
        C1["Claude Desktop<br/>stdio / SSE"]
        C2["Kai 9000<br/>Streamable HTTP"]
        C3["Cherry Studio<br/>Streamable HTTP"]
        C4["Web Dashboard<br/>browser Β· :8080"]
    end

    subgraph SERVER["🐍 Python Server · android_mcp/"]
        direction TB
        S1["FastMCP<br/>37 tools Β· :9000<br/>/sse + /mcp"]
        S2["Web GUI Β· FastAPI<br/>:8080 Β· WebSocket"]
        S3["tools/<br/>thin wrappers"]
        S4["bridge/<br/>JSON-RPC transport"]
        S5["vision/<br/>AI element locator"]
        S6["safety/<br/>risk gate Β· user confirm"]
        S7["tasks/<br/>submit Β· poll Β· result"]
        S1 --- S3
        S3 --- S4
        S3 --- S6
        S3 --- S7
        S7 --- S4
        S2 --- S4
        S2 --- S5
    end

    subgraph PHONE["πŸ“± Android App Β· Kotlin Β· Root / Shizuku"]
        direction TB
        P1["HttpServer<br/>:18080"]
        P2["Router<br/>JSON-RPC dispatch"]
        P3["api/<br/>shell Β· input Β· file Β· system"]
        P4["PrivilegeExecutor<br/>AUTO / ROOT / SHIZUKU"]
        P5["Root (su)<br/>UID 0"]
        P6["Shizuku<br/>UID 2000"]
        P7["TaskApi + TaskManager<br/>async task queue"]
        P1 --- P2
        P2 --- P3
        P2 --- P7
        P3 --- P4
        P7 --- P4
        P4 --- P5
        P4 --- P6
    end

    C1 --> S1
    C2 --> S1
    C3 --> S1
    C4 --> S2
    S4 -->|"HTTP JSON-RPC Β· X-MCP-Token<br/>ADB forward tcp:18080"| P1
    S5 -.->|"Claude Vision / GPT-4o"| V["🧠 Vision API"]
```

### Component breakdown

| Layer | Component | Responsibility | Key tech |
|-------|-----------|----------------|----------|
| **Clients** | Claude Desktop / Kai 9000 / Cherry Studio | Send tool calls as MCP messages | stdio, SSE, Streamable HTTP |
| | Web Dashboard | Browser panel, live screen, AI chat | FastAPI + WebSocket |
| **Python server** | `server.py` (FastMCP) | Registers 37 tools, speaks MCP | FastMCP |
| | `bridge/` | JSON-RPC β†’ device, auto ADB forward | httpx, JSON-RPC 2.0 |
| | `tools/` | Thin `@bridge_call` wrappers | decorators |
| | `safety/` | Risk classification + user confirmation gate | MCP elicitation, SAFETY_MODE |
| | `web/` | Dashboard API, chat, scrcpy stream | FastAPI, uvicorn |
| | `vision/` | AI screen-element recognition | Claude Vision / GPT-4o |
| | `tasks/` | Long-running task submit / poll / result | task.submit/status/result RPC |
| **Android app** | `HttpServer` | Embedded HTTP server :18080, token auth | Java `ServerSocket` |
| | `Router` | JSON-RPC method dispatch | JSON-RPC 2.0 |
| | `api/*` | Shell, input, package, file, system, task | PrivilegeExecutor (Root / Shizuku) |
| | `TaskManager` / `TaskApi` | Background task queue + JSON-RPC API | thread pool, task state machine |
| | `util/` | Privilege executor + token store | `PrivilegeExecutor`, `RootHelper`, `ShizukuHelper`, `TokenStore` |

### Request lifecycle

Every tool call follows one path β€” e.g. `click(x, y)`:

1. **Client** sends `click(x, y)` over MCP (`:9000`) or the Web Dashboard (`:8080`).
2. **FastMCP / FastAPI** routes it to the matching `tools/` wrapper.
3. **`safety/`** classifies the operation; high-risk commands (destructive shell, writes to protected paths, app uninstall/clear) trigger an interactive user confirmation via MCP elicitation before continuing.
4. **`bridge/_core.py`** serializes it as a JSON-RPC 2.0 request, attaches the `X-MCP-Token` header, and POSTs to `http://127.0.0.1:18080/mcp` (re-establishing the ADB forward if needed).
5. **`HttpServer`** authenticates the token, then hands the request to `Router`.
6. **`Router`** dispatches to the right `api/*` module (e.g. `InputApi.tap`), which runs it via **Shizuku** (UID 2000) or **Root** (su, UID 0) β€” no root required, but rooted devices get full root access.
7. The JSON-RPC result travels back up the same chain.

### Ports & transports

| Port | Service | Transport | Consumers |
|------|---------|-----------|-----------|
| `:9000/sse` | MCP (SSE) | HTTP SSE | Claude Desktop (remote), web frontends |
| `:9000/mcp` | MCP (Streamable HTTP) | HTTP POST/GET | Kai 9000, Cherry Studio |
| `:8080` | Web Dashboard | HTTP + WebSocket | Browser |
| `:18080` | Android bridge | HTTP JSON-RPC (ADB-forwarded) | Python `bridge/` |
| *(stdio)* | MCP (stdio) | local pipe | Claude Desktop (local) |

> πŸ’‘ The server can run on the phone itself (Termux / Kai 9000). Set `ANDROID_HOST=127.0.0.1` β€” no ADB needed.

---

## πŸ–₯️ CLI Commands

```bash
# Start modes
python -m android_mcp.main --mode all-sse   # SSE + Streamable HTTP + Web GUI (default)
python -m android_mcp.main --mode mcp       # stdio only (Claude Desktop)
python -m android_mcp.main --mode mcp-sse   # SSE + Streamable HTTP (headless)
python -m android_mcp.main --mode mcp-http  # Streamable HTTP only
python -m android_mcp.main --mode web       # Web GUI only

# Process management
python -m android_mcp.gateway start         # Start as daemon
python -m android_mcp.gateway status        # Check status
python -m android_mcp.gateway stop          # Stop daemon
python -m android_mcp.gateway forward       # Set up ADB port forward
```

---

## πŸ“ Project Structure

```
android-mcp/
β”œβ”€β”€ android_mcp/
β”‚   β”œβ”€β”€ server.py          # FastMCP server definition (tool registry)
β”‚   β”œβ”€β”€ main.py            # Entry point (mode dispatch)
β”‚   β”œβ”€β”€ config.py          # Environment config (.env loader)
β”‚   β”œβ”€β”€ safety.py          # Risk classification + user confirmation gate
β”‚   β”œβ”€β”€ console.py         # Colored console output helpers
β”‚   β”œβ”€β”€ utils.py           # LAN IP + version helpers
β”‚   β”œβ”€β”€ gateway.py         # CLI process manager
β”‚   β”œβ”€β”€ bridge/            # Low-level HTTP bridge to Android device
β”‚   β”‚   β”œβ”€β”€ __init__.py    # Re-exports all bridge functions
β”‚   β”‚   β”œβ”€β”€ _core.py       # JSON-RPC transport + ADB forward helpers
β”‚   β”‚   β”œβ”€β”€ device.py      # Health, info, screenshot, shell, reboot
β”‚   β”‚   β”œβ”€β”€ input.py       # Click, swipe, drag, keys, type_text
β”‚   β”‚   β”œβ”€β”€ apps.py        # Package management
β”‚   β”‚   β”œβ”€β”€ system.py      # Battery, clipboard, notifications, settings, mode
β”‚   β”‚   β”œβ”€β”€ files.py       # File read/write/list/delete
β”‚   β”‚   └── tasks.py       # Long-running task submit/status/result/cancel/list
β”‚   β”œβ”€β”€ tools/             # MCP tool layer (thin wrappers over bridge)
β”‚   β”‚   β”œβ”€β”€ __init__.py    # register_all_tools()
β”‚   β”‚   β”œβ”€β”€ decorators.py  # @bridge_call error-handling decorator
β”‚   β”‚   β”œβ”€β”€ device.py      # Health, info, battery, screenshot, UI hierarchy
β”‚   β”‚   β”œβ”€β”€ input.py       # Touch, swipe, keys
β”‚   β”‚   β”œβ”€β”€ apps.py        # Package management
β”‚   β”‚   β”œβ”€β”€ system.py      # Shell, settings, clipboard, privilege mode
β”‚   β”‚   β”œβ”€β”€ files.py       # File read/write
β”‚   β”‚   β”œβ”€β”€ tasks.py       # Task tools (submit/poll/run_task_and_wait)
β”‚   β”‚   └── vision.py      # AI element recognition
β”‚   β”œβ”€β”€ vision/            # Vision model clients
β”‚   β”‚   β”œβ”€β”€ models.py      # Data classes + Protocol
β”‚   β”‚   β”œβ”€β”€ clients.py     # Anthropic + OpenAI clients (+ screen description)
β”‚   β”‚   └── prompts.py     # Prompt builder + parser
β”‚   └── web/               # Web GUI
β”‚       β”œβ”€β”€ server.py      # FastAPI + WebSocket
β”‚       β”œβ”€β”€ chat_agent.py  # Multi-step agent loop (tool chaining + vision)
β”‚       β”œβ”€β”€ scrcpy_bridge.py # scrcpy + frame streaming
β”‚       └── static/        # HTML/CSS/JS frontend
β”œβ”€β”€ android/               # Android APK project
β”‚   β”œβ”€β”€ app/src/main/
β”‚   β”‚   β”œβ”€β”€ java/com/example/androidmcp/
β”‚   β”‚   β”‚   β”œβ”€β”€ App.kt             # Application class
β”‚   β”‚   β”‚   β”œβ”€β”€ MainActivity.kt    # Main UI + privilege mode + auth
β”‚   β”‚   β”‚   β”œβ”€β”€ McpService.kt      # Foreground service
β”‚   β”‚   β”‚   β”œβ”€β”€ api/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ FileApi.kt     # File read/write/delete
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ InputApi.kt    # Touch, swipe, key events
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ PackageApi.kt  # App install/uninstall
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ ShellApi.kt    # Shell command execution
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ SystemApi.kt   # Screenshot, clipboard, settings, mode
β”‚   β”‚   β”‚   β”‚   └── TaskApi.kt     # Task submit/status/result/cancel/list
β”‚   β”‚   β”‚   β”œβ”€β”€ server/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ HttpServer.kt  # Embedded HTTP server (:18080)
β”‚   β”‚   β”‚   β”‚   └── Router.kt      # JSON-RPC method dispatch
β”‚   β”‚   β”‚   └── util/
β”‚   β”‚   β”‚       β”œβ”€β”€ PrivilegeExecutor.kt # Mode routing (AUTO/ROOT/SHIZUKU)
β”‚   β”‚   β”‚       β”œβ”€β”€ RootHelper.kt        # su backend (uid 0)
β”‚   β”‚   β”‚       β”œβ”€β”€ ShizukuHelper.kt     # Shizuku binder backend
β”‚   β”‚   β”‚       β”œβ”€β”€ TaskManager.kt       # Background task queue + state machine
β”‚   β”‚   β”‚       β”œβ”€β”€ ExecResult.kt        # Command result + process handle
β”‚   β”‚   β”‚       └── TokenStore.kt        # Bridge auth token (generate + persist)
β”‚   β”‚   └── res/                   # Layout, drawable, strings
β”‚   β”œβ”€β”€ gradle/                    # Gradle wrapper
β”‚   β”œβ”€β”€ build.gradle.kts
β”‚   └── settings.gradle.kts
β”œβ”€β”€ scripts/setup.sh       # First-time setup
β”œβ”€β”€ tests/                 # Test scripts
β”‚   β”œβ”€β”€ test_adb.py        # ADB bridge tests
β”‚   └── test_all.py        # End-to-end tests
β”œβ”€β”€ start.sh               # One-click start
β”œβ”€β”€ start.bat              # Windows launcher
β”œβ”€β”€ pyproject.toml
└── .env.example
```

---

## πŸ“‹ Requirements

| Component | Requirement |
|-----------|-------------|
| Python | 3.10+ |
| Android | 11+ (API 30+) |
| Android App | Shizuku installed and running |
| ADB | Platform Tools (for port forward) |
| scrcpy | Optional (native mirroring) |
| AI Vision | Anthropic/OpenAI API key (optional) |
| MCP Client | Kai 9000 (F-Droid), Claude Desktop, or any SSE/stdio MCP client |

---

## πŸ”— Links

| Resource | URL |
|----------|-----|
| **GitHub** | [github.com/shuao-pro/android-mcp](https://github.com/shuao-pro/android-mcp) |
| **Issues** | [Report a bug / Request feature](https://github.com/shuao-pro/android-mcp/issues) |
| **README δΈ­ζ–‡** | [README_zh.md](./README_zh.md) |

---

## πŸ™ References

This project builds on the following open-source projects:

| Project | Purpose | License |
|---------|---------|---------|
| [Shizuku](https://github.com/RikkaApps/Shizuku) | Privileged shell execution on Android (no root) | Apache-2.0 |
| [MCP Python SDK](https://github.com/modelcontextprotocol/python-sdk) | Model Context Protocol server (FastMCP) | MIT |
| [FastAPI](https://github.com/fastapi/fastapi) | Web GUI backend | MIT |
| [httpx](https://github.com/encode/httpx) | Async HTTP client for the device bridge | BSD-3-Clause |
| [scrcpy](https://github.com/Genymobile/scrcpy) | Low-latency screen mirroring | Apache-2.0 |

**Agent MCP frontends (clients):**

This server works with a variety of agent MCP frontends (e.g. Claude Desktop, Cherry Studio).

| Project | Purpose | License |
|---------|---------|---------|
| [Cherry Studio](https://github.com/CherryHQ/cherry-studio) | Desktop AI assistant / MCP client | AGPL-3.0 |
| [Termux](https://github.com/termux/termux-app) | Android terminal (run the server on-device) | GPL-3.0 |

**Root managers:**

| Project | Purpose | License |
|---------|---------|---------|
| [Magisk](https://github.com/topjohnwu/Magisk) | Systemless root solution | GPL-3.0 |
| [KernelSU](https://github.com/tiann/KernelSU) | Kernel-based root solution | GPL-2.0 |
| [APatch](https://github.com/bmax121/APatch) | Kernel-patch root solution | GPL-3.0 |

---

## πŸ“„ License

MIT