pabal-web-mcp 0.1.0 → 0.1.1

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 CHANGED
@@ -8,6 +8,164 @@ MCP (Model Context Protocol) server for ASO (App Store Optimization) data manage
8
8
  npm install pabal-web-mcp
9
9
  ```
10
10
 
11
+ ## 🛠️ MCP Client Installation
12
+
13
+ ### Requirements
14
+
15
+ - Node.js >= 18
16
+ - MCP client: Cursor, Claude Code, VS Code, Windsurf, etc.
17
+
18
+ > [!TIP]
19
+ > If you repeatedly do ASO/store tasks, add a client rule like "always use pabal-web-mcp" so the MCP server auto-invokes without typing it every time.
20
+
21
+ ### Global install (recommended)
22
+
23
+ ```bash
24
+ npm install -g pabal-web-mcp
25
+
26
+ # or
27
+
28
+ yarn global add pabal-web-mcp
29
+ ```
30
+
31
+ Install globally first for fastest starts and to avoid npm download issues (proxy/firewall/offline). You can still use `npx -y pabal-web-mcp`, but global install is recommended. After global install, set your MCP config to `command: "pabal-web-mcp"` (no `npx` needed).
32
+
33
+ <details>
34
+ <summary><b>Install in Cursor</b></summary>
35
+
36
+ Add to `~/.cursor/mcp.json` (global) or project `.cursor/mcp.json`:
37
+
38
+ ```json
39
+ {
40
+ "mcpServers": {
41
+ "pabal-web-mcp": {
42
+ "command": "npx",
43
+ "args": ["-y", "pabal-web-mcp"]
44
+ }
45
+ }
46
+ }
47
+ ```
48
+
49
+ Or if installed globally:
50
+
51
+ ```json
52
+ {
53
+ "mcpServers": {
54
+ "pabal-web-mcp": {
55
+ "command": "pabal-web-mcp"
56
+ }
57
+ }
58
+ }
59
+ ```
60
+
61
+ </details>
62
+
63
+ <details>
64
+ <summary><b>Install in VS Code</b></summary>
65
+
66
+ Example `settings.json` MCP section:
67
+
68
+ ```json
69
+ "mcp": {
70
+ "servers": {
71
+ "pabal-web-mcp": {
72
+ "type": "stdio",
73
+ "command": "npx",
74
+ "args": ["-y", "pabal-web-mcp"]
75
+ }
76
+ }
77
+ }
78
+ ```
79
+
80
+ Or if installed globally:
81
+
82
+ ```json
83
+ "mcp": {
84
+ "servers": {
85
+ "pabal-web-mcp": {
86
+ "type": "stdio",
87
+ "command": "pabal-web-mcp"
88
+ }
89
+ }
90
+ }
91
+ ```
92
+
93
+ </details>
94
+
95
+ <details>
96
+ <summary><b>Install in Claude Code</b></summary>
97
+
98
+ > [!TIP]
99
+ > See the [official Claude Code MCP documentation](https://code.claude.com/docs/en/mcp#setting-up-enterprise-mcp-configuration) for detailed configuration options.
100
+
101
+ Add to Claude Code MCP settings (JSON format):
102
+
103
+ ```json
104
+ {
105
+ "mcpServers": {
106
+ "pabal-web-mcp": {
107
+ "command": "npx",
108
+ "args": ["-y", "pabal-web-mcp"]
109
+ }
110
+ }
111
+ }
112
+ ```
113
+
114
+ Or if installed globally (`npm install -g pabal-web-mcp`):
115
+
116
+ ```json
117
+ {
118
+ "mcpServers": {
119
+ "pabal-web-mcp": {
120
+ "command": "pabal-web-mcp"
121
+ }
122
+ }
123
+ }
124
+ ```
125
+
126
+ </details>
127
+
128
+ <details>
129
+ <summary><b>Install in Windsurf</b></summary>
130
+
131
+ ```json
132
+ {
133
+ "mcpServers": {
134
+ "pabal-web-mcp": {
135
+ "command": "npx",
136
+ "args": ["-y", "pabal-web-mcp"]
137
+ }
138
+ }
139
+ }
140
+ ```
141
+
142
+ Or if installed globally:
143
+
144
+ ```json
145
+ {
146
+ "mcpServers": {
147
+ "pabal-web-mcp": {
148
+ "command": "pabal-web-mcp"
149
+ }
150
+ }
151
+ }
152
+ ```
153
+
154
+ </details>
155
+
156
+ ## MCP Server
157
+
158
+ This package includes an MCP server for managing ASO data through Claude or other MCP-compatible clients.
159
+
160
+ ### Available Tools
161
+
162
+ | Tool | Description |
163
+ | ---------------- | -------------------------------------------------- |
164
+ | `aso-to-public` | Convert ASO data to public config format |
165
+ | `public-to-aso` | Convert public config to ASO data format |
166
+ | `improve-public` | Improve product locale content with AI suggestions |
167
+ | `init-project` | Initialize a new product project structure |
168
+
11
169
  ## Usage
12
170
 
13
171
  ### Importing Types
@@ -60,38 +218,6 @@ console.log(asoData.appStore?.name);
60
218
  console.log(asoData.googlePlay?.title);
61
219
  ```
