Skill
Community

Browser Store APIs

Install
1
Install the plugin
$
npx claudepluginhub arustydev/agents --plugin browser-extension-dev

Want just this skill?

Then install: npx claudepluginhub u/[userId]/[slug]

Description

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

Tool Access

This skill uses the workspace's default tool permissions.

Skill 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_extensionGet extension info from Firefox Add-ons
amo_get_versionsList version history from AMO
amo_check_compatibilityCheck Firefox version compatibility
cws_get_extensionGet extension info from Chrome Web Store
cws_get_reviewsGet user reviews and ratings
cws_check_statusCheck 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}/publishPOSTPublish item
/chromewebstore/v1.1/itemsPOSTCreate 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}/submissionsGETList submissions
/products/{productId}/submissionsPOSTCreate 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
AMOpending, reviewing, approved, rejected
ChromePENDING, PUBLISHED, REJECTED, SUSPENDED
EdgeInProgress, 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
Stats
Stars6
Forks2
Last CommitMar 18, 2026

Similar Skills