pabal-web-mcp 1.4.3 → 1.4.4
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/dist/bin/mcp-server.js +77 -5
- package/package.json +1 -1
package/dist/bin/mcp-server.js
CHANGED
|
@@ -517,6 +517,19 @@ function convertToMultilingual(data, locale) {
|
|
|
517
517
|
// src/tools/public-to-aso.ts
|
|
518
518
|
import fs4 from "fs";
|
|
519
519
|
var FIELD_LIMITS_DOC_PATH = "docs/aso/ASO_FIELD_LIMITS.md";
|
|
520
|
+
var APP_STORE_LIMITS = {
|
|
521
|
+
name: 30,
|
|
522
|
+
subtitle: 30,
|
|
523
|
+
keywords: 100,
|
|
524
|
+
promotionalText: 170,
|
|
525
|
+
description: 4e3,
|
|
526
|
+
whatsNew: 4e3
|
|
527
|
+
};
|
|
528
|
+
var GOOGLE_PLAY_LIMITS = {
|
|
529
|
+
title: 50,
|
|
530
|
+
shortDescription: 80,
|
|
531
|
+
fullDescription: 4e3
|
|
532
|
+
};
|
|
520
533
|
var toJsonSchema2 = zodToJsonSchema2;
|
|
521
534
|
var publicToAsoInputSchema = z2.object({
|
|
522
535
|
slug: z2.string().describe("Product slug"),
|
|
@@ -527,6 +540,51 @@ var jsonSchema2 = toJsonSchema2(publicToAsoInputSchema, {
|
|
|
527
540
|
$refStrategy: "none"
|
|
528
541
|
});
|
|
529
542
|
var inputSchema2 = jsonSchema2.definitions?.PublicToAsoInput || jsonSchema2;
|
|
543
|
+
function validateFieldLimits(configData) {
|
|
544
|
+
const issues = [];
|
|
545
|
+
if (configData.appStore) {
|
|
546
|
+
const appStoreData = configData.appStore;
|
|
547
|
+
const locales = isAppStoreMultilingual(appStoreData) ? appStoreData.locales : { [appStoreData.locale || DEFAULT_LOCALE]: appStoreData };
|
|
548
|
+
for (const [locale, data] of Object.entries(locales)) {
|
|
549
|
+
const { name, subtitle, keywords, promotionalText, description, whatsNew } = data;
|
|
550
|
+
const push = (field, value, limit) => {
|
|
551
|
+
if (typeof value === "string" && value.length > limit) {
|
|
552
|
+
issues.push(
|
|
553
|
+
`App Store [${locale}].${field}: ${value.length}/${limit}`
|
|
554
|
+
);
|
|
555
|
+
}
|
|
556
|
+
};
|
|
557
|
+
push("name", name, APP_STORE_LIMITS.name);
|
|
558
|
+
push("subtitle", subtitle, APP_STORE_LIMITS.subtitle);
|
|
559
|
+
push("keywords", keywords, APP_STORE_LIMITS.keywords);
|
|
560
|
+
push("promotionalText", promotionalText, APP_STORE_LIMITS.promotionalText);
|
|
561
|
+
push("description", description, APP_STORE_LIMITS.description);
|
|
562
|
+
push("whatsNew", whatsNew, APP_STORE_LIMITS.whatsNew);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
if (configData.googlePlay) {
|
|
566
|
+
const googlePlayData = configData.googlePlay;
|
|
567
|
+
const locales = isGooglePlayMultilingual(googlePlayData) ? googlePlayData.locales : { [googlePlayData.defaultLanguage || DEFAULT_LOCALE]: googlePlayData };
|
|
568
|
+
for (const [locale, data] of Object.entries(locales)) {
|
|
569
|
+
const { title, shortDescription, fullDescription } = data;
|
|
570
|
+
const push = (field, value, limit) => {
|
|
571
|
+
if (typeof value === "string" && value.length > limit) {
|
|
572
|
+
issues.push(
|
|
573
|
+
`Google Play [${locale}].${field}: ${value.length}/${limit}`
|
|
574
|
+
);
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
push("title", title, GOOGLE_PLAY_LIMITS.title);
|
|
578
|
+
push(
|
|
579
|
+
"shortDescription",
|
|
580
|
+
shortDescription,
|
|
581
|
+
GOOGLE_PLAY_LIMITS.shortDescription
|
|
582
|
+
);
|
|
583
|
+
push("fullDescription", fullDescription, GOOGLE_PLAY_LIMITS.fullDescription);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
return issues;
|
|
587
|
+
}
|
|
530
588
|
async function downloadScreenshotsToAsoDir(slug, asoData) {
|
|
531
589
|
const rootDir = getPushDataDir();
|
|
532
590
|
const productStoreRoot = path4.join(rootDir, "products", slug, "store");
|
|
@@ -672,6 +730,7 @@ This tool:
|
|
|
672
730
|
2. Converts to store-compatible format (removes screenshots from metadata, sets contactWebsite/marketingUrl)
|
|
673
731
|
3. Saves metadata to .aso/pushData/products/[slug]/store/ (path from ~/.config/pabal-mcp/config.json dataDir)
|
|
674
732
|
4. Copies/downloads screenshots to .aso/pushData/products/[slug]/store/screenshots/
|
|
733
|
+
5. Validates text field lengths against ${FIELD_LIMITS_DOC_PATH} (fails if over limits)
|
|
675
734
|
|
|
676
735
|
Before running, review ${FIELD_LIMITS_DOC_PATH} for per-store limits. This prepares data for pushing to stores without actually uploading.`,
|
|
677
736
|
inputSchema: inputSchema2
|
|
@@ -761,6 +820,11 @@ Possible causes:
|
|
|
761
820
|
);
|
|
762
821
|
}
|
|
763
822
|
const storeData = prepareAsoDataForPush(slug, configData);
|
|
823
|
+
const validationIssues = validateFieldLimits(configData);
|
|
824
|
+
const validationMessage = validationIssues.length > 0 ? `\u26A0\uFE0F Field limit issues (see ${FIELD_LIMITS_DOC_PATH}):
|
|
825
|
+
- ${validationIssues.join(
|
|
826
|
+
"\n- "
|
|
827
|
+
)}` : `Field limits OK (checked against ${FIELD_LIMITS_DOC_PATH})`;
|
|
764
828
|
const pushDataRoot = getPushDataDir();
|
|
765
829
|
if (dryRun) {
|
|
766
830
|
return {
|
|
@@ -773,11 +837,19 @@ ${JSON.stringify(
|
|
|
773
837
|
storeData,
|
|
774
838
|
null,
|
|
775
839
|
2
|
|
776
|
-
)}
|
|
840
|
+
)}
|
|
841
|
+
|
|
842
|
+
${validationMessage}`
|
|
777
843
|
}
|
|
778
844
|
]
|
|
779
845
|
};
|
|
780
846
|
}
|
|
847
|
+
if (validationIssues.length > 0) {
|
|
848
|
+
throw new Error(
|
|
849
|
+
`Field limit violations detected. Fix before pushing.
|
|
850
|
+
${validationMessage}`
|
|
851
|
+
);
|
|
852
|
+
}
|
|
781
853
|
saveRawAsoData(slug, storeData);
|
|
782
854
|
await downloadScreenshotsToAsoDir(slug, configData);
|
|
783
855
|
const localeCounts = {};
|
|
@@ -1314,10 +1386,10 @@ ${researchSections.join("\n")}
|
|
|
1314
1386
|
prompt += `**CRITICAL**: You MUST include the complete \`landing\` object in your optimized JSON output.
|
|
1315
1387
|
|
|
1316
1388
|
`;
|
|
1317
|
-
prompt += `## Step 3: Validate
|
|
1389
|
+
prompt += `## Step 3: Validate (after applying all keywords)
|
|
1318
1390
|
|
|
1319
1391
|
`;
|
|
1320
|
-
prompt += `Check all limits: title \u226430, subtitle \u226430, shortDescription \u226480, keywords \u2264100, intro \u2264300, outro \u2264200
|
|
1392
|
+
prompt += `Check all limits using ${FIELD_LIMITS_DOC_PATH2}: title \u226430, subtitle \u226430, shortDescription \u226480, keywords \u2264100, intro \u2264300, outro \u2264200
|
|
1321
1393
|
`;
|
|
1322
1394
|
prompt += `- Remove keyword duplicates (unique list; avoid repeating title/subtitle terms verbatim)
|
|
1323
1395
|
`;
|
|
@@ -1436,7 +1508,7 @@ function generateKeywordLocalizationPrompt(args) {
|
|
|
1436
1508
|
`;
|
|
1437
1509
|
prompt += `2. **Replace ONLY keywords with optimized keywords** - keep ALL existing content, structure, tone, and context unchanged. Only swap keywords for better ASO keywords.
|
|
1438
1510
|
`;
|
|
1439
|
-
prompt += `3.
|
|
1511
|
+
prompt += `3. After all keywords are applied, validate character limits + store rules (${FIELD_LIMITS_DOC_PATH2}) + keyword duplication
|
|
1440
1512
|
`;
|
|
1441
1513
|
prompt += `4. **SAVE the updated JSON to file** using the save-locale-file tool (only if file exists)
|
|
1442
1514
|
|
|
@@ -1633,7 +1705,7 @@ ${researchSections.join(
|
|
|
1633
1705
|
`;
|
|
1634
1706
|
prompt += `3. **CRITICAL**: Ensure ALL landing fields are translated (not English)
|
|
1635
1707
|
`;
|
|
1636
|
-
prompt += `4.
|
|
1708
|
+
prompt += `4. After swapping keywords, validate limits + store rules (${FIELD_LIMITS_DOC_PATH2}) + keyword duplication (unique list; avoid repeating title/subtitle terms verbatim)
|
|
1637
1709
|
`;
|
|
1638
1710
|
prompt += `5. **SAVE the updated JSON to file** using save-locale-file tool
|
|
1639
1711
|
`;
|