@mehmoodqureshi/chrome-mcp 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -108,29 +108,55 @@ explicitly (and is never written to disk).
108
108
 
109
109
  **2. Load the extension** — **required**; the server can drive nothing without it.
110
110
 
111
- `extension-dist/` ships prebuilt inside the npm package, so there is nothing to
112
- compile. Install globally to get a stable path to it:
111
+ The extension ships prebuilt inside the npm package, and every time the server
112
+ boots it copies it to a plain folder right under your home directory:
113
+
114
+ ```
115
+ ~/chrome-mcp-extension (macOS / Linux)
116
+ %USERPROFILE%\chrome-mcp-extension (Windows)
117
+ ```
118
+
119
+ So after step 1 has started the server once (restart your client, or `/mcp` in
120
+ Claude Code), the folder is already there. To create it without a client, or
121
+ to print the exact path:
113
122
 
114
123
  ```bash
115
- npm install -g @mehmoodqureshi/chrome-mcp
116
- chrome-mcp --extension-path # prints the absolute path of extension-dist
124
+ npx -y @mehmoodqureshi/chrome-mcp --extension-path
117
125
  ```
118
126
 
119
127
  Then `chrome://extensions` → enable **Developer mode** → **Load unpacked** →
120
- select that `extension-dist/` directory. (Working from a git clone instead? Run
121
- `npm install && npm run build:ext` first `extension-dist/` is gitignored.)
128
+ pick `chrome-mcp-extension` in your home folder. After upgrading the package the
129
+ server refreshes the files on its next boot; click **Reload** on
130
+ `chrome://extensions` to pick them up. `CHROME_MCP_EXTENSION_DIR` moves the
131
+ folder somewhere else. (Working from a git clone instead? Run
132
+ `npm install && npm run build:ext` first — `extension-dist/` is gitignored, and
133
+ the server mirrors it to the same home folder.)
122
134
 
123
135
  **3. Pair it — usually nothing to do.** Every time the server boots it writes
124
136
  `pairing.json` (mode 0600, never shipped in the tarball) into the very
125
- `extension-dist/` folder you just loaded. The extension reads that file from its
126
- own folder on startup and pairs itself, so the toolbar badge turns green with no
127
- token to paste. Load the extension before the server has ever run? It re-checks
128
- every 30 seconds and pairs as soon as the file appears.
129
-
130
- Manual fallback (a copied folder, a read-only install): run
131
- `npx chrome-mcp --print-pairing`, open the extension's **Options** page, and
132
- paste the `port` + `token` from `~/.chrome-mcp/handshake.json`. Values saved
133
- there take precedence over the bundled file.
137
+ `chrome-mcp-extension` folder you just loaded. The extension reads that file
138
+ from its own folder on startup and pairs itself, so the toolbar badge turns
139
+ green with no token to paste. Load the extension before the server has ever
140
+ run? It re-checks every 30 seconds and pairs as soon as the file appears.
141
+
142
+ **Where to see the badge:** it sits on the extension's icon in Chrome's
143
+ toolbar, not on the `chrome://extensions` page. Chrome hides new extensions
144
+ behind the puzzle-piece button at the right of the address bar, so click that,
145
+ find **Chrome MCP Bridge**, and click the pin next to it once; the icon then
146
+ stays in the toolbar. Hover it for the status in words.
147
+
148
+ | Badge | Meaning |
149
+ |---|---|
150
+ | green dot | paired and connected |
151
+ | yellow dots | connecting |
152
+ | grey circle | not paired yet (no server has run, or no pairing file) |
153
+ | red exclamation mark | token rejected; the server rotated it, re-pairs by itself in a moment |
154
+
155
+ Manual fallback (a copied folder, a read-only home): run
156
+ `npx -y @mehmoodqureshi/chrome-mcp --print-pairing`, open the extension's
157
+ **Options** page, and paste the `port` + `token` from
158
+ `~/.chrome-mcp/handshake.json`. Values saved there take precedence over the
159
+ bundled file.
134
160
 
135
161
  ### Running more than one session
136
162
 
@@ -178,7 +204,7 @@ start. Wrap it in `cmd /c`:
178
204
 
179
205
  Or from Claude Code: `claude mcp add chrome-mcp -- cmd /c npx -y @mehmoodqureshi/chrome-mcp --allow-domain example.com`
