mcpField
G
gpc-sdk-usage

gpc-sdk-usage

github.com/yasserstudio開発137 回インストール1 週間前に更新
リポジトリ

gpc-sdk-usage について

gpc-sdk-usageは、TypeScript開発者向けに@ gpc-cli/apiと@gpc-cli/authを使ったGoogle Playへのプログラムアクセスを案内するスキルです。認証やクライアント作成に加え、公開や運用の一般的な操作を扱います。

主な機能

  1. 01サービスアカウント認証の設定
  2. 02型付きAPIクライアントの作成
  3. 03アプリの編集とリリース管理
  4. 04レビュー、定期購入、購入の処理
  5. 05ページネーションとレート制限

活用シーン

  1. 01TypeScriptサービスからGoogle Playのリリースを管理
  2. 02スクリプトでアプリレビューを取得・返信
  3. 03バックエンドでアプリ内購入を検証
  4. 04既存アプリにPlay操作を組み込む

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/yasserstudio/gpc-skills --skill gpc-sdk-usage

インストール後、Claude Code で/gpc-sdk-usageを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

evals/
evals.json
references/
api-reference.md
auth-patterns.md
scripts/
detect_gpc.mjs

詳細

gpc-sdk-usage

@ gpc-cli/api と @gpc-cli/auth をスタンドアロンの TypeScript SDK パッケージとして使用し、Google Play にプログラムからアクセスします。

使用する場面

  • Google Play と連携するバックエンドサービスを構築する場合
  • カスタムダッシュボードや自動化スクリプトを作成する場合
  • TypeScript/JavaScript からプログラムでリリースを管理する場合
  • 型付き API クライアントを直接使用する場合(CLI 経由ではなく)
  • Google Play の操作をより大きなアプリケーションに統合する場合

必要な入力

  • Node.js 20+ と TypeScript 5+
  • @gpc-cli/api および @gpc-cli/auth パッケージ
  • サービスアカウントキー — JSON ファイルまたは生の JSON 文字列

手順

0. パッケージをインストールする
npm install @gpc-cli/api @gpc-cli/auth

これらはスタンドアロンパッケージのため、CLI 全体をインストールする必要はありません。

1. 認証する
import { resolveAuth } from "@gpc-cli/auth";

// From file path
const auth = await resolveAuth({
  serviceAccountPath: "/path/to/key.json",
});

// From JSON string (e.g., from environment variable)
const auth = await resolveAuth({
  serviceAccountJson: process.env.PLAY_SA_KEY,
});

// From environment (GPC_SERVICE_ACCOUNT or GOOGLE_APPLICATION_CREDENTIALS)
const auth = await resolveAuth();

Read: references/auth-patterns.md で、高度な認証パターンとトークンのキャッシュについて確認してください。

2. API クライアントを作成する
import { createApiClient } from "@gpc-cli/api";

const client = createApiClient({
  auth,
  maxRetries: 3,
  timeout: 30_000,
});

このクライアントを使用すると、Android Publisher v3、Play Developer Reporting v1beta1、Play Custom App Publishing v1(v0.9.56+)、Games Configuration v1configuration(v0.9.86+)の各 API にわたる、Google Play Developer API の全 234 エンドポイントに型付きでアクセスできます。

2a. Enterprise クライアントを作成する(v0.9.56+)

Play Custom App Publishing API を使用して非公開アプリを公開する場合は、別のファクトリーを使用してください。

import { createEnterpriseClient, type CustomApp } from "@gpc-cli/api";

const enterprise = createEnterpriseClient({ auth });

const app: CustomApp = await enterprise.apps.create(
  "1234567890",              // developer account ID (int64, from Play Console URL)
  "./app.aab",                // bundle path
  {
    title: "My Private App",
    languageCode: "en_US",
    organizations: [{ organizationId: "customer-org-id" }],
  },
);

console.log("Assigned package name:", app.packageName);
// com.google.customapp.A1B2C3D4E5 (Google-assigned, you cannot influence)

