release-pull-notes
Fetch release notes from App Store and Google Play to manage app updates and store metadata directly from AI clients.
Instructions
Fetch release notes from App Store/Google Play.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| app | No | Registered app slug | |
| packageName | No | Google Play package name | |
| bundleId | No | App Store bundle ID | |
| store | No | Target store (default: both) | |
| dryRun | No | If true, only outputs result without actually saving |
Input Schema (JSON Schema)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"additionalProperties": false,
"properties": {
"app": {
"description": "Registered app slug",
"type": "string"
},
"bundleId": {
"description": "App Store bundle ID",
"type": "string"
},
"dryRun": {
"description": "If true, only outputs result without actually saving",
"type": "boolean"
},
"packageName": {
"description": "Google Play package name",
"type": "string"
},
"store": {
"description": "Target store (default: both)",
"enum": [
"appStore",
"googlePlay",
"both"
],
"type": "string"
}
},
"type": "object"
}
Implementation Reference
- src/tools/release/pull-notes.ts:32-222 (handler)Main handler function `handleAsoPullReleaseNotes` that resolves the app, fetches release notes from App Store and/or Google Play services, logs progress, handles dry-run, and saves to local files if not dry-run.export async function handleAsoPullReleaseNotes( options: AsoPullReleaseNotesOptions ) { const { app, store, dryRun = false } = options; let { packageName, bundleId } = options; const { store: targetStore, includeAppStore, includeGooglePlay, } = getStoreTargets(store); const resolved = appResolutionService.resolve({ slug: app, packageName, bundleId, }); if (!resolved.success) { return { content: [ { type: "text" as const, text: resolved.error.message, }, ], }; } const { slug, bundleId: resolvedBundleId, packageName: resolvedPackageName, hasAppStore, hasGooglePlay, } = resolved.data; bundleId = resolvedBundleId; packageName = resolvedPackageName; console.error(`[MCP] π₯ Pulling release notes`); console.error(`[MCP] Store: ${targetStore}`); console.error(`[MCP] App: ${slug}`); if (packageName) console.error(`[MCP] Package Name: ${packageName}`); if (bundleId) console.error(`[MCP] Bundle ID: ${bundleId}`); console.error(`[MCP] Mode: ${dryRun ? "Dry run" : "Actual fetch"}`); let config; try { config = loadConfig(); } catch (error) { const message = error instanceof Error ? error.message : String(error); return { content: [ { type: "text" as const, text: `β Failed to load config: ${message}`, }, ], isError: true, }; } const releaseNotes: { googlePlay?: GooglePlayReleaseNote[]; appStore?: AppStoreReleaseNote[]; } = {}; if (includeGooglePlay) { if (!hasGooglePlay) { console.error( `[MCP] βοΈ Skipping Google Play (not registered for Google Play)` ); } else if (!config.playStore) { console.error( `[MCP] βοΈ Skipping Google Play (not configured in ~/.config/pabal-mcp/config.json)` ); } else if (!packageName) { console.error( `[MCP] βοΈ Skipping Google Play (no packageName provided)` ); } else { const notesResult = await googlePlayService.pullReleaseNotes(packageName); if (!notesResult.success) { console.error( `[MCP] β Failed to fetch Google Play release notes: ${notesResult.error.message}` ); } else { releaseNotes.googlePlay = notesResult.data; console.error(`[MCP] π Google Play Release Notes:`); console.error(`[MCP] Total versions: ${notesResult.data.length}`); for (const rn of notesResult.data) { const code = (rn as any).versionCode ?? "N/A"; console.error( `[MCP] Version ${rn.versionName} (${code}): ${ Object.keys(rn.releaseNotes).length } languages` ); } console.error(`[MCP] β Google Play release notes fetched`); } } } if (includeAppStore) { if (!hasAppStore) { console.error( `[MCP] βοΈ Skipping App Store (not registered for App Store)` ); } else if (!config.appStore) { console.error( `[MCP] βοΈ Skipping App Store (not configured in ~/.config/pabal-mcp/config.json)` ); } else if (!bundleId) { console.error(`[MCP] βοΈ Skipping App Store (no bundleId provided)`); } else { const notesResult = await appStoreService.pullReleaseNotes(bundleId); if (!notesResult.success) { console.error( `[MCP] β Failed to fetch App Store release notes: ${notesResult.error.message}` ); } else { releaseNotes.appStore = notesResult.data; console.error(`[MCP] π App Store Release Notes:`); console.error(`[MCP] Total versions: ${notesResult.data.length}`); for (const rn of notesResult.data) { console.error( `[MCP] Version ${rn.versionString}: ${ Object.keys(rn.releaseNotes).length } locales` ); } console.error(`[MCP] β App Store release notes fetched`); } } } if (dryRun) { return { content: [ { type: "text" as const, text: `π Dry run - Release notes:\n${JSON.stringify( releaseNotes, null, 2 )}`, }, ], }; } // Save to ASO directory const asoDir = getPullProductAsoDir(slug, getAsoPullDir()); if (releaseNotes.googlePlay) { const googlePlayDir = getStoreDir(asoDir, "google-play"); ensureDir(googlePlayDir); const filePath = getReleaseNotesPath(googlePlayDir); writeFileSync(filePath, JSON.stringify(releaseNotes.googlePlay, null, 2)); console.error(`[MCP] πΎ Google Play release notes saved to ${filePath}`); } if (releaseNotes.appStore) { const appStoreDir = getStoreDir(asoDir, "app-store"); ensureDir(appStoreDir); const filePath = getReleaseNotesPath(appStoreDir); writeFileSync(filePath, JSON.stringify(releaseNotes.appStore, null, 2)); console.error(`[MCP] πΎ App Store release notes saved to ${filePath}`); } return { content: [ { type: "text" as const, text: `β Release notes pulled\n` + ` Google Play: ${ releaseNotes.googlePlay ? `${releaseNotes.googlePlay.length} versions` : "β" }\n` + ` App Store: ${ releaseNotes.appStore ? `${releaseNotes.appStore.length} versions` : "β" }`, }, ], }; }
- src/index.ts:327-344 (registration)Registers the tool 'release-pull-notes' with MCP server, including description, Zod input schema, and references the handler function.registerToolWithInfo( "release-pull-notes", { description: "Fetch release notes from App Store/Google Play.", inputSchema: z.object({ app: z.string().optional().describe("Registered app slug"), packageName: z.string().optional().describe("Google Play package name"), bundleId: z.string().optional().describe("App Store bundle ID"), store: storeSchema.describe("Target store (default: both)"), dryRun: z .boolean() .optional() .describe("If true, only outputs result without actually saving"), }), }, handleAsoPullReleaseNotes, "Release Management" );
- TypeScript interface defining the input options for the handler function.interface AsoPullReleaseNotesOptions { app?: string; // Registered app slug packageName?: string; // For Google Play bundleId?: string; // For App Store store?: StoreType; dryRun?: boolean; }