Cette page est affichée en anglais. Une traduction française est en cours.
ScriptsMay 25, 2026·1 min de lecture

Codex Chrome MCP Proxy v3

Sanitized public Chrome MCP bundle for Codex: persistent CDP proxy, real Chrome login-state control, background tabs, focus protection, and multi-agent isolation.

Prêt pour agents

Copier un chemin de staging sûr pour cet actif

Cet actif est d'abord staged. Le prompt copié demande à l'agent d'inspecter les fichiers staged avant d'activer scripts, config MCP ou config globale.

Stage only · 19/100Policy : staging
Surface agent
Tout agent MCP/CLI
Type
Mcp Config
Installation
Stage only
Confiance
Éditeur vérifié
Point d'entrée
README.md
Commande de staging sûr
npx -y tokrepo@latest install 5846fbcf-238f-4730-9553-e36d111a6619 --target codex

Stage les fichiers d'abord; l'activation exige la revue du README et du plan staged.

Browser Control Rule

Any browser-related operation must use Chrome MCP through the persistent proxy connected to the user's real Chrome.

  • Use Chrome MCP for opening pages, navigation, clicking, typing, screenshots, DOM/page inspection, network/console/performance checks, authenticated pages, existing Chrome tabs, file uploads, and visual verification.
  • Do not use Codex Browser, Codex Chrome plugin, Computer Use, AppleScript, Playwright against a temporary browser profile, or shell-based UI automation as a browser fallback.
  • The required path is Chrome MCP proxy v3 -> 127.0.0.1:9401 -> real Chrome.
  • Use scripts/chrome-mcp-proxy.sh for the chrome MCP server.
  • Never connect directly to chrome-devtools-mcp without the proxy.
  • Never launch a stateless Chrome profile with --user-data-dir=/tmp/... or .cache/chrome-devtools-mcp/chrome-profile.
  • Do not kill the current Chrome MCP session processes. If cleanup is needed, use scripts/kill-old-chrome-mcp.sh only.
  • Keep browser work in background tabs unless the user explicitly asks to show a page.

Non-browser tasks such as reading files, editing code, running tests, using CLIs, or calling non-browser APIs should use the appropriate local tool directly.


name: codex-chrome-mcp-proxy-v3 description: Codex-ready Chrome MCP proxy bundle for controlling the user's real Chrome through a persistent CDP proxy with focus protection and multi-agent isolation.

Codex Chrome MCP Proxy v3

Use this asset when Codex browser work must go through the user's real Google Chrome instead of Codex Browser, a temporary Playwright profile, or a browser extension bridge.

This bundle packages a public, sanitized version of the Chrome MCP setup:

  • scripts/cdp-proxy.mjs - persistent CDP proxy with request-id remapping, session routing, background tabs, timeout cleanup, and reconnect handling.
  • scripts/chrome-mcp-proxy.sh - MCP stdio entrypoint that starts/reuses the persistent proxy and launches chrome-devtools-mcp.
  • scripts/kill-old-chrome-mcp.sh - conservative cleanup helper for stale chrome-devtools-mcp processes.
  • templates/codex-config.toml - Codex MCP server config snippet.
  • templates/mcp.json - generic MCP config snippet.
  • AGENTS.md - agent rule that forces browser operations through Chrome MCP.

What It Solves

Codex's built-in browser tools are useful for ordinary page testing, but they are not enough when a task requires:

  • the user's real Chrome login state, cookies, extensions, bookmarks, and existing tabs;
  • CDP-level network, console, runtime, performance, and target control;
  • background tabs that do not steal focus from the user;
  • multiple agents sharing one Chrome without request or event collisions;
  • a persistent connection that avoids repeated Chrome remote-debugging prompts.

The required path is:

Codex MCP client
  -> scripts/chrome-mcp-proxy.sh
  -> chrome-devtools-mcp
  -> scripts/cdp-proxy.mjs on 127.0.0.1:9401
  -> real Google Chrome DevTools endpoint

Install

  1. Copy the scripts to a stable local directory, for example:
