Metadata-Version: 2.4
Name: geelark-mcp
Version: 0.1.0
Summary: MCP server for managing GeeLark Cloud Phones and preparing them for Android automation over ADB.
Project-URL: Homepage, https://github.com/techinz/geelark-mcp
Project-URL: Repository, https://github.com/techinz/geelark-mcp
Project-URL: Issues, https://github.com/techinz/geelark-mcp/issues
Author: techinz
License: MIT
License-File: LICENSE
Keywords: adb,android,cloud-phone,geelark,mcp,uiautomator2
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Testing
Requires-Python: >=3.11
Requires-Dist: httpx>=0.27
Requires-Dist: mcp>=1.12
Requires-Dist: pydantic-settings>=2.3
Requires-Dist: pydantic>=2.7
Description-Content-Type: text/markdown

# geelark-mcp

An MCP (Model Context Protocol) server for managing **GeeLark Cloud Phones**
through the official GeeLark OpenAPI and preparing running devices for **Android
automation over ADB**.

> **Unofficial / community project.**
> This project is not affiliated with or endorsed by GeeLark.

High-level Android **UI** automation (screenshots, UI hierarchy, selectors,
clicks, text input, swipes) is handled by
[`uiautomator2-mcp-server`](https://github.com/tanbro/uiautomator2-mcp-server), a
separate package you register as a second MCP server alongside this one. The two
share a local ADB server and device serial. Running geelark-mcp on its own is not
a supported setup: without the UI server you can only drive the screen by raw
coordinates.

> **"Hey Claude, create a fresh GeeLark phone, install TikTok, open it, skip
> onboarding, scroll the For You feed for two minutes and stop the phone."**
>
> `geelark-mcp` provisions the phone, installs the app, and establishes ADB;
> `uiautomator2-mcp-server` handles the on-screen interaction.

---

## Features

- List, create, start, stop, delete and inspect GeeLark cloud phones.
- Install apps from the GeeLark catalog; start/stop/uninstall apps.
- Enable ADB and retrieve normalized ADB connection info (secret withheld by default).
- Connect a phone to the local ADB server, including the GeeLark `glogin` flow.
- Install local APKs over ADB (sandboxed to an allowed directory, size-limited).
- Execute allowlisted ADB diagnostics/UI input as argv lists; raw commands require two flags.
- Bounded logcat, file push/pull, and low-level coordinate fallbacks.
- Query / cancel / retry GeeLark automation tasks (read + lifecycle only).
- Readiness and setup tools to hand a shared serial to `uiautomator2-mcp-server`.
- A one-shot `prepare_phone_for_automation` workflow with per-stage results.

## Project boundaries

geelark-mcp handles GeeLark authentication and requests, cloud phone lifecycle,
application/file/automation-task management (as confirmed by the docs), enabling
ADB, retrieving connection info, the local `adb connect` + `glogin` flow, device
health checks, the phone ID ↔ ADB serial mapping, local APK installation, safe ADB
command execution and basic diagnostics.

The external `uiautomator2-mcp-server` handles screenshots, UI hierarchy dumps,
element search, selectors, XPath, clicks/long-clicks, text entry, scrolling and
swiping, permission-dialog handling and other high-level UI automation.

The two servers never call each other directly. They cooperate only through the
shared local ADB server:

```
MCP host (Claude Code / Claude Desktop / other)
├── geelark-mcp                → connects the GeeLark phone to local ADB
└── uiautomator2-mcp-server    → discovers & controls the already-connected serial
```

## Installation

Follow the five steps below in order. The result is two MCP servers registered in
your client, sharing one ADB server.

### Step 1 — Prerequisites

- Python **3.11+**
- **Android Platform Tools** (`adb`) on `PATH` (or set `GEELARK_ADB_PATH`)
- GeeLark API Bearer token
- **[uv](https://docs.astral.sh/uv/)**, which runs both servers

Install uv if `uv --version` fails:

```bash
# macOS / Linux
curl -LsSf https://astral.sh/uv/install.sh | sh

# Windows
winget install --id=astral-sh.uv -e
```

After installing uv, open a new terminal and verify both commands:

```bash
uv --version
uvx --version
```

If your MCP client was open during installation, fully quit and reopen it so it
inherits the updated `PATH`.

### Step 2 — Check that both servers resolve

`geelark-mcp` and `uiautomator2-mcp-server` are **independent packages**, and
`uvx` fetches each into its own isolated environment on demand. There is nothing
to install — your MCP client invokes `uvx` directly in step 4.

Run both once to warm the cache and confirm they resolve:

```bash
uvx geelark-mcp --version
uvx --from uiautomator2-mcp-server u2mcp --help
```

Contributors working on the code should follow [Development](#development)
instead.

### Step 3 — Start a shared ADB server

Both servers must talk to the *same* ADB server, otherwise the serial produced by
geelark-mcp is invisible to the UI server. Start it once:

```bash
adb start-server
```

This binds `127.0.0.1:5037`, the socket used in step 4. The daemon then stays up
until you reboot or run `adb kill-server`, so this is **once per boot, not once
per session** — reconnecting the MCP servers does not need it repeated.

> Do **not** write `adb -L tcp:127.0.0.1:5037 start-server`. The `-L` flag marks
> the socket as remote, and adb refuses to spawn a daemon for one:
> `cannot start server on remote host`. The same applies to `ADB_SERVER_SOCKET`
> being set in the environment — which is exactly what geelark-mcp does, so it
> cannot bootstrap the daemon for you. Something must start it first, and that is
> this step.

### Step 4 — Register both servers in your MCP client

The two servers are registered as two independent entries. Both must carry the
same ADB socket from step 3: `GEELARK_ADB_SERVER_SOCKET` for geelark-mcp,
`ADB_SERVER_SOCKET` for uiautomator2.

#### Option A — edit the configuration file (works in every client)

Copy the whole `mcpServers` object from
[examples/mcp-config.json](examples/mcp-config.json) — it already contains both
servers with a matching socket — into your client's configuration file:

| Client | File | `mcpServers` goes |
| --- | --- | --- |
| Claude Code (VS Code extension or terminal), all projects | `~/.claude.json` | at the top level |
| Claude Code, one project | `<project>/.mcp.json` | at the top level |
| Claude Desktop | `claude_desktop_config.json` | at the top level |

Replace every `REPLACE_WITH_...` placeholder, then reload the client (restart the
app for Claude Desktop) and check `/mcp`.

Never commit real credentials in a shared `.mcp.json` — reference them instead:

```json
"env": {
  "GEELARK_BEARER_TOKEN": "${GEELARK_BEARER_TOKEN}"
}
```

`~/.claude.json` and `claude_desktop_config.json` store `env` values as
plaintext. Keep them private, and prefer OS environment variables if you would
rather not store secrets there at all.

#### Option B — the `claude` CLI

Only if the standalone Claude Code CLI is installed and on your `PATH`. **The VS
Code extension does not provide it** — check with `claude --version` first, and
use Option A if that fails.

```bash
claude mcp add --transport stdio \
  --env GEELARK_BEARER_TOKEN=your_token \
  --env GEELARK_ADB_SERVER_SOCKET=tcp:127.0.0.1:5037 \
  geelark -- uvx geelark-mcp serve

claude mcp add --transport stdio \
  --env ADB_SERVER_SOCKET=tcp:127.0.0.1:5037 \
  uiautomator2 -- uvx --from uiautomator2-mcp-server u2mcp stdio
```

Everything after `--` is the launch command.

Scope defaults to `local` (private to the current project). Add `--scope user`
for every project, or `--scope project` to share through a committed `.mcp.json`.

> On Windows PowerShell the trailing `\` line continuations above are a syntax
> error. Put each command on one line, or use a backtick `` ` `` instead.

### Step 5 — Verify

**Restart the client** and check `/mcp`: both `geelark` and `uiautomator2` should
be connected. If uv was installed while Claude or VS Code was open, close all of
its processes before reopening it; reconnecting the MCP server alone does not
refresh `PATH`. Then confirm both servers agree on the ADB environment:

```
verify_shared_adb_environment()
```

The reported `adb_server_socket` must match the socket from step 3. See
[Configuration reference](#configuration-reference) for the remaining options and
[Troubleshooting](#troubleshooting) if a server fails to start.

## Configuration reference

Pass configuration to the server as environment variables through your MCP
client. `GEELARK_BEARER_TOKEN` is required; only Bearer authentication is
supported.

| Variable | Default | Purpose |
| --- | --- | --- |
| `GEELARK_BEARER_TOKEN` | required | GeeLark Bearer token; treat as a secret |
| `GEELARK_BASE_URL` | `https://openapi.geelark.com` | GeeLark API host root |
| `GEELARK_REQUEST_TIMEOUT` | `30` | API read/write/pool timeout in seconds |
| `GEELARK_CONNECT_TIMEOUT` | `10` | API connection timeout in seconds |
| `GEELARK_MAX_RETRIES` | `3` | Maximum retries for eligible API calls (`0`–`10`) |
| `GEELARK_LOG_LEVEL` | `INFO` | Python log level |
| `GEELARK_ADB_PATH` | `adb` | ADB executable name or absolute path |
| `GEELARK_ADB_SERVER_SOCKET` | unset | Shared ADB socket, e.g. `tcp:127.0.0.1:5037` |
| `GEELARK_ADB_CONNECT_TIMEOUT` | `30` | ADB connect/glogin timeout in seconds |
| `GEELARK_ADB_COMMAND_TIMEOUT` | `60` | Default ADB command timeout in seconds |
| `GEELARK_ALLOW_DANGEROUS_ADB` | `false` | Enables per-call raw ADB opt-in |
| `GEELARK_ALLOWED_APK_ROOT` | unset | Directory from which local APKs may be installed |
| `GEELARK_MAX_APK_SIZE_MB` | `500` | Maximum local APK size |
| `GEELARK_STOP_MANAGED_PHONES_ON_EXIT` | `false` | Stops phones started by this server on shutdown |

Secrets are withheld from tool output by default and redacted from logs. Options
that explicitly return a secret should be used only when the MCP client and its
history are trusted.

## Running the server manually

The MCP client normally launches the server for you; run it by hand only for
debugging or for a non-stdio transport.

```bash
geelark-mcp serve
# or
python -m geelark_mcp serve
```

Streamable HTTP (local only; see security note):

```bash
geelark-mcp serve --transport streamable-http --host 127.0.0.1 --port 8000
```

> Streamable HTTP binds to `127.0.0.1` by default and is **never** `0.0.0.0`.
> Public deployment requires authentication, TLS, a reverse proxy, origin
> validation and network access controls. The initial release targets stdio.

## How the ADB connection works

Internally, the connection flow is:

```text
GeeLark /adb/setStatus
  -> GeeLark /adb/getData
  -> adb connect host:port
  -> adb -s host:port shell glogin <connection-code>
  -> adb -s host:port get-state
```

The connection code is treated as a secret and is not returned by the combined
workflow. `adb_disconnect(phone_id="...")` disconnects only a session recorded
by this server; it never stops the shared ADB server. Cloud phones are stopped on
shutdown only when `GEELARK_STOP_MANAGED_PHONES_ON_EXIT=true`.

## Example end-to-end workflow

1. `prepare_phone_for_automation(phone_id="...")` — starts the phone, enables ADB,
   connects, runs `glogin`, verifies the device is online and returns the ADB
   serial (`host:port`) plus per-stage results.
2. Confirm the serial with `verify_shared_adb_environment(target_serial="host:port")`.
3. In `uiautomator2-mcp-server`, select that exact serial and use its UI tools
   (screenshot, dump hierarchy, click, set text, swipe, …).

Example tool call (arguments as JSON):

```json
{ "name": "list_phones", "arguments": { "page": 1, "page_size": 20 } }
```

## Multi-device behavior

When more than one device is connected, geelark-mcp never assumes the first one.
Command tools resolve the serial from the phone ID ↔ serial mapping created by
`adb_connect`. Readiness tools warn when multiple devices are online. Always pass
the exact serial to the external UI server rather than relying on auto-selection.

## Security considerations

Secrets are withheld from tool output and redacted from logs. ADB runs without
`shell=True`; raw commands require two explicit opt-ins. Local APK installation
is limited to `GEELARK_ALLOWED_APK_ROOT`.

## Troubleshooting

### Configuration errors on startup

- `GEELARK_BEARER_TOKEN must not be empty`: pass the token through the MCP client
  configuration or process environment.
- `GEELARK_BASE_URL must use HTTPS` / `must be a host root without an endpoint
  path`: use `https://openapi.geelark.com` without an endpoint path.
- `ADB executable ... was not found`: install Android Platform Tools or set
  `GEELARK_ADB_PATH`.
- `GEELARK_ALLOWED_APK_ROOT ... does not exist`: point it at an existing directory.
- `GEELARK_ADB_SERVER_SOCKET must look like 'tcp:<host>:<port>' or 'unix:<path>'`:
  fix the socket format.
- `cannot start server on remote host` / `cannot connect to daemon at
  tcp:127.0.0.1:5037`: no ADB daemon is running and `GEELARK_ADB_SERVER_SOCKET`
  prevents adb from starting one. Run `adb start-server` (no `-L` flag) and retry.

### Tool returns `ok: false`

Use `error.kind` to identify the failure:

| kind | meaning | typical fix |
| --- | --- | --- |
| `auth` | Bearer token rejected | check `GEELARK_BEARER_TOKEN` |
| `validation` | bad arguments | read the message and fix the inputs |
| `not_found` | phone, app, task or session missing | verify the ID; connect first |
| `rate_limit` | too many requests | back off before retrying |
| `timeout` | requested state was not reached in time | inspect current state before retrying |
| `unknown_outcome` | mutating call may have applied | verify state before retrying |
| `security` | command is outside the safe ADB allowlist | use a dedicated tool or explicitly enable raw ADB |
| `adb_error` | local ADB failed | inspect `stderr` or `data.stages` |

### `adb_connect` fails

Inspect `data.stages`:

- `wait_running`: increase `timeout_seconds` or check the phone in GeeLark.
- `wait_adb`: confirm that the phone model supports ADB (error `49002` means
  unsupported).
- `adb_connect`: check the network path and firewall access to `ip:port`.
- `adb_health_check`: retry if the device connected but did not reach `device`
  state; this is commonly a transient `glogin` issue.

If the UI server cannot see the device, run `adb_devices` and
`verify_shared_adb_environment`. Confirm both servers use the same ADB socket and
always select the exact serial when multiple devices are connected.

Logs go to stderr because stdout is reserved for the MCP protocol. Set
`GEELARK_LOG_LEVEL=DEBUG` for more detail.

## Development

```bash
git clone https://github.com/techinz/geelark-mcp
cd geelark-mcp
uv sync --dev
uv run ruff check .
uv run ruff format --check .
uv run mypy src
uv run pytest
```

See [CONTRIBUTING.md](CONTRIBUTING.md).

## License

[MIT](LICENSE).

## Links

- GeeLark OpenAPI docs: https://github.com/GeeLark/geelark-openapi
- Model Context Protocol: https://modelcontextprotocol.io
- Python MCP SDK / FastMCP: https://github.com/modelcontextprotocol/python-sdk
- External UI automation: https://github.com/tanbro/uiautomator2-mcp-server
