MCP server for Android 11+ devices. C# .NET 8 + Kotlin agent (submodule).
Talks ADB protocol directly via AdvancedSharpAdbClient; pushes an
app_process agent for UiAutomation operations.
License: Apache 2.0.
End users install via:
iwr https://raw.githubusercontent.com/TheDeathDragon/AndroidMCP/main/scripts/install.ps1 | iexcurl -fsSL https://raw.githubusercontent.com/TheDeathDragon/AndroidMCP/main/scripts/install.sh | shscripts/install.ps1 resolves the latest GitHub Release, downloads the
win-x64 zip, extracts to %LOCALAPPDATA%\Programs\android-mcp\, and prints
the Claude Code config snippet (or merges with -AutoConfig).
scripts/install.sh is the Linux twin: linux-x64 tarball ->
~/.local/share/android-mcp/, --auto-config merges via python3. Keep the
two in sync — POSIX sh only, no jq.
git submodule update --init --recursive
agent\build.bat
dotnet build AndroidMcp.slnx
dotnet test AndroidMcp.slnx
publish.bat
Agent compile needs an unstripped android.jar at
$ANDROID_HOME/platforms/android-36/android.jar (use
Reginer/aosp-android-jar).
CI overlays this automatically.
src/AndroidMcp/
├── Program.cs # entry, transport selection, tool registration
├── Adb/ # ADB layer, agent management, selector matching
├── Mcp/ # JSON-RPC protocol + stdio/HTTP transports
├── Tools/ # one file per cluster (Screen/Input/Shell/App/Element/Scroll/Nav/...)
└── Logging/Log.cs # stderr-only logger
agent/ # submodule -> github.com/TheDeathDragon/AndroidAgent
tools/agent-server.jar # built by agent/build.bat
scripts/ # install / publish / smoke / extract_changelog
.github/workflows/ # ci.yml + release.yml
- Transport: stdio (default) or HTTP+SSE on loopback.
- Multi-device: every tool except
list_devicesrequiresserial. No active-device state.AgentRegistrylazily creates oneAgentSessionper serial; each session gets a host port fromPortAllocator(9601-9699). - Agent reuse:
AgentSession.TryReuseCanonicalAgentfirst probestcp:9500(UiAutomationis a per-device singleton). If a healthy agent already runs there, MCP forwards instead of pushing its own jar. - Selector matching:
HierarchyMatcher.FindAllwalks the agent's accessibility XML. AT-tuned constants live inScrollTools.ScrollGeometry(40 px edge inset, 60 % default span, 150-900 ms stability window) — don't tweak without retesting. - Lean hierarchy:
HierarchyMatcher.ToLeancollapses XML into compact JSON (~5× smaller).dump_hierarchydefaults toformat=lean; CJK emitted raw viaUnsafeRelaxedJsonEscaping. - Initialize instructions:
Mcp/Protocol.csServerInstructions.Textships viainitialize.result.instructions. Clients inject into the LLM's system prompt once per session. - Versioning:
YYMM.NNNN(year-month + zero-paddedgit rev-list --count HEAD). MSBuildGenerateBuildInfotarget writesBuildInfo.g.csbefore compile. Tag releases asv<YYMM.NNNN>.
- Open-source terseness: no narrative comments, no obvious-WHAT comments, only why-comments for non-obvious decisions. CHANGELOG entries are 1-line bullets. README "Tips" are bare bullet lists with no preamble.
dotnet format --verify-no-changesmust pass before commit (CI enforces).private static readonlyandprivate constuse PascalCase; instance fields camelCase (.editorconfigenforces).- No Chinese in code or commits; READMEs are bilingual.
- stdio safety: never
Console.WriteLinefrom production paths. UseAndroidMcp.Logging.Log(writes stderr). - AdbClient calls always need
DeviceData device; obtain viaAdbHub.RequireDevice(serial).
- Land changes on
main(CI green). - Edit
CHANGELOG.md: rename[Unreleased]to[YYMM.NNNN]where NNNN is the commit count after the release commit you're about to make. git commit -m "release: <version>"andgit tag v<version>.git push origin main --tags.release.ymlbuilds + extracts the CHANGELOG section + creates the GitHub Release.
| Package | Purpose |
|---|---|
| AdvancedSharpAdbClient | ADB protocol client |
| xunit | tests |