mkdir -p "$HOME/scripts"
cp scripts/cdp-proxy.mjs scripts/chrome-mcp-proxy.sh scripts/kill-old-chrome-mcp.sh "$HOME/scripts/"
chmod +x "$HOME/scripts/chrome-mcp-proxy.sh" "$HOME/scripts/kill-old-chrome-mcp.sh"
  1. Install Node dependencies next to cdp-proxy.mjs:
cd "$HOME/scripts"
npm install ws
  1. Enable remote debugging for the current Chrome profile:
Open chrome://inspect/#remote-debugging
Enable "Allow remote debugging for this browser instance"
  1. Add the Codex MCP server config from templates/codex-config.toml to ~/.codex/config.toml.

  2. Restart Codex so the new MCP server is loaded.

Codex Routing Rule

For browser-related tasks, route through Chrome MCP only:

  • page open/navigation/click/type;
  • screenshots and visual verification;
  • DOM/page inspection;
  • authenticated pages and existing tabs;
  • network, console, runtime, performance, memory checks;
  • file upload flows.

Do not fall back to Codex Browser, a temporary browser profile, AppleScript, shell UI automation, or a direct chrome-devtools-mcp connection.

Non-browser work such as code edits, tests, CLI commands, and API calls can still use the normal local tools.

Health Check

curl -s --noproxy '*' http://127.0.0.1:9401/proxy/status

Expected:

{
  "chromeConnected": true,
  "pendingRequests": 0
}

If Chrome restarted, the proxy should reconnect. Chrome may ask for remote-debugging permission once after a restart.

Safety Rules

  • Always go through cdp-proxy.mjs; never connect chrome-devtools-mcp directly to Chrome.
  • Do not launch a throwaway Chrome profile for authenticated work.
  • Do not include proxy environment variables in the MCP server config.
  • Do not kill the active MCP process from the current session.
  • Use scripts/kill-old-chrome-mcp.sh only for stale process cleanup.
  • Treat cookies, local storage, passwords, tokens, and private page data as sensitive.
  • Prefer background tabs unless the user explicitly wants a visible page.

Why stage_only

This asset can modify a user's browser session, inspect authenticated pages, and run JavaScript in pages. It should be installed as a staged bundle and reviewed before activation.

{ "name": "codex-chrome-mcp-proxy-v3", "type": "module", "dependencies": { "ws": "^8.18.0" } }

#!/usr/bin/env node /**

  • CDP Background Proxy v3 — 持久连接 + 多 Agent 隔离
    1. 持久 WebSocket 连接到 Chrome(弹窗只出现一次)
    1. 拦截 Target.activateTarget / Page.bringToFront(防抢焦点)
    1. 强制 createTarget background=true
    1. 请求 ID 重映射(防多客户端 ID 冲突)
    1. 事件按 sessionId 路由到对应客户端(防多 Agent 互扰)
    1. 自动重连
  • 用法: node cdp-proxy.mjs [--port 9401] [--chrome-port 9222] */

import http from 'http'; import fs from 'fs'; import path from 'path'; import os from 'os'; import { WebSocketServer, WebSocket } from 'ws';

const args = process.argv.slice(2); const getArg = (name, def) => { const i = args.indexOf(name); return i >= 0 ? args[i + 1] : def; };

const PROXY_PORT = parseInt(getArg('--port', '9401')); const CHROME_PORT = parseInt(getArg('--chrome-port', '9222')); const CHROME_HOST = getArg('--chrome-host', '127.0.0.1'); const IDLE_TIMEOUT_MS = parseInt(getArg('--idle-timeout', '300000')); const IDLE_CHECK_INTERVAL_MS = parseInt(getArg('--idle-check', '60000'));

const DEVTOOLS_PORT_FILE = getArg('--devtools-port-file', path.join(os.homedir(), 'Library/Application Support/Google/Chrome/DevToolsActivePort'));

// ═══════════════════════════════════════════ // 持久 Chrome 连接 // ═══════════════════════════════════════════

let chromeWs = null; let chromeReady = false; let reconnecting = false;

const BLOCKED_METHODS = new Set([ 'Target.activateTarget', 'Page.bringToFront', ]);