62
220
 
63
- ## MCP Server
64
-
65
- This package includes an MCP server for managing ASO data through Claude or other MCP-compatible clients.
66
-
67
- ### Available Tools
68
-
69
- | Tool | Description |
70
- |------|-------------|
71
- | `aso-to-public` | Convert ASO data to public config format |
72
- | `public-to-aso` | Convert public config to ASO data format |
73
- | `improve-public` | Improve product locale content with AI suggestions |
74
- | `init-project` | Initialize a new product project structure |
75
-
76
- ### Running the MCP Server
77
-
78
- ```bash
79
- npx pabal-web-mcp
80
- ```
81
-
82
- Or add to your Claude Desktop config:
83
-
84
- ```json
85
- {
86
- "mcpServers": {
87
- "pabal-web-mcp": {
88
- "command": "npx",
89
- "args": ["pabal-web-mcp"]
90
- }
91
- }
92
- }
93
- ```
94
-
95
221
  ## Types Reference
96
222
 
97
223
  ### ASO Types
@@ -116,17 +242,17 @@ Or add to your Claude Desktop config:
116
242
  ## Supported Locales
117
243
 
118
244
  | Unified | App Store | Google Play |
119
- |---------|-----------|-------------|
120
- | en-US | en-US | en-US |
121
- | ko-KR | ko | ko-KR |
122
- | ja-JP | ja | ja-JP |
123
- | zh-CN | zh-Hans | zh-CN |
124
- | zh-TW | zh-Hant | zh-TW |
125
- | de-DE | de-DE | de-DE |
126
- | fr-FR | fr-FR | fr-FR |
127
- | es-ES | es-ES | es-ES |
128
- | pt-BR | pt-BR | pt-BR |
129
- | ... | ... | ... |
245
+ | ------- | --------- | ----------- |
246
+ | en-US | en-US | en-US |
247
+ | ko-KR | ko | ko-KR |
248
+ | ja-JP | ja | ja-JP |
249
+ | zh-CN | zh-Hans | zh-CN |
250
+ | zh-TW | zh-Hant | zh-TW |
251
+ | de-DE | de-DE | de-DE |
252
+ | fr-FR | fr-FR | fr-FR |
253
+ | es-ES | es-ES | es-ES |
254
+ | pt-BR | pt-BR | pt-BR |
255
+ | ... | ... | ... |
130
256
 
131
257
  ## License
132
258
 
@@ -2,6 +2,10 @@
2
2
  import {
3
3
  DEFAULT_LOCALE,
4
4
  appStoreToUnified,
5
+ getProductsDir,
6
+ getPublicDir,
7
+ getPullDataDir,
8
+ getPushDataDir,
5
9
  googlePlayToUnified,
6
10
  isAppStoreMultilingual,
7
11
  isGooglePlayMultilingual,
@@ -9,7 +13,7 @@ import {
9
13
  saveAsoToAsoDir,
10
14
  unifiedToAppStore,
11
15
  unifiedToGooglePlay
12
- } from "../chunk-YJWGBO7W.js";
16
+ } from "../chunk-MWXNTV3M.js";
13
17
 
14
18
  // src/bin/mcp-server.ts
15
19
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -28,10 +32,9 @@ import fs from "fs";
28
32
  import path from "path";
