MCP server

The same 27 tools, two ways in: a hosted MCP endpoint (URL + your API key, nothing to install or keep current) and a local stdio server in one stdlib-only Python file. Agents that speak the Model Context Protocol get PreviewAPK as native tools — including screenshots that return as images inside the conversation.

Hosted MCP — no install

Point your MCP client at https://previewapk.siteport.site/mcp with your account API key as a Bearer token. The server speaks MCP Streamable HTTP (2024-11-05 and 2025-03-26) and answers with the same tools, the same receipts, and the same error envelopes as the local server — your key's scope is enforced identically (a read-only key observes here and cannot drive).

Claude Code

claude mcp add --transport http previewapk https://previewapk.siteport.site/mcp \
  --header "Authorization: Bearer <your key>"

Cursor / generic remote-MCP clients

{
  "mcpServers": {
    "previewapk": {
      "url": "https://previewapk.siteport.site/mcp",
      "headers": { "Authorization": "Bearer <your key>" }
    }
  }
}

Codex

# ~/.codex/config.toml — with PREVIEWAPK_API_KEY set in the environment
[mcp_servers.previewapk]
url = "https://previewapk.siteport.site/mcp"
bearer_token_env_var = "PREVIEWAPK_API_KEY"

How selection works here: at connect, the server issues a session id (Mcp-Session-Id) and your working-device selection rides on it — select once with previewapk_use_device (or start a device), then drive, exactly like the local server. Two connections never share a selection, even on the same key. Selections live in the server's memory: they survive about two idle hours and are forgotten on a server restart (select again — nothing else is lost). Calls that name their device also work with no session at all.

Hosted differences — the server cannot read your disk: previewapk_push_apk takes apk_b64 (the file as base64) instead of path; previewapk_device push_file takes data_b64 instead of a local path, and pull_file returns the content as data_b64; previewapk_record_download returns the MP4 as data_b64 instead of writing a file. Request bodies cap at 25 MB and inline downloads at ~25 MB — for very large APKs or recordings, the CLI's resumable transfers are the right tool.

Local install (stdio)

curl -o previewapk_mcp.py https://previewapk.siteport.site/mcp/previewapk_mcp.py

Claude Code

claude mcp add previewapk \
  --env PREVIEWAPK_API_KEY=<your key> \
  -- python3 /path/to/previewapk_mcp.py

Cursor / generic MCP clients

{
  "mcpServers": {
    "previewapk": {
      "command": "python3",
      "args": ["/path/to/previewapk_mcp.py"],
      "env": { "PREVIEWAPK_API_KEY": "<your key>" }
    }
  }
}

Google Antigravity

PreviewAPK is not in Antigravity's built-in MCP Store — Google curates that shelf itself, and there is no submission path. The raw-config route below takes about two minutes and works the same in the Antigravity IDE, App, and CLI, which all share the one config file (~/.gemini/config/mcp_config.json).

In the IDE: open the agent panel's … menu → MCP Servers → Manage MCP Servers → View raw config, and add the entry:

{
  "mcpServers": {
    "previewapk": {
      "command": "python3",
      "args": ["/path/to/previewapk_mcp.py"],
      "env": { "PREVIEWAPK_API_KEY": "<your key>" }
    }
  }
}

Save, then click Refresh in the MCP servers panel. Antigravity's agent gets the same 27 tools listed below, screenshots included.

The working-device model: an agent session has a working device, like a working directory. previewapk_use_device selects it (or previewapk_start, which requires naming the device and selects it too); every other tool then talks to the working device without repeating its name, and a per-call device argument overrides for one call. With nothing selected, action tools refuse — there is no default device to guess. Each agent session holds its own selection, so two agents on one account never inherit each other's place.

Tools