function safeSend(ws, data) { if (ws && ws.readyState === WebSocket.OPEN) { ws.send(typeof data === 'string' ? data : JSON.stringify(data)); } }

function readDevToolsActivePort() { try { const content = fs.readFileSync(DEVTOOLS_PORT_FILE, 'utf-8').trim(); const lines = content.split('\n'); if (lines.length >= 2) { return { port: parseInt(lines[0]), wsPath: lines[1] }; } } catch (e) { /* ignore */ } return null; }

function getChromeWsUrl() { const portInfo = readDevToolsActivePort(); if (portInfo) { return ws://${CHROME_HOST}:${portInfo.port}${portInfo.wsPath}; } return null; }

// ═══════════════════════════════════════════ // 多客户端隔离 // ═══════════════════════════════════════════

// 全局自增 ID,保证唯一 let globalIdCounter = 1;

// proxyId → { clientWs, originalId, method, createdAt } const pendingRequests = new Map();

// 每30秒清理超过60秒未响应的 pending requests setInterval(() => { const now = Date.now(); let cleaned = 0; for (const [proxyId, req] of pendingRequests) { if (now - req.createdAt > 60000) { // 给客户端返回超时错误,避免它也卡住 safeSend(req.clientWs, { id: req.originalId, error: { code: -32000, message: 'CDP request timeout (60s)' } }); pendingRequests.delete(proxyId); const state = clientState.get(req.clientWs); if (state) state.proxyIds.delete(proxyId); cleaned++; } } if (cleaned > 0) { console.log([Proxy] 清理 ${cleaned} 个超时请求 (剩 ${pendingRequests.size} 个)); } }, 30000);

// sessionId → clientWs(哪个客户端 attach 了这个 session) const sessionOwners = new Map();

// clientWs → { tabs: Set, sessions: Set, proxyIds: Set } const clientState = new Map();

function getOrCreateState(clientWs) { if (!clientState.has(clientWs)) { clientState.set(clientWs, { tabs: new Set(), sessions: new Set(), proxyIds: new Set(), lastActivityAt: Date.now(), }); } return clientState.get(clientWs); }

// 空闲客户端自动回收:idle 超阈值则 detach 其全部 session(WebSocket 保留) setInterval(() => { if (!chromeReady) return; const now = Date.now(); for (const [, state] of clientState) { const idleMs = now - state.lastActivityAt; if (idleMs < IDLE_TIMEOUT_MS || state.sessions.size === 0) continue;

    const sids = Array.from(state.sessions);
    console.log(`[Proxy] 空闲回收: idle=${Math.floor(idleMs / 1000)}s, detach ${sids.length} sessions`);

    for (const sid of sids) {
        cdpRequest('Target.detachFromTarget', { sessionId: sid }).catch(() => {});
        sessionOwners.delete(sid);
        state.sessions.delete(sid);
    }
}

}, IDLE_CHECK_INTERVAL_MS);

// ═══════════════════════════════════════════ // Chrome 连接管理 // ═══════════════════════════════════════════