注意:

  • 非公開アプリは恒久的に非公開です。一度作成すると、公開アプリにはできません。
  • 作成後の操作(バージョンのアップロード、トラック、掲載情報)には、返された packageName を使用して通常の createApiClient() を使います。
  • Play Console で、サービスアカウントに「create and publish private apps」権限が必要です。
  • 基盤となる HttpClient.uploadCustomApp<T>(path, filePath, metadata, contentType) メソッドは、初回のセッション開始 POST で JSON メタデータを送信するマルチパートの再開可能なアップロードを処理します。他の Google API でもこのパターンを再利用する方法については、ResumableUploadOptions.initialMetadata を参照してください。

CLI での同等の操作とセットアップ全体の手順については、gpc-enterprise スキルを参照してください。

Read: references/api-reference.md で、すべての名前空間とメソッドを含むクライアント API の全容を確認してください。

3. 編集のライフサイクル

Google Play の多くの操作では、編集セッションが必要です。

const APP = "com.example.app";

// 1. Create an edit
const edit = await client.edits.insert(APP);

// 2. Make changes within the edit
const tracks = await client.tracks.list(APP, edit.id);
const details = await client.details.get(APP, edit.id);

// 3. Validate before committing
await client.edits.validate(APP, edit.id);

// 4. Commit the edit (applies all changes)
await client.edits.commit(APP, edit.id);

// Optional: commit with options (v0.9.51+)
await client.edits.commit(APP, edit.id, {
  changesNotSentForReview: true,
  changesInReviewBehavior: "HALT_REVIEW",
});

重要: 同時に開ける編集は 1 つだけです。編集は必ずコミットするか削除してください。

4. よく使われるパターン
カスタムのクローズドテストトラックを作成する(v0.9.79+)
// Create a custom closed testing track before uploading to it
const track = await client.edits.tracks.create(packageName, "my-custom-track");

リリースを割り当てる前に、カスタムトラックを作成する必要があります。作成後は、標準の client.tracks.update() 呼び出しを使用して、新しいトラックにリリースを送信します。

リリースをアップロードする
const edit = await client.edits.insert(APP);

// Upload the bundle
const bundle = await client.bundles.upload(APP, edit.id, "app-release.aab");

// Upload with device tier config (v0.9.51+)
const bundle2 = await client.bundles.upload(APP, edit.id, "app-release.aab", {
  deviceTierConfigId: "my-tier-config",
});

// Set the track
await client.tracks.update(APP, edit.id, "beta", {
  track: "beta",
  releases: [{
    versionCodes: [bundle.versionCode],
    status: "completed",
    releaseNotes: [
      { language: "en-US", text: "Bug fixes and improvements" },
    ],
  }],
});

// Commit
await client.edits.validate(APP, edit.id);
await client.edits.commit(APP, edit.id);
レビューを一覧表示して返信する
// No edit needed for reviews
const reviews = await client.reviews.list(APP, {
  maxResults: 50,
  translationLanguage: "en",
  startIndex: 0,  // pagination offset (v0.9.51+)
});

for (const review of reviews.reviews ?? []) {
  if (review.comments?.[0]?.userComment?.starRating <= 2) {
    await client.reviews.reply(APP, review.reviewId, "Thanks for the feedback!");
  }
}
サブスクリプションを管理する
// No edit needed for subscriptions
const subs = await client.subscriptions.list(APP);

// Get a specific subscription
const sub = await client.subscriptions.get(APP, "premium_monthly");

// Update with mutation options (v0.9.51+)
await client.subscriptions.update(APP, "premium_monthly", data, "price", {
  allowMissing: true,
  latencyTolerance: "PRODUCT_UPDATE_LATENCY_TOLERANCE_LATENCY_TOLERANT",
});

// Activate a base plan
await client.subscriptions.activateBasePlan(APP, "premium_monthly", "monthly");
購入を検証する
// No edit needed for purchases
const purchase = await client.purchases.getProduct(APP, "coins_100", purchaseToken);

if (purchase.purchaseState === 0 && purchase.acknowledgementState === 0) {
  await client.purchases.acknowledgeProduct(APP, "coins_100", purchaseToken);
}
難読化解除ファイルをアップロードする(v0.9.51+)
// Upload ProGuard mapping
await client.deobfuscation.upload(APP, edit.id, versionCode, "mapping.txt", "proguard");

