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)
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues