pabal-web-mcp 1.3.1 → 1.3.3
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 +38 -12
- package/package.json +1 -1
package/dist/bin/mcp-server.js
CHANGED
|
@@ -2448,6 +2448,9 @@ var keywordResearchInputSchema = z6.object({
|
|
|
2448
2448
|
writeTemplate: z6.boolean().default(false).describe("If true, write a JSON template at the output path."),
|
|
2449
2449
|
researchData: z6.string().trim().optional().describe(
|
|
2450
2450
|
"Optional JSON string with research results (e.g., from mcp-appstore tools). If provided, saves it to the output path."
|
|
2451
|
+
),
|
|
2452
|
+
researchDataPath: z6.string().trim().optional().describe(
|
|
2453
|
+
"Optional path to a JSON file containing research results. If set, file content is saved to the output path (preferred to avoid escape errors)."
|
|
2451
2454
|
)
|
|
2452
2455
|
});
|
|
2453
2456
|
var jsonSchema6 = zodToJsonSchema6(keywordResearchInputSchema, {
|
|
@@ -2489,9 +2492,10 @@ function buildTemplate({
|
|
|
2489
2492
|
"Discover competitors: search_app(term=seed keyword), get_similar_apps(appId=known competitor).",
|
|
2490
2493
|
"Collect candidates: suggest_keywords_by_seeds/by_category/by_similarity/by_competition/by_search + suggest_keywords_by_apps(apps=[top competitors]).",
|
|
2491
2494
|
"Score shortlist: get_keyword_scores for 15\u201330 candidates per platform/country.",
|
|
2492
|
-
"Context check: analyze_reviews and fetch_reviews on top apps for language/tone cues."
|
|
2495
|
+
"Context check: analyze_reviews and fetch_reviews on top apps for language/tone cues.",
|
|
2496
|
+
"If keywordSuggestions/similar/reviews are sparse, rerun calls (add more competitors/seeds) until you have 10\u201315 strong keywords."
|
|
2493
2497
|
],
|
|
2494
|
-
note: "Run per platform/country. Save raw tool outputs plus curated top keywords."
|
|
2498
|
+
note: "Run per platform/country. Save raw tool outputs plus curated top keywords (target 10\u201315 per locale: 2\u20133 high-traffic core, 4\u20136 mid-competition, 4\u20136 longtail)."
|
|
2495
2499
|
},
|
|
2496
2500
|
data: {
|
|
2497
2501
|
raw: {
|
|
@@ -2513,7 +2517,7 @@ function buildTemplate({
|
|
|
2513
2517
|
summary: {
|
|
2514
2518
|
recommendedKeywords: [],
|
|
2515
2519
|
rationale: "",
|
|
2516
|
-
nextActions: "Feed
|
|
2520
|
+
nextActions: "Feed 10\u201315 mixed keywords (core/mid/longtail) into improve-public Stage 1."
|
|
2517
2521
|
}
|
|
2518
2522
|
}
|
|
2519
2523
|
};
|
|
@@ -2545,7 +2549,8 @@ async function handleKeywordResearch(input) {
|
|
|
2545
2549
|
competitorApps = [],
|
|
2546
2550
|
filename,
|
|
2547
2551
|
writeTemplate = false,
|
|
2548
|
-
researchData
|
|
2552
|
+
researchData,
|
|
2553
|
+
researchDataPath
|
|
2549
2554
|
} = input;
|
|
2550
2555
|
const { config, locales } = loadProductLocales(slug);
|
|
2551
2556
|
const primaryLocale = resolvePrimaryLocale(config, locales);
|
|
@@ -2608,16 +2613,34 @@ async function handleKeywordResearch(input) {
|
|
|
2608
2613
|
const fileName = filename || defaultFileName;
|
|
2609
2614
|
let outputPath = path11.join(researchDir, fileName);
|
|
2610
2615
|
let fileAction;
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
+
const parseJsonWithContext = (text) => {
|
|
2617
|
+
try {
|
|
2618
|
+
return JSON.parse(text);
|
|
2619
|
+
} catch (err) {
|
|
2620
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2621
|
+
const match = /position (\d+)/i.exec(message) || /column (\d+)/i.exec(message) || /char (\d+)/i.exec(message);
|
|
2622
|
+
if (match) {
|
|
2623
|
+
const pos = Number(match[1]);
|
|
2624
|
+
const start = Math.max(0, pos - 40);
|
|
2625
|
+
const end = Math.min(text.length, pos + 40);
|
|
2626
|
+
const context = text.slice(start, end);
|
|
2616
2627
|
throw new Error(
|
|
2617
|
-
`Failed to parse researchData JSON: ${
|
|
2628
|
+
`Failed to parse researchData JSON: ${message}
|
|
2629
|
+
Context around ${pos}: ${context}`
|
|
2618
2630
|
);
|
|
2619
2631
|
}
|
|
2620
|
-
|
|
2632
|
+
throw new Error(`Failed to parse researchData JSON: ${message}`);
|
|
2633
|
+
}
|
|
2634
|
+
};
|
|
2635
|
+
const loadResearchDataFromPath = (p) => {
|
|
2636
|
+
if (!fs11.existsSync(p)) {
|
|
2637
|
+
throw new Error(`researchDataPath not found: ${p}`);
|
|
2638
|
+
}
|
|
2639
|
+
const raw = fs11.readFileSync(p, "utf-8");
|
|
2640
|
+
return parseJsonWithContext(raw);
|
|
2641
|
+
};
|
|
2642
|
+
if (writeTemplate || researchData) {
|
|
2643
|
+
const payload = researchData ? parseJsonWithContext(researchData) : researchDataPath ? loadResearchDataFromPath(researchDataPath) : buildTemplate({
|
|
2621
2644
|
slug,
|
|
2622
2645
|
locale,
|
|
2623
2646
|
platform,
|
|
@@ -2687,7 +2710,10 @@ async function handleKeywordResearch(input) {
|
|
|
2687
2710
|
`6) Context check: analyze_reviews and fetch_reviews on top apps to harvest native phrasing; keep snippets for improve-public.`
|
|
2688
2711
|
);
|
|
2689
2712
|
lines.push(
|
|
2690
|
-
`7) Save all raw responses + your final
|
|
2713
|
+
`7) Save all raw responses + your final 10\u201315 keywords (mix of core/high-traffic, mid, longtail) to: ${outputPath} (structure mirrors .aso/pullData/.aso/pushData under products/<slug>/locales/<locale>)`
|
|
2714
|
+
);
|
|
2715
|
+
lines.push(
|
|
2716
|
+
`8) If keywordSuggestions/similarApps/reviews are still empty or <10 solid candidates, add more competitors/seeds and rerun the calls above until you reach 10\u201315 strong keywords.`
|
|
2691
2717
|
);
|
|
2692
2718
|
if (fileAction) {
|
|
2693
2719
|
lines.push(`File: ${fileAction} at ${outputPath}`);
|