Skip to main content
Glama

Google Play MCP

An MCP server for Google Play — the Android sibling of appstore-api-mcp. It lets an AI agent ship and manage Play apps through the official Android Publisher API: upload builds, manage tracks and staged rollouts, edit store listings and screenshots, manage in-app products and subscriptions, and read and reply to reviews.

Status: 0.1.0, not yet verified against the live API. Every endpoint path and parameter comes from Google's published API discovery document and the logic is unit-tested with a mocked network, but no tool has been run against a real Play Console account yet. Start with doctor and dryRun, and see what's verified.

✨ What makes it stand out

  • Edits handled for you. Google Play changes happen inside an "edit" transaction. Each tool opens one, applies the change, validates and commits — and deletes it if anything fails. No dangling edits.

  • Real dry-run. dryRun: true applies your change in a temporary edit, has Google validate it, then throws the edit away. You see the diff and the verdict; nothing is published.

  • Batch when you want to. Open one edit, upload a build, create the release and swap screenshots in it, commit once — all live together.

  • Safe mode enforced by the server. Read-only mode, plus separate switches for production releases, price changes, review replies and subscription activation.

  • Full coverage. A raw_request escape hatch reaches anything without a dedicated tool — still subject to safe mode.

💬 Just ask

"Upload app-release.aab and roll it out to 10% of production with these release notes." "Bump the production rollout to 50%." · "Halt the rollout." "Rewrite the short description in all 12 languages — show me the diff first." "Replace the German phone screenshots with the files in ./shots/de." "What are this week's 1- and 2-star reviews? Draft replies." "Create a monthly subscription at $4.99 and convert the price for every region."

Table of contents

Related MCP server: play-console-mcp

Quick start

You need Node.js ≥ 18 and a Google Cloud service account key that has been granted access in Play Console (how).

Add this to your MCP client's config:

{
  "mcpServers": {
    "googleplay-api": {
      "command": "npx",
      "args": ["-y", "googleplay-api-mcp"],
      "env": {
        "GOOGLE_SERVICE_ACCOUNT_JSON": "/absolute/path/to/service-account.json"
      }
    }
  }
}

Or, with Claude Code:

# --scope user installs it for ALL your projects (recommended).
# Remove the --scope user line to install for the current project only.
claude mcp add googleplay-api \
  --scope user \
  --env GOOGLE_SERVICE_ACCOUNT_JSON="$HOME/.googleplay/service-account.json" \
  -- npx -y googleplay-api-mcp

Then ask: "Run the Google Play doctor for com.example.app."

Per-client instructions (Claude Desktop, Codex, Cursor, Windsurf, VS Code, Zed, Gemini CLI, …) are in docs/CLIENTS.md.

Getting your service account key

The short version — full walkthrough in docs/SETUP.md:

  1. Google Cloud Console → pick or create a project → enable the Google Play Android Developer API.

  2. IAM & Admin → Service Accounts → create one → Keys → Add key → JSON.

  3. Play Console → Users and permissions → Invite new users → paste the service account's email → grant it the apps and permissions it needs.

Step 3 is the one people miss: until the service account is invited in Play Console, every call returns 403.

Configuration

All configuration is by environment variable. Credentials are never accepted as tool arguments.

Variable

Purpose

GOOGLE_SERVICE_ACCOUNT_JSON

