Skip to main content
Glama
chrome-webstore-mcp

chrome-webstore-mcp

by Kein1945
README.md
# chrome-webstore-mcp

An MCP server that lets your agent (Cursor, Claude, and others) read Chrome Web Store metrics for your extension: installs, uninstalls, active users, impressions, and page views. It is read-only.

You need your own Google Cloud app. Setup takes about 10 minutes and only has to be done once.

## 1. Create a Google Cloud app

1. Open the [Google Cloud Console](https://console.cloud.google.com/) and create a project. An existing one also works.
2. Search the console for **Chrome Web Store API** and click **Enable**.
3. Open **Google Auth Platform** (called OAuth consent screen in the older interface).
   - In **Branding**, enter an app name and contact emails.
   - In **Audience**, choose **External**.
   - Also in **Audience**, add the Google account that owns the extension to **Test users**.
4. In **Clients**, click **Create client** and choose **Desktop app**. It has to be Desktop: this type needs no redirect URI.
5. Copy the **Client ID** and **Client secret**.

While the app is in Testing status, Google revokes the sign-in about every 7 days. To avoid this, click **Publish app** in **Audience**. If sign-in then shows an unverified app warning, click **Advanced** and continue: it is your own app. Personal use does not require Google's app verification.

## 2. Find your publisher ID and extension ID

Open the extension in the [Developer Dashboard](https://chrome.google.com/webstore/devconsole/). The page address looks like this:

```text
https://chrome.google.com/webstore/devconsole/<publisher-id>/<extension-id>/...
```

Take the publisher ID from this address. If you have several publishers, the ID of another one results in a 403 error.

## 3. Add the server to your MCP config

Only the app credentials are required in the config:

```json
{
  "mcpServers": {
    "chrome-webstore": {
      "command": "npx",
      "args": ["-y", "chrome-webstore-mcp"],
      "env": {
        "CWS_CLIENT_ID": "<client id>",
        "CWS_CLIENT_SECRET": "<client secret>"
      }
    }
  }
}
```

You can give the agent the publisher ID and extension ID in each request. If you work with one extension, add them to `env` as defaults so you don't have to repeat them:

```json
"CWS_PUBLISHER_ID": "<publisher id>",
"CWS_EXTENSION_ID": "<extension id>"
```

IDs from a request take priority over the config.

Where the config lives:

- **Cursor:** `~/.cursor/mcp.json`.
- **Claude Code:** the `claude mcp add` command.

Node.js 20 or newer is required.

If a client on Windows cannot start `npx`, use `"command": "cmd"` and `"args": ["/c", "npx", "-y", "chrome-webstore-mcp"]`.

The config holds a secret, so use the global file rather than one inside a repository. If the config does live in a project, don't commit it, and pass the secret through an environment variable. In Cursor that looks like `"CWS_CLIENT_SECRET": "${env:CWS_CLIENT_SECRET}"`.

## 4. Ask your agent for metrics

For example: "Show installs and uninstalls for October". If the IDs are not in the config, include them: "Show October installs for extension `<extension id>`, publisher `<publisher id>`".

The first request needs a Google sign-in with the account that owns the extension. The same happens automatically if the sign-in expires. How it looks depends on the client:

- If the client supports URL elicitation, it asks to open the sign-in page itself, and the agent gets the data right after you sign in.
- Otherwise the server opens your browser and gives the agent a link. After signing in, ask the agent to repeat the request. Cursor currently works this way.

A sign-in button in the client's settings, as remote servers have, is not available for local stdio servers.

The token is stored in `~/.config/chrome-webstore-mcp/token.json` with `0600` permissions and is sent only to Google. On Windows the file is `%USERPROFILE%\.config\chrome-webstore-mcp\token.json`, which has no `0600` mode and relies on the permissions of your profile folder.

To sign out, delete the token file and restart the server.

## The `query_metrics` tool

| Parameter | Description |
|---|---|
| `metrics` | `installs`, `installs-7d`, `uninstalls`, `users`, `users-7d`, `impressions`, `page-views` |
| `first_date`, `last_date` | inclusive bounds, `YYYY-MM-DD` |
| `dimension` | one breakdown: `os`, `app-version`, `country`, `install-source`, `install-type`, `language`, `enabled`, `utm-campaign`, `utm-content`, `utm-medium`, `utm-source`, `utm-term` |
| `extension_id` | extension ID, defaults to `CWS_EXTENSION_ID` |
| `publisher_id` | publisher ID, defaults to `CWS_PUBLISHER_ID` |
| `max_rows` | 1 to 10000, default 1000 |

Data arrives with a delay of about two days.

The sign-in is shared across the whole server, so the signed-in account must have access to every extension you request. For publishers in different Google accounts, add a separate server for each one and give each its own `XDG_CONFIG_HOME`, which keeps their tokens apart.

## Troubleshooting

| Error | What to check |
|---|---|
| `403 Permission denied` | the publisher ID from the extension's page address, and that you signed in with the owner account |
| `invalid_client` | `CWS_CLIENT_ID` and `CWS_CLIENT_SECRET` in the config |
| `access_denied` during sign-in | the account is listed in Test users |
| `Environment variable ... is not set` | `CWS_CLIENT_ID` and `CWS_CLIENT_SECRET` in the config's `env` block |
| `Pass publisher_id or set CWS_PUBLISHER_ID` | give the ID in the request or add it to the config; the same applies to `extension_id` |

## License

[MIT](LICENSE)