// Upload native debug symbols
await client.deobfuscation.upload(APP, edit.id, versionCode, "symbols.zip", "nativeCode");
拡張ファイルを管理する(v0.9.51+)
// Get expansion file info
const obb = await client.expansionFiles.get(APP, edit.id, versionCode, "main");

// Upload a new expansion file
const uploaded = await client.expansionFiles.upload(APP, edit.id, versionCode, "main", "main.obb");

// Patch expansion file references
await client.expansionFiles.patch(APP, edit.id, versionCode, "main", {
  referencesVersion: 10,
});
ページネーションを使用して一回限りの商品を一覧表示する(v0.9.51+)
const products = await client.oneTimeProducts.list(APP, {
  pageSize: 25,
  pageToken: nextToken,
});
5. ページネーション

組み込みのページネーションユーティリティを使用して、大量の結果を処理します。

import { paginate, paginateAll } from "@gpc-cli/api";

// Async generator (stream results)
for await (const page of paginate(
  (token) => client.subscriptions.list(APP, { pageToken: token }),
  { limit: 100 },
)) {
  for (const sub of page) {
    console.log(sub.productId);
  }
}

// Collect all results
const all = await paginateAll(
  (token) => client.subscriptions.list(APP, { pageToken: token }),
);

v0.9.83 の新機能: paginateAll が実際の継続トークンを返すようになりました。そのため、--limit と --next-page を組み合わせると、レビュー、ユーザー、購入、IAP、サブスクリプションの取得を正しく再開できます。すべての CLI の一覧表示コマンドで、統一された { <key>, nextPageToken, meta.count, message? } JSON エンベロープも共有されます(v0.9.87 では grants、testers、tracks にも拡張)。一覧表示コマンドが返す配列を直接読み込んでいるスクリプトは動作しなくなります。キー付きフィールドを分割代入するように更新してください。

v0.9.98: client.users.list() は常に pageSize=-1 を送信します。これは Google が受け付ける唯一の値です(「Pagination is not currently available」)。呼び出し元が指定した pageSize は無視され、型定義でも非推奨です。一覧全体が 1 回のレスポンスで返されます。

6. レート制限

v0.9.47 以降、createApiClient() は Google の 6 バケットモデル(各バケットで 1 分あたり 3,000 クエリ)を使用して、すべての API 呼び出しに自動でレート制限を適用します。手動で設定する必要はありません。

// Rate limiting is automatic — all calls are throttled by resource type
const client = createApiClient({ auth });
// Buckets: edits, purchases, reviews, reporting, monetization, default

レート制限をカスタマイズする場合(たとえば、複数のプロセス間でクォータを共有する場合):

import { createRateLimiter, RATE_LIMIT_BUCKETS } from "@gpc-cli/api";

// Override specific buckets
const limiter = createRateLimiter([
  { ...RATE_LIMIT_BUCKETS.edits, maxTokens: 1500 },     // Half of default
  { ...RATE_LIMIT_BUCKETS.purchases, maxTokens: 1500 },
]);

const client = createApiClient({ auth, rateLimiter: limiter });

resolveBucket(path) 関数は、API パスをバケットに自動で割り当てます。

  • /edits/ パス → edits バケット
  • /purchases/、/orders → purchases バケット
  • /reviews → reviews バケット
  • Reporting API → reporting バケット
  • /subscriptions、/oneTimeProducts、/inappproducts → monetization バケット
  • その他すべて → default バケット
7. エラー処理
import { PlayApiError } from "@gpc-cli/api";
import { AuthError } from "@gpc-cli/auth";

try {
  await client.edits.insert(APP);
} catch (error) {
  if (error instanceof AuthError) {
    console.error(`Auth failed: ${error.code}`);
  } else if (error instanceof PlayApiError) {
    console.error(`API error ${error.status}: ${error.code}`);
    console.error(`Suggestion: ${error.suggestion}`);
  }
}
変更履歴の生成(v0.9.62+)

gpc changelog generate の変更履歴パイプラインは、スタンドアロンの @gpc-cli/core エクスポートとして公開されています。クラスタリング/リント済みのデータ構造を直接利用したい CI ツールに便利です。

import {
  generateChangelog,
  resolveLocales,
  renderPlayStore,
  PLAY_STORE_LIMIT,     // 500
  type LocaleBundle,
  type GeneratedChangelog,
} from "@gpc-cli/core";

const generated: GeneratedChangelog = await generateChangelog({
  from: "v0.9.61",
  to: "HEAD",
});

// GitHub target: three renderers exposed as RENDERERS["md" | "json" | "prompt"]
// Play Store target: resolveLocales + renderPlayStore
const locales = await resolveLocales("en-US,fr-FR,de-DE");
const { output, bundle } = renderPlayStore(generated, {
  locales,
  format: "json",
});

for (const entry of bundle.locales) {
  console.log(`${entry.language}: ${entry.chars}/${entry.limit} (${entry.status})`);
}

--locales auto の場合、resolveLocales の第 2 引数として { client, packageName } を渡します。これにより、client.listings.list が呼び出され、公開中の Play ストア掲載情報からロケールの一覧が推定されます。

リリースノートをドラフトに適用する(v0.9.64+)
import {
  applyReleaseNotes,
  validateBundleForApply,
  bundleToReleaseNotes,
  waitForBundleProcessing,
} from "@gpc-cli/core";

// Convert a LocaleBundle to the API shape
const releaseNotes = bundleToReleaseNotes(bundle);

// Validate (returns blocked locale errors, if any)
const errors = validateBundleForApply(bundle);
if (errors.length > 0) throw new Error(errors.join(", "));

// Write into the latest draft on a track
await applyReleaseNotes(client, "com.example.app", "production", releaseNotes);

// waitForBundleProcessing (v0.9.64+, extended v0.9.77): polls bundles.list
// after AAB upload with Fibonacci backoff (2s, 3s, 5s, 8s, 13s, 21s, 34s ~86s)
// until the uploaded versionCode appears. Fixes large-AAB race.
// v0.9.77 also adds multi-retry guard on validate/commit (15s, 30s, 45s).
await waitForBundleProcessing(client, "com.example.app", editId, versionCode);
設定型の VitalsThresholds(v0.9.82+)

VitalsThresholds が、@gpc-cli/config で公開される型付き設定の一部になりました。

import type { GpcConfig } from "@gpc-cli/config";

const config: GpcConfig = {
  vitals: {
    thresholds: { crashRate: 2.0 },
  },
};

VitalsThresholds は ResolvedConfig(実行時に完全にマージされた形式)にも含まれています。GPC 設定ファイルをプログラムから読み書きするツールの構築にご利用ください。

Orders の OfferPhaseDetails(v0.9.79+)

Orders のフラットな offerPhase 文字列フィールドは非推奨です。代わりに offerPhaseDetails を参照してください。

const order = await client.purchases.orders.get(APP, orderId);
// Deprecated: order.offerPhase
// Preferred:
const phase = order.offerPhaseDetails; // OfferPhaseDetails — phase type, cycle counts, pricing
download() の指数バックオフ(v0.9.80+)

APK/AAB バイナリのダウンロードに使用する client.download() で、request() と同じ再試行動作に合わせて、指数バックオフによる自動再試行が行われるようになりました。コードを変更する必要はありません。一時的な 5xx エラーやネットワークのタイムアウトは、自動的に再試行されます。

Play GCS バケットからの一括レポート(v0.9.93+)

Play の月次一括レポートは、Publisher API リソースではなく、デベロッパーアカウントにリンクされた Cloud Storage バケット内の CSV オブジェクトです。**v0.9.93 の破壊的変更:**機能していなかった client.reports.list は @gpc-cli/api から削除され、古い downloadReport エクスポートは @gpc-cli/core から削除されました。代わりに、新しい @gpc-cli/core エクスポート 3 つを使用してください。

import { resolveAuth, DEFAULT_SCOPES, STORAGE_READ_ONLY_SCOPE } from "@gpc-cli/auth";
import {
  listReports,
  downloadStatsReport,
  downloadFinancialReport,
  resolveReportsBucket,
  parseMonth,
  type ListReportsResult,
} from "@gpc-cli/core";

// The reports path needs one extra scope. Request it only here: storage-scoped tokens are
// cached under a separate key, so other commands' tokens never carry storage access.
const auth = await resolveAuth({
  serviceAccountPath: "key.json",
  scopes: [...DEFAULT_SCOPES, STORAGE_READ_ONLY_SCOPE],
});

