Show HN:面向 Android 的 MCP 应用,可通过 AI 驱动 App(无需 Root,本地脱敏 PII)
英文标题:🔶 Show HN: MCP app for Android, drive apps via AI (no root, PII redacted locally)
Android Remote Control MCP 是一款直接运行在 Android 手机或模拟器上的 MCP(Model Context Protocol)服务器,可让 Claude 等 AI 模型通过无障碍服务和截图能力远程操作设备。项目提供 14 类共 57 个 MCP 工具,覆盖界面读取、点击、输入、滑动、文件管理、应用启动、相机、通知和位置等能力,并支持 HTTPS、OAuth 2.1、Cloudflare/ngrok 隧道及 ADB 无界面配置。与依赖主机 ADB 的替代方案相比,该项目强调在手机端独立运行、10—100 毫秒操作延迟、互联网访问以及更节省 Token 的界面状态表示。其 Privacy Mode 会在数据离开设备前本地检测并伪匿名化或遮盖邮箱、银行卡、凭据、电话等 PII,但基准测试显示姓名识别率目前仅为 0.8%。
2.0 MB
491.8 KB
1.1 MB
107.0 KB
中文
Android Remote Control MCP
一款运行 MCP(Model Context Protocol,模型上下文协议)服务器的 Android 应用程序,借助无障碍服务和截图捕获功能,使 AI 模型能够远程全面控制 Android 设备。
该应用直接运行在 Android 设备(或模拟器)上,并提供实现 MCP 协议的 HTTP 服务器(可选 HTTPS)。Claude 等 AI 模型可以连接它,并与设备上的任何应用交互——读取 UI 元素、点击按钮、输入文本、滑动、捕获截图、管理文件、启动应用等。
**警告:**本软件按“原样”提供,不附带任何形式的担保,仅供研究和教育用途。作者不赞成将此工具用于任何非法、未经授权或不道德的活动。用户须自行负责确保其使用行为遵守所有适用的法律法规。使用本软件即表示你同意以负责任的方式自行承担风险使用它。

