Agents browser-store-apis

Reference for browser extension store APIs for querying extension info, checking compatibility, and tracking submission status

install
source · Clone the upstream repo
git clone https://github.com/aRustyDev/agents
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/aRustyDev/agents "$T" && mkdir -p ~/.claude/skills && cp -r "$T/content/plugins/web/browser-extension-dev/skills/browser-store-apis" ~/.claude/skills/arustydev-agents-browser-store-apis && rm -rf "$T"
manifest: content/plugins/web/browser-extension-dev/skills/browser-store-apis/SKILL.md
source content

Browser Store APIs

Reference for browser extension store APIs used for querying extension info, checking compatibility, and tracking submission status.

MCP Server: browser-store-api

The plugin includes an MCP server for store API integration.

Configuration

{
  "mcpServers": {
    "browser-store-api": {
      "command": "npx",
      "args": ["-y", "@anthropic-ai/mcp-browser-store-api"],
      "env": {
        "AMO_API_KEY": "${AMO_API_KEY}",
        "AMO_API_SECRET": "${AMO_API_SECRET}",
        "CHROME_WEBSTORE_CLIENT_ID": "${CHROME_WEBSTORE_CLIENT_ID}",
        "CHROME_WEBSTORE_CLIENT_SECRET": "${CHROME_WEBSTORE_CLIENT_SECRET}",
        "CHROME_WEBSTORE_REFRESH_TOKEN": "${CHROME_WEBSTORE_REFRESH_TOKEN}"
      }
    }
  }
}

Available Tools

ToolDescription
amo_get_extension
Get extension info from Firefox Add-ons
amo_get_versions
List version history from AMO
amo_check_compatibility
Check Firefox version compatibility
cws_get_extension
Get extension info from Chrome Web Store
cws_get_reviews
Get user reviews and ratings
cws_check_status
Check publication/review status

Firefox Add-ons API (AMO)

Authentication

# Generate API credentials at:
# https://addons.mozilla.org/developers/addon/api/key/

export AMO_API_KEY="user:12345678:123"
export AMO_API_SECRET="your-api-secret"

API Endpoints

EndpointMethodDescription
/api/v5/addons/addon/{id}/
GETGet addon details
/api/v5/addons/addon/{id}/versions/
GETList versions
/api/v5/addons/search/
GETSearch addons
/api/v5/reviewers/addon/{id}/
GETReview information
/api/v5/accounts/profile/
GETAccount info

Extension Info Request

# Get extension by slug or ID
curl "https://addons.mozilla.org/api/v5/addons/addon/ublock-origin/" \
  -H "Authorization: JWT $AMO_JWT_TOKEN"

Response Structure

{
  "id": 123456,
  "guid": "uBlock0@example.com",
  "slug": "ublock-origin",
  "name": {
    "en-US": "uBlock Origin"
  },
  "current_version": {
    "version": "1.50.0",
    "files": [
      {
        "id": 7890,
        "created": "2024-01-15T12:00:00Z",
        "status": "public",
        "size": 1234567
      }
    ]
  },
  "ratings": {
    "average": 4.8,
    "count": 50000
  },
  "weekly_downloads": 500000
}

Version Compatibility

# Check version compatibility
curl "https://addons.mozilla.org/api/v5/addons/addon/{id}/versions/{version}/"
{
  "version": "1.50.0",
  "compatibility": {
    "firefox": {
      "min": "109.0",
      "max": "*"
    },
    "android": {
      "min": "109.0",
      "max": "*"
    }
  },
  "is_strict_compatibility_enabled": false
}

Chrome Web Store API

Authentication

# Create OAuth2 credentials at:
# https://console.cloud.google.com/apis/credentials

export CHROME_WEBSTORE_CLIENT_ID="your-client-id.apps.googleusercontent.com"
export CHROME_WEBSTORE_CLIENT_SECRET="your-client-secret"
export CHROME_WEBSTORE_REFRESH_TOKEN="your-refresh-token"

API Endpoints

EndpointMethodDescription
/chromewebstore/v1.1/items/{itemId}
GETGet item info
/chromewebstore/v1.1/items/{itemId}
PUTUpdate item
/chromewebstore/v1.1/items/{itemId}/publish
POSTPublish item
/chromewebstore/v1.1/items
POSTCreate new item

Get Access Token

# Exchange refresh token for access token
curl -X POST "https://oauth2.googleapis.com/token" \
  -d "client_id=$CHROME_WEBSTORE_CLIENT_ID" \
  -d "client_secret=$CHROME_WEBSTORE_CLIENT_SECRET" \
  -d "refresh_token=$CHROME_WEBSTORE_REFRESH_TOKEN" \
  -d "grant_type=refresh_token"

Extension Info Request

curl "https://www.googleapis.com/chromewebstore/v1.1/items/$ITEM_ID?projection=DRAFT" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "x-goog-api-version: 2"

Response Structure

{
  "kind": "chromewebstore#item",
  "id": "abcdefghijklmnopqrstuvwxyz123456",
  "publicKey": "...",
  "uploadState": "SUCCESS",
  "crxVersion": "1.0.0",
  "itemError": []
}

Upload New Version

curl -X PUT \
  "https://www.googleapis.com/upload/chromewebstore/v1.1/items/$ITEM_ID" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "x-goog-api-version: 2" \
  -T extension.zip