function connectToChrome() { if (reconnecting) return; reconnecting = true;

const wsUrl = getChromeWsUrl();
if (!wsUrl) {
    console.error('[Proxy] Chrome 未运行,5秒后重试...');
    setTimeout(() => { reconnecting = false; connectToChrome(); }, 5000);
    return;
}

console.log(`[Proxy] 连接 Chrome: ${wsUrl}`);
chromeWs = new WebSocket(wsUrl);

chromeWs.on('open', () => {
    console.log('[Proxy] ✓ Chrome 已连接');
    chromeReady = true;
    reconnecting = false;
});

chromeWs.on('message', (rawData) => {
    try {
        const msg = JSON.parse(rawData.toString());

        // ── 响应消息(有 id)→ 路由到发起者 ──
        if (msg.id !== undefined && pendingRequests.has(msg.id)) {
            const { clientWs, originalId, method } = pendingRequests.get(msg.id);
            pendingRequests.delete(msg.id);

            const state = clientState.get(clientWs);

            // 追踪 createTarget
            if (method === 'Target.createTarget' && msg.result?.targetId) {
                if (state) state.tabs.add(msg.result.targetId);
                console.log(`[Proxy] 新 tab: ${msg.result.targetId}`);
            }

            // 追踪 attachToTarget → 记录 session 归属
            if (method === 'Target.attachToTarget' && msg.result?.sessionId) {
                const sid = msg.result.sessionId;
                sessionOwners.set(sid, clientWs);
                if (state) state.sessions.add(sid);
                console.log(`[Proxy] session ${sid.slice(0, 8)}... → 客户端`);
            }

            // 还原原始 ID
            msg.id = originalId;
            safeSend(clientWs, msg);
            return;
        }

        // ── 事件消息(无 id)→ 按 sessionId 路由 ──
        if (msg.method) {
            // 带 sessionId 的事件:发给对应客户端
            if (msg.sessionId && sessionOwners.has(msg.sessionId)) {
                safeSend(sessionOwners.get(msg.sessionId), msg);
                return;
            }

            // Target 域的全局事件:按 targetId 路由或广播
            if (msg.method.startsWith('Target.')) {
                const targetId = msg.params?.targetInfo?.targetId || msg.params?.targetId;
                if (targetId) {
                    // 找到拥有这个 tab 的客户端
                    for (const [ws, state] of clientState) {
                        if (state.tabs.has(targetId)) {
                            safeSend(ws, msg);
                            return;
                        }
                    }
                }
                // 找不到归属,广播
                for (const [ws] of clientState) {
                    safeSend(ws, msg);
                }
                return;
            }

            // 其他无 sessionId 的事件,广播
            for (const [ws] of clientState) {
                safeSend(ws, msg);
            }
            return;
        }

        // 其他消息,广播
        for (const [ws] of clientState) {
            safeSend(ws, rawData);
        }
    } catch (e) {
        for (const [ws] of clientState) {
            safeSend(ws, rawData);
        }
    }
});

const handleChromeDisconnect = (reason) => {
    if (!chromeReady && chromeWs === null) return; // 已处理过
    console.log(`[Proxy] Chrome 断开 (${reason}),立即释放 ${pendingRequests.size} 个 pending 请求,5秒后重连...`);
    chromeReady = false;
    chromeWs = null;
    reconnecting = false;

    // 关键修复:给所有 pending 请求立即发 error,让客户端 Promise 立即 reject
    // 否则 chrome-devtools-mcp 的 Promise 永远悬空 → 后续命令排队僵死
    for (const [, req] of pendingRequests) {
        safeSend(req.clientWs, {
            id: req.originalId,
            error: { code: -32000, message: `Chrome disconnected (${reason}), request aborted` },
        });
    }
    pendingRequests.clear();

    // Chrome 重启后旧 session / tab 全部失效,清理客户端 state(保留 WebSocket)
    sessionOwners.clear();
    for (const [, state] of clientState) {
        state.sessions.clear();
        state.tabs.clear();
        state.proxyIds.clear();
    }

    setTimeout(() => connectToChrome(), 5000);
};

chromeWs.on('close', () => handleChromeDisconnect('close'));

chromeWs.on('error', (err) => {
    console.error('[Proxy] Chrome 错误:', err.message);
    handleChromeDisconnect('error');
});

}

// ═══════════════════════════════════════════ // HTTP 端点 // ═══════════════════════════════════════════

function cdpRequest(method, params = {}) { return new Promise((resolve, reject) => { if (!chromeReady) return reject(new Error('Chrome not connected')); const proxyId = globalIdCounter++; const timeout = setTimeout(() => { pendingRequests.delete(proxyId); reject(new Error('timeout')); }, 5000); const fakeClient = { send: (data) => { clearTimeout(timeout); resolve(typeof data === 'string' ? JSON.parse(data) : data); }, readyState: WebSocket.OPEN, }; pendingRequests.set(proxyId, { clientWs: fakeClient, originalId: proxyId, method }); chromeWs.send(JSON.stringify({ id: proxyId, method, params })); }); }

