Skip to main content
Glama

agentdev — Android device tools for AI agents (Termux + proot)

CI Python Dependencies Tools License

A zero-dependency toolkit that gives an AI agent hands on an Android phone: run adb commands, read logcat, dump and drive the UI, install APKs, pull files, grab screenshots.

Designed for the setup where it's actually painful: an agent running inside a proot distro on Termux, where a normal adb binary may not exist and a USB device is never available. agentdev ships its own implementation of the ADB wire protocol, so it talks straight to the adb server running on your Termux host — no binary, no pip install, pure Python stdlib (3.8+).

┌──────────── proot distro (where the agent lives) ─────────────┐
│  agent  ──►  agentdev  ──TCP 127.0.0.1:5037──►  adb server     │
│              (socket backend, no adb binary needed)            │
└───────────────────────────────────────────────────────────────┘
                    Termux host  ▲ adb binary + wireless debugging
                                 │ USB or TCP
                              📱 phone

Three ways to call it, same 41 tools underneath:

Interface

Use it for

MCP server (agentdev mcp)

Claude Desktop, Cline, Continue, any MCP client

CLI (agentdev shell_exec --command …)

shell-capable agents, scripts, cron

Python / tool schemas

OpenAI, Anthropic, Gemini function calling (schemas exported for you)


Quick start

# 1. On the Termux HOST
pkg install python android-tools proot-distro
adb start-server

# 2. Get the toolkit into the distro
proot-distro login debian
# (copy this directory in, or git clone it)

# 3. Verify end to end
export ADB_SERVER_SOCKET=tcp:127.0.0.1:5037
python3 -m agentdev doctor

doctor tells you exactly which link in the chain is broken — backend, server, device authorisation, or missing on-device binaries.

Then:

python3 -m agentdev device_info                     # model, Android version, screen, root
python3 -m agentdev shell_exec --command "pm list packages -3"   --allow-dangerous
python3 -m agentdev logcat_crashes --package com.example.app
python3 -m agentdev ui_dump --max-nodes 40
python3 -m agentdev tap_text --text "Sign in"
python3 -m agentdev screenshot --name home
python3 -m agentdev mcp                             # serve to an MCP client

No install step required — python3 -m agentdev works from the repo. Optional: pip install -e . puts an agentdev command on PATH.

Connecting the phone (wireless debugging)

# on the phone: Developer options → Wireless debugging → Pair device with pairing code
adb pair 192.168.1.5:37123        # enter the 6-digit code
adb connect 192.168.1.5:5555
adb devices -l                   # must say "device", not "unauthorized"

The phone only needs to reach the host's adb server; the proot distro never touches the phone directly.


Related MCP server: scrcpy-mcp

The 41 tools

Diagnostics — doctor, device_list, device_select, device_wait, adb_connect, device_info, battery, net_info, device_unlock

Shell & files — shell_exec, file_ls, file_read, file_pull, file_push, file_delete*

Logs — logcat_dump (bounded snapshot, level / tag / pid / keyword filters), logcat_watch (live window — trigger an action, then watch what it logs), logcat_crashes (Java exceptions, ANRs, native aborts, Force finishing activity)

UI & input — ui_dump (accessibility tree with tap centres), ui_find, ui_wait (poll until an element appears/disappears), scroll_to (swipe until visible), tap, tap_text, swipe, long_press, input_text, key_event, screenshot, screen_state

Apps & intents — app_start, app_stop, app_clear_data, app_current, app_list, package_info, app_install, open_url, intent_send

Settings — settings_get, settings_set*

* = gated behind --allow-dangerous / AGENTDEV_ALLOW_DANGEROUS=1.

Every tool returns JSON. Failures come back as {"error": "...", "kind": "adb|timeout|internal", "retryable": bool, "hint": "..."} instead of raising, so one bad call never kills the agent loop — and the hint tells the agent what to do next (accept the RSA prompt, reconnect the transport, wake the screen…).


Wiring it into an agent

MCP (Claude Desktop, Cline, Continue…)

{
  "mcpServers": {
    "android": {
      "command": "python3",
      "args": ["-m", "agentdev", "mcp"],
      "env": {
        "AGENTDEV_ALLOW_DANGEROUS": "1",
        "ADB_SERVER_SOCKET": "tcp:127.0.0.1:5037"
      }
    }
  }
}

See examples/claude_desktop_config.json. Smoke-test without a chatbot: python3 examples/mcp_client.py device_list

OpenAI-compatible function calling

export OPENAI_API_KEY=sk-...
python3 examples/openai_agent.py "Open Settings, enable Airplane mode, then show me the log"

Works with local models too — point OPENAI_BASE_URL at Ollama / LM Studio / vLLM.

Anthropic tool use

export ANTHROPIC_API_KEY=sk-ant-...
python3 examples/anthropic_agent.py "What app is in the foreground?"

Any custom agent

python3 -m agentdev schema --format openai     > tools.openai.json
python3 -m agentdev schema --format anthropic  > tools.anthropic.json
python3 -m agentdev schema --format gemini     > tools.gemini.json
from agentdev.tools import Context, run_tool
ctx = Context()                       # one per agent session
run_tool("tap_text", {"text": "Sign in"}, ctx)
run_tool("logcat_dump", {"level": "E", "grep": "NullPointer", "limit": 50}, ctx)

AGENTS.md has the operating playbook to paste into your system prompt.


Why agents don't get stuck

