MCP Scrcpy Vision
# mcp-scrcpy-vision
An **MCP server** that gives AI agents complete vision and control over Android devices.
**Features:**
- **Real-time Vision**: Continuous screen streaming via scrcpy H.264 + ffmpeg
- **Fast Input Control**: When streaming, input uses scrcpy control protocol (~5-10ms latency vs ~100-300ms with adb shell)
- **UI Automation**: Element detection via uiautomator with tap coordinates
- **Full Input Control**: Tap, swipe, long press, pinch, drag-drop, text, keycodes
- **System Access**: Shell commands, file transfer, clipboard, notifications
- **Multi-device**: Control multiple Android devices simultaneously
- **WiFi ADB**: Connect wirelessly for untethered automation
---
## Quick Start
### 1. Prerequisites
**Required:**
- Node.js 18+
- ADB (Android Platform Tools) in PATH
- Android device with USB debugging enabled
**For streaming (recommended for fast input):**
- [scrcpy](https://github.com/Genymobile/scrcpy/releases) - download release, extract `scrcpy-server` file
- [ffmpeg](https://ffmpeg.org/download.html) - install and add to PATH
### 2. Install
```bash
git clone https://github.com/anthropics/mcp-scrcpy-vision.git
cd mcp-scrcpy-vision
npm install
npm run build
```
### 3. Configure
Create `.env` file:
```bash
# Required for streaming + fast input
SCRCPY_SERVER_PATH="C:\scrcpy-win64-v3.2\scrcpy-server"
SCRCPY_SERVER_VERSION="3.2"
# Optional (defaults shown)
ADB_PATH="adb"
FFMPEG_PATH="ffmpeg"
DEFAULT_MAX_SIZE="1024"
DEFAULT_MAX_FPS="30"
DEFAULT_FRAME_FPS="2"
```
### 4. Add to MCP Client
**Claude Desktop** (`%APPDATA%\Claude\claude_desktop_config.json` on Windows):
```json
{
"mcpServers": {
"android": {
"command": "node",
"args": ["C:/path/to/mcp-scrcpy-vision/dist/index.js"],
"env": {
"SCRCPY_SERVER_PATH": "C:/scrcpy/scrcpy-server",
"SCRCPY_SERVER_VERSION": "3.2"
}
}
}
}
```
**Cursor** (Settings > MCP):
```json
{
"android": {
"command": "node",
"args": ["C:/path/to/mcp-scrcpy-vision/dist/index.js"],
"env": {
"SCRCPY_SERVER_PATH": "C:/scrcpy/scrcpy-server",
"SCRCPY_SERVER_VERSION": "3.2"
}
}
}
```
### 5. Connect Device
1. Enable USB debugging on Android device (Settings > Developer Options > USB Debugging)
2. Connect via USB
3. Accept RSA fingerprint prompt on device
4. Verify: `adb devices` should show your device
---
## How It Works
### Two Modes of Operation
**1. Snapshot Mode (No streaming required)**
- Uses `android.vision.snapshot` for screenshots
- Input uses ADB shell commands (~100-300ms per action)
- Works without scrcpy/ffmpeg
- Best for simple automation or when streaming isn't available
**2. Streaming Mode (Recommended)**
- Start with `android.vision.startStream`
- Continuous JPEG frames available via resource URI
- Input uses scrcpy control protocol (~5-10ms per action)
- **10-20x faster** than snapshot mode
- Best for real-time control and rapid interactions
### Performance Comparison
| Operation | Snapshot Mode | Streaming Mode |
|-----------|---------------|----------------|
| Tap | ~100-300ms | ~5-10ms |
| Swipe | ~300-500ms | ~50-100ms |
| Type text | ~50ms/char | ~5ms total |
| Screenshot | ~500ms | ~33ms (30fps) |
---
## Tools Reference (32 tools)
### Device Management
| Tool | Parameters | Description |
|------|------------|-------------|
| `android.devices.list` | - | List connected devices |
| `android.devices.info` | `serial` | Get device info (model, SDK, etc.) |
| `android.adb.enableTcpip` | `serial`, `port?` | Enable WiFi debugging |
| `android.adb.getDeviceIp` | `serial` | Get device WiFi IP |
| `android.adb.connectWifi` | `ipAddress`, `port?` | Connect via WiFi |
| `android.adb.disconnectWifi` | `ipAddress?` | Disconnect WiFi |
### Vision
| Tool | Parameters | Description |
|------|------------|-------------|
| `android.vision.startStream` | `serial`, `maxSize?`, `maxFps?`, `frameFps?` | Start continuous stream (enables fast input) |
| `android.vision.stopStream` | `serial` | Stop stream |
| `android.vision.snapshot` | `serial` | Take PNG screenshot (works without streaming) |
| `android.ui.dump` | `serial` | Get UI hierarchy XML |
| `android.ui.findElement` | `serial`, `text?`, `resourceId?`, `className?`, `contentDesc?` | Find elements with tap coords |
### Input Control
**Note:** These automatically use fast scrcpy control when streaming, otherwise fall back to ADB.
| Tool | Parameters | Description |
|------|------------|-------------|
| `android.input.tap` | `serial`, `x`, `y` | Tap at coordinates |
| `android.input.swipe` | `serial`, `x1`, `y1`, `x2`, `y2`, `durationMs?` | Swipe gesture |
| `android.input.longPress` | `serial`, `x`, `y`, `durationMs?` | Long press |
| `android.input.pinch` | `serial`, `centerX`, `centerY`, `startDistance`, `endDistance`, `durationMs?` | Pinch zoom |
| `android.input.dragDrop` | `serial`, `startX`, `startY`, `endX`, `endY`, `durationMs?` | Drag and drop |
| `android.input.text` | `serial`, `text` | Type text |
| `android.input.keyevent` | `serial`, `keycode` | Send keycode |
### App Control
| Tool | Parameters | Description |
|------|------------|-------------|
| `android.app.start` | `serial`, `packageName`, `activity?` | Launch app |
| `android.app.stop` | `serial`, `packageName` | Force-stop app |
| `android.apps.list` | `serial`, `system?` | List installed apps |
| `android.activity.current` | `serial` | Get foreground activity |
### System
| Tool | Parameters | Description |
|------|------------|-------------|
| `android.shell.exec` | `serial`, `command` | Execute shell command |
| `android.file.push` | `serial`, `localPath`, `remotePath` | Push file to device |
| `android.file.pull` | `serial`, `remotePath`, `localPath` | Pull file from device |
| `android.file.list` | `serial`, `path` | List directory |
| `android.clipboard.get` | `serial` | Get clipboard |
| `android.clipboard.set` | `serial`, `text` | Set clipboard |
| `android.notifications.get` | `serial` | Get notifications |
### Screen Control
| Tool | Parameters | Description |
|------|------------|-------------|
| `android.screen.wake` | `serial` | Wake screen |
| `android.screen.sleep` | `serial` | Sleep screen |
| `android.screen.isOn` | `serial` | Check if screen is on |
| `android.screen.unlock` | `serial` | Unlock (unsecured only) |
---
## Resources
The server exposes these MCP resources:
- `android://devices` - JSON list of connected devices
- `android://device/<serial>/frame/latest.jpg` - Latest JPEG frame (when streaming)
---
## Usage Examples
### Basic Automation Loop (Streaming Mode)
```
1. Start stream: android.vision.startStream { serial: "ABC123" }
2. Read resource: android://device/ABC123/frame/latest.jpg
3. AI analyzes image, decides to tap "Login" button
4. Find element: android.ui.findElement { serial: "ABC123", text: "Login" }
5. Tap at returned coordinates: android.input.tap { serial: "ABC123", x: 540, y: 1200 }
6. Wait 500ms, read resource again, repeat
7. When done: android.vision.stopStream { serial: "ABC123" }
```
### Simple Screenshot Mode
```
1. Take screenshot: android.vision.snapshot { serial: "ABC123" }
2. AI analyzes image
3. Find and tap: android.ui.findElement + android.input.tap
4. Take another screenshot to verify
```
### WiFi Connection Workflow
```
1. Connect device via USB
2. android.adb.enableTcpip { serial: "ABC123" }
3. android.adb.getDeviceIp { serial: "ABC123" } → "192.168.1.50"
4. Disconnect USB cable
5. android.adb.connectWifi { ipAddress: "192.168.1.50" }
6. Now use "192.168.1.50:5555" as serial for all commands
```
### App Testing Example
```
1. android.app.start { serial: "ABC123", packageName: "com.example.app" }
2. android.vision.startStream { serial: "ABC123" }
3. Wait for app to load, read frame
4. android.ui.findElement { serial: "ABC123", resourceId: "username_field" }
5. android.input.tap { serial: "ABC123", x: 540, y: 300 }
6. android.input.text { serial: "ABC123", text: "testuser@example.com" }
7. android.input.keyevent { serial: "ABC123", keycode: 66 } // Enter
8. Read frame, verify login succeeded
9. android.vision.stopStream { serial: "ABC123" }
```
---
## Common Keycodes
| Key | Code | Key | Code |
|-----|------|-----|------|
| HOME | 3 | BACK | 4 |
| VOLUME_UP | 24 | VOLUME_DOWN | 25 |
| POWER | 26 | ENTER | 66 |
| DELETE | 67 | TAB | 61 |
| MENU | 82 | APP_SWITCH | 187 |
| WAKEUP | 224 | SLEEP | 223 |
---
## Troubleshooting
### No devices found
```bash
adb kill-server
adb start-server
adb devices
```
Ensure USB debugging is enabled and RSA fingerprint accepted.
### Scrcpy version mismatch
`SCRCPY_SERVER_VERSION` must exactly match your scrcpy-server file. Check the scrcpy release version you downloaded.
### ffmpeg not found
- **Windows**: Download from https://ffmpeg.org/download.html, extract, add bin folder to PATH
- **macOS**: `brew install ffmpeg`
- **Linux**: `apt install ffmpeg` or `yum install ffmpeg`
Or set `FFMPEG_PATH` in .env to the full path.
### uiautomator dump fails
Some devices need screen on. Try `android.screen.wake` first.
### Clipboard not working (Android 10+)
Android 10+ restricts clipboard access. Use UI automation to paste instead.
### Stream won't start
1. Check scrcpy-server path is correct
2. Verify version numbers match
3. Try running scrcpy standalone first to verify it works
---
## Notes & Limitations
- **Fast input when streaming**: When a stream is active, tap/swipe/text/keyevent use the scrcpy control protocol (~5-10ms latency). Without streaming, falls back to `adb shell input` (~100-300ms).
- **One stream per device** at a time
- **Snapshot works without scrcpy** - useful fallback when streaming is not needed
- **Clipboard** has platform limitations on Android 10+
- **Notifications** may require permissions on newer Android
- **Pinch gesture** currently simulates single-finger; true multi-touch requires the streaming session
---
## Security Warning
This MCP server provides **full control** over connected Android devices:
- Execute arbitrary shell commands
- Read/write files on device
- Control UI and input
- Access clipboard and notifications
**Only connect devices you own and trust the AI agent.**
---
## Development
```bash
npm run dev # Development with tsx
npm run build # Compile TypeScript
npm start # Run production build
```
See [claude.md](claude.md) for developer documentation.
See [agents.md](agents.md) for AI agent integration guide.
---
## License
MIT
TDQS
Scored across 33 tools
Most tools have distinct purposes, but some overlap exists, such as android.input.swipe and android.input.dragDrop, which both involve screen gestures and could cause confusion. However, descriptions clarify their specific uses, and other tools are well-separated by domain (e.g., adb, input, screen, vision).
Tool names follow a highly consistent pattern of domain.subdomain.action (e.g., android.adb.connectWifi, android.input.tap, android.screen.wake). This hierarchical naming scheme is predictable and aids in understanding the tool's purpose and category.
With 33 tools, the count is excessive for a server focused on Android device control and vision. Many tools could be consolidated or omitted without losing functionality, leading to a bloated interface that may overwhelm agents and increase selection errors.
The tool set comprehensively covers Android device interaction, including adb connectivity, app management, file operations, input simulation, screen control, UI inspection, and vision streaming. There are no obvious gaps; it supports full lifecycle operations from setup to advanced automation.