const server = http.createServer(async (req, res) => { try { if (req.url === '/json/version') { const portInfo = readDevToolsActivePort(); const wsUrl = portInfo ? ws://${CHROME_HOST}:${PROXY_PORT}${portInfo.wsPath} : ws://${CHROME_HOST}:${PROXY_PORT}/devtools/browser/proxy; res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ Browser: 'Chrome (via CDP Proxy v3)', webSocketDebuggerUrl: wsUrl })); return; }

    if (req.url === '/json/list' || req.url === '/json') {
        if (!chromeReady) { res.writeHead(503); res.end('Chrome not connected'); return; }
        try {
            const result = await cdpRequest('Target.getTargets');
            const targets = (result.result?.targetInfos || []).map(t => ({
                ...t,
                webSocketDebuggerUrl: `ws://${CHROME_HOST}:${PROXY_PORT}/devtools/page/${t.targetId}`,
            }));
            res.writeHead(200, { 'Content-Type': 'application/json' });
            res.end(JSON.stringify(targets));
        } catch (e) { res.writeHead(500); res.end(e.message); }
        return;
    }

    if (req.url === '/json/new') {
        if (!chromeReady) { res.writeHead(503); res.end('Chrome not connected'); return; }
        try {
            const result = await cdpRequest('Target.createTarget', { url: 'about:blank', background: true });
            res.writeHead(200, { 'Content-Type': 'application/json' });
            res.end(JSON.stringify(result.result || {}));
        } catch (e) { res.writeHead(500); res.end(e.message); }
        return;
    }

    if (req.url === '/proxy/status') {
        const clientList = [];
        const now = Date.now();
        for (const [, state] of clientState) {
            clientList.push({
                tabs: state.tabs.size,
                sessions: state.sessions.size,
                idleMs: now - state.lastActivityAt,
            });
        }
        res.writeHead(200, { 'Content-Type': 'application/json' });
        res.end(JSON.stringify({
            chromeConnected: chromeReady,
            clients: clientState.size,
            sessions: sessionOwners.size,
            pendingRequests: pendingRequests.size,
            clientDetails: clientList,
        }, null, 2));
        return;
    }

    res.writeHead(404);
    res.end('Not found');
} catch (e) { res.writeHead(500); res.end(e.message); }

});

// ═══════════════════════════════════════════ // WebSocket 客户端处理 // ═══════════════════════════════════════════

const wss = new WebSocketServer({ server });

wss.on('connection', (clientWs, req) => { const state = getOrCreateState(clientWs); console.log([Proxy] 客户端连接 (共 ${clientState.size} 个));

clientWs.on('message', (data) => {
    state.lastActivityAt = Date.now();
    if (!chromeReady) {
        try {
            const msg = JSON.parse(data.toString());
            if (msg.id !== undefined) {
                safeSend(clientWs, { id: msg.id, error: { code: -1, message: 'Chrome not connected' } });
            }
        } catch (e) { /* ignore */ }
        return;
    }

    try {
        const msg = JSON.parse(data.toString());

        // 拦截抢焦点命令
        if (BLOCKED_METHODS.has(msg.method)) {
            console.log(`[Proxy] 拦截: ${msg.method}`);
            const reply = { id: msg.id, result: {} };
            if (msg.sessionId) reply.sessionId = msg.sessionId;
            safeSend(clientWs, reply);
            return;
        }

        // 强制后台创建 tab
        if (msg.method === 'Target.createTarget') {
            if (!msg.params) msg.params = {};
            msg.params.background = true;
            console.log(`[Proxy] 后台创建 tab: ${msg.params.url || 'about:blank'}`);
        }

        // ── ID 重映射 ──
        if (msg.id !== undefined) {
            const proxyId = globalIdCounter++;
            pendingRequests.set(proxyId, {
                clientWs,
                originalId: msg.id,
                method: msg.method,
                createdAt: Date.now(),
            });
            state.proxyIds.add(proxyId);
            msg.id = proxyId;
        }

        safeSend(chromeWs, msg);
    } catch (e) {
        safeSend(chromeWs, data);
    }
});

const cleanup = () => {
    // 清理这个客户端的 session 归属
    for (const sid of state.sessions) {
        sessionOwners.delete(sid);
    }
    // 清理未完成的请求
    for (const pid of state.proxyIds) {
        pendingRequests.delete(pid);
    }
    clientState.delete(clientWs);
    console.log(`[Proxy] 客户端断开 (剩 ${clientState.size} 个, 释放 ${state.tabs.size} tab, ${state.sessions.size} session)`);
};

clientWs.on('close', cleanup);
clientWs.on('error', cleanup);

});

// ═══════════════════════════════════════════ // 启动 // ═══════════════════════════════════════════

process.on('uncaughtException', (err) => { console.error('[Proxy] 异常(已恢复):', err.message); }); process.on('unhandledRejection', (reason) => { console.error('[Proxy] Promise 拒绝(已恢复):', reason); });

server.listen(PROXY_PORT, () => { console.log(╔══════════════════════════════════════════════════════╗ ║ CDP Background Proxy v3 — 持久连接 + 多Agent隔离 ║ ║ ║ ║ Proxy: http://127.0.0.1:${PROXY_PORT} ║ ║ ║ ║ ✓ 持久连接(弹窗只出现一次) ║ ║ ✓ 拦截 activateTarget / bringToFront ║ ║ ✓ 强制 createTarget background=true ║ ║ ✓ 请求 ID 重映射(防多客户端冲突) ║ ║ ✓ 事件按 sessionId 路由(防多 Agent 互扰) ║ ║ ✓ 自动重连 ║ ║ ✓ 空闲客户端自动回收 session ║ ║ ║ ║ 状态: http://127.0.0.1:${PROXY_PORT}/proxy/status ║ ╚══════════════════════════════════════════════════════╝); console.log([Proxy] 空闲回收阈值: ${IDLE_TIMEOUT_MS / 1000}s, 检查间隔: ${IDLE_CHECK_INTERVAL_MS / 1000}s); connectToChrome(); });

#!/bin/bash

Chrome MCP + persistent CDP proxy.

Usage: chrome-mcp-proxy.sh [proxy_port] [chrome_port]

set -euo pipefail

PROXY_PORT="${1:-9401}" CHROME_PORT="${2:-9222}" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROXY_SCRIPT="${SCRIPT_DIR}/cdp-proxy.mjs" PID_DIR="${HOME}/chrome-profiles/pids" LOG_DIR="${HOME}/chrome-profiles/logs"

mkdir -p "$PID_DIR" "$LOG_DIR"

Local CDP traffic must not be intercepted by user-level HTTP proxies.

unset http_proxy HTTP_PROXY https_proxy HTTPS_PROXY all_proxy ALL_PROXY export no_proxy="127.0.0.1,localhost" export NO_PROXY="127.0.0.1,localhost"

if ! lsof -i :"${PROXY_PORT}" >/dev/null 2>&1; then nohup node "${PROXY_SCRIPT}"
--port "${PROXY_PORT}"
--chrome-port "${CHROME_PORT}"
>"${LOG_DIR}/proxy-${PROXY_PORT}.log" 2>&1 & echo $! > "${PID_DIR}/proxy-${PROXY_PORT}.pid" disown || true sleep 2 fi

exec npx -y chrome-devtools-mcp@0.19.0 --browserUrl "http://127.0.0.1:${PROXY_PORT}"

#!/bin/bash

Conservative cleanup helper for stale chrome-devtools-mcp processes.

It keeps the newest chrome-devtools-mcp process and terminates older ones.

set -euo pipefail

PIDS="$(ps aux | grep "[c]hrome-devtools-mcp" | grep -v watchdog | grep -v "npm exec" | awk '{print $2}' || true)"

if [ -z "$PIDS" ]; then echo "No chrome-devtools-mcp process found." exit 0 fi

COUNT="$(echo "$PIDS" | wc -l | tr -d ' ')"

if [ "$COUNT" -le 1 ]; then echo "Only one Chrome MCP process exists (PID: $PIDS). Nothing to clean." exit 0 fi

NEWEST="$(echo "$PIDS" | sort -n | tail -1)" OLD_PIDS="$(echo "$PIDS" | sort -n | head -n -1)"

echo "Found $COUNT Chrome MCP processes." echo "Keeping newest PID: $NEWEST" echo "Stopping older PIDs: $OLD_PIDS"

for pid in $OLD_PIDS; do RELATED_PIDS="$(ps aux | grep -E "npm exec.*chrome-devtools|watchdog.*parent-pid=$pid" | grep -v grep | awk '{print $2}' || true)" kill "$pid" $RELATED_PIDS 2>/dev/null || true echo "Stopped PID $pid and related processes." done

echo "Cleanup complete. Kept PID $NEWEST."

[mcp_servers.chrome] command = "bash" args = ["$HOME/scripts/chrome-mcp-proxy.sh", "9401", "9222"]

Optional: keep Codex's bundled browser plugins disabled if you want strict

"browser work must use Chrome MCP" routing.

[plugins."browser@openai-bundled"] enabled = false

[plugins."chrome@openai-bundled"] enabled = false

{ "mcpServers": { "chrome": { "command": "bash", "args": [ "$HOME/scripts/chrome-mcp-proxy.sh", "9401", "9222" ] } } }

Fil de discussion

Connectez-vous pour rejoindre la discussion.
Aucun commentaire pour l'instant. Soyez le premier à partager votre avis.

Actifs similaires

Chrome MCP Background Proxy — Fix Popups, Focus Stealing & Multi-Agent Conflicts

Persistent CDP proxy + entry script that lets chrome-devtools-mcp run against your real, logged-in Chrome without the Chrome 146+ consent popup spamming on every connection, without focus stealing (Target.activateTarget / Page.bringToFront are intercepted, createTarget is forced to background), and without request-ID / event collisions when multiple Claude Code windows or sub-agents share one Chrome. Includes the proxy core (cdp-proxy.mjs v3), entry script, safe cleanup, pre-flight healthcheck, and a launchd-style self-healing watchdog with Feishu alerts.

MCP ConfigsScripts
henuwangkai· ⭐ 1

Chrome Fleet — Multi-Agent Browser Pool with Shared Login State

Multi-agent control plane for chrome-devtools-mcp. Two modes: (1) shared main Chrome — N CDP proxies on 9401/9402/9403... all multiplexing onto one logged-in Chrome :9222 so every agent inherits your real cookies/extensions, with focus protection and ID isolation handled by cdp-proxy.mjs; (2) isolated agent Chromes — dedicated Chrome instance per agent on :930N with its own user-data-dir for multi-account / persona-isolation testing. Includes a status tool to inspect the running fleet.

MCP Configs
henuwangkai

Chrome MCP Operations Runbook — Iron Rules, Architecture & Troubleshooting

Operations skill for running chrome-devtools-mcp against your real Chrome at scale. Covers the proxy architecture, five iron rules (always-via-proxy, real-browser-only, no-env-proxy, never-kill-current-session, persistent-proxy), Chrome 146+ remote-debug popup workaround, multi-agent isolation guarantees, configuration recipes for ~/.mcp.json and ~/.claude/settings.json (with the 'no glob in permissions' gotcha), step-by-step troubleshooting flow, and four field notes from real incidents — port cleanup heuristics that backfire, protocol-layer hang detection, why 'newest = keep' is wrong, and why heavy pages need filePath-first take_snapshot to avoid 25k token overflow. Pairs with the 'Chrome MCP Background Proxy' script bundle.

SkillsMCP Configs
henuwangkai

Multi-Browser MCP Proxies — Arc Browser & Chrome Beta Variants

Companion to 'Chrome MCP Background Proxy' for running parallel, isolated MCP fleets against Arc Browser and Chrome Beta on top of the same cdp-proxy.mjs. Arc-specific proxy auto-discovers the WebSocket path from /json/version (Arc doesn't write a DevToolsActivePort file in the standard location); Chrome Beta proxy points at Beta's own DevToolsActivePort. Lets you run mcp__chrome__*, mcp__beta__*, and mcp__arc__* side-by-side with independent client state and no cross-talk.

MCP Configs
henuwangkai