Path to the service account key file — or the JSON itself (inline when the value starts with {)

GOOGLE_SERVICE_ACCOUNT_JSON_BASE64

Base64 of the key file (handy for CI/containers)

GOOGLE_APPLICATION_CREDENTIALS

Fallback path (Google's standard variable)

PLAY_MCP_SNAPSHOT_DIR

Where snapshot_listing writes (default ~/.googleplay-api-mcp/snapshots)

PLAY_MCP_AUTO_SNAPSHOT

true = snapshot an app's listing text before the first listing edit of a session

PLAY_MCP_REGIONS_VERSION

Regions version for subscription pricing calls (default 2022/02)

Safe mode

Enforced by the server before a tool runs — not a suggestion to the model.

Variable

Effect

PLAY_MCP_READ_ONLY=true

Block all writes

PLAY_MCP_ALLOW_PRODUCTION_RELEASE=false

Block production releases and rollout-percentage changes (halting stays allowed)

PLAY_MCP_ALLOW_PRICE_CHANGES=false

Block price changes and price migrations

PLAY_MCP_ALLOW_REVIEW_REPLIES=false

Block public review replies

PLAY_MCP_ALLOW_SUBSCRIPTION_ACTIVATION=false

Block activating/deactivating base plans and offers

The four category flags default to allowed; set them to false to lock a category. A blocked call fails with a message naming the flag. Details in docs/TOOLS.md.

Tools

72 tools. Full reference with parameters: docs/TOOLS.md.

Group

Tools

Edits

insert_edit get_edit validate_edit commit_edit delete_edit

Store listing

list_listings list_languages get_listing update_listing bulk_upsert_listings delete_listing get_app_details update_app_details

Images

list_images upload_image delete_image delete_all_images replace_images

Builds

upload_bundle upload_apk list_bundles list_apks upload_deobfuscation_file upload_internal_app_sharing

Tracks & releases

list_tracks get_track create_release promote_release set_rollout_fraction halt_rollout resume_rollout complete_rollout update_track

Testers & availability

get_testers update_testers get_country_availability

In-app products

list_inapp_products get_inapp_product create_inapp_product update_inapp_product delete_inapp_product list_one_time_products

Subscriptions

list_subscriptions get_subscription create_subscription update_subscription delete_subscription activate_base_plan deactivate_base_plan delete_base_plan migrate_base_plan_prices list_subscription_offers get_subscription_offer create_subscription_offer update_subscription_offer delete_subscription_offer activate_subscription_offer deactivate_subscription_offer convert_region_prices

Reviews

list_reviews get_review reply_to_review

Snapshots

snapshot_listing restore_listing

Reporting (optional API)

list_apps reporting_get_metric_set reporting_query_metric_set reporting_list_anomalies reporting_search_error_issues reporting_request

Diagnostics

doctor raw_request

Coming from the App Store server?

App Store Connect

Google Play

App identifier

numeric appId

packageName (com.example.app)

Credentials

ASC_KEY_ID / ASC_ISSUER_ID / .p8

one service account .json

Safe-mode prefix

APPSTORE_MCP_*

PLAY_MCP_*

Applying changes

each call is live

changes go through an edit; tools auto-commit

Preview

dryRun shows a diff

dryRun shows a diff and Google validates it

Phased release

set_phased_release

create_release with userFraction, then set_rollout_fraction / halt_rollout / complete_rollout

Keywords field

yes

none — Play indexes the title and descriptions

Common workflows

Ship a build

upload_bundle   { packageName, filePath: "/path/app-release.aab" }      → versionCode 42
create_release  { packageName, track: "internal", versionCodes: [42], status: "completed" }
promote_release { packageName, fromTrack: "internal", toTrack: "production", userFraction: 0.1 }
set_rollout_fraction { packageName, userFraction: 0.5 }
complete_rollout     { packageName }

Something wrong? halt_rollout { packageName }.

To upload and release atomically, wrap them in one edit: insert_edit, pass editId to upload_bundle and create_release, then commit_edit.

Edit the listing

update_listing { packageName, language: "en-US", shortDescription: "…", dryRun: true }

Review the diff and Google's validation, then run it again without dryRun. For many languages at once use bulk_upsert_listings — one commit for all.

Replace screenshots

replace_images { packageName, language: "de-DE", imageType: "phoneScreenshots",
                 files: ["/shots/de/1.png", "/shots/de/2.png"] }

Old ones are removed and new ones added in a single edit, so the listing never shows a half-replaced set.

Before a bulk rewrite

snapshot_listing { packageName } first; restore_listing { file } puts the text back. Or set PLAY_MCP_AUTO_SNAPSHOT=true and it happens automatically.

Reviews

list_reviews { packageName, translationLanguage: "en" }, then reply_to_review { packageName, reviewId, replyText, dryRun: true } to check the reply before it goes public.

Anything else

raw_request { method, path, query?, body? } — path is relative to /androidpublisher/v3, e.g. /applications/com.example.app/orders/GPA.1234.

Field limits

Field

Limit

Title

30

Short description

80

Full description

4000

Release notes (per language)

500

Review reply

~350

Over-limit text is flagged in _warnings and in the dry-run diff; Google rejects it on validate/commit. The listing limits come from Play Console Help rather than the API reference.

What's verified

  • From Google's discovery documents (androidpublisher v3 and playdeveloperreporting v1beta1, revision 2026-11-08): every endpoint path, HTTP method, query parameter, enum and response field the tools use.

  • Unit-tested (npm test, mocked network): service-account auth and token caching, error shaping, retry, paging, uploads, the edit lifecycle including failure cleanup, release/rollout rules, validation limits, safe-mode gating, and the tool registry.

  • Needs live verification: everything, in the sense that no call has hit the real API. The specific open questions — large-bundle uploads, the validate-before-commit step, rollout body shape, regionsVersion, and which Play Console permission each tool needs — are listed in docs/TOOLS.md.

The Reporting API tools are optional and limited to what that API offers: Android vitals, error issues and anomalies. It has no installs, ratings or revenue endpoints.

Security

The service account key can change your live store presence. Use a dedicated service account with the fewest permissions that work, keep the key file out of any repository, and turn on safe mode where you can. Credentials are read only from the environment, sent only to Google, and never logged. Full notes: docs/SECURITY.md.

Troubleshooting

Run doctor with a packageName first. The usual suspects:

  • 403 on everything — the service account hasn't been invited in Play Console → Users and permissions (or the grant hasn't propagated yet).

  • Server won't start — your client is launching Node < 18.

  • list_reviews looks empty — the API only returns reviews from about the last week, and only those with text.

  • Commit fails mentioning changesNotSentForReview — pass (or remove) that option as the message instructs.

More in docs/SETUP.md.

Development

git clone https://github.com/fil-technology/googleplay-api-mcp.git
cd googleplay-api-mcp
npm install
npm test                                   # node --test, no network, no credentials
GOOGLE_SERVICE_ACCOUNT_JSON=/path/sa.json npm start

Layout: src/index.js (tool registry + server), src/client.js (auth, requests, paging, uploads), src/edits.js (edit lifecycle), src/releases.js (release/rollout rules), src/guardrails.js (safe mode), src/validation.js (limits + diff).

Releasing

Publishing runs in GitHub Actions on a v*.*.* tag, authenticated by npm trusted publishing (OIDC) — there is no NPM_TOKEN anywhere.

One-time setup on npmjs.com (package → Settings → Trusted Publisher → GitHub Actions): organization fil-technology, repository googleplay-api-mcp, workflow npm-publish.yml. npm currently only lets you configure a trusted publisher for a package that already exists, so the very first version may need a manual npm publish --access public from a maintainer's machine; every release after that is tag-driven.

npm version patch          # bumps package.json + creates the tag
git push --follow-tags     # the tag triggers the publish workflow

The workflow refuses to publish if the tag doesn't match package.json, and skips versions already on npm.

License

MIT © Sviatoslav Fil

Available Tools

72 tools
activate_base_planA

Activate a base plan: it becomes purchasable by users. Gated by PLAY_MCP_ALLOW_SUBSCRIPTION_ACTIVATION.

Inputs: productId (required), basePlanId (required), packageName (required), latencyTolerance.

activate_subscription_offerC

Activate a subscription offer: eligible users can now get it. Gated by PLAY_MCP_ALLOW_SUBSCRIPTION_ACTIVATION.

Inputs: offerId (required), productId (required), basePlanId (required), packageName (required), +1 more.

bulk_upsert_listingsA

Create or update store listings for MANY languages in one atomic edit. Pass listings as { 'en-US': { title, shortDescription, fullDescription, video }, 'de-DE': {...} }. Existing languages are patched (only given fields change); new ones are created. All languages go live together or not at all. Set dryRun:true to preview every diff and validate without publishing.

Inputs: listings (required), packageName (required), dryRun, editId, +1 more.

commit_editA

Commit an open edit: every change made in it goes live (or to review) atomically. The editId is consumed. If the edit contains production-track changes they were already checked against safe mode when they were added.

Inputs: editId (required), packageName (required), changesInReviewBehavior, changesNotSentForReview.

complete_rolloutA

Finish a staged rollout: release to 100% of users (status 'completed'). Track defaults to production. Gated on production by PLAY_MCP_ALLOW_PRODUCTION_RELEASE. Set dryRun:true to preview.

Inputs: packageName (required), track, dryRun, editId, +2 more.

convert_region_pricesA

Convert one price into Google's suggested local prices for every region (tax-exclusive input). A calculator only — it changes nothing. Use the output to fill regionalConfigs / prices. price: { currencyCode: 'USD', units: '4', nanos: 990000000 } = $4.99.

Inputs: price (required), packageName (required), productTaxCategoryCode.

create_inapp_productB

Create a managed (one-time) in-app product. product is an InAppProduct: { sku, status: 'active'|'inactive', purchaseType: 'managedUser', defaultLanguage, listings: { 'en-US': { title, description } }, defaultPrice: { priceMicros: '990000', currency: 'USD' } }. autoConvertMissingPrices fills other regions from defaultPrice. Takes effect immediately (no edit).

Inputs: product (required), packageName (required), autoConvertMissingPrices.

create_releaseA

Put build(s) on a track as a release. status: 'draft' (saved, not rolled out), 'completed' (full rollout), 'inProgress' (staged rollout — requires userFraction, e.g. 0.1 = 10%), 'halted'. versionCodes come from upload_bundle / list_bundles. Production releases are gated by PLAY_MCP_ALLOW_PRODUCTION_RELEASE. Set dryRun:true to have Google validate the release without publishing.

Inputs: track (required), status (required), packageName (required), versionCodes (required), +8 more.

create_subscriptionA

Create a subscription. subscription is a Subscription resource: { listings: [{ languageCode, title, benefits?, description? }], basePlans: [{ basePlanId, autoRenewingBasePlanType: { billingPeriodDuration: 'P1M' }, regionalConfigs: [{ regionCode: 'US', price: { currencyCode: 'USD', units: '4', nanos: 990000000 } }] }] }. New base plans start as DRAFT — activate them with activate_base_plan. Takes effect immediately (no edit).

Inputs: productId (required), packageName (required), subscription (required), regionsVersion.

create_subscription_offerA

Create an offer on a base plan. offer is a SubscriptionOffer: { phases: [{ recurrenceCount, duration: 'P1W', regionalConfigs: [{ regionCode, free: {} | price | relativeDiscount | absoluteDiscount }] }], regionalConfigs: [{ regionCode, newSubscriberAvailability: true }], targeting?, offerTags? }. New offers start as DRAFT — activate with activate_subscription_offer.

Inputs: offer (required), offerId (required), productId (required), basePlanId (required), +2 more.

deactivate_base_planA

Deactivate a base plan: no NEW purchases (existing subscribers keep renewing). Gated by PLAY_MCP_ALLOW_SUBSCRIPTION_ACTIVATION.

Inputs: productId (required), basePlanId (required), packageName (required), latencyTolerance.

deactivate_subscription_offerB

Deactivate a subscription offer: no longer offered to new users. Gated by PLAY_MCP_ALLOW_SUBSCRIPTION_ACTIVATION.

Inputs: offerId (required), productId (required), basePlanId (required), packageName (required), +1 more.

delete_all_imagesB

Delete ALL images of one type for one language. Set dryRun:true to validate without publishing.

Inputs: language (required), imageType (required), packageName (required), dryRun, +2 more.

delete_base_planA

Delete a base plan. Only draft base plans (never activated) can be deleted. Irreversible.

Inputs: productId (required), basePlanId (required), packageName (required).

delete_editB

Discard an open edit and every uncommitted change in it. Nothing is published.

Inputs: editId (required), packageName (required).

delete_imageB

Delete one image by id (from list_images). Set dryRun:true to validate without publishing.

Inputs: imageId (required), language (required), imageType (required), packageName (required), +3 more.

delete_inapp_productB

Delete an in-app product by sku. Irreversible; takes effect immediately.

Inputs: sku (required), packageName (required).

delete_listingA

Delete one language's store listing (the default language cannot be deleted). Set dryRun:true to validate without publishing.

Inputs: language (required), packageName (required), dryRun, editId, +1 more.

delete_subscriptionA

Delete a subscription. Only possible if it never had a base plan published. Irreversible.

Inputs: productId (required), packageName (required).

delete_subscription_offerA

Delete a subscription offer. Only draft offers (never activated) can be deleted. Irreversible.

Inputs: offerId (required), productId (required), basePlanId (required), packageName (required).

doctorA

Diagnose the setup: Node version, credentials present and parseable, whether Google issues an access token, whether the service account can actually reach a given app (pass packageName), whether the optional Reporting API is reachable, and the current safe-mode write settings. Changes nothing (the access probe opens and deletes an empty edit). Run this first if something isn't working.

Inputs: packageName.

get_app_detailsA

Read app-level details: defaultLanguage and the public contact email / phone / website. Read-only.

Inputs: packageName (required), editId.

get_country_availabilityA

Read which countries a track is available in (and whether it syncs with production). Read-only — availability itself is changed in Play Console.

Inputs: track (required), packageName (required), editId.

get_editB

Check that an edit is still open/valid and when it expires. Read-only.

Inputs: editId (required), packageName (required).

get_inapp_productB

Read one in-app product by sku. Read-only.

Inputs: sku (required), packageName (required).

get_listingB

Read one language's store listing. Read-only.

Inputs: language (required), packageName (required), editId.

get_reviewB

Read one review by id, including the developer reply if any. Read-only.

Inputs: reviewId (required), packageName (required), translationLanguage.

get_subscriptionA

Read one subscription (base plans, prices, listings, tax settings). Read-only.

Inputs: productId (required), packageName (required).

get_subscription_offerB

Read one subscription offer (phases, regional prices, eligibility, state). Read-only.

Inputs: offerId (required), productId (required), basePlanId (required), packageName (required).

get_testersB

Read the tester Google Groups for a testing track (internal, alpha, beta or custom). Read-only.

Inputs: track (required), packageName (required), editId.

get_trackB

Read one track and its releases (status, versionCodes, userFraction, notes). Read-only.

Inputs: track (required), packageName (required), editId.

halt_rolloutA

Halt a staged rollout: stop serving the staged release to new users (existing installs keep it). The emergency brake — it is NOT blocked by PLAY_MCP_ALLOW_PRODUCTION_RELEASE=false (only by read-only mode). Track defaults to production.

Inputs: packageName (required), track, dryRun, editId, +2 more.

insert_editA

Open a new edit (Google Play's transaction) for an app and return its editId. Use this only to BATCH several changes: pass the editId to the mutation tools, then call commit_edit once. Most tools open, validate and commit their own edit automatically, so you rarely need this. Always finish with commit_edit or delete_edit.

Inputs: packageName (required).

list_apksB

List the APKs uploaded for an app (versionCode, binary hashes). Read-only.

Inputs: packageName (required), editId.

list_appsA

List the apps this service account can see (packageName + display name). Uses the OPTIONAL Play Developer Reporting API — the Publisher API has no 'list apps' call — so it needs that API enabled in the Cloud project and 'View app information' access. Read-only.

Inputs: pageSize.

list_bundlesB

List the app bundles uploaded for an app (versionCode, sha1, sha256). Read-only.

Inputs: packageName (required), editId.

list_imagesB

List the images of one type for one language (id, sha256, preview url). Omit imageType to list every type. Read-only.

Inputs: language (required), packageName (required), editId, imageType.

list_inapp_productsA

List managed (one-time) in-app products via the legacy inappproducts API: sku, status, listings, defaultPrice, per-region prices. Read-only. Subscriptions are under list_subscriptions.

Inputs: packageName (required).

list_languagesA

List the languages an app's store listing is localized into, plus the default language. Read-only.

Inputs: packageName (required), editId.

list_listingsA

List the store listing (title, shortDescription, fullDescription, video) for every language of an app. Read-only.

Inputs: packageName (required), editId.

list_one_time_productsA

List one-time products via the NEW monetization.onetimeproducts API (purchase options, offers, regional pricing). Read-only; write them with raw_request until dedicated tools exist.

Inputs: packageName (required).

list_reviewsA

List recent user reviews (rating, text, device, app version, any developer reply). NOTE: the API only returns reviews created or modified in roughly the last week, and only reviews that have a text comment. translationLanguage (e.g. 'en') returns translated text. Read-only.

Inputs: packageName (required), token, allPages, maxResults, +1 more.

list_subscription_offersA

List the offers (free trials, intro prices, upgrades) under a base plan. Pass basePlanId '-' to list offers across all base plans of the subscription. Read-only.

Inputs: productId (required), packageName (required), basePlanId.

list_subscriptionsA

List subscriptions with their base plans (billing period, state, regional prices) and listings. Read-only.

Inputs: packageName (required).

list_tracksA

List every track of an app (internal, alpha, beta, production, custom and form-factor tracks) with their releases: status, versionCodes, userFraction, release notes. Read-only.

Inputs: packageName (required), editId.

migrate_base_plan_pricesA

Move EXISTING subscribers on legacy price cohorts to the base plan's current price in the given regions. This is a price change for paying users (Google notifies them; increases may need opt-in). Gated by PLAY_MCP_ALLOW_PRICE_CHANGES. regionalPriceMigrations: [{ regionCode: 'US', oldestAllowedPriceVersionTime: '2024-01-01T00:00:00Z', priceIncreaseType?: 'PRICE_INCREASE_TYPE_OPT_IN'|'PRICE_INCREASE_TYPE_OPT_OUT' }].

Inputs: productId (required), basePlanId (required), packageName (required), regionalPriceMigrations (required), +2 more.

promote_releaseA

Promote a build from one track to another (e.g. internal → beta, beta → production), carrying its versionCodes and release notes. By default promotes the newest release on fromTrack as a full rollout; pass status:'inProgress' + userFraction for a staged rollout, or 'draft' to stage it. Promoting to production is gated by PLAY_MCP_ALLOW_PRODUCTION_RELEASE. Set dryRun:true to validate without publishing.

Inputs: toTrack (required), fromTrack (required), packageName (required), dryRun, +6 more.

raw_requestA

Make a raw Google Play Android Publisher API call — use this for ANY endpoint not covered by a dedicated tool (orders, purchases, voided purchases, one-time product writes, device tier configs, app recovery, users/grants, data safety, etc.). path is relative (e.g. '/applications/{packageName}/reviews') and '/androidpublisher/v3' is added automatically. Edit-scoped paths need an editId from insert_edit — remember to commit_edit or delete_edit. Non-GET calls obey safe mode. See developers.google.com/android-publisher/api-ref/rest.

Inputs: path (required), method (required), body, query.

replace_imagesA

Replace the whole set of images of one type for one language: delete the existing ones and upload files in the given order — atomically, in one edit, so the listing never shows a half-replaced set. Set dryRun:true to upload into a temporary edit, validate and discard.

Inputs: files (required), language (required), imageType (required), packageName (required), +3 more.

reply_to_reviewA

Post (or replace) the PUBLIC developer reply to a review. Visible to everyone on Google Play. Max ~350 characters; HTML is stripped. Gated by PLAY_MCP_ALLOW_REVIEW_REPLIES. Set dryRun:true to see the review, any existing reply and length check without posting.

Inputs: reviewId (required), replyText (required), packageName (required), dryRun.

reporting_get_metric_setA

Describe a vitals metric set: its data freshness (latest available end time per aggregation period). Call before reporting_query_metric_set to pick a valid time range. OPTIONAL Reporting API. Read-only.

Inputs: metricSet (required), packageName (required).

reporting_list_anomaliesB

List anomalies Google detected in an app's vitals (sudden crash/ANR-rate changes). OPTIONAL Reporting API. Read-only.

Inputs: packageName (required), filter.

reporting_query_metric_setA

Query an Android vitals metric set from the OPTIONAL Play Developer Reporting API (crash rate, ANR rate, error counts, slow start/rendering, wakeups, wakelocks, LMK). query is the request body: { timelineSpec: { aggregationPeriod: 'DAILY', startTime: { year, month, day, timeZone: { id: 'America/Los_Angeles' } }, endTime: {...} }, metrics: ['crashRate', 'distinctUsers'], dimensions: ['versionCode'], filter?, pageSize? }. Use reporting_get_metric_set first to see the freshness window. Read-only.

Inputs: query (required), metricSet (required), packageName (required).

reporting_requestA

Raw call to the OPTIONAL Play Developer Reporting API (playdeveloperreporting.googleapis.com, v1beta1) for anything not covered above, e.g. '/apps/{packageName}/errorReports:search'. path is relative to /v1beta1. The Reporting API is read-only, so this is allowed in read-only mode.

Inputs: path (required), method (required), body, query.

reporting_search_error_issuesA

Search grouped crash / ANR issues for an app (cause, location, affected users, first/last version). filter e.g. 'errorIssueType = CRASH'; orderBy e.g. 'errorReportCount desc'. OPTIONAL Reporting API. Read-only.

Inputs: packageName (required), filter, orderBy, pageSize.

restore_listingA

Restore listing text (and optionally app details) from a snapshot_listing file, in one atomic edit. Languages added since the snapshot are left alone. Images are not restored. Set dryRun:true to see what would change and validate without publishing.

Inputs: file (required), dryRun, editId, languages, +3 more.

resume_rolloutA

Resume a halted rollout at its previous fraction (or a new userFraction). Track defaults to production. Gated on production by PLAY_MCP_ALLOW_PRODUCTION_RELEASE.

Inputs: packageName (required), track, dryRun, editId, +3 more.

set_rollout_fractionA

Change the percentage of a staged rollout (e.g. 0.05 → 0.2). Finds the staged release on the track (default production); userFraction is strictly between 0 and 1 — use complete_rollout for 100%. Gated on production by PLAY_MCP_ALLOW_PRODUCTION_RELEASE. Set dryRun:true to preview before/after.

Inputs: packageName (required), userFraction (required), track, dryRun, +3 more.

snapshot_listingA

Save a timestamped JSON snapshot of an app's editable TEXT metadata — app details plus title / short & full description / video for every language — so bulk edits can be reverted with restore_listing. includeImages:true also records each image's id, hashes and preview URL (a record only; image files are not downloaded). Returns the snapshot file path. Read-only on Google Play.

Inputs: packageName (required), label, includeImages.

update_app_detailsA

Update app-level details: defaultLanguage, contactEmail, contactPhone, contactWebsite. Only include the fields you want to change. Set dryRun:true to preview the diff without publishing.

Inputs: packageName (required), dryRun, editId, contactEmail, +4 more.

update_inapp_productA

Patch an in-app product: only the fields in product change (status, listings, defaultPrice, prices, ...). Changing defaultPrice/prices is a PRICE CHANGE and is gated by PLAY_MCP_ALLOW_PRICE_CHANGES. Set dryRun:true to see the before/after diff without writing. Takes effect immediately (no edit).

Inputs: sku (required), product (required), packageName (required), dryRun, +2 more.

update_listingA

Update the store listing TITLE, SHORT DESCRIPTION, FULL DESCRIPTION or promo video for one language (creates the language if it doesn't exist yet). Only include the fields you want to change. Opens an edit, applies, validates and commits in one call. Set dryRun:true to preview the diff (old→new + length checks) and have Google validate it without publishing anything.

Inputs: language (required), packageName (required), title, video, +5 more.

update_subscriptionA

Patch a subscription. updateMask lists the fields to replace (e.g. 'listings' or 'basePlans'); subscription carries their new values. Updating basePlans changes PRICES for NEW subscribers and is gated by PLAY_MCP_ALLOW_PRICE_CHANGES (existing subscribers move only via migrate_base_plan_prices). Set dryRun:true to see current vs proposed without writing.

Inputs: productId (required), updateMask (required), packageName (required), subscription (required), +3 more.

update_subscription_offerB

Patch a subscription offer. updateMask lists the fields to replace (e.g. 'phases', 'regionalConfigs', 'offerTags'); offer carries the new values.

Inputs: offer (required), offerId (required), productId (required), basePlanId (required), +4 more.

update_testersA

Set the tester Google Groups (group email addresses) for a testing track. This REPLACES the list. Individual tester emails are managed in Play Console, not through the API. Set dryRun:true to validate without publishing.

Inputs: track (required), packageName (required), googleGroups (required), dryRun, +2 more.

update_trackA

Low-level: replace a track's releases array with exactly what you pass (TrackRelease objects). Prefer create_release / promote_release / the rollout tools; use this for unusual shapes. Production tracks are gated by PLAY_MCP_ALLOW_PRODUCTION_RELEASE. Set dryRun:true to validate without publishing.

Inputs: track (required), releases (required), packageName (required), dryRun, +2 more.

upload_apkA

Upload an APK (.apk). Returns its versionCode for create_release. New apps must use bundles (upload_bundle); APK upload is for apps that still ship APKs. Set dryRun:true to validate and discard.

Inputs: filePath (required), packageName (required), dryRun, editId, +1 more.

upload_bundleA

Upload an Android App Bundle (.aab). Returns its versionCode — pass that to create_release to put it on a track. By default the upload is committed on its own (the bundle then sits in the app's library, unreleased); pass an editId to upload and release in the same edit. Set dryRun:true to have Google validate the bundle and discard it.

Inputs: filePath (required), packageName (required), dryRun, editId, +3 more.

upload_deobfuscation_fileB

Upload crash-symbolication files for a build: an R8/ProGuard mapping.txt (fileType 'proguard') or a native debug symbols .zip (fileType 'nativeCode'). versionCode is the build the file belongs to.

Inputs: filePath (required), fileType (required), packageName (required), versionCode (required), +3 more.

upload_imageA

Upload one PNG/JPEG image (icon, featureGraphic, tvBanner or a screenshot) for a language. Screenshots are appended; icon / featureGraphic / tvBanner hold a single image and are replaced. filePath is a local file. Set dryRun:true to validate without publishing.

Inputs: filePath (required), language (required), imageType (required), packageName (required), +3 more.

upload_internal_app_sharingA

Upload an .aab or .apk to Internal App Sharing and get a download URL for authorized testers. No edit, no review, no track — the fastest way to hand someone a build.

Inputs: filePath (required), packageName (required).

validate_editA

Validate an open edit WITHOUT committing — Google runs the same checks as a commit and reports any problem. The Play-native dry run for a batched session.

Inputs: editId (required), packageName (required).

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.5/5.0

    Scored across 72 tools

    Disambiguation4/5

    Despite 72 tools, the descriptions draw clear boundaries between edit lifecycle, listing, image, release, subscription, offer, base plan, review, and reporting operations. Some overlap remains between high-level release tools and low-level update_track, and between dedicated tools and raw_request, but the guidance mitigates most misselection risk.

    Naming Consistency4/5

    Predominantly uses a consistent verb_noun snake_case pattern such as list_listings, create_release, delete_image, and update_track, with coherent domain prefixes for reporting tools. Minor exceptions like raw_request and doctor, plus bulk_upsert_listings, prevent a perfect score.

    Tool Count2/5

    72 tools is far above the typical 3-15 range and even beyond the 16-25 heavy range, creating a very large selection surface for an agent. Although the Google Play API is broad, many dedicated tools duplicate functionality that is also available through raw_request.

    Completeness5/5

    The surface covers the major Play Publisher lifecycle: edits, listings, images, bundles/APKs, tracks/releases/rollouts, testers, subscriptions, base plans, offers, in-app products, reviews, and reporting. raw_request and reporting_request provide fallback coverage for endpoints not given dedicated tools, so there are no severe dead ends.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      A
      quality
      B
      maintenance
      Enables AI agents to manage Google Play Console reviews and Android app releases, including replying to reviews, promoting releases, managing staged rollouts, and checking crash/ANR vitals.
      44
      46 npm
      2
      MIT
    • F
      license
      B
      quality
      C
      maintenance
      Enables AI agents to manage Google Play apps with scope-based permissions, including publishing internal releases, updating listings, replying to reviews, and promoting to production.
      11
      8 npm
      -