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.
Files changed (111) hide show
  1. package/README.md +95 -0
  2. package/bin/pabal-mcp.js +6 -0
  3. package/dist/src/core/clients/app-store-factory.d.ts +29 -0
  4. package/dist/src/core/clients/app-store-factory.js +72 -0
  5. package/dist/src/core/clients/client-factory-helpers.d.ts +7 -0
  6. package/dist/src/core/clients/client-factory-helpers.js +10 -0
  7. package/dist/src/core/clients/google-play-factory.d.ts +29 -0
  8. package/dist/src/core/clients/google-play-factory.js +72 -0
  9. package/dist/src/core/clients/types.d.ts +8 -0
  10. package/dist/src/core/clients/types.js +1 -0
  11. package/dist/src/core/helpers/formatters.d.ts +3 -0
  12. package/dist/src/core/helpers/formatters.js +38 -0
  13. package/dist/src/core/helpers/registration.d.ts +21 -0
  14. package/dist/src/core/helpers/registration.js +21 -0
  15. package/dist/src/core/helpers/translate-release-notes.d.ts +46 -0
  16. package/dist/src/core/helpers/translate-release-notes.js +87 -0
  17. package/dist/src/core/services/app-resolution-service.d.ts +14 -0
  18. package/dist/src/core/services/app-resolution-service.js +35 -0
  19. package/dist/src/core/services/app-store-service.d.ts +41 -0
  20. package/dist/src/core/services/app-store-service.js +266 -0
  21. package/dist/src/core/services/google-play-service.d.ts +36 -0
  22. package/dist/src/core/services/google-play-service.js +203 -0
  23. package/dist/src/core/services/service-helpers.d.ts +15 -0
  24. package/dist/src/core/services/service-helpers.js +31 -0
  25. package/dist/src/core/services/types.d.ts +81 -0
  26. package/dist/src/core/services/types.js +1 -0
  27. package/dist/src/core/workflows/version-info.d.ts +29 -0
  28. package/dist/src/core/workflows/version-info.js +100 -0
  29. package/dist/src/index.d.ts +10 -0
  30. package/dist/src/index.js +279 -0
  31. package/dist/src/packages/common/errors/app-error.d.ts +39 -0
  32. package/dist/src/packages/common/errors/app-error.js +134 -0
  33. package/dist/src/packages/common/errors/error-codes.d.ts +63 -0
  34. package/dist/src/packages/common/errors/error-codes.js +71 -0
  35. package/dist/src/packages/common/errors/status-codes.d.ts +10 -0
  36. package/dist/src/packages/common/errors/status-codes.js +9 -0
  37. package/dist/src/packages/configs/aso-config/constants.d.ts +14 -0
  38. package/dist/src/packages/configs/aso-config/constants.js +102 -0
  39. package/dist/src/packages/configs/aso-config/locale-guards.d.ts +3 -0
  40. package/dist/src/packages/configs/aso-config/locale-guards.js +7 -0
  41. package/dist/src/packages/configs/aso-config/store.d.ts +11 -0
  42. package/dist/src/packages/configs/aso-config/store.js +11 -0
  43. package/dist/src/packages/configs/aso-config/types.d.ts +98 -0
  44. package/dist/src/packages/configs/aso-config/types.js +2 -0
  45. package/dist/src/packages/configs/aso-config/utils.d.ts +43 -0
  46. package/dist/src/packages/configs/aso-config/utils.js +223 -0
  47. package/dist/src/packages/configs/secrets-config/config.d.ts +12 -0
  48. package/dist/src/packages/configs/secrets-config/config.js +187 -0
  49. package/dist/src/packages/configs/secrets-config/constants.d.ts +1 -0
  50. package/dist/src/packages/configs/secrets-config/constants.js +1 -0
  51. package/dist/src/packages/configs/secrets-config/errors.d.ts +9 -0
  52. package/dist/src/packages/configs/secrets-config/errors.js +15 -0
  53. package/dist/src/packages/configs/secrets-config/registered-apps.d.ts +52 -0
  54. package/dist/src/packages/configs/secrets-config/registered-apps.js +108 -0
  55. package/dist/src/packages/configs/secrets-config/schemas.d.ts +21 -0
  56. package/dist/src/packages/configs/secrets-config/schemas.js +9 -0
  57. package/dist/src/packages/configs/secrets-config/types.d.ts +8 -0
  58. package/dist/src/packages/configs/secrets-config/types.js +1 -0
  59. package/dist/src/packages/stores/app-store/api-converters.d.ts +26 -0
  60. package/dist/src/packages/stores/app-store/api-converters.js +131 -0
  61. package/dist/src/packages/stores/app-store/api-endpoints.d.ts +33 -0
  62. package/dist/src/packages/stores/app-store/api-endpoints.js +157 -0
  63. package/dist/src/packages/stores/app-store/auth.d.ts +12 -0
  64. package/dist/src/packages/stores/app-store/auth.js +36 -0
  65. package/dist/src/packages/stores/app-store/client.d.ts +78 -0
  66. package/dist/src/packages/stores/app-store/client.js +637 -0
  67. package/dist/src/packages/stores/app-store/constants.d.ts +11 -0
  68. package/dist/src/packages/stores/app-store/constants.js +38 -0
  69. package/dist/src/packages/stores/app-store/generated-types.d.ts +118537 -0
  70. package/dist/src/packages/stores/app-store/generated-types.js +5 -0
  71. package/dist/src/packages/stores/app-store/types.d.ts +39 -0
  72. package/dist/src/packages/stores/app-store/types.js +9 -0
  73. package/dist/src/packages/stores/app-store/verify-auth.d.ts +16 -0
  74. package/dist/src/packages/stores/app-store/verify-auth.js +34 -0
  75. package/dist/src/packages/stores/play-store/api-converters.d.ts +58 -0
  76. package/dist/src/packages/stores/play-store/api-converters.js +209 -0
  77. package/dist/src/packages/stores/play-store/api-endpoints.d.ts +68 -0
  78. package/dist/src/packages/stores/play-store/api-endpoints.js +145 -0
  79. package/dist/src/packages/stores/play-store/client.d.ts +55 -0
  80. package/dist/src/packages/stores/play-store/client.js +628 -0
  81. package/dist/src/packages/stores/play-store/constants.d.ts +10 -0
  82. package/dist/src/packages/stores/play-store/constants.js +17 -0
  83. package/dist/src/packages/stores/play-store/types.d.ts +146 -0
  84. package/dist/src/packages/stores/play-store/types.js +9 -0
  85. package/dist/src/packages/stores/play-store/verify-auth.d.ts +13 -0
  86. package/dist/src/packages/stores/play-store/verify-auth.js +31 -0
  87. package/dist/src/tools/apps/add.d.ts +28 -0
  88. package/dist/src/tools/apps/add.js +307 -0
  89. package/dist/src/tools/apps/init.d.ts +58 -0
  90. package/dist/src/tools/apps/init.js +390 -0
  91. package/dist/src/tools/apps/search.d.ts +33 -0
  92. package/dist/src/tools/apps/search.js +147 -0
  93. package/dist/src/tools/aso/pull.d.ts +22 -0
  94. package/dist/src/tools/aso/pull.js +264 -0
  95. package/dist/src/tools/aso/push.d.ts +23 -0
  96. package/dist/src/tools/aso/push.js +189 -0
  97. package/dist/src/tools/auth/app-store.d.ts +9 -0
  98. package/dist/src/tools/auth/app-store.js +34 -0
  99. package/dist/src/tools/auth/check.d.ts +14 -0
  100. package/dist/src/tools/auth/check.js +50 -0
  101. package/dist/src/tools/auth/play-store.d.ts +9 -0
  102. package/dist/src/tools/auth/play-store.js +30 -0
  103. package/dist/src/tools/release/check-versions.d.ts +14 -0
  104. package/dist/src/tools/release/check-versions.js +65 -0
  105. package/dist/src/tools/release/create.d.ts +23 -0
  106. package/dist/src/tools/release/create.js +128 -0
  107. package/dist/src/tools/release/pull-notes.d.ts +22 -0
  108. package/dist/src/tools/release/pull-notes.js +151 -0
  109. package/dist/src/tools/release/update-notes.d.ts +110 -0
  110. package/dist/src/tools/release/update-notes.js +537 -0
  111. package/package.json +71 -0
