Skip to main content
Glama

aso-push

Push ASO metadata from local cache to App Store and Google Play stores for app store optimization management.

Instructions

Push ASO data from local cache to App Store/Google Play.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
appNoRegistered app slug
packageNameNoGoogle Play package name
bundleIdNoApp Store bundle ID
storeNoTarget store (default: both)
uploadImagesNoWhether to upload images as well
dryRunNoIf true, only outputs result without actually pushing

Implementation Reference

  • Main handler function that orchestrates ASO push: resolves app, loads and prepares local ASO data from cache, optionally pushes to App Store/Google Play services, supports dry-run.
    export async function handleAsoPush(options: AsoPushOptions) {
      const { store = "both", uploadImages = false, dryRun = false } = options;
    
      const resolved = appResolutionService.resolve({
        slug: options.app,
        packageName: options.packageName,
        bundleId: options.bundleId,
      });
    
      if (!resolved.success) {
        return {
          content: [
            {
              type: "text" as const,
              text: resolved.error.message,
            },
          ],
        };
      }
    
      const { slug, packageName, bundleId, hasAppStore, hasGooglePlay } =
        resolved.data;
    
      console.error(`[MCP] πŸ“€ Pushing ASO data`);
      console.error(`[MCP]   Store: ${store}`);
      console.error(`[MCP]   App: ${slug}`);
      if (packageName) console.error(`[MCP]   Package Name: ${packageName}`);
      if (bundleId) console.error(`[MCP]   Bundle ID: ${bundleId}`);
      console.error(`[MCP]   Upload Images: ${uploadImages ? "Yes" : "No"}`);
      console.error(`[MCP]   Mode: ${dryRun ? "Dry run" : "Actual push"}`);
    
      let config;
      try {
        config = loadConfig();
      } catch (error) {
        const message = error instanceof Error ? error.message : String(error);
        return {
          content: [
            { type: "text" as const, text: `❌ Failed to load config: ${message}` },
          ],
          isError: true,
        };
      }
    
      // Load local data from ASO directory
      const asoDir = getAsoPushDir();
      const { googlePlay: googlePlayDataPath, appStore: appStoreDataPath } =
        getAsoDataPaths(slug, asoDir);
    
      console.error(`[MCP]   πŸ“ ASO Directory: ${asoDir}`);
      console.error(`[MCP]   πŸ“ Base ASO Dir: ${getAsoDir()}`);
      console.error(`[MCP]   πŸ” Checking data files...`);
      console.error(`[MCP]     Google Play: ${googlePlayDataPath}`);
      console.error(
        `[MCP]       Exists: ${existsSync(googlePlayDataPath) ? "βœ… Yes" : "❌ No"}`
      );
      console.error(`[MCP]     App Store: ${appStoreDataPath}`);
      console.error(
        `[MCP]       Exists: ${existsSync(appStoreDataPath) ? "βœ… Yes" : "❌ No"}`
      );
    
      let configData;
      try {
        configData = loadAsoData(slug, { asoDir });
      } catch (error) {
        const message = error instanceof Error ? error.message : String(error);
        return {
          content: [
            {
              type: "text" as const,
              text: `❌ Failed to load ASO data: ${message}`,
            },
          ],
          isError: true,
        };
      }
    
      console.error(`[MCP]   πŸ“Š Loaded data status:`);
      console.error(
        `[MCP]     Google Play: ${
          configData.googlePlay ? "βœ… Loaded" : "❌ Not found"
        }`
      );
      if (configData.googlePlay) {
        const gpData = configData.googlePlay;
        if (isGooglePlayMultilingual(gpData)) {
          const locales = Object.keys(gpData.locales);
          console.error(
            `[MCP]       Locales: ${locales.join(", ")} (${locales.length} total)`
          );
        } else {
          console.error(
            `[MCP]       Language: ${gpData.defaultLanguage || "unknown"}`
          );
        }
      }
      console.error(
        `[MCP]     App Store: ${configData.appStore ? "βœ… Loaded" : "❌ Not found"}`
      );
      if (configData.appStore) {
        const asData = configData.appStore;
        if (isAppStoreMultilingual(asData)) {
          const locales = Object.keys(asData.locales);
          console.error(
            `[MCP]       Locales: ${locales.join(", ")} (${locales.length} total)`
          );
        } else {
          console.error(`[MCP]       Locale: ${asData.locale || "unknown"}`);
        }
      }
    
      if (!configData.googlePlay && !configData.appStore) {
        const errorDetails = `❌ No ASO data found for ${slug}.
    
    πŸ“ Expected paths:
       Google Play: ${googlePlayDataPath}
       App Store: ${appStoreDataPath}
    
    πŸ’‘ To fix this:
       1. Run \`aso-pull\` to fetch data from stores
       2. Or ensure data exists at the paths above
       3. Check that data directory is set correctly (current: ${getAsoDir()})`;
    
        console.error(`[MCP]   ${errorDetails}`);
        return {
          content: [{ type: "text" as const, text: errorDetails }],
        };
      }
    
      // Prepare data for push
      console.error(`[MCP]   πŸ”„ Preparing data for push...`);
      const localAsoData = prepareAsoDataForPush(slug, configData);
    
      console.error(`[MCP]   πŸ“Š Prepared data status:`);
      console.error(
        `[MCP]     Google Play: ${
          localAsoData.googlePlay ? "βœ… Ready" : "❌ Not available"
        }`
      );
      if (localAsoData.googlePlay) {
        const gpData = localAsoData.googlePlay;
        if (isGooglePlayMultilingual(gpData)) {
          const locales = Object.keys(gpData.locales);
          console.error(
            `[MCP]       Locales to push: ${locales.join(", ")} (${
              locales.length
            } total)`
          );
        }
      }
      console.error(
        `[MCP]     App Store: ${
          localAsoData.appStore ? "βœ… Ready" : "❌ Not available"
        }`
      );
      if (localAsoData.appStore) {
        const asData = localAsoData.appStore;
        if (isAppStoreMultilingual(asData)) {
          const locales = Object.keys(asData.locales);
          console.error(
            `[MCP]       Locales to push: ${locales.join(", ")} (${
              locales.length
            } total)`
          );
        }
      }
    
      if (dryRun) {
        return {
          content: [
            {
              type: "text" as const,
              text: `πŸ“‹ Dry run - Data that would be pushed:\n${JSON.stringify(
                localAsoData,
                null,
                2
              )}`,
            },
          ],
        };
      }
    
      // Save to ASO directory before pushing
      if (localAsoData.googlePlay || localAsoData.appStore) {
        saveAsoData(slug, localAsoData, { asoDir });
      }
    
      const results: string[] = [];
    
      // Push to Google Play
      if (store === "googlePlay" || store === "both") {
        if (!hasGooglePlay) {
          results.push(`⏭️  Skipping Google Play (not registered for Google Play)`);
        } else {
          const result = await googlePlayService.pushAsoData({
            config,
            packageName,
            localAsoData,
            googlePlayDataPath,
          });
          results.push(formatPushResult("Google Play", result));
        }
      }
    
      // Push to App Store
      if (store === "appStore" || store === "both") {
        if (!hasAppStore) {
          results.push(`⏭️  Skipping App Store (not registered for App Store)`);
        } else {
          const appStoreResult = await appStoreService.pushAsoData({
            config,
            bundleId,
            localAsoData,
            appStoreDataPath,
          });
          results.push(formatPushResult("App Store", appStoreResult));
        }
      }
    
      return {
        content: [
          {
            type: "text" as const,
            text: `πŸ“€ ASO Push Results:\n${results.join("\n")}`,
          },
        ],
      };
    }
  • src/index.ts:257-278 (registration)
    Registers the 'aso-push' tool with the MCP server, providing description, input schema validation using Zod, and linking to the handleAsoPush handler.
    registerToolWithInfo(
      "aso-push",
      {
        description: "Push ASO data from local cache to App Store/Google Play.",
        inputSchema: z.object({
          app: z.string().optional().describe("Registered app slug"),
          packageName: z.string().optional().describe("Google Play package name"),
          bundleId: z.string().optional().describe("App Store bundle ID"),
          store: storeSchema.describe("Target store (default: both)"),
          uploadImages: z
            .boolean()
            .optional()
            .describe("Whether to upload images as well"),
          dryRun: z
            .boolean()
            .optional()
            .describe("If true, only outputs result without actually pushing"),
        }),
      },
      handleAsoPush,
      "ASO Data Sync"
    );
  • TypeScript interface defining input options for the handler, matching the Zod schema.
    interface AsoPushOptions {
      app?: string; // Registered app slug
      packageName?: string; // For Google Play
      bundleId?: string; // For App Store
      store?: StoreType;
      uploadImages?: boolean;
      dryRun?: boolean;
    }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/quartz-labs-dev/pabal-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server