Long-tool-call agents fail on flaky transports, not on missing features. agentdev bakes in the recovery paths:

  • Error triage — every failure is classified (agentdev/errors.py) into a hint + retryable flag: unauthorized → "tap Allow on the phone"; device offline → "adb reconnect offline"; uiautomator dump failed → "wake the device, or use screenshot".

  • Transient retry — a dropped socket / stale transport is retried once automatically after resetting the session, so a server restart doesn't burn a turn.

  • Hierarchy cache — repeated ui_find / ui_wait / tap_text calls within 1.5 s reuse one uiautomator dump (AGENTDEV_UI_TTL); every mutating tool invalidates it.

  • Wait, don't sleep — ui_wait and device_wait replace blind sleeps, so slow devices cost latency instead of correctness.

  • Bounded everything — log output, stream windows, screenshots and file reads are all capped, so a chatty device can't blow the model's context window.

  • Secret hygiene — scripts/check_secrets.py (run in CI) blocks any commit containing GitHub/AWS/OpenAI/Anthropic tokens or private keys.

Configuration

Env var

Default

Meaning

AGENTDEV_ADB_HOST / ADB_SERVER_SOCKET

127.0.0.1 / tcp:127.0.0.1:5037

where the adb server listens

AGENTDEV_ADB_PORT

5037

adb server port

AGENTDEV_SERIAL / ANDROID_SERIAL

first online device

target serial

AGENTDEV_ALLOW_DANGEROUS

0

unlock *-gated tools

AGENTDEV_SHELL_TIMEOUT

60

seconds per shell call

AGENTDEV_MAX_OUTPUT

24000

chars returned to the model per call

AGENTDEV_WORKDIR

~/.agentdev/out

where screenshots / pulled files land

AGENTDEV_DEVICE_TMP

/data/local/tmp

on-device scratch dir

AGENTDEV_UI_TTL

1.5

seconds the hierarchy cache stays valid

ADB_BIN

auto

explicit path to an adb binary (binary backend)

CLI flags --allow-dangerous --serial --adb-host --adb-port --backend --raw work before or after the subcommand.


How the two backends work

socket (default, recommended) — speaks the adb protocol directly: %04x-framed requests, OKAY/FAIL status words, host:devices-l, host:transport:<serial> + shell:<cmd>. Works inside proot with no adb binary and no privileged mounts. Exit codes are recovered by appending a marker echo to each command (the adb shell service doesn't propagate them over the wire). Files move as base64 chunks over the shell channel, so sync: services and adb push aren't needed.

binary (fallback) — shells out to adb from PATH (or /data/data/com.termux/files/usr/bin/adb, or ADB_BIN), and drives adb push/adb install when available. Auto-detected if the server probe fails; force with --backend binary.

Long-lived streams (logcat) reuse one socket via iter_stream, with per-read timeouts so a silent device can never wedge the agent.


Notes for proot

  • The distro normally shares the host network namespace, so 127.0.0.1:5037 reaches the Termux adb server. If it doesn't, use the host's Wi-Fi IP: AGENTDEV_ADB_HOST=192.168.1.10. (Binding a server inside proot to a port the host can see does not work — run it on the host.)

  • Termux's adb binary can't be executed inside some rootfs images (glibc vs bionic), which is exactly why the socket backend exists.

  • No root required on the phone; everything here runs through the normal adb shell user. Root-only commands just fail with a clear error.

Safety

  • Destructive tools are gated; leave AGENTDEV_ALLOW_DANGEROUS unset for read-only agents.

  • screenshot / file_pull write into AGENTDEV_WORKDIR — never into system paths.

  • Every device action is logged to stderr by the MCP server, stdout stays protocol-clean.

  • shell_exec runs as the adb shell user: it cannot silently escalate.

Tests

python3 -m unittest discover -s tests -v      # 62 tests, no phone needed
python3 scripts/check_secrets.py --all        # credential scan (also runs in CI)
make test lint secrets                        # same thing via Makefile

tests/fake_adb.py is a real adb-protocol server that executes commands on the local machine (and serves canned uiautomator/wm/settings/screencap payloads), so the socket backend, framing, exit codes, file transfer, UI hierarchy parsing, tap-coordinate math, screenshot decoding, cache invalidation, retry logic, tool registry, MCP handshake and CLI are all exercised without hardware. Point it at a real phone any time with python3 tests/fake_adb.py 5038 (do not run it on 5037 next to a real server).

Limitations

  • input_text is ASCII-only (an Android input limitation); use shell_exec with a clipboard helper for Unicode.

  • ui_dump uses uiautomator — not available on games/Flutter canvases with no accessibility nodes; fall back to screenshot + tap.

  • Pushing files over base64 is fine for MBs, slower than adb push for large APKs (the binary backend uses adb push automatically when present).

MIT licensed.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables AI agents to see and control Android devices through ADB and screen mirroring, supporting screen capture, touch input, app management, UI element finding, file transfer, shell commands, clipboard access, and audio/video streaming.
    374 npm
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables AI agents to see and control Android devices through ADB and scrcpy, supporting screen capture, taps, swipes, typing, app management, UI inspection, file transfer, shell commands, and audio/video streaming.
    374 npm
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables AI agents to see and control Android devices through ADB and scrcpy, including screenshots, taps, swipes, typing, app management, UI inspection, file transfer, shell commands, audio/video streaming, and clipboard access.
    374 npm
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables AI agents to see and control Android devices via ADB and scrcpy, supporting screenshots, taps, swipes, typing, app management, UI inspection, file transfer, shell commands, and audio/video streaming.
    374 npm
    MIT