@predotdev/mcp 2.0.2 → 2.1.1

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
@@ -31,7 +31,7 @@ One MCP server gives your coding agent everything pre.dev does:
31
31
  - **Browser Agents in the cloud.** Send a URL and a task to pre.dev's browsers and get structured data back, many runs in parallel.
32
32
  - **Specs and plans.** Plan an app or feature before building it: architecture, tech stack, milestones and user stories.
33
33
 
34
- It works with **any coding agent that runs MCP servers locally**: Claude Code, Codex, Cursor, Windsurf, VS Code, Gemini CLI, OpenCode, Claude Desktop, the [pre.dev CLI](https://pre.dev) and more. Run as many agents as you like at the same time. They all share one connection to Chrome, so Chrome only asks you to allow it once.
34
+ It works with **any coding agent**: Claude Code, Codex, Hermes, Pi, OpenClaw, Cursor, GitHub Copilot, Cline, Gemini CLI and Antigravity, OpenCode, Zed, Goose, the [pre.dev CLI](https://pre.dev) and 20 more, and any agent that can run a shell command. Run as many agents as you like at the same time. They all share one connection to Chrome, so Chrome only asks you to allow it once.
35
35
 
36
36
  Built with the [pre.dev CLI](https://docs.pre.dev/cli/overview).
37
37
 
@@ -46,7 +46,7 @@ npx -y @predotdev/mcp setup
46
46
  It walks you through everything:
47
47
 
48
48
  1. **Signs you in to pre.dev** in your browser (or creates a free account). Approve, and your key is saved for every agent. Nothing to copy. Already signed in to the pre.dev CLI? It uses that login.
49
- 2. **Adds it to every coding agent on your Mac**: Claude Code, Codex, Cursor, Windsurf, VS Code, Gemini CLI, OpenCode, Claude Desktop and the pre.dev CLI. Already using the hosted pre.dev MCP? This one includes all of its tools, so setup replaces it.
49
+ 2. **Adds it to every coding agent on your Mac**: Claude Code, Codex, Hermes, Pi, OpenClaw, Cursor, Copilot CLI, Cline, Antigravity, Gemini CLI, OpenCode, VS Code, Claude Desktop, the pre.dev CLI and more (28 in all; [list](#setup-for-every-agent)). Already using the hosted pre.dev MCP? This one includes all of its tools, so setup replaces it.
50
50
  3. **Connects to Chrome.** The first time, it asks you to turn on remote debugging at `chrome://inspect/#remote-debugging` (it copies the address for you), and Chrome asks **"Allow remote debugging?"**: click **Allow**.
51
51
 
52
52
  Then restart your agent and try:
@@ -59,6 +59,10 @@ Then restart your agent and try:
59
59
 
60
60
  The command is safe to run again at any time, and running it again updates to the latest version. `npx -y @predotdev/mcp check` shows the state of everything, and `uninstall` removes it from every agent.
61
61
 
62
+ ### Coming from Claude in Chrome?
63
+
64
+ Run the setup command above, then type `/chrome` in Claude Code and turn off **Enabled by default** (or start a session with `claude --no-chrome`). Your Chrome profiles and logins carry over, because this server drives the Chrome you already use. The [switching guide](https://docs.pre.dev/browser-agents/switch-from-claude-in-chrome) maps each Claude in Chrome tool to its pre.dev tool and has a line you can paste into `CLAUDE.md`.
65
+
62
66
  ## Local or cloud?
63
67
 
64
68
  Both editions of pre.dev Browser Agents are in this server, and your agent picks per task.
@@ -138,6 +142,8 @@ Once you are signed in to pre.dev (`setup` or `login` does it, or set `PREDEV_AP
138
142
  npx -y @predotdev/mcp env: PREDEV_API_KEY=your_key (optional after login)
139
143
  ```
140
144
 
145
+ `setup` finds and configures each of these that is installed: Claude Code, Codex, Hermes, Pi, OpenClaw, Cursor (editor and CLI), Windsurf, GitHub Copilot CLI, Antigravity, Gemini CLI, Cline, Kiro, Qwen Code, Factory Droid, Augment, Amp, Kilo Code, Kimi Code, Junie, Warp, Rovo Dev, OpenHands, LM Studio, OpenCode, VS Code, VS Code Insiders, Claude Desktop and the pre.dev CLI. Devin CLI, Grok Build and Warp also read Claude Code's or Cursor's config, so they pick it up from there. Zed's settings file usually has comments, so `setup` leaves it to you (below).
146
+
141
147
  <details>
142
148
  <summary><b>Claude Code</b></summary>
143
149
 
@@ -147,10 +153,34 @@ claude mcp add --scope user predev -e PREDEV_API_KEY=your_key -- npx -y @predotd
147
153
 
148
154
  </details>
149
155
 
156
+ <details>
157
+ <summary><b>Hermes</b></summary>
158
+
159
+ ```bash
160
+ hermes mcp add predev --command npx --args -y @predotdev/mcp
161
+ hermes config set mcp_servers.predev.timeout 900
162
+ ```
163
+
164
+ Answer `Y` to turn on its tools. The longer timeout gives specs and cloud runs time to finish. In a running chat, `/reload-mcp` picks it up.
165
+
166
+ </details>
167
+
168
+ <details>
169
+ <summary><b>Pi</b></summary>
170
+
171
+ ```bash
172
+ pi mcp add predev -- npx -y @predotdev/mcp
173
+ pi mcp list
174
+ ```
175
+
176
+ `pi mcp list` should say `predev: connected`. `pi mcp add` needs Pi 0.99 or newer. On an older Pi, run `pi install npm:pi-mcp-adapter` and add the `predev` entry to `~/.pi/agent/mcp.json` by hand (`setup` writes it for you).
177
+
178
+ </details>
179
+
150
180
  <details>
151
181
  <summary><b>pre.dev CLI</b></summary>
152
182
 
153
- The pre.dev CLI has pre.dev's cloud tools built in, so it only needs the Chrome tools. `setup` adds this to `~/.predev/mcp.json`, and the server uses your pre.dev CLI login:
183
+ In the pre.dev CLI, type `/chrome` (and `/chrome off` to turn it off). The CLI has pre.dev's cloud tools built in, so it only needs the Chrome tools, and the server uses your pre.dev CLI login. `/chrome` and `setup` both add this to `~/.predev/mcp.json`:
154
184
 
155
185
  ```json
156
186
  {
@@ -166,6 +196,8 @@ The pre.dev CLI has pre.dev's cloud tools built in, so it only needs the Chrome
166
196
 
167
197
  The agent sees the tools as `predev_chrome_open`, `predev_chrome_act` and so on.
168
198
 
199
+ From the pre.dev CLI, `predev mcp setup` runs this package's `setup` for your other agents.
200
+
169
201
  </details>
170
202
 
171
203
  <details>
@@ -303,6 +335,114 @@ Put that entry inside `"mcpServers"` in `~/Library/Application Support/Claude/cl
303
335
 
304
336
  </details>
305
337
 
338
+ <details>
339
+ <summary><b>More agents, by hand</b></summary>
340
+
341
+ Every entry below is the same server: command `npx`, arguments `-y @predotdev/mcp`.
342
+
343
+ | Agent | Command, or file and key |
344
+ | --- | --- |
345
+ | GitHub Copilot CLI | `copilot mcp add predev -- npx -y @predotdev/mcp`, or `~/.copilot/mcp-config.json` under `mcpServers` (add `"type": "local", "tools": ["*"]`) |
346
+ | Antigravity (`agy`) | `~/.gemini/config/mcp_config.json` under `mcpServers` |
347
+ | Cline | `cline mcp add predev --yes -- npx -y @predotdev/mcp`, or `~/.cline/data/settings/cline_mcp_settings.json` under `mcpServers` |
348
+ | Kiro | `kiro-cli mcp add --name predev --command "npx -y @predotdev/mcp" --scope global`, or `~/.kiro/settings/mcp.json` |
349
+ | Qwen Code | `qwen mcp add --scope user predev npx -y @predotdev/mcp` |
350
+ | Factory Droid | `droid mcp add predev "npx -y @predotdev/mcp"` |
351
+ | Augment (Auggie) | `auggie mcp add predev --command npx --args "-y @predotdev/mcp"` |
352
+ | Amp | `amp mcp add predev -- npx -y @predotdev/mcp` |
353
+ | OpenClaw | `openclaw mcp set predev '{"command":"npx","args":["-y","@predotdev/mcp"]}'` |
354
+ | Grok Build | `grok mcp add predev -- npx -y @predotdev/mcp` |
355
+ | Devin CLI | `devin mcp add -s user predev -- npx -y @predotdev/mcp` |
356
+ | OpenHands | `openhands mcp add predev --transport stdio npx -- -y @predotdev/mcp` |
357
+ | Kilo Code | `~/.config/kilo/kilo.jsonc` under `mcp`: `"predev": {"type": "local", "command": ["npx", "-y", "@predotdev/mcp"], "enabled": true}` |
358
+ | Kimi Code | `~/.kimi-code/mcp.json` under `mcpServers` |
359
+ | Junie | `~/.junie/mcp/mcp.json` under `mcpServers` |
360
+ | Warp | `~/.warp/.mcp.json` under `mcpServers` |
361
+ | Rovo Dev | `~/.rovodev/mcp.json` under `mcpServers` (add `"transport": "stdio"`) |
362
+ | LM Studio | `~/.lmstudio/mcp.json` under `mcpServers` |
363
+ | JetBrains AI Assistant | Settings → MCP → Import from Claude (after `setup` adds it to Claude Desktop) |
364
+
365
+ </details>
366
+
367
+ <details>
368
+ <summary><b>Zed</b></summary>
369
+
370
+ In `~/.config/zed/settings.json`:
371
+
372
+ ```json
373
+ "context_servers": {
374
+ "predev": { "command": "npx", "args": ["-y", "@predotdev/mcp"], "env": {} }
375
+ }
376
+ ```
377
+
378
+ </details>
379
+
380
+ <details>
381
+ <summary><b>Goose</b></summary>
382
+
383
+ In `~/.config/goose/config.yaml`, under `extensions:`:
384
+
385
+ ```yaml
386
+ predev:
387
+ type: stdio
388
+ name: predev
389
+ enabled: true
390
+ cmd: npx
391
+ args: [-y, "@predotdev/mcp"]
392
+ envs: {}
393
+ timeout: 900
394
+ ```
395
+
396
+ For one session only: `goose session --with-extension "npx -y @predotdev/mcp"`.
397
+
398
+ </details>
399
+
400
+ <details>
401
+ <summary><b>Continue, Crush and Mistral Vibe</b></summary>
402
+
403
+ Continue, in `~/.continue/config.yaml`:
404
+
405
+ ```yaml
406
+ mcpServers:
407
+ - name: predev
408
+ command: npx
409
+ args: ["-y", "@predotdev/mcp"]
410
+ ```
411
+
412
+ Crush, one line in `~/.config/crush/crushrc`:
413
+
414
+ ```bash
415
+ mcp add predev --command npx --args -y --args @predotdev/mcp
416
+ ```
417
+
418
+ Mistral Vibe, in `~/.vibe/config.toml`:
419
+
420
+ ```toml
421
+ [[mcp_servers]]
422
+ name = "predev"
423
+ transport = "stdio"
424
+ command = "npx"
425
+ args = ["-y", "@predotdev/mcp"]
426
+ ```
427
+
428
+ </details>
429
+
430
+ <details>
431
+ <summary><b>Agents without MCP (Aider, scripts, anything with a shell)</b></summary>
432
+
433
+ Every tool also runs from a shell, so any agent that can run commands can drive your Chrome:
434
+
435
+ ```bash
436
+ npx -y @predotdev/mcp tools # list the tools and their arguments
437
+ npx -y @predotdev/mcp call chrome_open '{"profile":"Personal","url":"https://example.com"}'
438
+ npx -y @predotdev/mcp call chrome_snapshot '{"tab":"A1B2C3"}'
439
+ npx -y @predotdev/mcp call chrome_click '{"tab":"A1B2C3","ref":"e4"}'
440
+ ```
441
+
442
+ Text goes to stdout, a screenshot is saved to a file and its path is printed, and the exit code is 1 when the tool failed. Point your agent at this section, or paste `npx -y @predotdev/mcp tools` into its instructions.
443
+
444
+ </details>
445
+
306
446
  <details>
307
447
  <summary><b>Any other MCP client</b></summary>
308
448
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@predotdev/mcp",
3
- "version": "2.0.2",
3
+ "version": "2.1.1",
4
4
  "description": "pre.dev in any coding agent: Browser Agents in the Chrome you already use and in the cloud, plus specs and plans. One command to set up. Free and open source.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/bridge.mjs CHANGED
@@ -428,6 +428,10 @@ Commands:
428
428
  predev-mcp stop stop the background daemon (it restarts on the next tool call)
429
429
  predev-mcp uninstall remove it from every agent and delete its files
430
430
  predev-mcp run the MCP server (what your agent launches)
431
+ predev-mcp tools list the tools and their arguments
432
+ predev-mcp call <tool> '<json args>'
433
+ run one tool from a shell, for agents without MCP; e.g.
434
+ predev-mcp call chrome_open '{"profile":"Default","url":"https://example.com"}'
431
435
  predev-mcp --version
432
436
 
433
437
  Docs: https://docs.pre.dev/browser-agents/local
@@ -495,6 +499,53 @@ if (typeof WebSocket === 'undefined') {
495
499
  process.exit(1);
496
500
  }
497
501
 
502
+ // `tools` and `call`: the same tools from a shell, for agents that run commands but don't speak MCP.
503
+ // Text goes to stdout; an image (a screenshot) is saved to a private temp file and its path printed.
504
+ async function runTools() {
505
+ const ctx = shimCtx();
506
+ const tools = [...(await loadTools()).toolList(ctx), ...(CLOUD ? await remote.listTools(ctx).catch(() => []) : [])];
507
+ for (const tool of tools) {
508
+ const props = Object.keys(tool.inputSchema?.properties || {});
509
+ const required = tool.inputSchema?.required || [];
510
+ console.log(`${tool.name}(${props.map(p => (required.includes(p) ? p : `${p}?`)).join(', ')})`);
511
+ console.log(` ${String(tool.description || '').split('\n')[0]}`);
512
+ }
513
+ }
514
+
515
+ async function runCall(name, raw) {
516
+ if (!name) {
517
+ process.stderr.write(`Usage: predev-mcp call <tool> '<json args>' (predev-mcp tools lists them)\n`);
518
+ process.exit(2);
519
+ }
520
+ let args = {};
521
+ try { args = raw ? JSON.parse(raw) : {}; } catch {
522
+ process.stderr.write('The arguments must be one JSON object, e.g. \'{"tab":"A1B2C3"}\'.\n');
523
+ process.exit(2);
524
+ }
525
+ const ctx = shimCtx();
526
+ let result;
527
+ try {
528
+ if (CLOUD && await remote.isRemote(ctx, name)) {
529
+ result = ctx.apiKey ? await remote.callTool(ctx, name, args)
530
+ : { content: [{ type: 'text', text: 'pre.dev tools need a free pre.dev account. Sign in with: npx -y @predotdev/mcp login' }], isError: true };
531
+ } else {
532
+ result = await request(await ensureDaemon(), 'POST', '/call', { name, args });
533
+ }
534
+ } catch (error) {
535
+ result = { content: [{ type: 'text', text: `Error: ${error.message}` }], isError: true };
536
+ }
537
+ for (const part of result.content || []) {
538
+ if (part.type === 'text') console.log(part.text);
539
+ else if (part.type === 'image' && part.data) {
540
+ const ext = /png/.test(part.mimeType || '') ? 'png' : 'jpg';
541
+ const file = path.join(os.tmpdir(), `predev-${name}-${Date.now()}.${ext}`);
542
+ fs.writeFileSync(file, Buffer.from(part.data, 'base64'), { mode: 0o600 });
543
+ console.log(`Image saved to ${file}`);
544
+ }
545
+ }
546
+ process.exit(result.isError ? 1 : 0);
547
+ }
548
+
498
549
  async function runSetupCommand(name) {
499
550
  const { createSetup } = await import(pathToFileURL(path.join(path.dirname(SELF), 'setup.mjs')).href);
500
551
  const setup = createSetup({
@@ -509,6 +560,8 @@ const command = process.argv[2] || '';
509
560
  if (command === 'daemon') runDaemon();
510
561
  else if (['setup', 'install', 'login', 'logout', 'uninstall'].includes(command)) runSetupCommand(command === 'install' ? 'setup' : command);
511
562
  else if (command === 'check') runCheck();
563
+ else if (command === 'tools') runTools();
564
+ else if (command === 'call') runCall(process.argv[3], process.argv[4]);
512
565
  else if (command === 'stop') runStop();
513
566
  else if (command === '--version' || command === '-v') console.log(PKG.version || '0.0.0');
514
567
  else if (command === '--help' || command === '-h' || command === 'help') process.stdout.write(HELP);
package/src/setup.mjs CHANGED
@@ -59,7 +59,8 @@ export function createSetup(ctx) {
59
59
  }
60
60
 
61
61
  const has = bin => spawnSync(process.platform === 'win32' ? 'where' : 'which', [bin], { stdio: 'ignore' }).status === 0;
62
- const run = (bin, args) => spawnSync(bin, args, { encoding: 'utf8', timeout: 30000 });
62
+ // `input` answers a CLI's own confirmation prompts (Hermes asks before it adds or removes a server).
63
+ const run = (bin, args, input, timeout = 30000) => spawnSync(bin, args, { encoding: 'utf8', timeout, ...(input ? { input } : {}) });
63
64
 
64
65
  // ------------------------------------------------------------------ sign in
65
66
 
@@ -232,6 +233,22 @@ export function createSetup(ctx) {
232
233
  try { return Object.fromEntries(JSON.parse(out.stdout).map(server => [server.name, server])); } catch { return {}; }
233
234
  };
234
235
 
236
+ // Hermes keeps its servers in config.yaml (under mcp_servers); each entry's block of lines, by name.
237
+ const hermesHome = process.env.HERMES_HOME || path.join(home, '.hermes');
238
+ const hermesEntries = () => {
239
+ let lines;
240
+ try { lines = fs.readFileSync(path.join(hermesHome, 'config.yaml'), 'utf8').split('\n'); } catch { return {}; }
241
+ const at = lines.findIndex(line => /^mcp_servers:\s*$/.test(line));
242
+ const entries = {};
243
+ let name = null;
244
+ for (const line of at < 0 ? [] : lines.slice(at + 1)) {
245
+ if (/^\S/.test(line)) break;
246
+ const key = /^ {2}([A-Za-z0-9_.-]+):\s*$/.exec(line);
247
+ if (key) { name = key[1]; entries[name] = ''; } else if (name) entries[name] += `${line}\n`;
248
+ }
249
+ return entries;
250
+ };
251
+
235
252
  function cliAgent(label, bin, entries, remove, add) {
236
253
  return {
237
254
  label, detect: () => has(bin),
@@ -276,10 +293,56 @@ export function createSetup(ctx) {
276
293
  return added;
277
294
  }),
278
295
  // The pre.dev CLI runs the chrome_* tools only: it has pre.dev's cloud tools built in.
296
+ // Hermes registers it with its own CLI, which checks the server and asks to enable its tools.
297
+ cliAgent('Hermes', 'hermes', hermesEntries,
298
+ name => run('hermes', ['mcp', 'remove', name], 'y\n', 90000),
299
+ name => {
300
+ const added = run('hermes', ['mcp', 'add', name, '--command', NODE, '--args', APP_ENTRY], 'Y\nY\n', 90000);
301
+ // Specs and cloud browser runs need more than Hermes's default tool timeout.
302
+ if (added.status === 0) run('hermes', ['config', 'set', `mcp_servers.${name}.timeout`, '900']);
303
+ return added;
304
+ }),
305
+ // Pi's built-in MCP (and the pi-mcp-adapter extension) read ~/.pi/agent/mcp.json.
306
+ jsonAgent('Pi', path.join(process.env.PI_CODING_AGENT_DIR || path.join(home, '.pi/agent'), 'mcp.json'), 'mcpServers',
307
+ () => ({ ...stdio(), timeout: 900 }),
308
+ () => exists(process.env.PI_CODING_AGENT_DIR || path.join(home, '.pi')) || has('pi')),
309
+ // OpenClaw keeps its servers in JSON5; its CLI edits them (set replaces, unset removes) and `show` prints JSON.
310
+ cliAgent('OpenClaw', 'openclaw', () => {
311
+ const out = String(run('openclaw', ['mcp', 'show'], null, 60000).stdout || '');
312
+ try { return JSON.parse(out.slice(out.indexOf('{'))); } catch { return {}; }
313
+ },
314
+ name => run('openclaw', ['mcp', 'unset', name], null, 60000),
315
+ name => run('openclaw', ['mcp', 'set', name, JSON.stringify({ ...stdio(), requestTimeoutMs: 900000 })], null, 60000)),
279
316
  jsonAgent('pre.dev CLI', path.join(home, '.predev/mcp.json'), 'mcpServers',
280
317
  () => ({ type: 'stdio', ...stdio(), env: { PREDEV_MCP_CLOUD: 'off' } }),
281
318
  () => exists(path.join(home, '.predev/bin')) || exists(path.join(home, '.predev/auth.json'))),
282
319
  jsonAgent('Cursor', path.join(home, '.cursor/mcp.json'), 'mcpServers', stdio, () => exists(path.join(home, '.cursor'))),
320
+ // Copilot CLI's file, which VS Code's agent host reads too.
321
+ jsonAgent('GitHub Copilot CLI', path.join(process.env.COPILOT_HOME || path.join(home, '.copilot'), 'mcp-config.json'), 'mcpServers',
322
+ () => ({ type: 'local', ...stdio(), tools: ['*'] }), () => exists(process.env.COPILOT_HOME || path.join(home, '.copilot')) || has('copilot')),
323
+ jsonAgent('Antigravity', path.join(home, '.gemini/config/mcp_config.json'), 'mcpServers', stdio,
324
+ () => exists(path.join(home, '.gemini/config')) || has('agy')),
325
+ jsonAgent('Cline', path.join(home, '.cline/data/settings/cline_mcp_settings.json'), 'mcpServers',
326
+ () => ({ ...stdio(), timeout: 900 }), () => exists(path.join(home, '.cline')) || has('cline')),
327
+ jsonAgent('Kiro', path.join(home, '.kiro/settings/mcp.json'), 'mcpServers', stdio, () => exists(path.join(home, '.kiro')) || has('kiro-cli')),
328
+ jsonAgent('Qwen Code', path.join(home, '.qwen/settings.json'), 'mcpServers', () => ({ ...stdio(), timeout: 900000 }),
329
+ () => exists(path.join(home, '.qwen')) || has('qwen')),
330
+ jsonAgent('Factory Droid', path.join(home, '.factory/mcp.json'), 'mcpServers', () => ({ type: 'stdio', ...stdio() }),
331
+ () => exists(path.join(home, '.factory')) || has('droid')),
332
+ jsonAgent('Augment', path.join(home, '.augment/settings.json'), 'mcpServers', stdio, () => exists(path.join(home, '.augment')) || has('auggie')),
333
+ jsonAgent('Amp', path.join(home, '.config/amp/settings.json'), 'amp.mcpServers', stdio, () => exists(path.join(home, '.config/amp')) || has('amp')),
334
+ jsonAgent('Kilo Code', path.join(home, '.config/kilo/kilo.jsonc'), 'mcp', () => ({ type: 'local', command: [NODE, APP_ENTRY], enabled: true }),
335
+ () => exists(path.join(home, '.config/kilo'))),
336
+ jsonAgent('Zed', path.join(home, '.config/zed/settings.json'), 'context_servers', () => ({ ...stdio(), env: {} }),
337
+ () => exists(path.join(home, '.config/zed'))),
338
+ jsonAgent('Kimi Code', path.join(home, '.kimi-code/mcp.json'), 'mcpServers', () => ({ ...stdio(), toolTimeoutMs: 900000 }),
339
+ () => exists(path.join(home, '.kimi-code'))),
340
+ jsonAgent('Junie', path.join(home, '.junie/mcp/mcp.json'), 'mcpServers', stdio, () => exists(path.join(home, '.junie'))),
341
+ jsonAgent('Warp', path.join(home, '.warp/.mcp.json'), 'mcpServers', stdio, () => exists(path.join(home, '.warp'))),
342
+ jsonAgent('Rovo Dev', path.join(home, '.rovodev/mcp.json'), 'mcpServers', () => ({ ...stdio(), transport: 'stdio' }),
343
+ () => exists(path.join(home, '.rovodev'))),
344
+ jsonAgent('OpenHands', path.join(home, '.openhands/mcp.json'), 'mcpServers', stdio, () => exists(path.join(home, '.openhands'))),
345
+ jsonAgent('LM Studio', path.join(home, '.lmstudio/mcp.json'), 'mcpServers', stdio, () => exists(path.join(home, '.lmstudio'))),
283
346
  jsonAgent('Windsurf', path.join(home, '.codeium/windsurf/mcp_config.json'), 'mcpServers', stdio, () => exists(path.join(home, '.codeium/windsurf'))),
284
347
  jsonAgent('Gemini CLI', path.join(home, '.gemini/settings.json'), 'mcpServers', stdio, () => exists(path.join(home, '.gemini')) || has('gemini')),
285
348
  jsonAgent('OpenCode', path.join(home, '.config/opencode/opencode.json'), 'mcp',
@@ -288,6 +351,10 @@ export function createSetup(ctx) {
288
351
  ...(appSupport ? [
289
352
  jsonAgent('VS Code', path.join(appSupport, 'Code/User/mcp.json'), 'servers',
290
353
  () => ({ type: 'stdio', ...stdio() }), () => exists(path.join(appSupport, 'Code/User'))),
354
+ jsonAgent('VS Code Insiders', path.join(appSupport, 'Code - Insiders/User/mcp.json'), 'servers',
355
+ () => ({ type: 'stdio', ...stdio() }), () => exists(path.join(appSupport, 'Code - Insiders/User'))),
356
+ jsonAgent('Cline in VS Code', path.join(appSupport, 'Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json'), 'mcpServers',
357
+ () => ({ ...stdio(), timeout: 900 }), () => exists(path.join(appSupport, 'Code/User/globalStorage/saoudrizwan.claude-dev'))),
291
358
  jsonAgent('Claude Desktop', path.join(appSupport, 'Claude/claude_desktop_config.json'), 'mcpServers', stdio,
292
359
  () => exists(path.join(appSupport, 'Claude'))),
293
360
  ] : []),
@@ -389,7 +456,7 @@ export function createSetup(ctx) {
389
456
  step(4, 'Connect to Chrome');
390
457
  await connectChrome();
391
458
  console.log(failed ? '\nFix the ✗ items above, then run this again (it is safe to repeat).'
392
- : `\nAll set.${added.length ? ` Restart ${added.join(', ')} so ${added.length === 1 ? 'it loads' : 'they load'} the new server,` : ''} then ask your agent:\n "List my Chrome profiles and the tabs I have open."`);
459
+ : `\nAll set.${added.length ? ` Restart ${added.length > 4 ? `your agents (${added.length} set up above)` : added.join(', ')} so ${added.length === 1 ? 'it loads' : 'they load'} the new server,` : ''} then ask your agent:\n "List my Chrome profiles and the tabs I have open."`);
393
460
  process.exit(failed ? 1 : 0);
394
461
  }
395
462
 
@@ -414,10 +481,18 @@ export function createSetup(ctx) {
414
481
  if (!found) continue;
415
482
  try { if (agent.unregister()) ok(`Removed from ${agent.label}`); } catch (error) { skip(`${agent.label}: ${error.message}`); }
416
483
  }
417
- const state = (() => { try { return JSON.parse(fs.readFileSync(path.join(stateDir, 'daemon.json'), 'utf8')); } catch { return null; } })();
418
- if (state) await request(state, 'POST', '/shutdown', {}, 3000).catch(() => {});
419
- fs.rmSync(APP_DIR, { recursive: true, force: true });
420
- fs.rmSync(credentialsFile, { force: true });
484
+ // Every daemon it runs (one per Chrome data dir), then everything it keeps in ~/.predev/mcp:
485
+ // the stable copy, the saved key, logs and the cached profile list (names and emails).
486
+ const subdirs = (() => { try { return fs.readdirSync(baseStateDir, { withFileTypes: true }).filter(d => d.isDirectory()).map(d => path.join(baseStateDir, d.name)); } catch { return []; } })();
487
+ const alive = pid => { try { process.kill(pid, 0); return true; } catch { return false; } };
488
+ for (const dir of new Set([stateDir, baseStateDir, ...subdirs])) {
489
+ const state = (() => { try { return JSON.parse(fs.readFileSync(path.join(dir, 'daemon.json'), 'utf8')); } catch { return null; } })();
490
+ if (!state) continue;
491
+ await request(state, 'POST', '/shutdown', {}, 3000).catch(() => {});
492
+ // It exits just after answering; wait, so its last log line can't recreate the folder.
493
+ for (let i = 0; i < 30 && state.pid && alive(state.pid); i++) await sleep(100);
494
+ }
495
+ fs.rmSync(baseStateDir, { recursive: true, force: true });
421
496
  ok('Stopped it and deleted its files and saved key');
422
497
  console.log('\nDone. Restart your agents. To also turn off remote debugging, open chrome://inspect/#remote-debugging.');
423
498
  }