@@ -0,0 +1,390 @@
1
+ /**
2
+ * setup-apps: Query apps from store and auto-register
3
+ */
4
+ import { loadConfig } from "../../packages/configs/secrets-config/config.js";
5
+ import { registerApp, findApp, loadRegisteredApps, saveRegisteredApps, } from "../../packages/configs/secrets-config/registered-apps.js";
6
+ import { toRegisteredAppStoreInfo, toRegisteredGooglePlayInfo, } from "../../core/helpers/registration.js";
7
+ import { AppStoreService } from "../../core/services/app-store-service.js";
8
+ import { GooglePlayService } from "../../core/services/google-play-service.js";
9
+ const appStoreService = new AppStoreService();
10
+ const googlePlayService = new GooglePlayService();
11
+ /**
12
+ * Check Play Store access (기존 호환성을 위한 래퍼)
13
+ */
14
+ async function checkPlayStoreAccess(packageName) {
15
+ const appInfo = await googlePlayService.fetchAppInfo(packageName);
16
+ if (!appInfo.found) {
17
+ return { accessible: false };
18
+ }
19
+ return {
20
+ accessible: true,
21
+ title: appInfo.name,
22
+ supportedLocales: appInfo.supportedLocales,
23
+ };
24
+ }
25
+ export async function handleSetupApps(options) {
26
+ const { store = "both", packageName } = options;
27
+ let config;
28
+ try {
29
+ config = loadConfig();
30
+ }
31
+ catch (error) {
32
+ const message = error instanceof Error ? error.message : String(error);
33
+ return {
34
+ content: [
35
+ {
36
+ type: "text",
37
+ text: `❌ Failed to load config: ${message}`,
38
+ },
39
+ ],
40
+ isError: true,
41
+ };
42
+ }
43
+ console.error(`[MCP] 📱 Initializing apps (store: ${store})`);
44
+ // both: Query App Store apps then check Play Store
45
+ if (store === "both" || store === "appStore") {
46
+ if (!config.appStore) {
47
+ return {
48
+ content: [
49
+ {
50
+ type: "text",
51
+ text: "❌ App Store authentication not configured. Please check ~/.config/pabal-mcp/config.json.",
52
+ },
53
+ ],
54
+ };
55
+ }
56
+ const clientResult = appStoreService.createClient("dummy"); // listAllApps() does not use bundleId
57
+ if (!clientResult.success) {
58
+ return {
59
+ content: [
60
+ {
61
+ type: "text",
62
+ text: `❌ Failed to create App Store client: ${clientResult.error.message}`,
63
+ },
64
+ ],
65
+ };
66
+ }
67
+ try {
68
+ console.error(`[MCP] 📋 Fetching app list from App Store...`);
69
+ const apps = await clientResult.data.listAllApps({
70
+ onlyReleased: true,
71
+ });
72
+ console.error(`[MCP] ✅ Found ${apps.length} apps`);
73
+ // 모든 앱의 언어 정보를 미리 가져오기 위한 클라이언트 인스턴스
74
+ const appInfoClientResult = appStoreService.createClient("dummy");
75
+ if (!appInfoClientResult.success) {
76
+ return {
77
+ content: [
78
+ {
79
+ type: "text",
80
+ text: `❌ Failed to create App Store info client: ${appInfoClientResult.error.message}`,
81
+ },
82
+ ],
83
+ };
84
+ }
85
+ const appInfoClient = appInfoClientResult.data;
86
+ if (apps.length === 0) {
87
+ return {
88
+ content: [
89
+ {
90
+ type: "text",
91
+ text: "📱 No apps registered in App Store.",
92
+ },
93
+ ],
94
+ };
95
+ }
96
+ // Prepare Play Store service account
97
+ const playStoreEnabled = store === "both" && !!config.playStore?.serviceAccountJson;
98
+ // Auto-register
99
+ const registered = [];
100
+ const skipped = [];
101
+ const playStoreFound = [];
102
+ const playStoreNotFound = [];
103
+ for (let i = 0; i < apps.length; i++) {
104
+ const app = apps[i];
105
+ // Use only last part of bundleId as slug (com.quartz.postblackbelt -> postblackbelt)
106
+ const parts = app.bundleId.split(".");
107
+ const slug = parts[parts.length - 1].toLowerCase();
108
+ console.error(`[MCP] [${i + 1}/${apps.length}] Processing: ${app.name} (${app.bundleId})`);
109
+ // Check if already registered (findApp searches by slug, bundleId, packageName)
110
+ let existing;
111
+ try {
112
+ existing = findApp(app.bundleId);
113
+ }
114
+ catch (error) {
115
+ console.error(`[MCP] ❌ Failed to load registered apps: ${error instanceof Error ? error.message : String(error)}`);
116
+ continue;
117
+ }
118
+ if (existing) {
119
+ // Update language info for existing apps
120
+ let appsConfig;
121
+ try {
122
+ appsConfig = loadRegisteredApps();
123
+ }
124
+ catch (error) {
125
+ console.error(`[MCP] ❌ Failed to load registered apps: ${error instanceof Error ? error.message : String(error)}`);
126
+ continue;
127
+ }
128
+ const appIndex = appsConfig.apps.findIndex((a) => a.slug === existing.slug);
129
+ if (appIndex >= 0) {
130
+ let updated = false;
131
+ // Update App Store language info
132
+ if (existing.appStore) {
133
+ const appStoreInfo = await appStoreService.fetchAppInfo(app.bundleId, appInfoClient);
134
+ if (appStoreInfo.found && appStoreInfo.supportedLocales) {
135
+ if (!appsConfig.apps[appIndex].appStore) {
136
+ appsConfig.apps[appIndex].appStore = {
137
+ bundleId: app.bundleId,
138
+ appId: app.id,
139
+ name: app.name,
140
+ };
141
+ }
142
+ appsConfig.apps[appIndex].appStore.supportedLocales =
143
+ appStoreInfo.supportedLocales;
144
+ updated = true;
145
+ }
146
+ }
147
+ // Update Google Play info (when in both mode)
148
+ if (playStoreEnabled) {
149
+ const playResult = await checkPlayStoreAccess(app.bundleId);
150
+ if (playResult.accessible) {
151
+ if (!appsConfig.apps[appIndex].googlePlay) {
152
+ appsConfig.apps[appIndex].googlePlay = {
153
+ packageName: app.bundleId,
154
+ name: playResult.title,
155
+ };
156
+ }
157
+ appsConfig.apps[appIndex].googlePlay.supportedLocales =
158
+ playResult.supportedLocales;
159
+ appsConfig.apps[appIndex].googlePlay.name = playResult.title;
160
+ updated = true;
161
+ playStoreFound.push(app.name);
162
+ }
163
+ else {
164
+ playStoreNotFound.push(app.name);
165
+ }
166
+ }
167
+ if (updated) {
168
+ saveRegisteredApps(appsConfig);
169
+ skipped.push(`${app.name} (${app.bundleId}) - language info updated`);
170
+ }
171
+ else {
172
+ skipped.push(`${app.name} (${app.bundleId}) - already registered`);
173
+ }
174
+ }
175
+ else {
176
+ skipped.push(`${app.name} (${app.bundleId}) - already registered`);
177
+ }
178
+ continue;
179
+ }
180
+ // App Store 정보 가져오기 (언어 정보 포함)
181
+ const appStoreInfo = await appStoreService.fetchAppInfo(app.bundleId, appInfoClient);
182
+ // Check Play Store (when in both mode)
183
+ let googlePlayInfo = undefined;
184
+ if (playStoreEnabled) {
185
+ const playResult = await checkPlayStoreAccess(app.bundleId);
186
+ if (playResult.accessible) {
187
+ googlePlayInfo = {
188
+ packageName: app.bundleId,
189
+ name: playResult.title,
190
+ supportedLocales: playResult.supportedLocales,
191
+ };
192
+ playStoreFound.push(app.name);
193
+ }
194
+ else {
195
+ playStoreNotFound.push(app.name);
196
+ }
197
+ }
198
+ try {
199
+ const registeredAppStoreInfo = toRegisteredAppStoreInfo({
200
+ bundleId: app.bundleId,
201
+ appInfo: appStoreInfo,
202
+ }) || {
203
+ bundleId: app.bundleId,
204
+ appId: app.id,
205
+ name: app.name,
206
+ };
207
+ registerApp({
208
+ slug,
209
+ name: app.name,
210
+ appStore: registeredAppStoreInfo,
211
+ googlePlay: googlePlayInfo,
212
+ });
213
+ console.error(`[MCP] ✅ Registered: ${slug}`);
214
+ registered.push({
215
+ name: app.name,
216
+ slug,
217
+ appStoreLocales: registeredAppStoreInfo.supportedLocales,
218
+ googlePlayLocales: googlePlayInfo?.supportedLocales,
219
+ });
220
+ }
221
+ catch (error) {
222
+ skipped.push(`${app.name} (${app.bundleId}) - registration failed`);
223
+ }
224
+ }
225
+ const lines = [`📱 **App Setup Complete**\n`];
226
+ if (registered.length > 0) {
227
+ lines.push(`✅ **Registered** (${registered.length}):`);
228
+ for (const r of registered) {
229
+ const storeInfo = r.googlePlayLocales ? " (🍎+🤖)" : " (🍎)";
230
+ let localeInfo = "";
231
+ if (r.appStoreLocales && r.appStoreLocales.length > 0) {
232
+ localeInfo += `\n 🍎 App Store: ${r.appStoreLocales.join(", ")}`;
233
+ }
234
+ if (r.googlePlayLocales && r.googlePlayLocales.length > 0) {
235
+ localeInfo += `\n 🤖 Google Play: ${r.googlePlayLocales.join(", ")}`;
236
+ }
237
+ lines.push(` • ${r.name}${storeInfo} → slug: "${r.slug}"${localeInfo}`);
238
+ }
239
+ lines.push("");
240
+ }
241
+ if (skipped.length > 0) {
242
+ lines.push(`⏭️ **Skipped** (${skipped.length}):`);
243
+ for (const s of skipped) {
244
+ lines.push(` • ${s}`);
245
+ }
246
+ lines.push("");
247
+ }
248
+ if (playStoreEnabled) {
249
+ lines.push(`**Play Store Check Results:**`);
250
+ lines.push(` 🤖 Found: ${playStoreFound.length}`);
251
+ if (playStoreFound.length > 0) {
252
+ for (const name of playStoreFound) {
253
+ lines.push(` • ${name}`);
254
+ }
255
+ }
256
+ lines.push(` ❌ Not found: ${playStoreNotFound.length}`);
257
+ if (playStoreNotFound.length > 0) {
258
+ for (const name of playStoreNotFound) {
259
+ lines.push(` • ${name}`);
260
+ }
261
+ }
262
+ lines.push("");
263
+ }
264
+ lines.push('You can now reference apps in other tools using the `app: "slug"` parameter.');
265
+ return {
266
+ content: [{ type: "text", text: lines.join("\n") }],
267
+ _meta: {
268
+ registered: registered.length,
269
+ skipped: skipped.length,
270
+ playStoreFound: playStoreFound.length,
271
+ playStoreNotFound: playStoreNotFound.length,
272
+ apps,
273
+ },
274
+ };
275
+ }
276
+ catch (error) {
277
+ const msg = error instanceof Error ? error.message : String(error);
278
+ return {
279
+ content: [
280
+ {
281
+ type: "text",
282
+ text: `❌ Failed to query App Store apps: ${msg}`,
283
+ },
284
+ ],
285
+ };
286
+ }
287
+ }
288
+ if (store === "googlePlay") {
289
+ console.error(`[MCP] 📋 Processing Google Play app: ${packageName || "N/A"}`);
290
+ if (!config.playStore) {
291
+ return {
292
+ content: [
293
+ {
294
+ type: "text",
295
+ text: "❌ Google Play authentication not configured. Please check ~/.config/pabal-mcp/config.json.",
296
+ },
297
+ ],
298
+ };
299
+ }
300
+ if (!packageName) {
301
+ return {
302
+ content: [
303
+ {
304
+ type: "text",
305
+ text: `⚠️ Google Play API does not support listing apps.
306
+
307
+ Provide packageName to verify and register that app:
308
+ \`\`\`json
309
+ { "store": "googlePlay", "packageName": "com.example.app" }
310
+ \`\`\``,
311
+ },
312
+ ],
313
+ };
314
+ }
315
+ try {
316
+ // Google Play 정보 가져오기 (언어 정보 포함)
317
+ console.error(`[MCP] 🔍 Fetching Google Play app info...`);
318
+ const googlePlayInfo = await googlePlayService.fetchAppInfo(packageName);
319
+ if (!googlePlayInfo.found) {
320
+ throw new Error("Failed to access Google Play app");
321
+ }
322
+ // Use only last part of packageName as slug (com.quartz.postblackbelt -> postblackbelt)
323
+ const parts = packageName.split(".");
324
+ const slug = parts[parts.length - 1].toLowerCase();
325
+ // Check if already registered (findApp searches by slug, bundleId, packageName)
326
+ const existing = findApp(packageName);
327
+ if (existing) {
328
+ return {
329
+ content: [
330
+ {
331
+ type: "text",
332
+ text: `⏭️ App is already registered: "${existing.slug}"`,
333
+ },
334
+ ],
335
+ _meta: { app: existing },
336
+ };
337
+ }
338
+ // Register
339
+ const registeredGooglePlayInfo = toRegisteredGooglePlayInfo({
340
+ packageName,
341
+ appInfo: googlePlayInfo,
342
+ });
343
+ console.error(`[MCP] 💾 Registering app with slug: ${slug}`);
344
+ const newApp = registerApp({
345
+ slug,
346
+ name: googlePlayInfo.name || packageName,
347
+ googlePlay: registeredGooglePlayInfo,
348
+ });
349
+ console.error(`[MCP] ✅ App registered successfully`);
350
+ const localeInfo = registeredGooglePlayInfo?.supportedLocales &&
351
+ registeredGooglePlayInfo.supportedLocales.length > 0
352
+ ? `\n• Supported Languages: ${registeredGooglePlayInfo.supportedLocales.join(", ")}`
353
+ : "";
354
+ return {
355
+ content: [
356
+ {
357
+ type: "text",
358
+ text: `✅ Google Play app registration complete
359
+
360
+ • Package Name: \`${packageName}\`
361
+ • Slug: \`${slug}\`
362
+ • Name: ${newApp.name}${localeInfo}
363
+
364
+ You can now reference this app in other tools using the \`app: "${slug}"\` parameter.`,
365
+ },
366
+ ],
367
+ _meta: { app: newApp },
368
+ };
369
+ }
370
+ catch (error) {
371
+ const msg = error instanceof Error ? error.message : String(error);
372
+ return {
373
+ content: [
374
+ {
375
+ type: "text",
376
+ text: `❌ Failed to access Google Play app: ${msg}`,
377
+ },
378
+ ],
379
+ };
380
+ }
381
+ }
382
+ return {
383
+ content: [
384
+ {
385
+ type: "text",
386
+ text: "❌ store parameter must be 'appStore', 'googlePlay', or 'both'.",
387
+ },
388
+ ],
389
+ };
390
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * apps-search: Search registered apps
3
+ *
4
+ * Search apps from registered-apps.json.
5
+ * - Called without query: Returns all app list
6
+ * - Called with query: Search by slug, bundleId, packageName, name
7
+ */
8
+ import { type RegisteredApp } from "../../packages/configs/secrets-config/registered-apps.js";
9
+ interface SearchAppsOptions {
10
+ /** Search term (slug, bundleId, packageName, name). Returns all apps if empty */
11
+ query?: string;
12
+ /** Store filter (default: all) */
13
+ store?: "all" | "appStore" | "googlePlay";
14
+ }
15
+ export declare function handleSearchApps(options: SearchAppsOptions): Promise<{
16
+ content: {
17
+ type: "text";
18
+ text: string;
19
+ }[];
20
+ _meta: {
21
+ apps: RegisteredApp[];
22
+ count: number;
23
+ };
24
+ isError?: undefined;
25
+ } | {
26
+ content: {
27
+ type: "text";
28
+ text: string;
29
+ }[];
30
+ isError: boolean;
31
+ _meta?: undefined;
32
+ }>;
33
+ export {};
@@ -0,0 +1,147 @@
1
+ /**
2
+ * apps-search: Search registered apps
3
+ *
4
+ * Search apps from registered-apps.json.
5
+ * - Called without query: Returns all app list
6
+ * - Called with query: Search by slug, bundleId, packageName, name
7
+ */
8
+ import { loadRegisteredApps, findApp, } from "../../packages/configs/secrets-config/registered-apps.js";
9
+ /**
10
+ * Check if app matches query
11
+ */
12
+ function matchesQuery(app, query) {
13
+ const lowerQuery = query.toLowerCase();
14
+ // slug match
15
+ if (app.slug.toLowerCase().includes(lowerQuery))
16
+ return true;
17
+ // name match
18
+ if (app.name.toLowerCase().includes(lowerQuery))
19
+ return true;
20
+ // App Store bundleId match
21
+ if (app.appStore?.bundleId?.toLowerCase().includes(lowerQuery))
22
+ return true;
23
+ // App Store name match
24
+ if (app.appStore?.name?.toLowerCase().includes(lowerQuery))
25
+ return true;
26
+ // Google Play packageName match
27
+ if (app.googlePlay?.packageName?.toLowerCase().includes(lowerQuery))
28
+ return true;
29
+ // Google Play name match
30
+ if (app.googlePlay?.name?.toLowerCase().includes(lowerQuery))
31
+ return true;
32
+ return false;
33
+ }
34
+ /**
35
+ * Apply store filter
36
+ */
37
+ function filterByStore(apps, store) {
38
+ if (store === "all")
39
+ return apps;
40
+ return apps.filter((app) => {
41
+ if (store === "appStore")
42
+ return !!app.appStore;
43
+ if (store === "googlePlay")
44
+ return !!app.googlePlay;
45
+ return true;
46
+ });
47
+ }
48
+ /**
49
+ * Format app information
50
+ */
51
+ function formatAppInfo(app) {
52
+ const lines = [];
53
+ lines.push(`📱 **${app.name}** (\`${app.slug}\`)`);
54
+ if (app.appStore) {
55
+ lines.push(` 🍎 App Store: \`${app.appStore.bundleId}\``);
56
+ if (app.appStore.appId) {
57
+ lines.push(` App ID: ${app.appStore.appId}`);
58
+ }
59
+ }
60
+ if (app.googlePlay) {
61
+ lines.push(` 🤖 Google Play: \`${app.googlePlay.packageName}\``);
62
+ }
63
+ return lines.join("\n");
64
+ }
65
+ export async function handleSearchApps(options) {
66
+ const { query, store = "all" } = options;
67
+ console.error(`[MCP] 🔍 Searching apps (query: ${query || "all"}, store: ${store})`);
68
+ try {
69
+ const config = loadRegisteredApps();
70
+ console.error(`[MCP] Loaded ${config.apps.length} apps from config`);
71
+ if (config.apps.length > 0) {
72
+ console.error(`[MCP] App slugs: ${config.apps.map((a) => a.slug).join(", ")}`);
73
+ }
74
+ let results;
75
+ if (!query) {
76
+ // If no query, return full list
77
+ results = config.apps;
78
+ }
79
+ else {
80
+ // Try exact match first (by slug, bundleId, packageName)
81
+ const exactMatch = findApp(query);
82
+ // Also search for partial matches
83
+ const partialMatches = config.apps.filter((app) => matchesQuery(app, query));
84
+ // Combine results: exact match first, then partial matches
85
+ // Remove duplicates by slug
86
+ const seenSlugs = new Set();
87
+ results = [];
88
+ // Add exact match first if found
89
+ if (exactMatch && !seenSlugs.has(exactMatch.slug)) {
90
+ results.push(exactMatch);
91
+ seenSlugs.add(exactMatch.slug);
92
+ }
93
+ // Add partial matches (excluding exact match if already added)
94
+ for (const app of partialMatches) {
95
+ if (!seenSlugs.has(app.slug)) {
96
+ results.push(app);
97
+ seenSlugs.add(app.slug);
98
+ }
99
+ }
100
+ }
101
+ // Apply store filter
102
+ results = filterByStore(results, store);
103
+ if (results.length === 0) {
104
+ const message = query
105
+ ? `No apps found matching "${query}".`
106
+ : "No apps registered.";
107
+ return {
108
+ content: [
109
+ {
110
+ type: "text",
111
+ text: `❌ ${message}
112
+
113
+ 💡 Register apps using apps-add or apps-init tools.`,
114
+ },
115
+ ],
116
+ _meta: { apps: [], count: 0 },
117
+ };
118
+ }
119
+ const header = query
120
+ ? `🔍 Search results for "${query}": ${results.length}`
121
+ : `📋 Registered app list: ${results.length}`;
122
+ const appList = results.map(formatAppInfo).join("\n\n");
123
+ return {
124
+ content: [
125
+ {
126
+ type: "text",
127
+ text: `${header}
128
+
129
+ ${appList}`,
130
+ },
131
+ ],
132
+ _meta: { apps: results, count: results.length },
133
+ };
134
+ }
135
+ catch (error) {
136
+ const message = error instanceof Error ? error.message : String(error);
137
+ return {
138
+ content: [
139
+ {
140
+ type: "text",
141
+ text: `❌ App search failed: ${message}`,
142
+ },
143
+ ],
144
+ isError: true,
145
+ };
146
+ }
147
+ }
@@ -0,0 +1,22 @@
1
+ import type { StoreType } from "../../packages/configs/aso-config/types.js";
2
+ interface AsoPullOptions {
3
+ app?: string;
4
+ packageName?: string;
5
+ bundleId?: string;
6
+ store?: StoreType;
7
+ dryRun?: boolean;
8
+ }
9
+ export declare function handleAsoPull(options: AsoPullOptions): Promise<{
10
+ content: {
11
+ type: "text";
12
+ text: string;
13
+ }[];
14
+ isError?: undefined;
15
+ } | {
16
+ content: {
17
+ type: "text";
18
+ text: string;
19
+ }[];
20
+ isError: boolean;
21
+ }>;
22
+ export {};