polyscreen-mcp
An Android-first MCP server for automating real devices, including multi-display handhelds and emulators, with structured tools for device inspection, input, app lifecycle, and diagnostics via ADB.
README
PolyScreen MCP
An Android-first Model Context Protocol server built for reliable automation on real devices, including multi-display handhelds, gamepads, emulators, and test labs.
The server uses the official adb executable for its portable core. It probes each connected device at runtime instead of assuming capabilities from the Android version. Optional tool profiles add diagnostics, app operations, constrained file transfer, performance snapshots, permission control, and an instrumentation companion for explicit key down/up plus accessibility windows on every display.
Why
Existing mobile MCP servers commonly:
- confuse Android logical display IDs with SurfaceFlinger physical IDs;
- capture one display while injecting input into another;
- expose only a small hard-coded set of physical buttons;
- treat
uiautomator dumpas multi-display aware when it is not; - return prose that agents must parse;
- expose unrestricted shell commands as ordinary tools.
PolyScreen MCP keeps those boundaries explicit and returns structured evidence for every operation.
Requirements
- Node.js 22.12 or newer
- pnpm 11
- Android platform tools with
adbonPATH, orADB_PATH=/absolute/path/to/adb - Android 11 or newer for the supported multi-display baseline
- JDK 17 and Android SDK only when building the optional companion
Install and build
pnpm install
pnpm check
pnpm build
Run through an MCP client:
{
"mcpServers": {
"polyscreen": {
"command": "node",
"args": [
"/absolute/path/to/polyscreen-mcp/dist/cli.js",
"--profile",
"core",
"diagnostics",
"companion"
]
}
}
}
After publication, the intended command is:
{
"mcpServers": {
"polyscreen": {
"command": "npx",
"args": ["-y", "polyscreen-mcp@latest"]
}
}
}
For local Streamable HTTP:
polyscreen-mcp --listen 3300 --token "replace-with-a-secret"
The endpoint is http://127.0.0.1:3300/mcp. HTTP always binds to loopback, validates Host and Origin, and optionally requires the configured bearer token. Stdio remains the default.
Tool profiles
The default core profile is deliberately compact. Additional profiles advertise tools only when requested.
| Profile | Capabilities |
|---|---|
core |
Devices, capabilities, displays, screenshots, UI, touch, keys, text, app lifecycle/install |
apps |
Package inventory and scoped broadcasts |
diagnostics |
Bounded dumpsys bundles and filtered logcat |
files |
Constrained push/pull under approved roots |
performance |
CPU, power, battery, memory, and frame snapshots |
device-admin |
Explicit runtime permission grant/revoke |
companion |
All-display accessibility windows and explicit key press/down/up |
all |
Every implemented profile |
unsafe and emulator profiles are reserved but do not expose raw shell in this release.
Core tools
mobile_devices_listmobile_device_inspectmobile_displays_listmobile_screen_capturemobile_screen_recordmobile_ui_snapshotmobile_ui_findmobile_ui_waitmobile_input_tapmobile_input_swipemobile_input_dragmobile_input_keymobile_input_key_combinationmobile_input_textmobile_app_inspectmobile_app_launchmobile_app_stopmobile_app_installmobile_app_uninstall
Every display-sensitive tool takes a framework logical displayId. Screenshot and recording implementations resolve that to a SurfaceFlinger physical ID internally.
Screenshots can be retained with saveArtifact. Pulled files and retained captures are exposed through the mobile://artifacts/{name} MCP resource template, keeping large binary payloads out of structured JSON.
Multi-display model
Android has multiple identifier spaces:
- logical display IDs are small framework integers used by WindowManager, ActivityManager,
input -d, and accessibility; - physical display IDs are unsigned 64-bit SurfaceFlinger identifiers used by
screencap -dandscreenrecord --display-id; - virtual displays may have a logical ID but no capturable physical ID.
Physical IDs are represented as decimal strings so JavaScript never loses precision. Correlation uses DisplayInfo.uniqueId, display addresses, and bounded evidence from:
dumpsys displaydumpsys SurfaceFlinger --display-iddumpsys inputdumpsys window displaysdumpsys activity activities
Requested activity placement is always treated as a request. Callers should inspect tasks and window focus to verify the observed result.
Input behavior
The ADB backend probes the device's own input help and exposes only supported options:
- keyboard, dpad, gamepad, and touchscreen sources;
- display targeting with
-d; - key press, long press, double tap, and explicit duration where supported;
- any symbolic or numeric Android keycode, including face buttons, shoulders, triggers, thumb buttons, START, SELECT, and MODE;
- taps, swipes, text, and modern command capabilities reported by inspection.
ADB shell gamepad events still use a synthetic virtual device identity. They are not equivalent to a physical controller descriptor.
Optional companion
Build:
gradle -p companion :app:assembleDebug :app:assembleDebugAndroidTest :fixture:assembleDebug
Outputs:
companion/app/build/outputs/apk/debug/app-debug.apkcompanion/app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk
Enable the companion profile, then call:
mobile_companion_installmobile_companion_startmobile_companion_keyormobile_companion_windowsmobile_companion_stop
The host creates a random per-session token and an owned adb forward to a local-abstract socket. Frames are length-prefixed JSON with a 1 MiB maximum. The instrumentation process preserves key downTime, supports explicit press/down/up and repeats, and releases all held keys when a client disconnects.
Platform limits are reported rather than hidden:
- keys follow Android's focused-display policy because public
KeyEventhas no portable display setter; - a normal APK cannot hold the signature-only
INJECT_EVENTSpermission; - synthetic gamepad events do not have physical controller identity;
- accessibility exposes interactive windows and nodes, not secure or inaccessible rendering.
Security
- No
/bin/sh -cor command-string interpolation. - Device serials, packages, components, keycodes, tags, permissions, paths, and display IDs are validated.
- Mutations are serialized per device.
- Subprocesses have deadlines, cancellation, and output caps.
- Push paths must remain under the server host root.
- Pull/push device paths are restricted to shared storage and
/data/local/tmp. - Raw shell, root, remount, verity, SELinux, partition, credential, and system-process operations are not exposed.
See SECURITY.md for reporting and deployment guidance.
Testing and development
pnpm format
pnpm lint
pnpm typecheck
pnpm test
pnpm test:coverage
pnpm build
gradle -p companion :app:assembleDebug :app:assembleDebugAndroidTest
The local suite covers:
- ADB subprocess timeouts, cancellation, output limits, and argv safety;
- logical/physical display parsing, virtual displays, capture routing, and launch verification;
- UI hierarchy parsing and matching;
- per-device mutation serialization;
- host/device path confinement and typed command validation;
- MCP tool-profile registration and Streamable HTTP security;
- artifact storage and traversal protection;
- companion framing and request correlation.
Device integration checks are intentionally separate from deterministic unit tests. The Thor acceptance pass additionally verifies displays 0 and 4, physical-ID capture and recording, all-display accessibility windows, authenticated forwarding, and independent key down/up injection.
Never silently select the first connected device: pass the exact serial returned by mobile_devices_list.
Occasional Logical display N is not available flakes on multi-display handhelds usually clear after re-listing displays, tapping the target display (or waking it), and retrying. The device acceptance scripts recover via runWithDisplay.
Repeatable device acceptance
The generic suite is read-mostly. It captures requested displays and can optionally record, inspect one package, and exercise an already-installed companion:
POLYSCREEN_DEVICE=10.0.0.174:5555 \
POLYSCREEN_DISPLAY_IDS=0,4 \
POLYSCREEN_RECORD=1 \
POLYSCREEN_TEST_PACKAGE=com.wajiha \
POLYSCREEN_COMPANION=1 \
pnpm test:device
The destructive Thor suite uses a dedicated integration-fixture APK, separate from the production companion. It installs and launches the fixture, verifies tap/swipe/drag/text input, grants and revokes CAMERA, force-stops the fixture, and uninstalls it during cleanup:
POLYSCREEN_DEVICE=10.0.0.174:5555 \
POLYSCREEN_DISPLAY_ID=0 \
POLYSCREEN_ALLOW_DESTRUCTIVE=1 \
pnpm test:thor:destructive
The destructive suite refuses to run without the acknowledgement variable and never chooses a device serial implicitly. Override POLYSCREEN_FIXTURE_APK when testing an externally built fixture.
License
Apache-2.0.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。