Publish Extension

curl -X POST \
  "https://www.googleapis.com/chromewebstore/v1.1/items/$ITEM_ID/publish" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "x-goog-api-version: 2" \
  -H "Content-Length: 0"

Edge Add-ons API

Authentication

Uses Azure AD authentication via Partner Center.

# Register app in Azure Portal
# Grant permissions to Partner Center API

export AZURE_CLIENT_ID="your-client-id"
export AZURE_CLIENT_SECRET="your-client-secret"
export AZURE_TENANT_ID="your-tenant-id"

API Endpoints

EndpointMethodDescription
/products/{productId}
GETGet product info
/products/{productId}/submissions
GETList submissions
/products/{productId}/submissions
POSTCreate submission

Get Token

curl -X POST \
  "https://login.microsoftonline.com/$AZURE_TENANT_ID/oauth2/v2.0/token" \
  -d "client_id=$AZURE_CLIENT_ID" \
  -d "client_secret=$AZURE_CLIENT_SECRET" \
  -d "scope=https://api.partner.microsoft.com/.default" \
  -d "grant_type=client_credentials"

Status Tracking

Submission States

StoreStates
AMO
pending
,
reviewing
,
approved
,
rejected
Chrome
PENDING
,
PUBLISHED
,
REJECTED
,
SUSPENDED
Edge
InProgress
,
Published
,
Failed

Polling for Status

async function pollSubmissionStatus(
  store: 'amo' | 'chrome' | 'edge',
  extensionId: string,
  maxAttempts: number = 60,
  intervalMs: number = 60000
): Promise<string> {
  for (let i = 0; i < maxAttempts; i++) {
    const status = await getSubmissionStatus(store, extensionId);

    if (isTerminalState(status)) {
      return status;
    }

    await sleep(intervalMs);
  }

  throw new Error('Status check timed out');
}

function isTerminalState(status: string): boolean {
  const terminal = [
    'approved', 'rejected',      // AMO
    'PUBLISHED', 'REJECTED',     // Chrome
    'Published', 'Failed'        // Edge
  ];
  return terminal.includes(status);
}

Version Checking

Compare Versions

function compareVersions(v1: string, v2: string): number {
  const parts1 = v1.split('.').map(Number);
  const parts2 = v2.split('.').map(Number);

  for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
    const p1 = parts1[i] || 0;
    const p2 = parts2[i] || 0;

    if (p1 > p2) return 1;
    if (p1 < p2) return -1;
  }

  return 0;
}

function isNewerVersion(current: string, published: string): boolean {
  return compareVersions(current, published) > 0;
}

Check All Stores

interface StoreVersions {
  amo: string | null;
  chrome: string | null;
  edge: string | null;
}

async function checkAllStoreVersions(
  extensionIds: { amo?: string; chrome?: string; edge?: string }
): Promise<StoreVersions> {
  const [amo, chrome, edge] = await Promise.all([
    extensionIds.amo ? getAMOVersion(extensionIds.amo) : null,
    extensionIds.chrome ? getChromeVersion(extensionIds.chrome) : null,
    extensionIds.edge ? getEdgeVersion(extensionIds.edge) : null
  ]);

  return { amo, chrome, edge };
}

Rate Limits

StoreLimitWindow
AMO100 requestsper minute
Chrome Web Store2000 requestsper day
Edge Partner Center500 requestsper minute

Retry Strategy

async function withRetry<T>(
  fn: () => Promise<T>,
  maxRetries: number = 3,
  baseDelayMs: number = 1000
): Promise<T> {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      if (i === maxRetries - 1) throw error;

      const isRateLimit = error.status === 429;
      const delay = isRateLimit
        ? parseInt(error.headers['retry-after'] || '60') * 1000
        : baseDelayMs * Math.pow(2, i);

      await sleep(delay);
    }
  }
  throw new Error('Max retries exceeded');
}

Common Use Cases

Check for Updates Needed

async function checkUpdatesNeeded(
  manifestVersion: string,
  extensionIds: { amo?: string; chrome?: string; edge?: string }
): Promise<{ store: string; needsUpdate: boolean }[]> {
  const storeVersions = await checkAllStoreVersions(extensionIds);

  return Object.entries(storeVersions)
    .filter(([_, version]) => version !== null)
    .map(([store, version]) => ({
      store,
      needsUpdate: isNewerVersion(manifestVersion, version!)
    }));
}

Submit to All Stores

async function submitToAllStores(
  zipPath: string,
  extensionIds: { amo: string; chrome: string; edge: string }
): Promise<{ store: string; success: boolean; error?: string }[]> {
  const results = await Promise.allSettled([
    submitToAMO(zipPath, extensionIds.amo),
    submitToChrome(zipPath, extensionIds.chrome),
    submitToEdge(zipPath, extensionIds.edge)
  ]);

  return [
    { store: 'amo', ...resultToStatus(results[0]) },
    { store: 'chrome', ...resultToStatus(results[1]) },
    { store: 'edge', ...resultToStatus(results[2]) }
  ];
}

Related Resources

  • store-submission-reviewer agent: Pre-submission compliance checks
  • extension-store-submission skill: Submission process guides
  • validate-extension command: Local validation before submission