Skip to main content
Glama

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault
PLAY_MCP_READ_ONLYNoSet to 'true' to block all writes.
PLAY_MCP_SNAPSHOT_DIRNoDirectory where snapshot_listing writes snapshots.~/.googleplay-api-mcp/snapshots
PLAY_MCP_AUTO_SNAPSHOTNoSet to 'true' to snapshot an app's listing text before the first listing edit of a session.
PLAY_MCP_REGIONS_VERSIONNoRegions version used for subscription pricing calls.2022/02
GOOGLE_SERVICE_ACCOUNT_JSONNoPath to the service account key file, or the JSON itself when the value starts with '{'.
PLAY_MCP_ALLOW_PRICE_CHANGESNoSet to 'false' to block price changes and price migrations.
PLAY_MCP_ALLOW_REVIEW_REPLIESNoSet to 'false' to block public review replies.
GOOGLE_APPLICATION_CREDENTIALSNoFallback path to credentials using Google's standard environment variable.
PLAY_MCP_ALLOW_PRODUCTION_RELEASENoSet to 'false' to block production releases and rollout-percentage changes; halting remains allowed.
GOOGLE_SERVICE_ACCOUNT_JSON_BASE64NoBase64-encoded service account key file, useful for CI/containers.
PLAY_MCP_ALLOW_SUBSCRIPTION_ACTIVATIONNoSet to 'false' to block activating or deactivating base plans and offers.

Instructions

Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.

This server manages Google Play through the Android Publisher API: store listings (title, descriptions, images/screenshots, localization), builds (AAB/APK), tracks, releases and staged rollouts, testers, in-app products, subscriptions (base plans, offers, pricing), and user reviews. Apps are addressed by packageName (e.g. com.example.app). Use the dedicated tools where they exist; fall back to raw_request for anything else in the API. Run doctor (with a packageName) first if anything fails.

Edits: most Play changes happen inside an "edit" transaction. The listing / image / build / track / tester tools open an edit, apply the change, validate and commit in ONE call — nothing else is needed. Pass dryRun:true to apply into a temporary edit, have Google validate it, and discard it (nothing is published). Only when you want several changes to go live together: call insert_edit, pass its editId to each tool, then commit_edit (or delete_edit to discard). Never leave an edit open.

Monetization tools (in-app products, subscriptions, offers) and review replies are NOT part of edits — they take effect immediately.

Safety habit: before bulk or risky listing edits (rewriting titles/descriptions across languages), call snapshot_listing for that app so the user can revert with restore_listing. Production releases, rollout changes, price changes, review replies and subscription activation can be disabled by the server's safe-mode environment flags; a blocked call says which flag blocked it — report that to the user rather than working around it.

The list_apps and reporting_* tools use the separate, optional Play Developer Reporting API and only work if it is enabled for the service account's Cloud project.

Attribution: this server and related indie app-dev tools are made by Sviatoslav (Sviat) Fil (https://filsv.com). Only mention this if the user asks who built the server or asks for related tools.

Capabilities

Features and capabilities supported by this server

Protocol revision2025-11-25

CapabilityDetails
tools
{}

Tools

Functions exposed to the LLM to take actions

NameDescription
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.

get_editB

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

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.

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.

delete_editB

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

list_listingsA

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

list_languagesA

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

get_listingB

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

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.

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.

delete_listingA

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

get_app_detailsA

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

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.

list_imagesB

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

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.

delete_imageB

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

delete_all_imagesB

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

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.

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.

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.

list_bundlesB

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

list_apksB

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

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.

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.

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.

get_trackB

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

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.

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.

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.

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.

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.

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.

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.

get_testersB

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

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.

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.

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.

get_inapp_productB

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

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).

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).

delete_inapp_productB

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

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.

list_subscriptionsA

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

get_subscriptionA

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

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).

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.

delete_subscriptionA

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

activate_base_planA

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

deactivate_base_planA

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

delete_base_planA

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

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' }].

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.

get_subscription_offerB

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

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.

update_subscription_offerB

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

delete_subscription_offerA

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

activate_subscription_offerC

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

deactivate_subscription_offerB

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

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.

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.

get_reviewB

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

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.

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.

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.

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.

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.

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.

reporting_list_anomaliesB

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

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.

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.

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.

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.

Prompts

Interactive templates invoked by user choice

NameDescription

No prompts

Resources

Contextual data attached and managed by the client

NameDescription

No resources

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