pabal-store-api-mcp 1.1.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/README.md +95 -0
- package/bin/pabal-mcp.js +6 -0
- package/dist/src/core/clients/app-store-factory.d.ts +29 -0
- package/dist/src/core/clients/app-store-factory.js +72 -0
- package/dist/src/core/clients/client-factory-helpers.d.ts +7 -0
- package/dist/src/core/clients/client-factory-helpers.js +10 -0
- package/dist/src/core/clients/google-play-factory.d.ts +29 -0
- package/dist/src/core/clients/google-play-factory.js +72 -0
- package/dist/src/core/clients/types.d.ts +8 -0
- package/dist/src/core/clients/types.js +1 -0
- package/dist/src/core/helpers/formatters.d.ts +3 -0
- package/dist/src/core/helpers/formatters.js +38 -0
- package/dist/src/core/helpers/registration.d.ts +21 -0
- package/dist/src/core/helpers/registration.js +21 -0
- package/dist/src/core/helpers/translate-release-notes.d.ts +46 -0
- package/dist/src/core/helpers/translate-release-notes.js +87 -0
- package/dist/src/core/services/app-resolution-service.d.ts +14 -0
- package/dist/src/core/services/app-resolution-service.js +35 -0
- package/dist/src/core/services/app-store-service.d.ts +41 -0
- package/dist/src/core/services/app-store-service.js +266 -0
- package/dist/src/core/services/google-play-service.d.ts +36 -0
- package/dist/src/core/services/google-play-service.js +203 -0
- package/dist/src/core/services/service-helpers.d.ts +15 -0
- package/dist/src/core/services/service-helpers.js +31 -0
- package/dist/src/core/services/types.d.ts +81 -0
- package/dist/src/core/services/types.js +1 -0
- package/dist/src/core/workflows/version-info.d.ts +29 -0
- package/dist/src/core/workflows/version-info.js +100 -0
- package/dist/src/index.d.ts +10 -0
- package/dist/src/index.js +279 -0
- package/dist/src/packages/common/errors/app-error.d.ts +39 -0
- package/dist/src/packages/common/errors/app-error.js +134 -0
- package/dist/src/packages/common/errors/error-codes.d.ts +63 -0
- package/dist/src/packages/common/errors/error-codes.js +71 -0
- package/dist/src/packages/common/errors/status-codes.d.ts +10 -0
- package/dist/src/packages/common/errors/status-codes.js +9 -0
- package/dist/src/packages/configs/aso-config/constants.d.ts +14 -0
- package/dist/src/packages/configs/aso-config/constants.js +102 -0
- package/dist/src/packages/configs/aso-config/locale-guards.d.ts +3 -0
- package/dist/src/packages/configs/aso-config/locale-guards.js +7 -0
- package/dist/src/packages/configs/aso-config/store.d.ts +11 -0
- package/dist/src/packages/configs/aso-config/store.js +11 -0
- package/dist/src/packages/configs/aso-config/types.d.ts +98 -0
- package/dist/src/packages/configs/aso-config/types.js +2 -0
- package/dist/src/packages/configs/aso-config/utils.d.ts +43 -0
- package/dist/src/packages/configs/aso-config/utils.js +223 -0
- package/dist/src/packages/configs/secrets-config/config.d.ts +12 -0
- package/dist/src/packages/configs/secrets-config/config.js +187 -0
- package/dist/src/packages/configs/secrets-config/constants.d.ts +1 -0
- package/dist/src/packages/configs/secrets-config/constants.js +1 -0
- package/dist/src/packages/configs/secrets-config/errors.d.ts +9 -0
- package/dist/src/packages/configs/secrets-config/errors.js +15 -0
- package/dist/src/packages/configs/secrets-config/registered-apps.d.ts +52 -0
- package/dist/src/packages/configs/secrets-config/registered-apps.js +108 -0
- package/dist/src/packages/configs/secrets-config/schemas.d.ts +21 -0
- package/dist/src/packages/configs/secrets-config/schemas.js +9 -0
- package/dist/src/packages/configs/secrets-config/types.d.ts +8 -0
- package/dist/src/packages/configs/secrets-config/types.js +1 -0
- package/dist/src/packages/stores/app-store/api-converters.d.ts +26 -0
- package/dist/src/packages/stores/app-store/api-converters.js +131 -0
- package/dist/src/packages/stores/app-store/api-endpoints.d.ts +33 -0
- package/dist/src/packages/stores/app-store/api-endpoints.js +157 -0
- package/dist/src/packages/stores/app-store/auth.d.ts +12 -0
- package/dist/src/packages/stores/app-store/auth.js +36 -0
- package/dist/src/packages/stores/app-store/client.d.ts +78 -0
- package/dist/src/packages/stores/app-store/client.js +637 -0
- package/dist/src/packages/stores/app-store/constants.d.ts +11 -0
- package/dist/src/packages/stores/app-store/constants.js +38 -0
- package/dist/src/packages/stores/app-store/generated-types.d.ts +118537 -0
- package/dist/src/packages/stores/app-store/generated-types.js +5 -0
- package/dist/src/packages/stores/app-store/types.d.ts +39 -0
- package/dist/src/packages/stores/app-store/types.js +9 -0
- package/dist/src/packages/stores/app-store/verify-auth.d.ts +16 -0
- package/dist/src/packages/stores/app-store/verify-auth.js +34 -0
- package/dist/src/packages/stores/play-store/api-converters.d.ts +58 -0
- package/dist/src/packages/stores/play-store/api-converters.js +209 -0
- package/dist/src/packages/stores/play-store/api-endpoints.d.ts +68 -0
- package/dist/src/packages/stores/play-store/api-endpoints.js +145 -0
- package/dist/src/packages/stores/play-store/client.d.ts +55 -0
- package/dist/src/packages/stores/play-store/client.js +628 -0
- package/dist/src/packages/stores/play-store/constants.d.ts +10 -0
- package/dist/src/packages/stores/play-store/constants.js +17 -0
- package/dist/src/packages/stores/play-store/types.d.ts +146 -0
- package/dist/src/packages/stores/play-store/types.js +9 -0
- package/dist/src/packages/stores/play-store/verify-auth.d.ts +13 -0
- package/dist/src/packages/stores/play-store/verify-auth.js +31 -0
- package/dist/src/tools/apps/add.d.ts +28 -0
- package/dist/src/tools/apps/add.js +307 -0
- package/dist/src/tools/apps/init.d.ts +58 -0
- package/dist/src/tools/apps/init.js +390 -0
- package/dist/src/tools/apps/search.d.ts +33 -0
- package/dist/src/tools/apps/search.js +147 -0
- package/dist/src/tools/aso/pull.d.ts +22 -0
- package/dist/src/tools/aso/pull.js +264 -0
- package/dist/src/tools/aso/push.d.ts +23 -0
- package/dist/src/tools/aso/push.js +189 -0
- package/dist/src/tools/auth/app-store.d.ts +9 -0
- package/dist/src/tools/auth/app-store.js +34 -0
- package/dist/src/tools/auth/check.d.ts +14 -0
- package/dist/src/tools/auth/check.js +50 -0
- package/dist/src/tools/auth/play-store.d.ts +9 -0
- package/dist/src/tools/auth/play-store.js +30 -0
- package/dist/src/tools/release/check-versions.d.ts +14 -0
- package/dist/src/tools/release/check-versions.js +65 -0
- package/dist/src/tools/release/create.d.ts +23 -0
- package/dist/src/tools/release/create.js +128 -0
- package/dist/src/tools/release/pull-notes.d.ts +22 -0
- package/dist/src/tools/release/pull-notes.js +151 -0
- package/dist/src/tools/release/update-notes.d.ts +110 -0
- package/dist/src/tools/release/update-notes.js +537 -0
- package/package.json +71 -0
|
@@ -0,0 +1,537 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Release notes update tool
|
|
3
|
+
*
|
|
4
|
+
* Supports both App Store and Google Play.
|
|
5
|
+
* Automatically translates to all supported languages if text is provided.
|
|
6
|
+
*/
|
|
7
|
+
import { separateTranslationsByStore, collectSupportedLocales, } from "../../core/helpers/translate-release-notes.js";
|
|
8
|
+
import { AppResolutionService } from "../../core/services/app-resolution-service.js";
|
|
9
|
+
import { AppStoreService } from "../../core/services/app-store-service.js";
|
|
10
|
+
import { GooglePlayService } from "../../core/services/google-play-service.js";
|
|
11
|
+
import { formatReleaseNotesUpdate } from "../../core/helpers/formatters.js";
|
|
12
|
+
const appStoreService = new AppStoreService();
|
|
13
|
+
const googlePlayService = new GooglePlayService();
|
|
14
|
+
const appResolutionService = new AppResolutionService();
|
|
15
|
+
export async function handleUpdateNotes(options) {
|
|
16
|
+
const { app, versionId, whatsNew, text, sourceLocale = "en-US", store = "both", } = options;
|
|
17
|
+
let { bundleId, packageName } = options;
|
|
18
|
+
const { loadConfig } = await import("../../packages/configs/secrets-config/config.js");
|
|
19
|
+
const resolved = appResolutionService.resolve({
|
|
20
|
+
slug: app,
|
|
21
|
+
bundleId,
|
|
22
|
+
packageName,
|
|
23
|
+
});
|
|
24
|
+
if (!resolved.success) {
|
|
25
|
+
return {
|
|
26
|
+
content: [
|
|
27
|
+
{
|
|
28
|
+
type: "text",
|
|
29
|
+
text: resolved.error.message,
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
const { slug, bundleId: resolvedBundleId, packageName: resolvedPackageName, hasAppStore, hasGooglePlay, app: registeredApp, } = resolved.data;
|
|
35
|
+
bundleId = resolvedBundleId;
|
|
36
|
+
packageName = resolvedPackageName;
|
|
37
|
+
const includeAppStore = store === "both" || store === "appStore";
|
|
38
|
+
const includeGooglePlay = store === "both" || store === "googlePlay";
|
|
39
|
+
// Determine what to update
|
|
40
|
+
let finalWhatsNew = {};
|
|
41
|
+
if (whatsNew && Object.keys(whatsNew).length > 0) {
|
|
42
|
+
// Step 1: Get supported locales for the app
|
|
43
|
+
let config;
|
|
44
|
+
try {
|
|
45
|
+
config = loadConfig();
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
49
|
+
return {
|
|
50
|
+
content: [
|
|
51
|
+
{
|
|
52
|
+
type: "text",
|
|
53
|
+
text: `❌ Failed to load config: ${message}`,
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
isError: true,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
let appStoreLocales = [];
|
|
60
|
+
let googlePlayLocales = [];
|
|
61
|
+
// Check if locales are already in registered app
|
|
62
|
+
const { appStore: existingAppStoreLocales, googlePlay: existingGooglePlayLocales, } = collectSupportedLocales({ app: registeredApp, store });
|
|
63
|
+
// If locales are missing, fetch from API
|
|
64
|
+
if (includeAppStore && hasAppStore && bundleId) {
|
|
65
|
+
if (existingAppStoreLocales.length > 0) {
|
|
66
|
+
appStoreLocales = existingAppStoreLocales;
|
|
67
|
+
}
|
|
68
|
+
else if (config.appStore) {
|
|
69
|
+
// Fetch from App Store API
|
|
70
|
+
const appInfo = await appStoreService.fetchAppInfo(bundleId);
|
|
71
|
+
if (appInfo.found) {
|
|
72
|
+
if (appInfo.supportedLocales) {
|
|
73
|
+
appStoreLocales = appInfo.supportedLocales;
|
|
74
|
+
// Update registered app with fetched locales
|
|
75
|
+
if (registeredApp.appStore) {
|
|
76
|
+
registeredApp.appStore.supportedLocales =
|
|
77
|
+
appInfo.supportedLocales;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
else if (appInfo.error) {
|
|
82
|
+
console.error(`[MCP] ⚠️ Failed to fetch App Store locales: ${appInfo.error.message}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (includeGooglePlay && hasGooglePlay && packageName) {
|
|
87
|
+
if (existingGooglePlayLocales.length > 0) {
|
|
88
|
+
googlePlayLocales = existingGooglePlayLocales;
|
|
89
|
+
}
|
|
90
|
+
else if (config.playStore?.serviceAccountJson) {
|
|
91
|
+
// Fetch from Google Play API
|
|
92
|
+
const appInfo = await googlePlayService.fetchAppInfo(packageName);
|
|
93
|
+
if (appInfo.found) {
|
|
94
|
+
if (appInfo.supportedLocales) {
|
|
95
|
+
googlePlayLocales = appInfo.supportedLocales;
|
|
96
|
+
// Update registered app with fetched locales
|
|
97
|
+
if (registeredApp.googlePlay) {
|
|
98
|
+
registeredApp.googlePlay.supportedLocales =
|
|
99
|
+
appInfo.supportedLocales;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else if (appInfo.error) {
|
|
104
|
+
console.error(`[MCP] ⚠️ Failed to fetch Google Play locales: ${appInfo.error.message}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// Collect all unique supported locales
|
|
109
|
+
const allSupportedLocales = new Set();
|
|
110
|
+
if (appStoreLocales.length > 0) {
|
|
111
|
+
appStoreLocales.forEach((locale) => allSupportedLocales.add(locale));
|
|
112
|
+
}
|
|
113
|
+
if (googlePlayLocales.length > 0) {
|
|
114
|
+
googlePlayLocales.forEach((locale) => allSupportedLocales.add(locale));
|
|
115
|
+
}
|
|
116
|
+
// Step 2: Check if all supported locales are provided
|
|
117
|
+
const providedLocales = Object.keys(whatsNew);
|
|
118
|
+
const missingLocales = Array.from(allSupportedLocales).filter((locale) => !providedLocales.includes(locale));
|
|
119
|
+
// Step 3: Detect the language of provided whatsNew
|
|
120
|
+
const providedText = Object.values(whatsNew)[0]; // Get first provided text
|
|
121
|
+
const detectedLocale = providedLocales[0]; // Use first provided locale as detected locale
|
|
122
|
+
// If not all supported locales are provided, request translation
|
|
123
|
+
if (missingLocales.length > 0 && providedText) {
|
|
124
|
+
// Step 3a: If detected locale is not sourceLocale, translate to sourceLocale first
|
|
125
|
+
if (detectedLocale !== sourceLocale) {
|
|
126
|
+
return {
|
|
127
|
+
content: [
|
|
128
|
+
{
|
|
129
|
+
type: "text",
|
|
130
|
+
text: `🌐 Translation Pipeline Required
|
|
131
|
+
|
|
132
|
+
**Step 1: Translate to Default Locale**
|
|
133
|
+
|
|
134
|
+
**Detected Locale**: ${detectedLocale}
|
|
135
|
+
**Default Locale** (sourceLocale): ${sourceLocale}
|
|
136
|
+
|
|
137
|
+
**Text to translate** (${detectedLocale}):
|
|
138
|
+
${providedText}
|
|
139
|
+
|
|
140
|
+
**Instructions**:
|
|
141
|
+
1. First, translate the text from "${detectedLocale}" to "${sourceLocale}" (default locale)
|
|
142
|
+
2. Then, translate the ${sourceLocale} text to all missing supported locales
|
|
143
|
+
3. Call this function again with the \`whatsNew\` parameter containing all translations
|
|
144
|
+
|
|
145
|
+
**App Store Supported Locales** (${appStoreLocales.length}):
|
|
146
|
+
${appStoreLocales.length > 0 ? appStoreLocales.join(", ") : "N/A"}
|
|
147
|
+
|
|
148
|
+
**Google Play Supported Locales** (${googlePlayLocales.length}):
|
|
149
|
+
${googlePlayLocales.length > 0 ? googlePlayLocales.join(", ") : "N/A"}
|
|
150
|
+
|
|
151
|
+
**Provided Locales** (${providedLocales.length}):
|
|
152
|
+
${providedLocales.join(", ")}
|
|
153
|
+
|
|
154
|
+
**Missing Locales** (${missingLocales.length}):
|
|
155
|
+
${missingLocales.join(", ")}
|
|
156
|
+
|
|
157
|
+
**All Required Locales** (${Array.from(allSupportedLocales).length}):
|
|
158
|
+
${Array.from(allSupportedLocales).join(", ")}
|
|
159
|
+
|
|
160
|
+
Example:
|
|
161
|
+
\`\`\`json
|
|
162
|
+
{
|
|
163
|
+
"app": "${slug}",
|
|
164
|
+
"store": "${store}",
|
|
165
|
+
"whatsNew": {
|
|
166
|
+
"${detectedLocale}": "${providedText}",
|
|
167
|
+
"${sourceLocale}": "Translated to default locale",
|
|
168
|
+
"${missingLocales.join(`": "Translation", "`)}": "Translation",
|
|
169
|
+
...
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
\`\`\`
|
|
173
|
+
|
|
174
|
+
Note: Provide translations for ALL supported locales including the ones already provided.`,
|
|
175
|
+
},
|
|
176
|
+
],
|
|
177
|
+
_meta: {
|
|
178
|
+
translationPipeline: {
|
|
179
|
+
step: 1,
|
|
180
|
+
detectedLocale,
|
|
181
|
+
sourceLocale,
|
|
182
|
+
providedText,
|
|
183
|
+
providedLocales,
|
|
184
|
+
missingLocales: Array.from(missingLocales),
|
|
185
|
+
allSupportedLocales: Array.from(allSupportedLocales),
|
|
186
|
+
appStoreLocales,
|
|
187
|
+
googlePlayLocales,
|
|
188
|
+
},
|
|
189
|
+
registeredApp,
|
|
190
|
+
slug,
|
|
191
|
+
store,
|
|
192
|
+
versionId,
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
// Step 3b: If sourceLocale is already provided or detected, translate to all missing locales
|
|
197
|
+
const hasSourceLocale = providedLocales.includes(sourceLocale) ||
|
|
198
|
+
detectedLocale === sourceLocale;
|
|
199
|
+
const sourceText = whatsNew[sourceLocale] || providedText;
|
|
200
|
+
if (hasSourceLocale) {
|
|
201
|
+
return {
|
|
202
|
+
content: [
|
|
203
|
+
{
|
|
204
|
+
type: "text",
|
|
205
|
+
text: `🌐 Translation Required
|
|
206
|
+
|
|
207
|
+
**Step 2: Translate Default Locale to All Supported Locales**
|
|
208
|
+
|
|
209
|
+
**Source Text** (${sourceLocale}):
|
|
210
|
+
${sourceText}
|
|
211
|
+
|
|
212
|
+
**App Store Supported Locales** (${appStoreLocales.length}):
|
|
213
|
+
${appStoreLocales.length > 0 ? appStoreLocales.join(", ") : "N/A"}
|
|
214
|
+
|
|
215
|
+
**Google Play Supported Locales** (${googlePlayLocales.length}):
|
|
216
|
+
${googlePlayLocales.length > 0 ? googlePlayLocales.join(", ") : "N/A"}
|
|
217
|
+
|
|
218
|
+
**Already Provided Locales** (${providedLocales.length}):
|
|
219
|
+
${providedLocales.join(", ")}
|
|
220
|
+
|
|
221
|
+
**Missing Locales to Translate** (${missingLocales.length}):
|
|
222
|
+
${missingLocales.join(", ")}
|
|
223
|
+
|
|
224
|
+
**Instructions**:
|
|
225
|
+
Translate the ${sourceLocale} text to all missing supported locales and call this function again with the \`whatsNew\` parameter containing ALL translations (including the ones already provided).
|
|
226
|
+
|
|
227
|
+
Example:
|
|
228
|
+
\`\`\`json
|
|
229
|
+
{
|
|
230
|
+
"app": "${slug}",
|
|
231
|
+
"store": "${store}",
|
|
232
|
+
"whatsNew": {
|
|
233
|
+
"${providedLocales.join(`": "${whatsNew[providedLocales[0]]}", "`)}": "${whatsNew[providedLocales[0]]}",
|
|
234
|
+
"${sourceLocale}": "${sourceText}",
|
|
235
|
+
"${missingLocales.join(`": "Translation", "`)}": "Translation",
|
|
236
|
+
...
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
\`\`\`
|
|
240
|
+
|
|
241
|
+
Note: Provide translations for ALL supported locales. Include the already provided translations as well.`,
|
|
242
|
+
},
|
|
243
|
+
],
|
|
244
|
+
_meta: {
|
|
245
|
+
translationPipeline: {
|
|
246
|
+
step: 2,
|
|
247
|
+
sourceLocale,
|
|
248
|
+
sourceText,
|
|
249
|
+
providedLocales,
|
|
250
|
+
missingLocales: Array.from(missingLocales),
|
|
251
|
+
allSupportedLocales: Array.from(allSupportedLocales),
|
|
252
|
+
appStoreLocales,
|
|
253
|
+
googlePlayLocales,
|
|
254
|
+
},
|
|
255
|
+
registeredApp,
|
|
256
|
+
slug,
|
|
257
|
+
store,
|
|
258
|
+
versionId,
|
|
259
|
+
},
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
// All supported locales are provided, use directly
|
|
264
|
+
finalWhatsNew = whatsNew;
|
|
265
|
+
}
|
|
266
|
+
else if (text) {
|
|
267
|
+
// Step 1: Get supported locales for the app (from registered app or fetch from API)
|
|
268
|
+
let config;
|
|
269
|
+
try {
|
|
270
|
+
config = loadConfig();
|
|
271
|
+
}
|
|
272
|
+
catch (error) {
|
|
273
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
274
|
+
return {
|
|
275
|
+
content: [
|
|
276
|
+
{
|
|
277
|
+
type: "text",
|
|
278
|
+
text: `❌ Failed to load config: ${message}`,
|
|
279
|
+
},
|
|
280
|
+
],
|
|
281
|
+
isError: true,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
let appStoreLocales = [];
|
|
285
|
+
let googlePlayLocales = [];
|
|
286
|
+
// Check if locales are already in registered app
|
|
287
|
+
const { appStore: existingAppStoreLocales, googlePlay: existingGooglePlayLocales, } = collectSupportedLocales({ app: registeredApp, store });
|
|
288
|
+
// If locales are missing, fetch from API
|
|
289
|
+
if (includeAppStore && hasAppStore && bundleId) {
|
|
290
|
+
if (existingAppStoreLocales.length > 0) {
|
|
291
|
+
appStoreLocales = existingAppStoreLocales;
|
|
292
|
+
}
|
|
293
|
+
else if (config.appStore) {
|
|
294
|
+
// Fetch from App Store API
|
|
295
|
+
const appInfo = await appStoreService.fetchAppInfo(bundleId);
|
|
296
|
+
if (appInfo.found) {
|
|
297
|
+
if (appInfo.supportedLocales) {
|
|
298
|
+
appStoreLocales = appInfo.supportedLocales;
|
|
299
|
+
// Update registered app with fetched locales
|
|
300
|
+
if (registeredApp.appStore) {
|
|
301
|
+
registeredApp.appStore.supportedLocales =
|
|
302
|
+
appInfo.supportedLocales;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
else if (appInfo.error) {
|
|
307
|
+
console.error(`[MCP] ⚠️ Failed to fetch App Store locales: ${appInfo.error.message}`);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
if (includeGooglePlay && hasGooglePlay && packageName) {
|
|
312
|
+
if (existingGooglePlayLocales.length > 0) {
|
|
313
|
+
googlePlayLocales = existingGooglePlayLocales;
|
|
314
|
+
}
|
|
315
|
+
else if (config.playStore?.serviceAccountJson) {
|
|
316
|
+
// Fetch from Google Play API
|
|
317
|
+
const appInfo = await googlePlayService.fetchAppInfo(packageName);
|
|
318
|
+
if (appInfo.found) {
|
|
319
|
+
if (appInfo.supportedLocales) {
|
|
320
|
+
googlePlayLocales = appInfo.supportedLocales;
|
|
321
|
+
// Update registered app with fetched locales
|
|
322
|
+
if (registeredApp.googlePlay) {
|
|
323
|
+
registeredApp.googlePlay.supportedLocales =
|
|
324
|
+
appInfo.supportedLocales;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
else if (appInfo.error) {
|
|
329
|
+
console.error(`[MCP] ⚠️ Failed to fetch Google Play locales: ${appInfo.error.message}`);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
// Collect all unique locales that need translation
|
|
334
|
+
const allLocales = new Set();
|
|
335
|
+
if (appStoreLocales.length > 0) {
|
|
336
|
+
appStoreLocales.forEach((locale) => allLocales.add(locale));
|
|
337
|
+
}
|
|
338
|
+
if (googlePlayLocales.length > 0) {
|
|
339
|
+
googlePlayLocales.forEach((locale) => allLocales.add(locale));
|
|
340
|
+
}
|
|
341
|
+
// If no locales found, return error
|
|
342
|
+
if (allLocales.size === 0) {
|
|
343
|
+
return {
|
|
344
|
+
content: [
|
|
345
|
+
{
|
|
346
|
+
type: "text",
|
|
347
|
+
text: `❌ No supported locales found for the app. Please ensure the app is registered and has supported locales configured, or check authentication settings.`,
|
|
348
|
+
},
|
|
349
|
+
],
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
// Add source locale if not already in the set
|
|
353
|
+
allLocales.add(sourceLocale);
|
|
354
|
+
const targetLocales = Array.from(allLocales).filter((locale) => locale !== sourceLocale);
|
|
355
|
+
// Step 2: Return translation request
|
|
356
|
+
// The calling LLM should perform the translation and call this function again with whatsNew
|
|
357
|
+
return {
|
|
358
|
+
content: [
|
|
359
|
+
{
|
|
360
|
+
type: "text",
|
|
361
|
+
text: `🌐 Translation Required
|
|
362
|
+
|
|
363
|
+
**Source Text** (${sourceLocale}):
|
|
364
|
+
${text}
|
|
365
|
+
|
|
366
|
+
**App Store Supported Locales** (${appStoreLocales.length}):
|
|
367
|
+
${appStoreLocales.length > 0 ? appStoreLocales.join(", ") : "N/A"}
|
|
368
|
+
|
|
369
|
+
**Google Play Supported Locales** (${googlePlayLocales.length}):
|
|
370
|
+
${googlePlayLocales.length > 0 ? googlePlayLocales.join(", ") : "N/A"}
|
|
371
|
+
|
|
372
|
+
**All Target Locales to Translate** (${targetLocales.length}):
|
|
373
|
+
${targetLocales.join(", ")}
|
|
374
|
+
|
|
375
|
+
**Instructions**:
|
|
376
|
+
Please translate the text to all target locales and call this function again with the \`whatsNew\` parameter containing all translations.
|
|
377
|
+
|
|
378
|
+
Example:
|
|
379
|
+
\`\`\`json
|
|
380
|
+
{
|
|
381
|
+
"app": "${slug}",
|
|
382
|
+
"store": "${store}",
|
|
383
|
+
"whatsNew": {
|
|
384
|
+
"${sourceLocale}": "${text}",
|
|
385
|
+
"ko": "번역된 텍스트",
|
|
386
|
+
"ko-KR": "번역된 텍스트",
|
|
387
|
+
"en-US": "Translated text",
|
|
388
|
+
...
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
\`\`\`
|
|
392
|
+
|
|
393
|
+
Note: App Store and Google Play may use different locale formats (e.g., "ko" vs "ko-KR"). Please provide translations for all supported locales.`,
|
|
394
|
+
},
|
|
395
|
+
],
|
|
396
|
+
_meta: {
|
|
397
|
+
translationRequests: {
|
|
398
|
+
appStore: appStoreLocales.length > 0
|
|
399
|
+
? {
|
|
400
|
+
sourceText: text,
|
|
401
|
+
sourceLocale,
|
|
402
|
+
targetLocales: appStoreLocales.filter((l) => l !== sourceLocale),
|
|
403
|
+
store: "appStore",
|
|
404
|
+
}
|
|
405
|
+
: undefined,
|
|
406
|
+
googlePlay: googlePlayLocales.length > 0
|
|
407
|
+
? {
|
|
408
|
+
sourceText: text,
|
|
409
|
+
sourceLocale,
|
|
410
|
+
targetLocales: googlePlayLocales.filter((l) => l !== sourceLocale),
|
|
411
|
+
store: "googlePlay",
|
|
412
|
+
}
|
|
413
|
+
: undefined,
|
|
414
|
+
},
|
|
415
|
+
registeredApp,
|
|
416
|
+
slug,
|
|
417
|
+
store,
|
|
418
|
+
versionId,
|
|
419
|
+
},
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
else {
|
|
423
|
+
return {
|
|
424
|
+
content: [
|
|
425
|
+
{
|
|
426
|
+
type: "text",
|
|
427
|
+
text: "❌ Either whatsNew or text is required. Provide whatsNew directly or text to translate to all supported languages.",
|
|
428
|
+
},
|
|
429
|
+
],
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
// Continue with update logic only if finalWhatsNew is set
|
|
433
|
+
if (Object.keys(finalWhatsNew).length === 0) {
|
|
434
|
+
return {
|
|
435
|
+
content: [
|
|
436
|
+
{
|
|
437
|
+
type: "text",
|
|
438
|
+
text: "❌ No release notes to update.",
|
|
439
|
+
},
|
|
440
|
+
],
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
const config = loadConfig();
|
|
444
|
+
console.error(`[MCP] 📝 Updating release notes`);
|
|
445
|
+
console.error(`[MCP] Store: ${store}`);
|
|
446
|
+
console.error(`[MCP] App: ${slug}`);
|
|
447
|
+
if (packageName)
|
|
448
|
+
console.error(`[MCP] Package Name: ${packageName}`);
|
|
449
|
+
if (bundleId)
|
|
450
|
+
console.error(`[MCP] Bundle ID: ${bundleId}`);
|
|
451
|
+
if (versionId)
|
|
452
|
+
console.error(`[MCP] Version ID: ${versionId}`);
|
|
453
|
+
const results = [];
|
|
454
|
+
const appStoreResults = [];
|
|
455
|
+
const googlePlayResults = [];
|
|
456
|
+
// Separate translations by store
|
|
457
|
+
const { appStore: appStoreTranslations, googlePlay: googlePlayTranslations } = separateTranslationsByStore({
|
|
458
|
+
translations: finalWhatsNew,
|
|
459
|
+
app: registeredApp,
|
|
460
|
+
sourceLocale,
|
|
461
|
+
store,
|
|
462
|
+
});
|
|
463
|
+
console.error(`[MCP] 📝 Locales to update: ${Object.keys(finalWhatsNew).length}`);
|
|
464
|
+
if (Object.keys(appStoreTranslations).length > 0) {
|
|
465
|
+
console.error(`[MCP] 🍎 App Store locales: ${Object.keys(appStoreTranslations).join(", ")}`);
|
|
466
|
+
}
|
|
467
|
+
if (Object.keys(googlePlayTranslations).length > 0) {
|
|
468
|
+
console.error(`[MCP] 🤖 Google Play locales: ${Object.keys(googlePlayTranslations).join(", ")}`);
|
|
469
|
+
}
|
|
470
|
+
// App Store update
|
|
471
|
+
if ((store === "both" || store === "appStore") && bundleId) {
|
|
472
|
+
console.error(`[MCP] 📤 Updating App Store release notes...`);
|
|
473
|
+
if (!config.appStore) {
|
|
474
|
+
appStoreResults.push("❌ App Store authentication not configured.");
|
|
475
|
+
}
|
|
476
|
+
else if (Object.keys(appStoreTranslations).length === 0) {
|
|
477
|
+
appStoreResults.push("⚠️ No translations available for App Store locales.");
|
|
478
|
+
}
|
|
479
|
+
else {
|
|
480
|
+
const updateResult = await appStoreService.updateReleaseNotes(bundleId, appStoreTranslations, versionId, registeredApp.appStore?.supportedLocales);
|
|
481
|
+
if (!updateResult.success) {
|
|
482
|
+
appStoreResults.push(`❌ App Store release notes update failed: ${updateResult.error.message}`);
|
|
483
|
+
}
|
|
484
|
+
else {
|
|
485
|
+
console.error(`[MCP] ✅ Updated ${updateResult.data.updated.length} locales`);
|
|
486
|
+
appStoreResults.push(...formatReleaseNotesUpdate("App Store", updateResult.data));
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
// Google Play update
|
|
491
|
+
if ((store === "both" || store === "googlePlay") && packageName) {
|
|
492
|
+
if (!config.playStore?.serviceAccountJson) {
|
|
493
|
+
googlePlayResults.push("❌ Google Play authentication not configured.");
|
|
494
|
+
}
|
|
495
|
+
else if (Object.keys(googlePlayTranslations).length === 0) {
|
|
496
|
+
googlePlayResults.push("⚠️ No translations available for Google Play locales.");
|
|
497
|
+
}
|
|
498
|
+
else {
|
|
499
|
+
console.error(`[MCP] 📤 Updating Google Play release notes...`);
|
|
500
|
+
const updateResult = await googlePlayService.updateReleaseNotes(packageName, googlePlayTranslations, "production", registeredApp.googlePlay?.supportedLocales);
|
|
501
|
+
if (!updateResult.success) {
|
|
502
|
+
googlePlayResults.push(`❌ Google Play release notes update failed: ${updateResult.error.message}`);
|
|
503
|
+
}
|
|
504
|
+
else {
|
|
505
|
+
console.error(`[MCP] ✅ Updated ${updateResult.data.updated.length} locales`);
|
|
506
|
+
googlePlayResults.push(...formatReleaseNotesUpdate("Google Play", updateResult.data));
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
// Combine results
|
|
511
|
+
if (appStoreResults.length > 0) {
|
|
512
|
+
results.push(`**🍎 App Store:**`);
|
|
513
|
+
results.push(...appStoreResults.map((r) => ` ${r}`));
|
|
514
|
+
}
|
|
515
|
+
if (googlePlayResults.length > 0) {
|
|
516
|
+
results.push(`**🤖 Google Play:**`);
|
|
517
|
+
results.push(...googlePlayResults.map((r) => ` ${r}`));
|
|
518
|
+
}
|
|
519
|
+
if (results.length === 0) {
|
|
520
|
+
return {
|
|
521
|
+
content: [
|
|
522
|
+
{
|
|
523
|
+
type: "text",
|
|
524
|
+
text: "⚠️ No store to update. Please check bundleId or packageName.",
|
|
525
|
+
},
|
|
526
|
+
],
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
return {
|
|
530
|
+
content: [
|
|
531
|
+
{
|
|
532
|
+
type: "text",
|
|
533
|
+
text: `📝 Release Notes Update Results:\n\n${results.join("\n")}`,
|
|
534
|
+
},
|
|
535
|
+
],
|
|
536
|
+
};
|
|
537
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pabal-store-api-mcp",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "MCP server for App Store / Play Store ASO workflows",
|
|
5
|
+
"main": "dist/src/index.js",
|
|
6
|
+
"types": "dist/src/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"bin": {
|
|
9
|
+
"pabal-mcp": "./bin/pabal-mcp.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist/src",
|
|
13
|
+
"bin"
|
|
14
|
+
],
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/src/index.d.ts",
|
|
18
|
+
"import": "./dist/src/index.js"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"dev:mcp": "tsx src/index.ts",
|
|
23
|
+
"test": "tsx --test \"tests/**/*.test.ts\"",
|
|
24
|
+
"build": "tsc && tsc-alias --resolve-full-paths",
|
|
25
|
+
"typecheck": "tsc --noEmit",
|
|
26
|
+
"format": "prettier --write --ignore-unknown .",
|
|
27
|
+
"format:check": "prettier --check --ignore-unknown .",
|
|
28
|
+
"prepare": "husky",
|
|
29
|
+
"prepublishOnly": "yarn build"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"mcp",
|
|
33
|
+
"app-store",
|
|
34
|
+
"play-store",
|
|
35
|
+
"aso",
|
|
36
|
+
"model-context-protocol"
|
|
37
|
+
],
|
|
38
|
+
"author": "",
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=18"
|
|
42
|
+
},
|
|
43
|
+
"repository": {
|
|
44
|
+
"type": "git",
|
|
45
|
+
"url": "git+https://github.com/quartz-labs-dev/pabal-store-api-mcp.git"
|
|
46
|
+
},
|
|
47
|
+
"bugs": {
|
|
48
|
+
"url": "https://github.com/quartz-labs-dev/pabal-store-api-mcp/issues"
|
|
49
|
+
},
|
|
50
|
+
"homepage": "https://github.com/quartz-labs-dev/pabal-store-api-mcp#readme",
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"@modelcontextprotocol/sdk": "^1.22.0",
|
|
53
|
+
"appstore-connect-sdk": "^1.3.2",
|
|
54
|
+
"googleapis": "^144.0.0",
|
|
55
|
+
"jose": "^6.1.2",
|
|
56
|
+
"zod": "^3.25.76"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@types/node": "^24.10.1",
|
|
60
|
+
"husky": "^9.1.7",
|
|
61
|
+
"lint-staged": "^16.2.7",
|
|
62
|
+
"prettier": "^3.7.3",
|
|
63
|
+
"tsc-alias": "^1.8.16",
|
|
64
|
+
"tsconfig-paths": "^4.2.0",
|
|
65
|
+
"tsx": "^4.20.6",
|
|
66
|
+
"typescript": "^5.9.3"
|
|
67
|
+
},
|
|
68
|
+
"lint-staged": {
|
|
69
|
+
"*": "prettier --write --ignore-unknown"
|
|
70
|
+
}
|
|
71
|
+
}
|