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;
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It mentions pushing data but doesn't cover critical aspects such as required permissions, rate limits, error handling, or the irreversible nature of the operation (unless 'dryRun' is used). This leaves significant gaps in understanding the tool's behavior and risks.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, direct sentence that efficiently conveys the core action without unnecessary words. It is front-loaded with the key verb and resource, making it easy to parse quickly. Every part of the sentence serves a clear purpose, earning a high score for conciseness.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity of a data-pushing operation with 6 parameters, no annotations, and no output schema, the description is insufficient. It lacks details on behavioral traits, error cases, and output expectations, leaving the agent with incomplete information to use the tool effectively in a real-world context.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema has 100% description coverage, so the baseline is 3. The description adds no additional meaning beyond the schema, as it doesn't explain parameter interactions (e.g., how 'app', 'packageName', and 'bundleId' relate) or provide usage examples. This meets the minimum viable level without enhancing parameter understanding.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('push') and resource ('ASO data from local cache to App Store/Google Play'), making the purpose evident. However, it doesn't explicitly differentiate from sibling tools like 'aso-pull', which handles data retrieval rather than pushing, leaving room for improvement in sibling distinction.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance is provided on when to use this tool versus alternatives. For instance, it doesn't mention prerequisites like needing local cache data from 'aso-pull' or clarify usage relative to other tools like 'release-create'. This lack of context makes it harder for an agent to decide when this tool is appropriate.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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