180
206
 
181
- Everything else is the same — load the folder `chrome-mcp --extension-path` prints and pair
207
+ Everything else is the same — load `%USERPROFILE%\chrome-mcp-extension` and pair
182
208
  as above.
183
209
 
184
210
  The tools cover tabs, navigation, interaction (`click`/`type`/`press`/`hover`/
@@ -423,10 +449,13 @@ RUN_EXT_SMOKE=1 node --test dist/test/extension-smoke.test.js # live, headed
423
449
  ## The extension
424
450
 
425
451
  `extension/` builds (esbuild) to `extension-dist/`, loaded via
426
- `chrome://extensions` → **Load unpacked** → select `extension-dist/`. It pairs
427
- itself from the `pairing.json` the server writes into that folder on boot; the
428
- **Options** page paste of `port` + `token` from `~/.chrome-mcp/handshake.json`
429
- (run `npx chrome-mcp --print-pairing` to get the path) is only the fallback.
452
+ `chrome://extensions` → **Load unpacked** → select `~/chrome-mcp-extension`, the
453
+ mirror the server refreshes from `extension-dist/` on every boot (loading
454
+ `extension-dist/` directly also works). It pairs itself from the `pairing.json`
455
+ the server writes into that folder; the **Options** page paste of `port` +
456
+ `token` from `~/.chrome-mcp/handshake.json` (run
457
+ `npx -y @mehmoodqureshi/chrome-mcp --print-pairing` to get the path) is only
458
+ the fallback.
430
459
 
431
460
  > **Reads/interaction use `chrome.scripting`/`chrome.tabs`** — no "is being
432
461
  > debugged" banner, CSP-safe reads (isolated world), testable under Playwright.
package/SETUP.md CHANGED
@@ -33,25 +33,22 @@ npm --version
33
33
 
34
34
  If Node is below 18, stop and tell the human to upgrade before anything else.
35
35
 
36
- ## Step 2 — Install the package globally
36
+ ## Step 2 — Install the extension folder
37
37
 
38
- A global install gives the extension a stable path that Chrome can load from.
38
+ No global install is needed. This one command fetches the package, copies the
39
+ extension to a plain folder directly under the home directory, and prints its
40
+ path:
39
41
 
40
42
  ```
41
- npm install -g @mehmoodqureshi/chrome-mcp
42
- chrome-mcp --version
43
+ npx -y @mehmoodqureshi/chrome-mcp --extension-path
43
44
  ```
44
45
 
45
- Verify: the second command prints a version. Then locate the extension folder:
46
-
47
- ```
48
- chrome-mcp --extension-path
49
- ```
50
-
51
- It prints the absolute path of the bundled `extension-dist` folder. Verify with
52
- `ls` (or `dir`) that the folder holds `manifest.json` and `background.js`.
53
- Record the absolute path; the human needs it in step 4. The install is small
54
- and downloads no browser: the server drives the Chrome the human already has.
46
+ It prints `~/chrome-mcp-extension` (on Windows `%USERPROFILE%\chrome-mcp-extension`).
47
+ Verify with `ls` (or `dir`) that the folder holds `manifest.json` and
48
+ `background.js`. Record the path; the human needs it in step 4. The install is
49
+ small and downloads no browser: the server drives the Chrome the human already
50
+ has. The server refreshes this folder on every boot, so upgrades need no
51
+ re-copy, only a Reload on `chrome://extensions`.
55
52
 
56
53
  ## Step 3 — Wire the server into this MCP client
57
54
 
@@ -108,7 +105,8 @@ You cannot do this step yourself. Give the human these exact instructions:
108
105
 
109
106
  1. Open `chrome://extensions` in Chrome.
110
107
  2. Turn on **Developer mode** (top right).
111
- 3. Click **Load unpacked** and choose the `extension-dist` folder from step 2.
108
+ 3. Click **Load unpacked** and choose the `chrome-mcp-extension` folder in
109
+ their home directory (the path from step 2).
112
110
  4. Confirm an extension named **Chrome MCP Bridge** now appears in the list.
113
111
 
114
112
  Wait for the human to confirm before continuing.
@@ -116,7 +114,7 @@ Wait for the human to confirm before continuing.
116
114
  ## Step 5 — Pairing (automatic; verify it)
117
115
 
118
116
  Pairing needs no paste. Every time the server boots it writes `pairing.json`
119
- (mode 0600) into the same `extension-dist` folder the human loaded in step 4.
117
+ (mode 0600) into the same `chrome-mcp-extension` folder the human loaded in step 4.
120
118
  The extension reads that file from its own folder and pairs itself. So make
121
119
  sure a server has booted at least once since step 2, in either of these ways:
122
120
 
@@ -124,19 +122,33 @@ sure a server has booted at least once since step 2, in either of these ways:
124
122
  - start one yourself in pairing mode and leave it running in the background:
125
123
 
126
124
  ```
127
- chrome-mcp --print-pairing --persist-token
125
+ npx -y @mehmoodqureshi/chrome-mcp --print-pairing --persist-token
128
126
  ```
129
127
 
130
128
  Verify the file exists (do not print it):
131
129
 
132
130
  ```
133
- ls "$(chrome-mcp --extension-path)/pairing.json"
131
+ ls "$(npx -y @mehmoodqureshi/chrome-mcp --extension-path)/pairing.json"
134
132
  ```
135
133
 
136
- Then ask the human to look at the extension's toolbar icon on
137
- `chrome://extensions` or in the toolbar: a green dot badge means paired and
138
- connected. It can take up to 30 seconds if the extension was loaded before the
139
- server first ran. Wait for the human to confirm.
134
+ Then tell the human where to look. The status badge is on the extension's
135
+ icon in Chrome's toolbar, not on the `chrome://extensions` page. Chrome hides
136
+ new extensions behind the puzzle-piece button at the right of the address bar,
137
+ so give them these exact steps:
138
+
139
+ 1. Click the puzzle-piece button at the right of the address bar.
140
+ 2. Find **Chrome MCP Bridge** in the list and click the pin icon next to it.
141
+ The extension icon now stays in the toolbar.
142
+ 3. Look at the small badge on that icon. Hovering it shows the status in words.
143
+
144
+ Badge meanings: a green dot means paired and connected. Yellow dots mean
145
+ connecting. A grey circle means not paired yet, usually because no server has
146
+ run since the extension was loaded. A red exclamation mark means the token was
147
+ rejected, which the extension fixes by itself within a few seconds by re-reading
148
+ the pairing file.
149
+
150
+ A green dot can take up to 30 seconds if the extension was loaded before the
151
+ server first ran. Wait for the human to confirm the green dot.
140
152
 
141
153
  **Manual fallback**, only if the badge stays grey after a minute (a copied
142
154
  extension folder or a read-only global install): read the port without
@@ -178,10 +190,10 @@ not stick. Re-check the port and token before anything else.
178
190
  Summarize for the human:
179
191
 
180
192
  - Node and package versions installed.
181
- - The absolute `extension-dist` path they loaded.
193
+ - The absolute `chrome-mcp-extension` path they loaded.
182
194
  - Which MCP client was configured, at which scope, with which domains and gates.
183
- - Whether pairing, `tabs_list`, an allowed navigation, and a refused navigation
184
- each verified green.
195
+ - Whether the badge went green, and whether `tabs_list`, an allowed
196
+ navigation, and a refused navigation each verified.
185
197
  - Anything still open on their side, such as restarting the client.
186
198
 
187
199
  For any failure, name the exact symptom, what you tried, and the matching
package/dist/src/cli.js CHANGED
@@ -21,6 +21,7 @@ const datadir_1 = require("./bridge/datadir");
21
21
  const workspace_1 = require("./bridge/workspace");
22
22
  const auth_1 = require("./bridge/auth");
23
23
  const server_2 = require("./mcp/server");
24
+ const extension_install_1 = require("./extension-install");
24
25
  /** Hard deadline for clean shutdown before we force-exit (a stuck socket must not hang us). */
25
26
  const SHUTDOWN_DEADLINE_MS = 3000;
26
27
  /**
@@ -52,12 +53,23 @@ function version() {
52
53
  }
53
54
  }
54
55
  /**
55
- * Absolute path of the bundled extension. `dist/src/cli.js` -> `<pkg>/extension-dist`,
56
- * which `files` in package.json ships in the tarball, so this is right for a global
57
- * install, an npx cache entry, and a git checkout alike.
56
+ * Mirror the bundled extension into the visible install folder
57
+ * (`~/chrome-mcp-extension`) and report where Chrome should load it from. On a
58
+ * read-only home the bundled folder is returned instead; both are loadable.
58
59
  */
59
- function extensionPath() {
60
- return (0, node_path_1.resolve)(__dirname, '..', '..', 'extension-dist');
60
+ function installExtension() {
61
+ const r = (0, extension_install_1.syncExtension)();
62
+ if (!r.ok) {
63
+ (0, server_2.logDebug)(`extension not mirrored to the home folder (${r.error}); using the bundled copy at ${r.dir}`);
64
+ return r.dir;
65
+ }
66
+ if (r.created) {
67
+ (0, server_2.logErr)(`extension installed at ${r.dir} — chrome://extensions -> Load unpacked -> pick that folder`);
68
+ }
69
+ else if (r.copied.length > 0) {
70
+ (0, server_2.logErr)(`extension files updated at ${r.dir} — click Reload on chrome://extensions to pick up the new version`);
71
+ }
72
+ return r.dir;
61
73
  }
62
74
  /** Render a byte count as a short human string (1.2 MB, 904 KB, …). */
63
75
  function humanBytes(n) {
@@ -163,7 +175,7 @@ async function main() {
163
175
  return;
164
176
  }
165
177
  if (cfg.showExtensionPath) {
166
- process.stdout.write(`${extensionPath()}\n`);
178
+ process.stdout.write(`${installExtension()}\n`);
167
179
  return;
168
180
  }
169
181
  const dataDir = (0, datadir_1.ensureDataDir)(cfg.dataDir);
@@ -194,13 +206,18 @@ async function main() {
194
206
  // Drop the same port + token into the bundled extension folder so a Load
195
207
  // unpacked from there pairs itself. Best-effort: a read-only install just
196
208
  // falls back to the Options-page paste.
197
- const bundled = (0, auth_1.writeBundledPairing)(extensionPath(), { port, token });
209
+ const extDir = installExtension();
210
+ const bundled = (0, auth_1.writeBundledPairing)(extDir, { port, token });
198
211
  if (bundled) {
199
- (0, server_2.logErr)(`auto-pairing file written to ${bundled} — Load unpacked from that folder needs no token paste`);
212
+ (0, server_2.logErr)(`auto-pairing file written to ${bundled} — Load unpacked from ${extDir} needs no token paste`);
200
213
  }
201
214
  else {
202
- (0, server_2.logDebug)(`auto-pairing file not written (extension folder missing or read-only at ${extensionPath()})`);
215
+ (0, server_2.logDebug)(`auto-pairing file not written (extension folder missing or read-only at ${extDir})`);
203
216
  }
217
+ // Anyone who loaded the extension straight from the package folder (0.8.0
218
+ // docs) keeps pairing too.
219
+ if (extDir !== (0, extension_install_1.bundledExtensionDir)())
220
+ (0, auth_1.writeBundledPairing)((0, extension_install_1.bundledExtensionDir)(), { port, token });
204
221
  if (process.env.CHROME_MCP_TOKEN) {
205
222
  (0, server_2.logErr)('token: pinned from CHROME_MCP_TOKEN (stable; pair once, never again).');
206
223
  }
@@ -274,8 +274,10 @@ Connection:
274
274
  land in profiles/<profile>/tasks/<task>/.
275
275
  --print-pairing Write the handshake, print its path, and keep the bridge
276
276
  up until Ctrl-C (manual pairing; never serves MCP)
277
- --extension-path Print the absolute path of the bundled extension folder
278
- (what to pick in chrome://extensions -> Load unpacked)
277
+ --extension-path Install the extension to ~/chrome-mcp-extension (or
278
+ CHROME_MCP_EXTENSION_DIR) and print that path the folder
279
+ to pick in chrome://extensions -> Load unpacked. The server
280
+ also refreshes it on every boot.
279
281
  --persist-token Reuse a stable on-disk token across restarts so the
280
282
  extension never has to re-pair (default: fresh per boot).
281
283
  CHROME_MCP_TOKEN env, if set, pins the token explicitly.
@@ -0,0 +1,42 @@
1
+ /**
2
+ * src/extension-install.ts — put the extension somewhere a human can find.
3
+ *
4
+ * The extension ships inside the npm package, which is the wrong place to send
5
+ * someone with a "Load unpacked" file dialog: a global install buries it five
6
+ * folders deep under `npm root -g`, and an npx run hides it in a cache. So on
7
+ * every boot (and on `--extension-path`) the server mirrors the bundled
8
+ * `extension-dist/` into a plain, visible folder directly under the home
9
+ * directory — `~/chrome-mcp-extension` — and that is the folder the docs, the
10
+ * pairing file, and `--extension-path` all point at.
11
+ *
12
+ * The copy is a one-way mirror of top-level regular files, written only when
13
+ * content differs, so an unchanged boot touches nothing. It never deletes: the
14
+ * target may hold `pairing.json` (ours) and, if the operator pointed
15
+ * CHROME_MCP_EXTENSION_DIR somewhere of their own, files that are not ours to
16
+ * remove. It never throws — a read-only home just falls back to the bundled
17
+ * folder, which still works for Load unpacked.
18
+ */
19
+ /** Folder name under the home directory. */
20
+ export declare const EXTENSION_DIR_NAME = "chrome-mcp-extension";
21
+ /** The auto-pairing file the server writes; never copied, never overwritten by the mirror. */
22
+ export declare const PAIRING_FILE = "pairing.json";
23
+ /** `dist/src/extension-install.js` -> `<pkg>/extension-dist`, which the tarball ships. */
24
+ export declare function bundledExtensionDir(): string;
25
+ /** Where the extension is mirrored for Load unpacked. `CHROME_MCP_EXTENSION_DIR` overrides. */
26
+ export declare function extensionInstallDir(): string;
27
+ export interface SyncResult {
28
+ /** The folder that now holds the extension (the target on success, the source on failure). */
29
+ dir: string;
30
+ /** Files written this run (new or changed). Empty means the target was already current. */
31
+ copied: string[];
32
+ /** True when the target folder did not exist before this run (first install). */
33
+ created: boolean;
34
+ ok: boolean;
35
+ error?: string;
36
+ }
37
+ /**
38
+ * Mirror the bundled extension into the install dir. Returns the folder to
39
+ * point Chrome at either way: the target when the mirror succeeded, the source
40
+ * when it did not (so callers always have a loadable folder).
41
+ */
42
+ export declare function syncExtension(from?: string, to?: string): SyncResult;
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ /**
3
+ * src/extension-install.ts — put the extension somewhere a human can find.
4
+ *
5
+ * The extension ships inside the npm package, which is the wrong place to send
6
+ * someone with a "Load unpacked" file dialog: a global install buries it five
7
+ * folders deep under `npm root -g`, and an npx run hides it in a cache. So on
8
+ * every boot (and on `--extension-path`) the server mirrors the bundled
9
+ * `extension-dist/` into a plain, visible folder directly under the home
10
+ * directory — `~/chrome-mcp-extension` — and that is the folder the docs, the
11
+ * pairing file, and `--extension-path` all point at.
12
+ *
13
+ * The copy is a one-way mirror of top-level regular files, written only when
14
+ * content differs, so an unchanged boot touches nothing. It never deletes: the
15
+ * target may hold `pairing.json` (ours) and, if the operator pointed
16
+ * CHROME_MCP_EXTENSION_DIR somewhere of their own, files that are not ours to
17
+ * remove. It never throws — a read-only home just falls back to the bundled
18
+ * folder, which still works for Load unpacked.
19
+ */
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.PAIRING_FILE = exports.EXTENSION_DIR_NAME = void 0;
22
+ exports.bundledExtensionDir = bundledExtensionDir;
23
+ exports.extensionInstallDir = extensionInstallDir;
24
+ exports.syncExtension = syncExtension;
25
+ const node_os_1 = require("node:os");
26
+ const node_path_1 = require("node:path");
27
+ const node_fs_1 = require("node:fs");
28
+ /** Folder name under the home directory. */
29
+ exports.EXTENSION_DIR_NAME = 'chrome-mcp-extension';
30
+ /** The auto-pairing file the server writes; never copied, never overwritten by the mirror. */
31
+ exports.PAIRING_FILE = 'pairing.json';
32
+ /** `dist/src/extension-install.js` -> `<pkg>/extension-dist`, which the tarball ships. */
33
+ function bundledExtensionDir() {
34
+ return (0, node_path_1.resolve)(__dirname, '..', '..', 'extension-dist');
35
+ }
36
+ /** Where the extension is mirrored for Load unpacked. `CHROME_MCP_EXTENSION_DIR` overrides. */
37
+ function extensionInstallDir() {
38
+ const override = process.env.CHROME_MCP_EXTENSION_DIR;
39
+ return override && override.trim() ? (0, node_path_1.resolve)(override.trim()) : (0, node_path_1.join)((0, node_os_1.homedir)(), exports.EXTENSION_DIR_NAME);
40
+ }
41
+ function sameBytes(a, b) {
42
+ return a.length === b.length && a.equals(b);
43
+ }
44
+ /**
45
+ * Mirror the bundled extension into the install dir. Returns the folder to
46
+ * point Chrome at either way: the target when the mirror succeeded, the source
47
+ * when it did not (so callers always have a loadable folder).
48
+ */
49
+ function syncExtension(from = bundledExtensionDir(), to = extensionInstallDir()) {
50
+ const copied = [];
51
+ let created = false;
52
+ try {
53
+ const entries = (0, node_fs_1.readdirSync)(from, { withFileTypes: true }).filter((e) => e.isFile() && e.name !== exports.PAIRING_FILE);
54
+ if (!entries.some((e) => e.name === 'manifest.json')) {
55
+ return { dir: from, copied, created, ok: false, error: `no manifest.json in ${from}` };
56
+ }
57
+ try {
58
+ (0, node_fs_1.statSync)(to);
59
+ }
60
+ catch {
61
+ created = true;
62
+ }
63
+ (0, node_fs_1.mkdirSync)(to, { recursive: true });
64
+ for (const e of entries) {
65
+ const src = (0, node_fs_1.readFileSync)((0, node_path_1.join)(from, e.name));
66
+ const dst = (0, node_path_1.join)(to, e.name);
67
+ let current = null;
68
+ try {
69
+ if ((0, node_fs_1.statSync)(dst).isFile())
70
+ current = (0, node_fs_1.readFileSync)(dst);
71
+ }
72
+ catch {
73
+ /* absent */
74
+ }
75
+ if (current && sameBytes(current, src))
76
+ continue;
77
+ (0, node_fs_1.writeFileSync)(dst, src);
78
+ copied.push(e.name);
79
+ }
80
+ return { dir: to, copied, created, ok: true };
81
+ }
82
+ catch (e) {
83
+ return { dir: from, copied, created, ok: false, error: e instanceof Error ? e.message : String(e) };
84
+ }
85
+ }
86
+ //# sourceMappingURL=extension-install.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "Chrome MCP Bridge",
4
- "version": "0.8.0",
4
+ "version": "0.9.0",
5
5
  "description": "Lets a local chrome-mcp server drive this browser. Pair it with the server's handshake token.",
6
6
  "minimum_chrome_version": "116",
7
7
  "background": {
@@ -18,9 +18,9 @@
18
18
  <h1>Pair with chrome-mcp</h1>
19
19
  <p class="hint" id="source">Checking pairing source…</p>
20
20
  <p class="hint">
21
- Manual pairing: run <code>npx chrome-mcp --print-pairing</code>, open the
22
- printed <code>handshake.json</code>, and paste its <code>port</code> and
23
- <code>token</code> below.
21
+ Manual pairing: run <code>npx -y @mehmoodqureshi/chrome-mcp --print-pairing</code>,
22
+ open the printed <code>handshake.json</code>, and paste its <code>port</code>
23
+ and <code>token</code> below.
24
24
  </p>
25
25
  <label for="port">Port</label>
26
26
  <input id="port" type="number" inputmode="numeric" placeholder="38017" />
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mehmoodqureshi/chrome-mcp",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "Drive your real Chrome browser over MCP — real logins, real cookies. A stdio MCP server (CLI) plus an MV3 extension, driving Chrome via chrome.scripting/chrome.tabs. Multi-tab batch automation, accessibility snapshots, deny-all security by default.",
5
5
  "author": "Mehmood Ur Rehman Qureshi",
6
6
  "license": "MIT",