// pubsite_prod_<developerId>, or an explicit reports.bucket value
const bucket = resolveReportsBucket({ developerId: "1234567890" });
const month = parseMonth("2026-02");

const { reports, nextPageToken }: ListReportsResult = await listReports(
  auth,
  bucket,
  "installs",
  { packageName: "com.example.app", month, maxResults: 50 },
);

// Stats: per app, one CSV per dimension (reviews reports have no dimension)
const stats = await downloadStatsReport(
  auth, bucket, "com.example.app", "installs", month, "country",
);
console.log(stats.objectName, stats.csv); // gunzipped, UTF-16 decoded to UTF-8

// Financial: account-level, ZIP archives
const fin = await downloadFinancialReport(auth, bucket, "earnings", month);
if (fin.kind === "csv") {
  console.log(fin.text);
} else {
  for (const entry of fin.entries) console.log(entry.name, entry.csv.length);
}

auth は ReportsAuth({ getAccessToken(): Promise<string> })を満たすだけでよいため、任意のトークンソースを使用できます。失敗時には REPORT_* コードを持つ GpcError がスローされます(gpc-troubleshooting を参照してください)。初回実行時に発生するのは REPORT_ACCESS_DENIED で、Play Console でサービスアカウントに「アプリ情報の表示と一括レポートのダウンロード(読み取り専用)」を付与するまで発生します。

API の正確性に関する最近の変更履歴
  • v0.9.57: apprecovery.cancel/deploy の URL が複数形の /appRecoveries/ を使用するようになりました。dataSafety.update は PUT ではなく POST です。実在しない dataSafety.get は削除されました。onetimeproducts.offers.activateOffer/deactivateOffer が追加されました。新しい getVitalsErrorCount 関数が追加されました。
  • v0.9.58/v0.9.59: Vitals LMK のメトリクスセットは lmkRateMetricSet で、メトリクスは userPerceivedLmkRate、userPerceivedLmkRate7dUserWeighted、userPerceivedLmkRate28dUserWeighted、distinctUsers です。(v0.9.58 では誤ったリソース名でリリースされ、v0.9.59 で修正されました。)

検証

  • resolveAuth() が有効な認証クライアントを返す
  • createApiClient({ auth }) が動作するクライアントを作成する
  • client.edits.insert(APP) が正常に編集を開始する
  • API 呼び出しが型付きレスポンスを返す
  • エラー処理が PlayApiError と AuthError を捕捉する

障害モード/デバッグ

症状考えられる原因解決方法
AUTH_NO_CREDENTIALS認証情報の取得元が見つかりませんserviceAccountPath を渡すか、GPC_SERVICE_ACCOUNT を設定します
AUTH_INVALID_KEYキーファイル内の JSON が不正ですGoogle Cloud Console から再ダウンロードします
編集内容の挿入が 403 で失敗するサービスアカウントに API アクセス権がありませんGCP で Google Play Developer API を有効にします
同時編集の競合別の編集が開かれています先に既存の編集をコミットするか、削除します
ステータス 429 の PlayApiErrorレート制限に達しています適切なバケットを設定して createRateLimiter() を使用します
型を解決できないTypeScript の設定が誤っていますmoduleResolution: "bundler" または "node16" が設定されていることを確認します
client.reports.list が関数ではないv0.9.93 で削除されました(以前から動作していませんでした)@gpc-cli/core の listReports/downloadStatsReport/downloadFinancialReport を使用します
レポートの呼び出しで REPORT_ACCESS_DENIED が返されるサービスアカウントに一括レポートの権限がありませんPlay Console で「アプリ情報の表示と一括レポートのダウンロード(読み取り専用)」を有効にしてから、再試行します
権限を付与してもレポートの呼び出しで 403 が返されるdevstorage.read_only を指定せずにトークンが発行されていますresolveAuth に scopes: [...DEFAULT_SCOPES, STORAGE_READ_ONLY_SCOPE] を渡します

関連スキル

  • gpc-setup — サービスアカウントの作成と認証設定
  • gpc-plugin-development — SDK を内部で使用するプラグインの構築
  • gpc-troubleshooting — API エラーコードの解釈

コメント