ToolArgumentsReturns
previewapk_list_devices—Every device on the account — name, id, profile, Android, status, attached app, and which one is your working device
previewapk_use_devicedeviceSelect the working device (the cd) — later tools talk to it without naming it
previewapk_statusdevice?Device state (the API's device object) — including the handoff block while the agent waits for a human step, the filed finish_report, and — exactly once after a handback — handback_note + resumed
previewapk_push_apkpath, device?, run?, driver?Attach result + app label/version. On a running device (you hold the wheel) the build is hot-swapped in live — no reboot
previewapk_startdevice (name or id), driver? (agent default)Session starting confirmation
previewapk_stopdevice?Stopped confirmation
previewapk_get_linksdevice?{status, driver, driver_changed_at, driver_note, handoff, finish_report, session_url}
previewapk_tapx, y (0–1 fractions), device?OK / driver-gating error
previewapk_swipex1, y1, x2, y2, ms?, device?OK
previewapk_press_keycode (Android keyevent), device?OK
previewapk_type_texttext, device?OK — fails honestly when no field is focused
previewapk_relaunchdevice?Bring the app back to the foreground (about 2s, no reboot)
previewapk_screenshotdevice?An image of the current screen (PNG content block, the real screen) — the agent can look at it
previewapk_get_logssince_seconds?, lines?, errors_only?, contains?, device?The app's logcat for this session (or the saved tail after it ends) — check after actions; a crash's exact reason lives here
previewapk_hierarchydevice?The screen as an element tree (text outline) — navigate and assert without image tokens
previewapk_tap_texttext, index?, device?Tap the element with that label (text or content-desc), resolved by the hierarchy
previewapk_handoffdriver (agent|user), device?New driver confirmation
previewapk_observescreenshot?, scale?, device?A compact numbered snapshot of the screen (snapshot_id, foreground app, keyboard state, elements with refs) — the cheap default read. With screenshot: true, the frame comes back as an image too
previewapk_actactions (max 20), observe?, device?Run a batch of actions in order; stops at the first failure and returns per-action results. Never replay a batch whose outcome you don't know
previewapk_wait_forcondition? (element|driver), text?, desc?, id?, present?, timeout_s?, device?Wait until an element is present (or gone). {found, waited_ms} — found: false at the timeout is a result, not an error. With condition: "driver": wait until the human hands the wheel back (default timeout 300s) — the result carries the handback note
previewapk_request_humanreason, device?Ask the human for a step only a human should do (password, 2FA, payment, CAPTCHA). Returns message_for_human — relay it verbatim, then wait with previewapk_wait_for condition driver
previewapk_finishsummary, verdict? (passed|failed|mixed), end_session? (default false), device?File the session wrap-up; returns message_for_human with the server's own counts (duration, actions, errors) — relay it verbatim. The report is stored on the session and its archive
previewapk_feedbackworked?, broke?, wish?, workaround?, rating?, device?File a field report — read and tallied, and they set the roadmap. Refused when the account opted out of diagnostic sharing
previewapk_record_startname?, device?Start recording the screen (MP4; one recording caps at 180s)
previewapk_record_stopdevice?Stop the recording; the MP4 is pulled to the server and indexed (rec_id, size, duration)
previewapk_record_downloadpath (local file), rec_id?, device?Download a finished recording to a local file — resumable; recordings stay downloadable after the session ends. Hosted MCP: returns data_b64 instead of writing a file
previewapk_deviceaction: set_timezone (tz) · set_locale (locale, package?) · set_location (lat, lon) · clear_location · push_file / pull_file (local_path, remote_path; 10 MB cap) · list_files · delete_file · rebootDevice controls for realistic QA: timezone, per-app language, mock GPS fix, fixture files in shared storage, and a data-preserving phone restart. No clipboard action — not available on these phones

The workflow that makes this worthwhile: push → start (agent-driven) → screenshot → fix what looks wrong → rebuild → handoff to user → present the session link from previewapk_get_links. The agent verifies its own UI work against real pixels before a human ever sees it. The agent guide has the full briefing to give your agent.