在 Reddit 发帖(已加速,原始时长约 6 分钟) | 
安装 booking.com 应用 | 
在 Skyscanner 搜索航班,苏黎世 → 伊斯坦布尔(已加速,原始时长约 4 分钟)
---|---|---
AI 模型通过 MCP 服务器控制 Android 设备——点击任意片段查看完整分辨率。
- 直接在 Android 上运行的 HTTP 服务器(Ktor + Netty),可选 HTTPS
- 位于
/mcp的流式 HTTP 传输(符合 MCP 规范,仅 JSON,无 SSE) - 组合认证:静态 Bearer Token 和/或自包含的 OAuth 2.1 服务器(Claude.ai / Claude Desktop 自定义连接器,支持在设备上批准)
- 自动生成自签名 TLS 证书(或上传自定义证书)
- 可配置绑定地址:localhost(127.0.0.1)或网络地址(0.0.0.0)
- 开机自动启动
- 通过 Cloudflare Quick Tunnels 或 ngrok 提供远程访问隧道(公共 HTTPS URL)
覆盖 14 个类别的 57 个 MCP 工具
屏幕内省、系统操作、触控操作、手势、节点操作、文本输入、实用工具、文件操作、应用管理、相机、Intent、通知、位置和分享。
默认情况下,所有工具名称均使用 android_ 前缀(例如 android_tap)。配置设备 slug(例如 pixel7)后,前缀将变为 android_pixel7_(例如 android_pixel7_tap)。
完整的工具参考资料,包括输入/输出 Schema 和示例,见 docs/MCP_TOOLS.md。
- 采用 Material Design 3 的标签页 UI 布局(Server / Settings / About),支持深色模式
- 服务器状态监控(运行中/已停止),附带权限警告横幅
- 连接信息展示(IP、端口、Token、隧道 URL)
- 按工具和参数配置权限(启用/禁用单个 MCP 工具)
- 权限管理(无障碍、通知、相机、麦克风)
- 远程访问隧道配置(Cloudflare / ngrok)
- 存储位置管理(自动位置 + 供文件工具使用的 SAF 授权)
- 服务器日志查看器(MCP 工具调用、隧道事件)
- 通过 ADB 进行无界面设置(无需 UI 即可配置、授予权限、启动/停止服务器)
与替代方案的对比
| 功能 | 本项目 | mobile-mcp | Android-MCP | android-mcp-server | adb-mcp | droidrun-mcp |
|---|---|---|---|---|---|---|
| MCP 工具 | 57 | 21 | 11 | 5 | 10 | 11 |
| 在手机上运行(无需 ADB) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| 操作延迟 | 10-100 ms | 1-4 s | 1-4 s | 1-4 s | 1-4 s | 1-4 s |
| 可通过互联网使用 | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| 节省 Token 的屏幕状态 | ✅ | ❌ | ✅ | ❌ | ❌ | ✅ |
| 带标注的截图 | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ |
| 可配置截图分辨率/质量 | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| 可按工具启用/禁用 | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| 多设备支持 | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
| 相机、剪贴板、文件、下载 | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| iOS 支持 | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
大多数替代方案依赖运行在主机上的 ADB,这意味着需要 USB 数据线或本地网络连接,以及一台放在手机旁边的电脑。本项目完全在设备本身运行,因此你可以通过隧道暴露 MCP 端点,并从任何地点控制手机。
在 Token 效率方面,基于 ADB 的工具通常返回原始 uiautomator XML 转储,其冗长程度很容易达到此处紧凑表示形式的 10—50 倍。结合编号截图标注、可配置的图像质量,以及禁用不需要工具的能力(每个工具定义在每一轮都会消耗 Token),该项目能显著降低 Agent 循环中每次交互的成本。
应下载哪个构建版本:GMS 还是 FOSS
每个版本均提供两种 APK 变体——请选择与你设备相匹配的一种:
- GMS(
…-gms-release.apk)——完整构建版本。要求手机已安装 Google Mobile Services(Google Play 服务)。对于预装 Google 服务的标准设备/模拟器,请选择此版本。 - FOSS(
…-foss-release.apk)——不含 Google 的构建版本,不依赖 Google Mobile Services。可在任意 Android 手机上运行,包括去 Google 化 ROM,以及无法使用 Google 服务的设备。
如不确定,对于安装了 Play Store 的普通手机,GMS 构建版本是正确选择。
选项 A:在手机上下载(最简单)
- 在手机浏览器中打开 Releases 页面
- 从最新版本下载 APK
- 打开下载的 APK,并按提示安装(可能需要允许从未知来源安装)
选项 B:在 PC 上下载并通过 ADB 安装
- 从 Releases 页面下载 APK
- 通过 USB 连接手机(启用 USB 调试)
- 安装 APK(使用下载的 GMS 或 FOSS 文件):
adb install android-remote-control-mcp-<version>-gms-release.apk
选项 C:从源代码构建
git clone https://github.com/danielealbano/android-remote-control-mcp.git
cd android-remote-control-mcp
make build
make install # 安装到已连接的设备/模拟器
完整的构建要求与说明请参见 CONTRIBUTING.md。
-
**打开应用。**Server 标签页会显示权限警告横幅;可从该处或通过 Settings > Permissions 授予权限。
-
授予权限:
- Accessibility Service(无障碍服务)——**必需。**为 UI 内省、操作执行和截图提供支持;没有它,任何功能都无法运行。
- 如果开关呈灰色无法启用(Android 13+,通过 APK 安装的应用中较常见于 Samsung/One UI):这是 Android 的“受限设置”保护机制。会显示一个对话框:“Restricted setting — For your security, this setting is currently unavailable.”(受限设置——为保障你的安全,此设置目前不可用。) 要解除限制,请进入 Settings → Apps → Android Remote Control MCP → App info,点击 ⋮ 菜单(右上角),选择 Allow restricted settings,并使用 PIN 码、图案或生物识别确认,之后即可启用 Accessibility Service。(通过 Play Store / F-Droid 安装不受此影响。)
- Notifications(通知)——可选(建议为 OAuth 启用)。OAuth 登录批准请求(Claude.ai / Claude Desktop、ChatGPT 或任何第三方 OAuth 客户端)会以浮动通知的形式到达;你点击通知即可确认两位数代码并批准请求。不过,OAuth 登录不再要求此权限:若不授予,待处理请求仍会作为卡片显示在应用的 Server 页面,你可点击查看并批准或拒绝。**Event Channel(事件通道)**推送功能——例如向 Claude Code 推送设备事件——会作为前台服务运行,并在 Android 13+ 上通过此权限发布状态通知。
- 其他权限(可选)——相机、麦克风和位置权限会启用相应的 MCP 工具。应用无需这些权限也能工作;仅依赖它们的功能会在权限授予前保持禁用状态。完整列表请参阅下方的 Permissions Reference。
- 存储位置——若计划使用文件工具,请在 Settings > Storage 中配置(例如 Downloads 等自动位置,以及通过 SAF 添加的自定义位置)。
- Accessibility Service(无障碍服务)——**必需。**为 UI 内省、操作执行和截图提供支持;没有它,任何功能都无法运行。
-
**启动服务器。**返回 Server 标签页并点击 Start。
服务器默认在 http://127.0.0.1:8080 启动。连接信息(IP、端口、Token、URL)显示在 Server 标签页中。
注意:
127.0.0.1指的是手机的 localhost,而不是你的电脑。要从电脑连接,请使用 adb 端口转发、绑定至0.0.0.0(网络模式),或启用远程访问隧道。
Privacy Mode(隐私模式)会检测并隐藏 MCP 工具返回内容中的个人数据——邮箱、信用卡和 IBAN、凭据、电话号码、国家身份证件号码、地址和姓名——涵盖屏幕内容、通知、剪贴板、文件等所有内容,且会在这些数据离开设备并发送给 AI 提供商之前完成处理。检测完全在设备端运行:通过快速的基于规则检查(校验和、格式、字段上下文)结合本地 NER 模型,不会进行云端调用。检测到的值会被使用一致的占位符进行伪匿名化(如 EMAIL#a1b2c,从而使 AI 工具可跨屏幕持续工作),或者被完全遮盖。检测属于尽力而为的缓解措施,并非保证。
所用模型为 Ai4Privacy multilingual anonymiser(MIT 许可证,Built with Llama)。首次启用时会直接从固定的 Hugging Face 修订版本下载一次(约 150 MB),并在设备上进行校验和验证。
完整管线在各类别中检测到的个人数据项比例:使用仓库内有效性基准(make privacy-benchmark),在涵盖 8 种语言的真实 UI 风格内容上测量:
| 类别 | 检出率 |
|---|---|
| 地址 | 77.9% |
| 银行卡和 IBAN | 75.3% |
| 凭据 | 90.9% |
| 邮箱 | 100.0% |
| 姓名 | 0.8% |
| 国家身份证件号码 | 93.0% |
| 电话号码 | 84.4% |
姓名是设备端模型已知的薄弱环节(相较于基准测试中全球多样化的姓名,它对常见西方姓名的识别效果好得多);通过微调模型改善姓名检测已列入路线图。实际结果因内容和语言而异。
- 从主 Server 页面:在 Privacy Mode 卡片上点击 Set up Privacy Mode(该模式关闭时会显示该卡片)。
- 或者通过 Settings → Privacy:打开 Enable Privacy Mode,并确认一次性模型下载。下载和预热完成后,系统会运行一个简短的设备端基准测试,测量预期的单屏额外开销,并在同一页面显示。
- 受保护类别、选择伪匿名化还是遮盖,以及占位符格式,均可在 Settings → Privacy 中配置。
Claude Desktop / Claude Code
将服务器添加到 .mcp.json 配置文件:
{
"mcpServers": {
"android-phone": {
"type": "http",
"url": "http://DEVICE_IP:PORT/mcp",
"headers": {
"Authorization": "Bearer YOUR_TOKEN"
}
}
}
}
将 DEVICE_IP、PORT 和 YOUR_TOKEN 替换为应用 Server 标签页中显示的值。如果服务器绑定在 localhost(默认设置),则需要使用 adb 端口转发或远程访问隧道进行连接。
针对不支持自定义请求头的客户端的说明:如果 MCP 客户端无法发送
Authorization请求头且不支持 OAuth,可在应用中关闭两种认证方式(Settings → Access——禁用 Bearer token 和 OAuth)。应用会发出警告并要求确认,因为此时服务器将处于开放状态:任何能够访问它的人都拥有完整控制权。仅应在可信网络中这样做。
从 Claude.ai 与 Claude Desktop 连接(自定义连接器,OAuth)
Claude.ai(网页版)和 Claude Desktop 可通过 OAuth 2.1 以自定义连接器(远程 MCP)的方式连接——该应用自身就是 OAuth Authorization Server,因此无需外部账户或预先注册。这要求服务器可通过公共 HTTPS URL访问,因此必须先启用远程访问隧道;localhost/LAN 地址或 adb 端口转发将无法使用。
- OAuth 默认已启用——无需操作(如果此前禁用过,可在 Settings → Access 中重新启用)。Bearer Token 也保持启用;两者均可被接受。
- 打开 Settings → Tunnel,启用 Remote Access(Cloudflare Quick Tunnels 无需账户),并启动服务器。从 t 中复制公共
https://…URL
English / 原文
Android Remote Control MCP
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.

Posting on Reddit (sped up, ~6 min original) | 
Installing the booking.com app | 
Flight search on Skyscanner, Zurich → Istanbul (sped up, ~4 min original)
---|---|---
An AI model controlling an Android device through the MCP server — click any clip for full resolution.
- HTTP server running directly on Android (Ktor + Netty), with optional HTTPS
- Streamable HTTP transport at
/mcp(MCP specification compliant, JSON-only, no SSE) - Combined authentication: static bearer token and/or a self-contained OAuth 2.1 server (Claude.ai / Claude Desktop custom connectors, with on-device approval)
- 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)
57 MCP Tools across 14 Categories
Screen introspection, system actions, touch actions, gestures, node actions, text input, utilities, file operations, app management, camera, intents, notifications, location, and sharing.
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)
Comparison with Alternatives
| Feature | This project | mobile-mcp | Android-MCP | android-mcp-server | adb-mcp | droidrun-mcp |
|---|---|---|---|---|---|---|
| MCP tools | 57 | 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.
Which build to download: GMS or FOSS
Each release provides two flavors of the APK — pick the one that matches your device:
- GMS (
…-gms-release.apk) — the full build. Requires Google Mobile Services (Google Play Services) installed on the phone. Choose this on standard devices/emulators that ship with Google services. - FOSS (
…-foss-release.apk) — a Google-free build with no dependency on Google Mobile Services. Works on any Android phone, including de-Googled ROMs and devices where Google services are unavailable.
If unsure, the GMS build is the right choice for a typical phone with the Play Store.
Option A: Download on your phone (easiest)
- 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)
Option B: Download on your PC and install via ADB
- Download the APK from the Releases page
- Connect your phone via USB (with USB Debugging enabled)
- Install the APK (use the GMS or FOSS file you downloaded):
adb install android-remote-control-mcp-<version>-gms-release.apk
Option C: Build from sources
git clone https://github.com/danielealbano/android-remote-control-mcp.git
cd android-remote-control-mcp
make build
make install # installs on connected device/emulator
See CONTRIBUTING.md for full build requirements and instructions.
-
Open the app. The Server tab shows a permission warning banner; grant permissions from there or via Settings > Permissions.
-
Grant permissions:
- Accessibility Service — required. Powers UI introspection, action execution, and screenshots; nothing works without it.
- If the toggle is greyed out (Android 13+, common on Samsung/One UI for apps installed from an APK): this is Android's "Restricted settings" protection. A dialog reads "Restricted setting — For your security, this setting is currently unavailable." To unblock it, go to Settings → Apps → Android Remote Control MCP → App info , tap the ⋮ menu (top-right), choose Allow restricted settings , confirm with your PIN/pattern/biometric, then enable the Accessibility Service. (Play Store / F-Droid installs are not affected.)
- Notifications — optional (recommended for OAuth). OAuth sign-in approvals (Claude.ai / Claude Desktop, ChatGPT, or any third-party OAuth client) arrive as heads-up notifications you tap to confirm the 2-digit code and approve. It is no longer required for OAuth login, though: if you don't grant this permission, pending requests still appear as a card on the app's Server screen that you tap to review and approve/deny. The Event Channel push feature — e.g. pushing device events to Claude Code — runs as a foreground service that posts its status notification through this permission on Android 13+.
- Other permissions (optional) — Camera, Microphone, and Location enable their corresponding MCP tools. The app works without them; only the dependent features are disabled until granted. See the Permissions Reference below for the complete list.
- Storage locations — configure in Settings > Storage if you plan to use the file tools (automatic locations like Downloads, plus custom locations via SAF).
- Accessibility Service — required. Powers UI introspection, action execution, and screenshots; nothing works without it.
-
Start the server. Go back to the Server tab and tap Start.
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.
Privacy Mode detects and hides personal data — emails, credit cards and IBANs, credentials, phone numbers, national IDs, addresses, and names — in everything the MCP tools return (screen content, notifications, clipboard, files, …) before it leaves the device for the AI provider. Detection runs fully on-device: fast rule-based checks (checksums, formats, field context) combined with a local NER model — no cloud calls. Detected values are either pseudonymized with consistent placeholders (EMAIL#a1b2c, so AI tools keep working across screens) or fully redacted. Detection is best-effort mitigation, not a guarantee.
The model is the Ai4Privacy multilingual anonymiser (MIT license, Built with Llama), downloaded once on first enable (~150 MB) directly from the pinned Hugging Face revision and checksum-verified on device.
Share of personal data items detected per category by the full pipeline, measured with the in-repo effectiveness benchmark (make privacy-benchmark) on realistic UI-style content across 8 languages:
| Category | Detected |
|---|---|
| Addresses | 77.9% |
| Cards & IBANs | 75.3% |
| Credentials | 90.9% |
| Emails | 100.0% |
| Names | 0.8% |
| National IDs | 93.0% |
| Phone numbers | 84.4% |
Names are the on-device model's known weak spot (it recognizes common Western names far better than the globally diverse names in the benchmark); improving name detection with a fine-tuned model is on the roadmap. Actual results vary with content and language.
- From the main Server screen : tap Set up Privacy Mode on the Privacy Mode card (shown while the mode is off).
- Or via Settings → Privacy : turn on Enable Privacy Mode and confirm the one-time model download. After the download and warm-up, a short on-device benchmark measures the expected per-screen overhead and shows it on the same screen.
- Protected categories, pseudonymize-vs-redact, and the placeholder format are configurable in Settings → Privacy.
Claude Desktop / Claude Code
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: if your MCP client cannot send an
Authorizationheader and does not support OAuth, you can turn off both auth methods in the app (Settings → Access — disable Bearer token and OAuth). The app warns and asks you to confirm, because the server is then open : anyone who can reach it has full control. Only do this on a network you trust.
Connect from Claude.ai & Claude Desktop (Custom Connector, OAuth)
Claude.ai (web) and Claude Desktop connect as a custom connector (remote MCP) using OAuth 2.1 — the app is its own OAuth Authorization Server, so no external account or pre-registration is needed. This requires the server to be reachable over a public HTTPS URL , so you must first enable a remote access tunnel — a localhost/LAN address or adb port-forward will not work.
- OAuth is enabled by default — no action needed (if you previously disabled it, re-enable it under Settings → Access). The bearer token stays enabled too; both are accepted.
- Open Settings → Tunnel , enable Remote Access (Cloudflare Quick Tunnels needs no account), and start the server. Copy the public
https://…URL from the Server tab and append/mcp(e.g.https://your-tunnel.trycloudflare.com/mcp). - In Claude, open Customize → Connectors → Add custom connector , paste the
https://…/mcpURL, and leave the OAuth Client ID and Client Secret blank (the app uses Dynamic Client Registration). Click Add. - Claude opens a browser approval page showing a 2-digit code. On the device, tap the heads-up notification — or, if the Notifications permission is off, open the app's Server screen and tap the pending approvals card — to open the approval screen, confirm the code matches, and Approve.
- Manage or revoke connected clients any time under Settings → Access → Connected clients. Revoking immediately invalidates that client's tokens.
Public URL override (optional): if your tunnel/host topology needs a fixed public host (or you bind to
0.0.0.0without a trusted proxy), set a Public URL override in Settings → Access so OAuth metadata and links use a stable host.
Custom connectors are available on the Free (1 connector), Pro, Max, Team, and Enterprise plans (currently in beta).
⚠️ Security: treat the public tunnel URL as sensitive, approve only connections you initiated (verify the 2-digit code), revoke clients you no longer use, and stop the tunnel and server when you are done. Never point it at a device holding sensitive data.
Connect from ChatGPT (Custom Connector, OAuth)
ChatGPT connects to the server as a custom MCP connector using the same self-contained OAuth 2.1 flow. As with Claude, the server must be reachable over a public HTTPS URL , so enable a remote access tunnel first — a localhost/LAN address or adb port-forward will not work. Custom connectors require a paid plan (Plus, Pro, Business, Enterprise, or Edu — not Free) and are set up from the ChatGPT web app.
- Enable Developer mode — in ChatGPT on the web, open Settings → Connectors → Advanced settings (labelled Apps & Connectors on some builds) and turn on Developer mode.
- Start the tunnel — same as the Claude flow above: OAuth stays enabled by default, turn on Remote Access under Settings → Tunnel, start the server, and copy the public
https://…/mcpURL from the Server tab. - Add the connector — open Settings → Connectors → Create , give it a name and description, paste the
https://…/mcpURL, select OAuth as the authentication method, and leave any Client ID / Client Secret blank (the app self-registers clients via Dynamic Client Registration). - Approve on the device — ChatGPT starts the OAuth flow. Tap the heads-up notification — or, if the Notifications permission is off, open the app's Server screen and tap the pending approvals card — confirm the 2-digit code matches, and Approve (identical to the Claude approval step).
- Manage or revoke the connected client any time under Settings → Access → Connected clients in the app.
⚠️ Security: the same cautions apply — treat the tunnel URL as sensitive, approve only connections you initiated (verify the code), revoke unused clients, and stop the tunnel and server when you are done.
The MCP server exposes a standard Streamable HTTP endpoint at /mcp with combined authentication (static bearer token and/or OAuth 2.1). Any MCP-compatible client can connect to it — refer to your client's documentation for the specific configuration format.
Testing with MCP Inspector
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/inspector
This 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.
The bearer token is shown in the app's connection info and can be copied directly. Bearer authentication is controlled by the Bearer token toggle in Settings → Access (not by clearing the value): with it enabled, every /mcp request must present the token (or a valid OAuth access token). The server accepts unauthenticated requests only when both the Bearer token and OAuth are disabled.
Server Settings (via App UI)
| 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 | Enabled, auto-generated UUID | Static token for MCP requests (Settings → Access). Enforcement is set by the Bearer toggle, not by clearing the value. |
| OAuth | Enabled | Self-contained OAuth 2.1 server for Claude.ai / Claude Desktop custom connectors (Settings → Access). |
| Public URL override | Empty (auto-detect) | Pin the public host used for OAuth metadata and share links. |
| 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) |
Using with adb Port Forwarding
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.
Using Remote Access Tunnels
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). Available on arm64-v8a and x86_64 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.androidremotecontrolmcpEnable Accessibility Service (required for UI introspection, actions, and screenshots)
adb shell settings put secure enabled_accessibility_services
/com.danielealbano.androidremotecontrolmcp.services.accessibility.McpAccessibilityServiceEnable Notification Listener Service (required for notification tools)
adb shell cmd notification allow_listener
/com.danielealbano.androidremotecontrolmcp.services.notifications.McpNotificationListenerServiceGrant 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
Grant location permissions
adb shell pm grant android.permission.ACCESS_FINE_LOCATION adb shell pm grant android.permission.ACCESS_COARSE_LOCATION adb shell pm grant android.permission.ACCESS_BACKGROUND_LOCATION
Grant nearby WiFi devices permission (Android 13+)
adb shell pm grant android.permission.NEARBY_WIFI_DEVICES
Grant media read permissions (Android 13+)
adb shell pm grant android.permission.READ_MEDIA_IMAGES adb shell pm grant android.permission.READ_MEDIA_VIDEO adb shell pm grant android.permission.READ_MEDIA_AUDIO
See docs/PERMISSIONS.md for the full reference and a copy-paste script that grants everything in one go.
All extras are optional — only the ones provided are updated. The app does not need to be open.
Requires adb. The configuration receiver and the service trampoline are gated on
android.permission.DUMP, which the adb shell UID holds but ordinary apps cannot obtain. The commands below work unchanged fromadb shell; an app on the device cannot use them to reconfigure the server. See the Security section.
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 cloudflare_tunnel_mode "TOKEN" \
--es cloudflare_tunnel_token "your-cloudflare-tunnel-token" \
--es cloudflare_tunnel_extra_args "--edge region1.v2.argotunnel.com:7844" \
--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"]}}'
# Disable bearer authentication (use only on trusted networks; the server is open
# only if OAuth is also disabled — see the Security section)
adb shell am broadcast \
-a com.danielealbano.androidremotecontrolmcp.ADB_CONFIGURE \
-n <app-id>/com.danielealbano.androidremotecontrolmcp.services.mcp.AdbConfigReceiver \
--ez bearer_token_enabled false
Bearer enforcement is controlled by --ez bearer_token_enabled <bool>, NOT by clearing the value. Clearing the value (--es bearer_token "") while bearer_token_enabled=true makes /mcp fail CLOSED (401) — it does NOT open the server. The server accepts unauthenticated requests only when BOTH bearer_token_enabled and oauth_enabled are false.
| Extra | Type | Description |
|---|---|---|
bearer_token |
string | Static bearer-token value (clearing it while bearer_token_enabled=true fails closed, it does NOT disable auth) |
bearer_token_enabled |
boolean | Enable/disable bearer-token authentication (controls enforcement, independent of the value) |
oauth_enabled |
boolean | Enable/disable the self-contained OAuth 2.1 server (Claude.ai / Claude Desktop connectors) |
public_url_override |
string | Pin the public host used for OAuth metadata and share links (empty = auto-detect from the request) |
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 |
cloudflare_tunnel_mode |
string | FREE (Quick Tunnel, random *.trycloudflare.com URL) or TOKEN (named tunnel with static hostname) |
cloudflare_tunnel_token |
string | Cloudflare named-tunnel token (used when cloudflare_tunnel_mode is TOKEN) |
cloudflare_tunnel_extra_args |
string | Optional extra arguments passed to cloudflared (e.g. --edge region1.v2.argotunnel.com:7844) |
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"]}} |
storage_location_id |
string | ID of an existing storage location (from android_list_storage_locations or Settings > Storage); required by the two extras below, unknown IDs are ignored |
storage_allow_write |
boolean | Allow write operations on the location identified by storage_location_id |
storage_allow_delete |
boolean | Allow delete operations on the location identified by storage_location_id |
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 start
adb shell am start \
-n <app-id>/com.danielealbano.androidremotecontrolmcp.services.mcp.AdbServiceTrampolineActivity \
--es action stop
The app declares the permissions below. Normal permissions are granted automatically at install. Runtime permissions and Special access require explicit user action (via the app's Settings > Permissions tab, or programmatically — see Granting Permissions Programmatically). All runtime permissions are optional: the app works without them, but the features that depend on them are disabled until granted.
| Permission | Type | Used for |
|---|---|---|
INTERNET |
Normal | HTTP/HTTPS server and remote access tunnels |
ACCESS_NETWORK_STATE |
Normal | Detect network connectivity |
FOREGROUND_SERVICE |
Normal | Run the MCP server as a foreground service |
FOREGROUND_SERVICE_SPECIAL_USE |
Normal | Foreground service type for the MCP server |
FOREGROUND_SERVICE_LOCATION |
Normal | Foreground service type for the Event Channel (geofence/WiFi) |
RECEIVE_BOOT_COMPLETED |
Normal | Auto-start the server on device boot |
QUERY_ALL_PACKAGES |
Normal | Enumerate installed apps for app-management tools |
KILL_BACKGROUND_PROCESSES |
Normal | Stop background apps via app-management tools |
ACCESS_WIFI_STATE |
Normal | Read WiFi state for the Event Channel |
CHANGE_WIFI_STATE |
Normal | Manage WiFi for the Event Channel |
POST_NOTIFICATIONS |
Runtime | Show the foreground service notification (Android 13+) |
CAMERA |
Runtime | Camera photo/video MCP tools |
RECORD_AUDIO |
Runtime | Audio capture for camera video tools |
ACCESS_FINE_LOCATION |
Runtime | Location tools and geofence events |
ACCESS_COARSE_LOCATION |
Runtime | Approximate location |
ACCESS_BACKGROUND_LOCATION |
Runtime | Location/geofence events while the app is in the background |
NEARBY_WIFI_DEVICES |
Runtime | WiFi scanning for the Event Channel (Android 13+) |
READ_MEDIA_IMAGES |
Runtime | "All files" mode for built-in image storage locations (Android 13+) |
READ_MEDIA_VIDEO |
Runtime | "All files" mode for built-in video storage locations (Android 13+) |
READ_MEDIA_AUDIO |
Runtime | "All files" mode for built-in audio storage locations (Android 13+) |
| Accessibility Service | Special access | UI introspection, action execution, and screenshots — required for core functionality |
| Notification Listener | Special access | Reading and managing notifications via notification tools |
The application runs entirely within Android's standard permission model. No root access, no unlocked bootloader, no custom ROM required.
Authentication is combined (dual-accept): a /mcp request is authorized by a valid static bearer token OR a valid issued OAuth access token. Enforcement is controlled by two independent toggles in Settings → Access — bearer_token_enabled (default on) and oauth_enabled (default on) — not by the token value. The bearer token is auto-generated once on first launch (UUID, preserved across app upgrades) and can be viewed, copied, or regenerated in the app. Clearing the value while bearer is enabled makes the server fail CLOSED (401). The server accepts unauthenticated requests only when BOTH toggles are off (the app shows a warning and a confirmation before that happens) — 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.