app · Rust · Playwright · MCP · Google Play first

app

app is a command-line tool and MCP server for getting Android apps to testers through Google Play. It registers the package, creates the Play Console listing, sets up internal testers, fetches their invitation links and installs builds on your devices, using Google's APIs where they exist and a signed-in headless browser where they don't.

app android create --name MyApp --package com.example.myapp -f phone,wear --dry-run
MyApp (com.example.myapp)  [dry run]
  · register         planned
  · create           planned
  · testers:phone    planned
  · testers:wear     planned
app android invitation --name MyApp        # tester links, after a release
app android accept-invitation --name MyApp && app android install --name MyApp

How each step is done

Google offers APIs for some of this and not for the rest: there is no API to create a Play app, and the testers API only takes Google Groups on closed tracks. So app uses the Android Developer Console and Play Developer APIs where it can, and a headless Chromium session that you sign in to once for the rest. Each browser flow is local to one function, so when Play's UI changes, the fix is in one place.

From nothing to installed The steps app android create and its companions take, and how each is done. Registering the package and signing key uses the Android Developer Console API. Creating the app has no API, so it is done in Play Console through a headless browser session you signed in to once, as are the internal testers' email lists. Release readiness comes from the Play Developer API. The tester invitation link is read from Play Console and cached per form factor, accepting the invitation uses Play's opt-in page, and installing on your devices uses the Play Store's device picker; the store link is derived. Every step is recorded per app, so a rerun skips what is done and resumes from a failure. registerpackage + key create appPlay Console testersper form factor release ready?internal track invitationlink, cached acceptPlay opt-in installyour devices store linkderived official API signed-in headless browser derived
Resumable and idempotent. Each step is recorded per app; a rerun skips finished steps, adopts a Play app that already exists, and adding a form factor later runs only its new testers step.

Credentials

The APIs use an OAuth refresh credential from a desktop OAuth client you create once; the browser steps use a Playwright storage state saved after you sign in to Play Console in a real browser. Both are plain files, so they can live in a secret store, and both can be copied to another machine over SSH without being printed. Access tokens and refreshed cookies go to a cache, so the source files never change in use.

When something is missing, app says what to run next:

app android auth status
credentials  file ~/.config/app/android-auth.json (missing)
error        Android OAuth credentials not found
next:
  app android auth login --client-secret <desktop-oauth-client.json>

Real output from a fresh configuration, with its paths shortened.

Exit codes
0success
2invalid input
3configuration, including an API not enabled
4authorisation needed; the next command is printed
5not found
6not ready, such as no completed release
7busy: another command is updating this app
8a Google API or HTTP error
9a browser flow failed; screenshot and accessibility snapshot paths are printed
10 · 11unsupported · timeout

MCP

app mcp stdio exposes the same operations, from android_create to android_install. Tools that change Play state need confirmed: true unless they are a dry run, and the app-android agent skill teaches the workflow: create the app before its first internal release, then fetch the tester link and install. Configuration (account, signing key, tester lists, devices) is declarative YAML, kept apart from the secrets so a managed config in a Git checkout never attracts them.