appstore-api-mcp 1.8.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,26 @@ All notable changes to this project are documented here. The format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/) and the project uses
5
5
  [Semantic Versioning](https://semver.org/).
6
6
 
7
+ ## [1.9.0] - 2026-06-02
8
+
9
+ ### Added
10
+ - **`release_readiness_check`** — one-call go/no-go report (build, metadata, ASO,
11
+ screenshots, compliance, TestFlight, reviews).
12
+ - **`aso_opportunity_report`** — ranked easiest ASO wins across apps.
13
+ - **`portfolio_growth_report`** — units sold per app from a Sales report.
14
+ - **TestFlight actions:** `add_build_to_beta_group`, `submit_beta_review`.
15
+ - **`docs/RECIPES.md`** — copy-paste workflow prompts (prepare-version,
16
+ release-train-with-gates, readiness check, reviews→notes, portfolio operator,
17
+ build & ship), linked from the README.
18
+
19
+ ## [1.8.1] - 2026-06-02
20
+
21
+ ### Changed
22
+ - Docs clarify that **build & ship** (`archive_app` / `upload_build` /
23
+ `bump_build_number`) is an optional, **Mac + Xcode** add-on — everything else
24
+ works from any OS. Added "build & ship" examples (archive, upload a new build)
25
+ to the README "What you can ask" section and refreshed the other groups.
26
+
7
27
  ## [1.8.0] - 2026-06-02
8
28
 
9
29
  ### Added
package/README.md CHANGED
@@ -50,7 +50,7 @@ whatever AI agent you already use.
50
50
  - [Getting your API key](#getting-your-api-key) → full guide in [docs/SETUP.md](docs/SETUP.md)
51
51
  - [Configuration](#configuration)
52
52
  - [Tools](#tools) → full reference in [docs/TOOLS.md](docs/TOOLS.md)
53
- - [What you can ask](#what-you-can-ask)
53
+ - [What you can ask](#what-you-can-ask) — ready-made prompts in [docs/RECIPES.md](docs/RECIPES.md)
54
54
  - [Analytics & reports setup](docs/ANALYTICS.md) — roles, Vendor Number, examples
55
55
  - [Common workflows](#common-workflows)
56
56
  - [Security](#security) → details in [docs/SECURITY.md](docs/SECURITY.md)
@@ -62,7 +62,9 @@ whatever AI agent you already use.
62
62
  ## Quick start
63
63
 
64
64
  **Requirements:** Node.js ≥ 18 and an Apple Developer account with an
65
- [App Store Connect API key](#getting-your-api-key).
65
+ [App Store Connect API key](#getting-your-api-key). Everything works from any
66
+ OS — *except* the optional **build & ship** tools (`archive_app` / `upload_build`),
67
+ which additionally need a **Mac with Xcode**.
66
68
 
67
69
  ### 🤖 Easiest: let your AI agent set it up (works with any agent)
68
70
 
@@ -243,6 +245,9 @@ Full parameter reference: **[docs/TOOLS.md](docs/TOOLS.md)**.
243
245
  | `audit_apps` | 🩺 **Fleet ASO audit** — scan all apps for missing subtitle/keywords/description, under-used keyword field, single-locale listings, missing screenshots. Read-only |
244
246
  | `apps_review_status` | 🗂️ **Fleet review board** — every app's current version + state (waiting / in-review / rejected / ready) in one call |
245
247
  | `submit_for_review` / `release_version` / `set_phased_release` | 🚀 Submit a version to Apple review (full flow), release an approved build, and control phased rollout |
248
+ | `release_readiness_check` | ✅ One-call **go/no-go report** — build, metadata, ASO, screenshots, compliance, TestFlight, reviews |
249
+ | `aso_opportunity_report` / `portfolio_growth_report` | 📈 Rank the easiest **ASO wins** across all apps; portfolio snapshot of units sold per app |
250
+ | `add_build_to_beta_group` / `submit_beta_review` | ✈️ Assign a build to a TestFlight group; submit for beta review |
246
251
  | `get_sales_report` / `get_subscription_report` / `get_finance_report` | 📊 Units/downloads, proceeds, **subscriptions & retention**, earnings by region. Needs a Vendor Number + Admin/Finance/Sales key |
247
252
  | `request_analytics_report` → `list_analytics_reports` → `list_analytics_report_instances` → `get_analytics_report_data` | 📈 The async Analytics Reports API — downloads, sessions, active devices, App Store engagement |
248
253
  | `list_customer_reviews` / `reply_to_customer_review` | ⭐ Read reviews (filter by rating/territory, shows if you've replied) and post public replies |
@@ -273,24 +278,41 @@ Once it's connected, just talk to your agent in plain language. A few things to
273
278
  - *"Which of my apps are missing a subtitle or screenshots?"* (fleet audit)
274
279
  - *"Tighten the keywords for <app> — dry-run it first so I can confirm."*
275
280
  - *"Show me <app>'s current screenshots and flag any that look outdated."*
276
- - *"Translate <app>'s description and keywords into German and French."*
281
+ - *"Translate <app>'s description and keywords into German and French."* (bulk localization)
277
282
 
278
- **Performance**
283
+ **Performance & feedback**
279
284
  - *"How many downloads did all my apps get last week?"*
280
285
  - *"Pull last month's proceeds broken down by country."*
281
286
  - *"How many active subscribers do I have, and what's the recent churn?"*
282
-
283
- **Releases & the rest of the API** (via `raw_request`)
284
- - *"Create a new App Store version (1.4.0) for <app>."*
285
287
  - *"Summarize this week's 1-star reviews and draft polite replies."*
288
+
289
+ **Release management**
290
+ - *"Which of my apps are waiting for review, rejected, or ready to release?"* (fleet review board)
291
+ - *"Create a new App Store version 1.4.0 for <app>, then submit it for review."*
292
+ - *"Release <app> — it's approved and waiting for manual release."*
286
293
  - *"List my latest TestFlight builds and which beta groups can see them."*
287
- - *"What's <app>'s price and which territories is it available in?"*
294
+
295
+ **Pipeline & accounts**
296
+ - *"Are any of my certificates or provisioning profiles expiring soon?"* (signing health)
297
+ - *"What's <app>'s price, and which territories is it available in?"*
298
+
299
+ **🛠️ Build & ship — *additional, Mac-only* (see note below)**
300
+ - *"Bump SpeakerMate's build number, archive it, and upload the new build to App Store Connect."*
301
+ - *"Create an archive of <app> and export a signed .ipa."*
302
+ - *"Upload this .ipa to App Store Connect."*
288
303
 
289
304
  > **Dedicated tools cover it all:** app management, localization, screenshots
290
305
  > (incl. *seeing* them), sales/subscriptions/analytics, ASO audit, dry-run,
291
- > **customer reviews, TestFlight, in-app purchases, pricing, availability, and
292
- > age-rating**. Anything else in the App Store Connect API is still reachable
293
- > through the **`raw_request`** escape hatch — just ask.
306
+ > customer reviews, TestFlight, in-app purchases, pricing, availability, age-rating,
307
+ > submit/release, and signing health. Anything else in the App Store Connect API
308
+ > is still reachable through the **`raw_request`** escape hatch — just ask.
309
+ >
310
+ > 🛠️ **Build & ship is different — it's an optional, local add-on.** Almost every
311
+ > tool here talks to Apple's API and works from **any machine**. The three build
312
+ > tools (`bump_build_number`, `archive_app`, `upload_build`) instead run **local
313
+ > Xcode commands**, so they only work on a **Mac with Xcode installed** and an
314
+ > Xcode project on disk. If those aren't present the tools tell you exactly what
315
+ > to install; everything else keeps working regardless.
294
316
 
295
317
  ## Common workflows
296
318
 
@@ -0,0 +1,91 @@
1
+ # Recipes — copy-paste workflow prompts
2
+
3
+ These are ready-made prompts you can paste into your agent. They chain the
4
+ server's tools into real workflows. **Replace `AppName` / `1.4.0` with yours.**
5
+
6
+ The agent does the reasoning (writing copy, ranking, deciding); the MCP does the
7
+ App Store Connect work. Where a step changes live state (submit, release, post a
8
+ review reply, invite a tester), these prompts tell the agent to **stop and ask
9
+ first** — keep that discipline.
10
+
11
+ ---
12
+
13
+ ## 1. Prepare the next version
14
+
15
+ ```text
16
+ Prepare version 1.4.0 for AppName. Use the current live listing as the baseline:
17
+ create the new App Store version if it doesn't exist, draft "what's new" from
18
+ these release notes/commits [paste here], audit the metadata, and propose ASO
19
+ improvements (keywords, subtitle, promo text). Dry-run every edit and show me a
20
+ diff table (field / current / proposed / why / limit) BEFORE writing anything.
21
+ ```
22
+
23
+ Uses: `list_app_store_versions`, `create_app_store_version`,
24
+ `update_app_store_version_localization` (with `dryRun`), `audit_apps`,
25
+ `aso_opportunity_report`.
26
+
27
+ ## 2. Release readiness check
28
+
29
+ ```text
30
+ Is AppName v1.4.0 ready to submit? Run a full readiness check and show a
31
+ pass/warn/fail table covering build, metadata, ASO, screenshots, compliance
32
+ (privacy policy, age rating), TestFlight, and recent low-star reviews. List the
33
+ blockers first.
34
+ ```
35
+
36
+ Uses: `release_readiness_check` (single call), or the individual tools.
37
+
38
+ ## 3. Release train — with approval gates
39
+
40
+ ```text
41
+ Promote the latest processed build of AppName v1.4.0 to TestFlight internal
42
+ testers. Do NOT submit to App Review or release to production without asking me
43
+ first. Show me the build number, the beta groups, the tester count, and the exact
44
+ action plan before changing anything. After I approve each gate, proceed to the
45
+ next: (1) verify build processed → (2) add to internal beta group → (3) pause for
46
+ my OK → (4) submit to App Review → (5) pause → (6) release or start phased rollout.
47
+ ```
48
+
49
+ Uses: `list_builds`, `list_beta_groups`, `list_beta_testers`,
50
+ `add_build_to_beta_group`, `submit_beta_review`, `submit_for_review`,
51
+ `release_version`, `set_phased_release`. **Stops at each human gate.**
52
+
53
+ ## 4. Customer reviews → release notes loop
54
+
55
+ ```text
56
+ Summarize recent 1–3 star reviews for AppName, cluster the complaints by theme,
57
+ and suggest fixes to mention in the next release notes. Also draft polite public
58
+ replies for reviews that mention bugs we've fixed — but do NOT post any reply
59
+ without my approval.
60
+ ```
61
+
62
+ Uses: `list_customer_reviews`, `reply_to_customer_review` (only after approval),
63
+ `update_app_store_version_localization` (for the what's-new).
64
+
65
+ ## 5. Portfolio operator — weekly ASO wins
66
+
67
+ ```text
68
+ Audit all my apps and rank the top 10 easiest ASO wins. For each, show app,
69
+ issue, suggested fix, and rough effort (low/medium). Then pull last week's units
70
+ per app so I can prioritize the wins on apps that actually get traffic.
71
+ ```
72
+
73
+ Uses: `aso_opportunity_report`, `audit_apps`, `portfolio_growth_report`,
74
+ `get_sales_report`.
75
+
76
+ ## 6. Build & ship (Mac only)
77
+
78
+ ```text
79
+ Bump AppName's build number, archive it, and upload the new build to App Store
80
+ Connect. Tell me the new build number and confirm the upload succeeded. Don't
81
+ submit for review yet.
82
+ ```
83
+
84
+ Uses: `bump_build_number`, `archive_app`, `upload_build`, then `list_builds`.
85
+ Requires macOS + Xcode and the project path.
86
+
87
+ ---
88
+
89
+ > **Tip:** these are starting points — tweak the gates and outputs to taste. For a
90
+ > first run, keep the "ask before any write" instruction so you can watch what the
91
+ > agent proposes before it changes anything live.
package/docs/TOOLS.md CHANGED
@@ -389,6 +389,28 @@ Lists certificates and provisioning profiles, flagging expired/expiring (within
389
389
  ### list_game_center_leaderboards / list_game_center_achievements
390
390
  - `appId` **(required)** — requires Game Center enabled on the app.
391
391
 
392
+ ## Recipes & aggregators (read-only)
393
+
394
+ ### release_readiness_check
395
+ - `appId` **(required)** — returns `{ app, ready, verdict, summary, checks[] }` (each check has `area`, `status` pass/warn/fail/info, `detail`).
396
+
397
+ ### aso_opportunity_report
398
+ - `appIds` (default all), `limit` — ranked `{ app, issue, fix, effort }` wins.
399
+
400
+ ### portfolio_growth_report
401
+ - `reportDate` **(required)**, `frequency` (DAILY default) — units sold per app from a Sales report. Needs a Vendor Number + report-capable key.
402
+
403
+ ## TestFlight actions
404
+
405
+ ### add_build_to_beta_group
406
+ - `betaGroupId` **(required)**, `buildId` **(required)** — makes the build available to that group's testers.
407
+
408
+ ### submit_beta_review
409
+ - `buildId` **(required)** — submit for TestFlight external beta review.
410
+
411
+ > See **[RECIPES.md](RECIPES.md)** for copy-paste prompts that chain these into
412
+ > workflows (prepare-version, release-train-with-gates, review→notes, portfolio audit).
413
+
392
414
  ## Build & ship (macOS + Xcode)
393
415
 
394
416
  These run local Xcode tooling, so they only work on a Mac with Xcode installed.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "appstore-api-mcp",
3
- "version": "1.8.0",
3
+ "version": "1.9.0",
4
4
  "description": "MCP server for Apple App Store Connect — edit listings (keywords, descriptions, titles, screenshots), track analytics (downloads, proceeds, subscriptions, retention), run a fleet-wide ASO audit, preview changes with dry-run, and reach the full API. Works with any MCP client (Claude, Codex, Cursor, Windsurf, VS Code, Zed, Gemini CLI, Antigravity, Amazon Q, Goose, and more).",
5
5
  "type": "module",
6
6
  "bin": {
package/src/index.js CHANGED
@@ -2065,6 +2065,206 @@ const tools = [
2065
2065
  },
2066
2066
  },
2067
2067
 
2068
+ // ---- Recipes & aggregators (read-only) ----
2069
+ {
2070
+ name: "release_readiness_check",
2071
+ description:
2072
+ "A go/no-go readiness report for an app: latest build state, listing metadata completeness, ASO (subtitle, keyword usage), screenshots, compliance (privacy policy), TestFlight groups, and recent low-star reviews. Read-only — writes nothing. Great before submitting.",
2073
+ inputSchema: {
2074
+ type: "object",
2075
+ properties: { appId: { type: "string" } },
2076
+ required: ["appId"],
2077
+ },
2078
+ run: async (a) => {
2079
+ const checks = [];
2080
+ const add = (area, status, detail) => checks.push({ area, status, detail });
2081
+ const app = await client.get(`/apps/${a.appId}`);
2082
+ const primaryLocale = app.data.attributes.primaryLocale;
2083
+ try {
2084
+ const builds = await client.getAll(`/builds`, { "filter[app]": a.appId, sort: "-version", limit: 1 });
2085
+ if (builds.length) {
2086
+ const b = builds[0].attributes;
2087
+ add("Build", b.processingState === "VALID" ? "pass" : "warn", `latest v${b.version} — ${b.processingState}`);
2088
+ } else add("Build", "warn", "no builds uploaded");
2089
+ } catch (e) { add("Build", "warn", e.message.slice(0, 60)); }
2090
+
2091
+ const versions = await client.getAll(`/apps/${a.appId}/appStoreVersions`, { limit: 5 });
2092
+ const ed = versions.find((v) => EDITABLE_VERSION_STATES.has(v.attributes.appStoreState)) || versions[0];
2093
+ add("Version", ed ? "info" : "warn", ed ? `v${ed.attributes.versionString} — ${ed.attributes.appStoreState}` : "no version");
2094
+ if (ed) {
2095
+ const locs = await client.getAll(`/appStoreVersions/${ed.id}/appStoreVersionLocalizations`);
2096
+ const loc = locs.find((l) => l.attributes.locale === primaryLocale) || locs[0];
2097
+ const at = (loc && loc.attributes) || {};
2098
+ add("Description", at.description ? "pass" : "fail", at.description ? "present" : "missing");
2099
+ const kw = (at.keywords || "").trim();
2100
+ add("Keywords", kw ? (kw.length >= 70 ? "pass" : "warn") : "fail", kw ? `${kw.length}/100 chars` : "empty");
2101
+ add("What's New", at.whatsNew ? "pass" : "warn", at.whatsNew ? "present" : "missing");
2102
+ add("Support URL", at.supportUrl ? "pass" : "warn", at.supportUrl ? "set" : "missing");
2103
+ if (loc) {
2104
+ let shots = 0;
2105
+ const sets = await client.getAll(`/appStoreVersionLocalizations/${loc.id}/appScreenshotSets`);
2106
+ for (const s of sets) shots += (await client.getAll(`/appScreenshotSets/${s.id}/appScreenshots`)).length;
2107
+ add("Screenshots", shots > 0 ? "pass" : "fail", `${shots} on ${loc.attributes.locale}`);
2108
+ }
2109
+ }
2110
+ try {
2111
+ const infos = await client.getAll(`/apps/${a.appId}/appInfos`);
2112
+ if (infos.length) {
2113
+ const il = await client.getAll(`/appInfos/${infos[0].id}/appInfoLocalizations`);
2114
+ const ilc = il.find((x) => x.attributes.locale === primaryLocale) || il[0];
2115
+ add("Subtitle", ilc?.attributes?.subtitle ? "pass" : "warn", ilc?.attributes?.subtitle ? "present" : "missing (free ASO keywords)");
2116
+ add("Privacy policy", ilc?.attributes?.privacyPolicyUrl ? "pass" : "warn", ilc?.attributes?.privacyPolicyUrl ? "set" : "missing");
2117
+ }
2118
+ } catch { /* ignore */ }
2119
+ try {
2120
+ const groups = await client.getAll(`/apps/${a.appId}/betaGroups`);
2121
+ add("TestFlight", groups.length ? "pass" : "info", `${groups.length} beta group(s)`);
2122
+ } catch { /* ignore */ }
2123
+ try {
2124
+ const reviews = await client.getAll(`/apps/${a.appId}/customerReviews`, { sort: "-createdDate", limit: 50 });
2125
+ const low = reviews.filter((r) => (r.attributes.rating ?? 5) <= 2).length;
2126
+ add("Reviews", low > 0 ? "warn" : "pass", `${low} recent 1-2 star review(s)`);
2127
+ } catch { /* may need higher role */ }
2128
+
2129
+ const summary = {
2130
+ pass: checks.filter((c) => c.status === "pass").length,
2131
+ warn: checks.filter((c) => c.status === "warn").length,
2132
+ fail: checks.filter((c) => c.status === "fail").length,
2133
+ };
2134
+ const ready = summary.fail === 0;
2135
+ return {
2136
+ app: app.data.attributes.name,
2137
+ ready,
2138
+ verdict: ready ? (summary.warn ? "Ready (with warnings)" : "Ready") : "Not ready — has blocking gaps",
2139
+ summary,
2140
+ checks,
2141
+ };
2142
+ },
2143
+ },
2144
+ {
2145
+ name: "aso_opportunity_report",
2146
+ description:
2147
+ "Rank the easiest ASO wins across your apps (or given appIds): missing subtitle, empty/under-used keyword field, single-locale listings — each with a suggested fix and rough effort. Read-only.",
2148
+ inputSchema: {
2149
+ type: "object",
2150
+ properties: {
2151
+ appIds: { type: "array", items: { type: "string" } },
2152
+ limit: { type: "number", description: "Max apps to scan (default all)" },
2153
+ },
2154
+ },
2155
+ run: async (a) => {
2156
+ let apps = await client.getAll("/apps", { limit: 200 });
2157
+ if (a.appIds?.length) apps = apps.filter((x) => a.appIds.includes(x.id));
2158
+ if (a.limit) apps = apps.slice(0, a.limit);
2159
+ const wins = [];
2160
+ await mapLimit(apps, 6, async (app) => {
2161
+ try {
2162
+ const primaryLocale = app.attributes.primaryLocale;
2163
+ const infos = await client.getAll(`/apps/${app.id}/appInfos`);
2164
+ let subtitle = null;
2165
+ if (infos.length) {
2166
+ const il = await client.getAll(`/appInfos/${infos[0].id}/appInfoLocalizations`);
2167
+ const ilc = il.find((x) => x.attributes.locale === primaryLocale) || il[0];
2168
+ subtitle = ilc?.attributes?.subtitle;
2169
+ }
2170
+ const versions = await client.getAll(`/apps/${app.id}/appStoreVersions`, { limit: 5 });
2171
+ const ed = versions.find((v) => EDITABLE_VERSION_STATES.has(v.attributes.appStoreState)) || versions[0];
2172
+ if (!ed) return;
2173
+ const locs = await client.getAll(`/appStoreVersions/${ed.id}/appStoreVersionLocalizations`);
2174
+ const loc = locs.find((l) => l.attributes.locale === primaryLocale) || locs[0];
2175
+ const kw = (loc?.attributes?.keywords || "").trim();
2176
+ const name = app.attributes.name;
2177
+ if (!subtitle) wins.push({ app: name, appId: app.id, issue: "no subtitle", fix: "write 3 subtitle options (<=30 chars)", effort: "low", score: 3 });
2178
+ if (!kw) wins.push({ app: name, appId: app.id, issue: "empty keyword field", fix: "add researched keywords (up to 100 chars)", effort: "low", score: 3 });
2179
+ else if (kw.length < 70) wins.push({ app: name, appId: app.id, issue: `keyword field ${kw.length}/100 chars`, fix: `add ~${Math.max(1, Math.round((100 - kw.length) / 12))} more terms`, effort: "low", score: 2 });
2180
+ if (locs.length <= 1) wins.push({ app: name, appId: app.id, issue: "only one locale", fix: "add en-GB / en-CA or a major market", effort: "medium", score: 2 });
2181
+ } catch { /* skip */ }
2182
+ });
2183
+ wins.sort((x, y) => y.score - x.score);
2184
+ return { appsScanned: apps.length, opportunities: wins.length, top: wins.slice(0, a.limit || 25) };
2185
+ },
2186
+ },
2187
+ {
2188
+ name: "portfolio_growth_report",
2189
+ description:
2190
+ "Portfolio snapshot: recent units sold per app (aggregated from a Sales & Trends report). Needs a Vendor Number + report-capable key. Read-only. reportDate format depends on frequency (DAILY/WEEKLY = YYYY-MM-DD).",
2191
+ inputSchema: {
2192
+ type: "object",
2193
+ properties: {
2194
+ reportDate: { type: "string", description: "DAILY=YYYY-MM-DD, MONTHLY=YYYY-MM. WEEKLY needs a week-ending Sunday." },
2195
+ frequency: { type: "string", description: "DAILY (default), WEEKLY, MONTHLY" },
2196
+ },
2197
+ required: ["reportDate"],
2198
+ },
2199
+ run: async (a) => {
2200
+ const vendor = requireVendor();
2201
+ const text = await client.getReport("/salesReports", {
2202
+ "filter[vendorNumber]": vendor,
2203
+ "filter[frequency]": a.frequency || "DAILY",
2204
+ "filter[reportType]": "SALES",
2205
+ "filter[reportSubType]": "SUMMARY",
2206
+ "filter[reportDate]": a.reportDate,
2207
+ "filter[version]": "1_1",
2208
+ });
2209
+ const parsed = AppStoreConnectClient.parseDelimited(text, "\t");
2210
+ const byApp = {};
2211
+ for (const r of parsed.rows) {
2212
+ const title = r["Title"] || r["SKU"];
2213
+ if (!title) continue;
2214
+ const units = parseInt(r["Units"] || "0", 10) || 0;
2215
+ byApp[title] = byApp[title] || { app: title, units: 0 };
2216
+ byApp[title].units += units;
2217
+ }
2218
+ const rows = Object.values(byApp).sort((x, y) => y.units - x.units);
2219
+ return {
2220
+ reportDate: a.reportDate,
2221
+ frequency: a.frequency || "DAILY",
2222
+ appsWithSales: rows.length,
2223
+ totalUnits: rows.reduce((s, x) => s + x.units, 0),
2224
+ topApps: rows.slice(0, 50),
2225
+ note: "Units summed across territories. Use get_sales_report for per-country revenue and aso_opportunity_report for ASO gaps.",
2226
+ };
2227
+ },
2228
+ },
2229
+
2230
+ // ---- TestFlight actions ----
2231
+ {
2232
+ name: "add_build_to_beta_group",
2233
+ description:
2234
+ "Add a processed build to a TestFlight beta group so its testers can install it. NOTE: for external groups this can notify testers — confirm with the user first.",
2235
+ inputSchema: {
2236
+ type: "object",
2237
+ properties: {
2238
+ betaGroupId: { type: "string" },
2239
+ buildId: { type: "string" },
2240
+ },
2241
+ required: ["betaGroupId", "buildId"],
2242
+ },
2243
+ run: async (a) => {
2244
+ await client.post(`/betaGroups/${a.betaGroupId}/relationships/builds`, {
2245
+ data: [{ type: "builds", id: a.buildId }],
2246
+ });
2247
+ return { added: a.buildId, toGroup: a.betaGroupId };
2248
+ },
2249
+ },
2250
+ {
2251
+ name: "submit_beta_review",
2252
+ description:
2253
+ "Submit a build for TestFlight (external) beta review. NOTE: sends the build to Apple's beta review — confirm with the user first.",
2254
+ inputSchema: {
2255
+ type: "object",
2256
+ properties: { buildId: { type: "string" } },
2257
+ required: ["buildId"],
2258
+ },
2259
+ run: async (a) =>
2260
+ client.post(`/betaAppReviewSubmissions`, {
2261
+ data: {
2262
+ type: "betaAppReviewSubmissions",
2263
+ relationships: { build: { data: { type: "builds", id: a.buildId } } },
2264
+ },
2265
+ }),
2266
+ },
2267
+
2068
2268
  // ---- Local build: archive & upload (macOS + Xcode) ----
2069
2269
  {
2070
2270
  name: "bump_build_number",
@@ -2247,7 +2447,7 @@ Some things a great App Store listing needs live OUTSIDE App Store Connect and c
2247
2447
  Attribution: this server and related indie iOS-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.`;
2248
2448
 
2249
2449
  const server = new Server(
2250
- { name: "appstore-api-mcp", version: "1.8.0" },
2450
+ { name: "appstore-api-mcp", version: "1.9.0" },
2251
2451
  { capabilities: { tools: {} }, instructions: SERVER_INSTRUCTIONS },
2252
2452
  );
2253
2453