29
33
  function loadPullData(slug) {
30
34
  const asoData = {};
35
+ const pullDataDir = getPullDataDir();
31
36
  const googlePlayPath = path.join(
32
- process.cwd(),
33
- ".aso",
34
- "pullData",
37
+ pullDataDir,
35
38
  "products",
36
39
  slug,
37
40
  "store",
@@ -50,9 +53,7 @@ function loadPullData(slug) {
50
53
  }
51
54
  }
52
55
  const appStorePath = path.join(
53
- process.cwd(),
54
- ".aso",
55
- "pullData",
56
+ pullDataDir,
56
57
  "products",
57
58
  slug,
58
59
  "store",
@@ -202,14 +203,14 @@ var jsonSchema = toJsonSchema(asoToPublicInputSchema, {
202
203
  var inputSchema = jsonSchema.definitions?.AsoToPublicInput || jsonSchema;
203
204
  var asoToPublicTool = {
204
205
  name: "aso-to-public",
205
- description: `Converts ASO data from .aso/pullData to public/products/[slug]/ structure.
206
+ description: `Converts ASO data from pullData to public/products/[slug]/ structure.
206
207
 
207
208
  **IMPORTANT:** The 'slug' parameter is REQUIRED. If the user does not provide a slug, you MUST ask them to provide it. This tool processes only ONE product at a time.
208
209
 
209
210
  This tool:
210
- 1. Loads ASO data from .aso/pullData/products/[slug]/store/
211
+ 1. Loads ASO data from pullData/products/[slug]/store/ (path from ~/.config/pabal-mcp/config.json dataDir)
211
212
  2. Generates per-locale conversion prompts to map fullDescription into structured locale JSON (template intro/outro + landing features/screenshots captions)
212
- 3. Next steps (manual): paste converted JSON into public/products/[slug]/locales/[locale].json and copy screenshots from .aso/pullData if needed
213
+ 3. Next steps (manual): paste converted JSON into public/products/[slug]/locales/[locale].json and copy screenshots from pullData if needed
213
214
 
214
215
  The conversion from unstructured to structured format is performed by Claude based on the conversion prompt.`,
215
216
  inputSchema
@@ -218,7 +219,7 @@ async function handleAsoToPublic(input) {
218
219
  const { slug } = input;
219
220
  const asoData = loadPullData(slug);
220
221
  if (!asoData.googlePlay && !asoData.appStore) {
221
- throw new Error(`No ASO data found in .aso/pullData for ${slug}`);
222
+ throw new Error(`No ASO data found in pullData for ${slug}`);
222
223
  }
223
224
  const mergedDataByLocale = /* @__PURE__ */ new Map();
224
225
  if (asoData.googlePlay) {
@@ -270,7 +271,8 @@ async function handleAsoToPublic(input) {
270
271
  ).find((loc) => googlePlayToUnified(loc) === unifiedLocale);
271
272
  if (googlePlayLocale) {
272
273
  if (!screenshotPaths) screenshotPaths = {};
273
- screenshotPaths.googlePlay = `.aso/pullData/products/${slug}/store/google-play/screenshots/${googlePlayLocale}/`;
274
+ const pullDataDir2 = getPullDataDir();
275
+ screenshotPaths.googlePlay = `${pullDataDir2}/products/${slug}/store/google-play/screenshots/${googlePlayLocale}/`;
274
276
  }
275
277
  }
276
278
  if (mergedData.appStore) {
@@ -279,7 +281,8 @@ async function handleAsoToPublic(input) {
279
281
  ).find((loc) => appStoreToUnified(loc) === unifiedLocale);
280
282
  if (appStoreLocale) {
281
283
  if (!screenshotPaths) screenshotPaths = {};
282
- screenshotPaths.appStore = `.aso/pullData/products/${slug}/store/app-store/screenshots/${appStoreLocale}/`;
284
+ const pullDataDir2 = getPullDataDir();
285
+ screenshotPaths.appStore = `${pullDataDir2}/products/${slug}/store/app-store/screenshots/${appStoreLocale}/`;
283
286
  }
284
287
  }
285
288
  const prompt = generateConversionPrompt(
@@ -303,7 +306,8 @@ async function handleAsoToPublic(input) {
303
306
  --- ${unifiedLocale} (${sourcesText}) ---
304
307
  ${prompt}`);
305
308
  }
306
- let responseText = `Converting ASO data from .aso/pullData to public/products/${slug}/ structure.
309
+ const pullDataDir = getPullDataDir();
310
+ let responseText = `Converting ASO data from pullData to public/products/${slug}/ structure.
307
311
 
308
312
  `;
309
313
  responseText += `Found ${conversionTasks.length} unified locale(s) to convert.
@@ -322,7 +326,7 @@ Next steps (manual):
322
326
  `;
323
327
  responseText += ` Example: public/products/${slug}/locales/ar.json (not ar-SA.json)
324
328
  `;
325
- responseText += `2. Copy screenshots from .aso/pullData/products/${slug}/store/ to public/products/${slug}/screenshots/
329
+ responseText += `2. Copy screenshots from ${pullDataDir}/products/${slug}/store/ to public/products/${slug}/screenshots/
326
330
  `;
327
331
  return {
328
332
  content: [
@@ -413,7 +417,7 @@ function prepareAsoDataForPush(slug, configData) {
413
417
 
414
418
  // src/tools/utils/public-to-aso/save-raw-aso-data.util.ts
415
419
  function saveRawAsoData(slug, asoData, options) {
416
- const rootDir = options?.rootDir ?? ".aso/pushData";
420
+ const rootDir = options?.rootDir ?? getPushDataDir();
417
421
  saveAsoToAsoDir(slug, asoData, { rootDir });
418
422
  const localeCounts = {};
419
423
  if (asoData.googlePlay) {
@@ -475,8 +479,9 @@ function isLocalAssetPath(assetPath) {
475
479
  import fs3 from "fs";
476
480
  import path3 from "path";
477
481
  function copyLocalAssetToAsoDir(assetPath, outputPath) {
482
+ const publicDir = getPublicDir();
478
483
  const trimmedPath = assetPath.replace(/^\.\//, "").replace(/^public\//, "").replace(/^\/+/, "");
479
- const sourcePath = path3.join(process.cwd(), "public", trimmedPath);
484
+ const sourcePath = path3.join(publicDir, trimmedPath);
480
485
  if (!fs3.existsSync(sourcePath)) {
481
486
  console.warn(`\u26A0\uFE0F Local image not found: ${sourcePath}`);
482
487
  return false;
@@ -514,9 +519,8 @@ var jsonSchema2 = toJsonSchema2(publicToAsoInputSchema, {
514
519
  });
515
520
  var inputSchema2 = jsonSchema2.definitions?.PublicToAsoInput || jsonSchema2;
516
521
  async function downloadScreenshotsToAsoDir(slug, asoData, options) {
517
- const rootDir = options?.rootDir ?? ".aso/pushData";
522
+ const rootDir = options?.rootDir ?? getPushDataDir();
518
523
  const productStoreRoot = path4.join(
519
- process.cwd(),
520
524
  rootDir,
521
525
  "products",
522
526
  slug,
@@ -654,15 +658,15 @@ async function downloadScreenshotsToAsoDir(slug, asoData, options) {
654
658
  }
655
659
  var publicToAsoTool = {
656
660
  name: "public-to-aso",
657
- description: `Prepares ASO data from public/products/[slug]/ to .aso/pushData format.
661
+ description: `Prepares ASO data from public/products/[slug]/ to pushData format.
658
662
 
659
663
  **IMPORTANT:** The 'slug' parameter is REQUIRED. If the user does not provide a slug, you MUST ask them to provide it. This tool processes only ONE product at a time.
660
664
 
661
665
  This tool:
662
666
  1. Loads ASO data from public/products/[slug]/config.json + locales/
663
667
  2. Converts to store-compatible format (removes screenshots from metadata, sets contactWebsite/marketingUrl)
664
- 3. Saves metadata to .aso/pushData/products/[slug]/store/
665
- 4. Copies/downloads screenshots to .aso/pushData/products/[slug]/store/screenshots/
668
+ 3. Saves metadata to pushData/products/[slug]/store/ (path from ~/.config/pabal-mcp/config.json dataDir)
669
+ 4. Copies/downloads screenshots to pushData/products/[slug]/store/screenshots/
666
670
 
667
671
  Before running, review ${FIELD_LIMITS_DOC_PATH} for per-store limits. This prepares data for pushing to stores without actually uploading.`,
668
672
  inputSchema: inputSchema2
@@ -674,12 +678,13 @@ async function handlePublicToAso(input) {
674
678
  throw new Error(`No ASO data found in config.json + locales/ for ${slug}`);
675
679
  }
676
680
  const storeData = prepareAsoDataForPush(slug, configData);
681
+ const pushDataRoot = getPushDataDir();
677
682
  if (dryRun) {
678
683
  return {
679
684
  content: [
680
685
  {
681
686
  type: "text",
682
- text: `Preview mode - Data that would be saved to .aso/pushData:
687
+ text: `Preview mode - Data that would be saved to ${pushDataRoot}:
683
688
 
684
689
  ${JSON.stringify(
685
690
  storeData,
@@ -690,7 +695,6 @@ ${JSON.stringify(
690
695
  ]
691
696
  };
692
697
  }
693
- const pushDataRoot = ".aso/pushData";
694
698
  saveRawAsoData(slug, storeData, { rootDir: pushDataRoot });
695
699
  await downloadScreenshotsToAsoDir(slug, configData, {
696
700
  rootDir: pushDataRoot
@@ -708,7 +712,7 @@ ${JSON.stringify(
708
712
  const locales = isAppStoreMultilingual(appStoreData) ? appStoreData.locales : { [appStoreData.locale || DEFAULT_LOCALE]: appStoreData };
709
713
  localeCounts.appStore = Object.keys(locales).length;
710
714
  }
711
- let responseText = `\u2705 ${slug} .aso/pushData files prepared from config.json + locales/ (images + metadata synced)
715
+ let responseText = `\u2705 ${slug} pushData files prepared from config.json + locales/ (images + metadata synced)
712
716
 
713
717
  `;
714
718
  if (localeCounts.googlePlay) {
@@ -741,7 +745,8 @@ import { zodToJsonSchema as zodToJsonSchema3 } from "zod-to-json-schema";
741
745
  import fs4 from "fs";
742
746
  import path5 from "path";
743
747
  function loadProductLocales(slug) {
744
- const productDir = path5.join(process.cwd(), "public", "products", slug);
748
+ const productsDir = getProductsDir();
749
+ const productDir = path5.join(productsDir, slug);
745
750
  const configPath = path5.join(productDir, "config.json");
746
751
  const localesDir = path5.join(productDir, "locales");
747
752
  let config = null;
@@ -1711,7 +1716,7 @@ var listSlugDirs = (dir) => {
1711
1716
  };
1712
1717
  var initProjectInputSchema = z4.object({
1713
1718
  slug: z4.string().trim().optional().describe(
1714
- "Optional product slug to focus on. Defaults to all slugs in .aso/pullData/products/"
1719
+ "Optional product slug to focus on. Defaults to all slugs in pullData/products/"
1715
1720
  )
1716
1721
  });
1717
1722
  var jsonSchema4 = zodToJsonSchema4(initProjectInputSchema, {
@@ -1727,14 +1732,14 @@ var initProjectTool = {
1727
1732
  This tool is read-only and returns a checklist. It does not call pabal-mcp directly or write files.
1728
1733
 
1729
1734
  Steps:
1730
- 1) Ensure pabal-mcp 'init' ran and .aso/pullData/products/[slug]/ exists
1735
+ 1) Ensure pabal-mcp 'init' ran and pullData/products/[slug]/ exists (path from ~/.config/pabal-mcp/config.json dataDir)
1731
1736
  2) Convert pulled ASO data -> public/products/[slug]/ using pabal-web-mcp tools (aso-to-public, public-to-aso dry run)
1732
1737
  3) Validate outputs and next actions`,
1733
1738
  inputSchema: inputSchema4
1734
1739
  };
1735
1740
  async function handleInitProject(input) {
1736
- const pullDataDir = path6.join(process.cwd(), ".aso", "pullData", "products");
1737
- const publicDir = path6.join(process.cwd(), "public", "products");
1741
+ const pullDataDir = path6.join(getPullDataDir(), "products");
1742
+ const publicDir = getProductsDir();
1738
1743
  const pullDataSlugs = listSlugDirs(pullDataDir);
1739
1744
  const publicSlugs = listSlugDirs(publicDir);
1740
1745
  const targetSlugs = input.slug?.length && input.slug.trim().length > 0 ? [input.slug.trim()] : pullDataSlugs.length > 0 ? pullDataSlugs : publicSlugs;
@@ -1752,7 +1757,7 @@ async function handleInitProject(input) {
1752
1757
  lines.push("");
1753
1758
  if (targetSlugs.length === 0) {
1754
1759
  lines.push(
1755
- "No products detected. Run pabal-mcp 'init' for your slug(s) to populate .aso/pullData/products/, then rerun this tool."
1760
+ "No products detected. Run pabal-mcp 'init' for your slug(s) to populate pullData/products/, then rerun this tool."
1756
1761
  );
1757
1762
  return {
1758
1763
  content: [
@@ -1790,7 +1795,7 @@ async function handleInitProject(input) {
1790
1795
  lines.push("");
1791
1796
  lines.push("Step 3: Verify and prepare for push (optional)");
1792
1797
  lines.push(
1793
- "Use pabal-web-mcp 'public-to-aso' with dryRun=true to validate structure and build .aso/pushData before uploading via store tooling."
1798
+ "Use pabal-web-mcp 'public-to-aso' with dryRun=true to validate structure and build pushData before uploading via store tooling."
1794
1799
  );
1795
1800
  lines.push("");
1796
1801
  lines.push("Notes:");