@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 +143 -3
- package/package.json +1 -1
- package/src/bridge.mjs +53 -0
- package/src/setup.mjs +81 -6
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
|
|
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,
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
418
|
-
|
|
419
|
-
fs.
|
|
420
|
-
|
|
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
|
}
|