cdp-bridge-mcp-browser-control

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

CDP Bridge MCP Browser Control

CDP Bridge MCP Browser Control

Skill by ara.so — MCP Skills collection.
ara.so开发的Skill — 属于MCP技能集。

Overview

概述

CDP Bridge MCP is a Model Context Protocol server that bridges LLM clients to real, active browser sessions through Chrome DevTools Protocol (CDP) and a companion browser extension. Unlike headless automation tools, it connects to your already-open, already-logged-in browser tabs, preserving authentication state, cookies, and rendered page content.
Key differentiators:
  • Reuses real login sessions — no need to re-authenticate or transfer cookies
  • Works with current browser state — connects to tabs you already have open
  • LLM-optimized page scanning — filters HTML to preserve useful content while reducing tokens
  • Automatic CSP handling — falls back to CDP when content security policies block script injection
  • Lightweight setup — no separate browser instances or complex configuration
CDP Bridge MCP是一款Model Context Protocol服务器,通过Chrome DevTools Protocol (CDP)和配套浏览器扩展,将LLM客户端连接到真实活跃的浏览器会话。与无头自动化工具不同,它连接到你已打开、已登录的浏览器标签页,保留认证状态、Cookie和渲染后的页面内容。
核心差异化特性:
  • 复用真实登录会话 — 无需重新认证或转移Cookie
  • 适配当前浏览器状态 — 连接到你已打开的标签页
  • 针对LLM优化的页面扫描 — 过滤HTML以保留有用内容并减少Token消耗
  • 自动处理CSP — 当内容安全策略阻止脚本注入时,自动回退到CDP
  • 轻量部署 — 无需独立浏览器实例或复杂配置

Installation

安装

1. Install the MCP Server

1. 安装MCP服务器

The server is available via PyPI and can be run with
uvx
:
bash
undefined
该服务器可通过PyPI获取,使用
uvx
运行:
bash
undefined

Test with stdio mode (default)

以标准输入输出模式测试(默认)

uvx cdp-bridge@latest
uvx cdp-bridge@latest

Run as HTTP server (for multi-client scenarios)

以HTTP服务器模式运行(适用于多客户端场景)

uvx cdp-bridge@latest --transport streamable-http --port 8000
undefined
uvx cdp-bridge@latest --transport streamable-http --port 8000
undefined

2. Load Browser Extension

2. 加载浏览器扩展

  1. Navigate to
    chrome://extensions/
  2. Enable "Developer mode"
  3. Click "Load unpacked"
  4. Select the
    src/cdp_bridge/tmwd_cdp_bridge
    folder from the repository
The extension automatically attempts to connect to
127.0.0.1:18765
(WebSocket) and retries every ~5 seconds if the server isn't running yet.
  1. 访问
    chrome://extensions/
  2. 启用「开发者模式」
  3. 点击「加载已解压的扩展程序」
  4. 选择仓库中的
    src/cdp_bridge/tmwd_cdp_bridge
    文件夹
扩展会自动尝试连接
127.0.0.1:18765
(WebSocket),若服务器未运行,每约5秒重试一次。

3. Configure MCP Client

3. 配置MCP客户端

For stdio mode (Claude Desktop, most local clients):
json
{
  "mcpServers": {
    "cdp-bridge": {
      "command": "uvx",
      "args": ["cdp-bridge@latest"]
    }
  }
}
For streamable-http mode (shared/Docker deployments):
First start the server:
bash
uvx cdp-bridge@latest --transport streamable-http --port 8000
Then configure the client:
json
{
  "mcpServers": {
    "cdp-bridge": {
      "type": "streamableHttp",
      "url": "http://127.0.0.1:8000/mcp"
    }
  }
}
Claude Code:
bash
undefined
标准输入输出模式(适用于Claude Desktop、多数本地客户端):
json
{
  "mcpServers": {
    "cdp-bridge": {
      "command": "uvx",
      "args": ["cdp-bridge@latest"]
    }
  }
}
流式HTTP模式(适用于共享/ Docker部署):
先启动服务器:
bash
uvx cdp-bridge@latest --transport streamable-http --port 8000
然后配置客户端:
json
{
  "mcpServers": {
    "cdp-bridge": {
      "type": "streamableHttp",
      "url": "http://127.0.0.1:8000/mcp"
    }
  }
}
Claude Code:
bash
undefined

stdio

标准输入输出模式

claude mcp add cdp-bridge uvx cdp-bridge@latest
claude mcp add cdp-bridge uvx cdp-bridge@latest

streamable-http

流式HTTP模式

claude mcp add cdp-bridge --transport streamable-http http://127.0.0.1:8000/mcp

**Codex:**
```bash
claude mcp add cdp-bridge --transport streamable-http http://127.0.0.1:8000/mcp

**Codex:**
```bash

stdio

标准输入输出模式

codex mcp add cdp-bridge uvx cdp-bridge@latest
codex mcp add cdp-bridge uvx cdp-bridge@latest

streamable-http

流式HTTP模式

codex mcp add cdp-bridge --transport streamable-http --url http://127.0.0.1:8000/mcp
undefined
codex mcp add cdp-bridge --transport streamable-http --url http://127.0.0.1:8000/mcp
undefined

Available Tools

可用工具

ToolPurpose
browser_get_tabs
List all connected browser tabs with URLs and titles
browser_scan
Extract page content as simplified HTML or plain text
browser_execute_js
Run JavaScript in the active tab
browser_switch_tab
Change which tab is active for MCP operations
browser_batch
Execute multiple CDP/extension commands atomically
browser_wait
Poll a JavaScript condition until true or timeout
browser_navigate
Navigate the active tab to a URL
browser_screenshot
Capture page screenshot as base64 PNG
browser_cookies
Read cookies for the current domain
工具用途
browser_get_tabs
列出所有已连接的浏览器标签页,包含URL和标题
browser_scan
以简化HTML或纯文本格式提取页面内容
browser_execute_js
在活跃标签页中运行JavaScript
browser_switch_tab
切换MCP操作的目标活跃标签页
browser_batch
原子化执行多个CDP/扩展命令
browser_wait
轮询JavaScript条件,直到条件成立或超时
browser_navigate
导航活跃标签页至指定URL
browser_screenshot
捕获页面截图并以base64编码的PNG格式返回
browser_cookies
读取当前域名的Cookie

Common Usage Patterns

常见使用场景

Get Available Tabs

获取可用标签页

python
undefined
python
undefined

When user asks: "what tabs do I have open?"

当用户询问:"我打开了哪些标签页?"

result = await use_mcp_tool( server_name="cdp-bridge", tool_name="browser_get_tabs", arguments={} )
result = await use_mcp_tool( server_name="cdp-bridge", tool_name="browser_get_tabs", arguments={} )

Result structure:

返回结果结构:

{

{

"tabs": [

"tabs": [

{"id": "tab_0", "url": "https://example.com", "title": "Example Domain"},

{"id": "tab_0", "url": "https://example.com", "title": "Example Domain"},

{"id": "tab_1", "url": "https://github.com", "title": "GitHub"}

{"id": "tab_1", "url": "https://github.com", "title": "GitHub"}

],

],

"active_tab": "tab_0"

"active_tab": "tab_0"

}

}

undefined
undefined

Scan Page Content

扫描页面内容

python
undefined
python
undefined

Extract simplified HTML (default)

提取简化HTML(默认格式)

result = await use_mcp_tool( server_name="cdp-bridge", tool_name="browser_scan", arguments={ "tab_id": "tab_0", "format": "html" # or "text" for plain text } )
result = await use_mcp_tool( server_name="cdp-bridge", tool_name="browser_scan", arguments={ "tab_id": "tab_0", "format": "html" # 或使用"text"获取纯文本 } )

Returns cleaned HTML with scripts/styles removed

返回已清理的HTML,移除了脚本和样式

Preserves semantic structure for LLM consumption

保留语义结构以适配LLM处理


**How `browser_scan` works:**
- Removes `<script>`, `<style>`, `<svg>`, hidden elements
- Keeps text content, links, headings, form controls
- Converts `<img>` to `[Image: alt_text]`
- Ideal for reducing token usage while preserving page semantics

**`browser_scan`工作原理:**
- 移除`<script>`、`<style>`、`<svg>`及隐藏元素
- 保留文本内容、链接、标题、表单控件
- 将`<img>`转换为`[图片: alt_text]`格式
- 在减少Token消耗的同时保留页面语义,非常适合LLM处理

Execute JavaScript

执行JavaScript

python
undefined
python
undefined

Extract data or interact with the page

提取数据或与页面交互

result = await use_mcp_tool( server_name="cdp-bridge", tool_name="browser_execute_js", arguments={ "tab_id": "tab_0", "code": """ return { title: document.title, links: Array.from(document.querySelectorAll('a')) .slice(0, 10) .map(a => ({href: a.href, text: a.innerText})) }; """ } )
result = await use_mcp_tool( server_name="cdp-bridge", tool_name="browser_execute_js", arguments={ "tab_id": "tab_0", "code": """ return { title: document.title, links: Array.from(document.querySelectorAll('a')) .slice(0, 10) .map(a => ({href: a.href, text: a.innerText})) }; """ } )

Result contains the returned object

返回结果包含执行后返回的对象


**Execution modes:**
1. **Primary**: Uses `chrome.scripting.executeScript` in MAIN world
2. **Fallback**: Uses CDP `Runtime.evaluate` if CSP blocks injection

**执行模式:**
1. **主模式**:在MAIN环境中使用`chrome.scripting.executeScript`
2. **回退模式**:若CSP阻止注入,则使用CDP的`Runtime.evaluate`

Wait for Dynamic Content

等待动态内容加载

python
undefined
python
undefined

Wait for element to appear (useful for SPAs)

等待元素出现(适用于单页应用)

result = await use_mcp_tool( server_name="cdp-bridge", tool_name="browser_wait", arguments={ "tab_id": "tab_0", "condition": "document.querySelector('.dynamic-content') !== null", "timeout": 10, # seconds "interval": 0.5 # poll every 500ms } )
result = await use_mcp_tool( server_name="cdp-bridge", tool_name="browser_wait", arguments={ "tab_id": "tab_0", "condition": "document.querySelector('.dynamic-content') !== null", "timeout": 10, # 秒 "interval": 0.5 # 每500毫秒轮询一次 } )

Returns True if condition met, raises timeout error otherwise

若条件满足返回True,超时则抛出错误

undefined
undefined

Navigate and Screenshot

导航页面并截图

python
undefined
python
undefined

Navigate to a URL

导航至指定URL

await use_mcp_tool( server_name="cdp-bridge", tool_name="browser_navigate", arguments={ "tab_id": "tab_0", "url": "https://example.com" } )
await use_mcp_tool( server_name="cdp-bridge", tool_name="browser_navigate", arguments={ "tab_id": "tab_0", "url": "https://example.com" } )

Wait for page load

等待页面加载完成

await use_mcp_tool( server_name="cdp-bridge", tool_name="browser_wait", arguments={ "tab_id": "tab_0", "condition": "document.readyState === 'complete'", "timeout": 30 } )
await use_mcp_tool( server_name="cdp-bridge", tool_name="browser_wait", arguments={ "tab_id": "tab_0", "condition": "document.readyState === 'complete'", "timeout": 30 } )

Capture screenshot

捕获截图

screenshot = await use_mcp_tool( server_name="cdp-bridge", tool_name="browser_screenshot", arguments={ "tab_id": "tab_0", "format": "png" # returns base64-encoded image } )
undefined
screenshot = await use_mcp_tool( server_name="cdp-bridge", tool_name="browser_screenshot", arguments={ "tab_id": "tab_0", "format": "png" # 返回base64编码的图片 } )
undefined

Read Cookies

读取Cookie

python
undefined
python
undefined

Get cookies for authenticated session analysis

获取Cookie用于分析认证会话

result = await use_mcp_tool( server_name="cdp-bridge", tool_name="browser_cookies", arguments={ "tab_id": "tab_0" } )
result = await use_mcp_tool( server_name="cdp-bridge", tool_name="browser_cookies", arguments={ "tab_id": "tab_0" } )

Returns array of cookie objects:

返回Cookie对象数组:

[{"name": "session_id", "value": "...", "domain": "example.com", ...}]

[{"name": "session_id", "value": "...", "domain": "example.com", ...}]

undefined
undefined

Batch Operations (Advanced)

批量操作(进阶)

python
undefined
python
undefined

Execute multiple CDP commands in sequence

按顺序执行多个CDP命令

result = await use_mcp_tool( server_name="cdp-bridge", tool_name="browser_batch", arguments={ "tab_id": "tab_0", "commands": [ { "method": "Runtime.evaluate", "params": { "expression": "document.title", "returnByValue": True } }, { "method": "Network.getCookies", "params": {} } ] } )
result = await use_mcp_tool( server_name="cdp-bridge", tool_name="browser_batch", arguments={ "tab_id": "tab_0", "commands": [ { "method": "Runtime.evaluate", "params": { "expression": "document.title", "returnByValue": True } }, { "method": "Network.getCookies", "params": {} } ] } )

Returns array of results matching command order

返回与命令顺序对应的结果数组

undefined
undefined

Real-World Examples

实际应用示例

Extract Article Content from Logged-In Site

从已登录站点提取文章内容

python
undefined
python
undefined

User is already logged in to medium.com

用户已登录medium.com

1. Find the article tab

1. 找到文章标签页

tabs = await use_mcp_tool("cdp-bridge", "browser_get_tabs", {}) article_tab = next(t for t in tabs["tabs"] if "medium.com" in t["url"])
tabs = await use_mcp_tool("cdp-bridge", "browser_get_tabs", {}) article_tab = next(t for t in tabs["tabs"] if "medium.com" in t["url"])

2. Switch to it

2. 切换到该标签页

await use_mcp_tool("cdp-bridge", "browser_switch_tab", {"tab_id": article_tab["id"]})
await use_mcp_tool("cdp-bridge", "browser_switch_tab", {"tab_id": article_tab["id"]})

3. Extract article text

3. 提取文章文本

content = await use_mcp_tool( "cdp-bridge", "browser_execute_js", { "tab_id": article_tab["id"], "code": """ const article = document.querySelector('article'); return article ? article.innerText : 'No article found'; """ } )
undefined
content = await use_mcp_tool( "cdp-bridge", "browser_execute_js", { "tab_id": article_tab["id"], "code": """ const article = document.querySelector('article'); return article ? article.innerText : '未找到文章'; """ } )
undefined

Monitor Dashboard Data

监控仪表盘数据

python
undefined
python
undefined

User has analytics dashboard open

用户已打开分析仪表盘

Poll for updated metrics every 30 seconds

每30秒轮询更新指标

while True: metrics = await use_mcp_tool( "cdp-bridge", "browser_execute_js", { "tab_id": "tab_0", "code": """ return { visitors: document.querySelector('.visitor-count')?.innerText, revenue: document.querySelector('.revenue')?.innerText, timestamp: Date.now() }; """ } )
# Process metrics...
await asyncio.sleep(30)
undefined
while True: metrics = await use_mcp_tool( "cdp-bridge", "browser_execute_js", { "tab_id": "tab_0", "code": """ return { visitors: document.querySelector('.visitor-count')?.innerText, revenue: document.querySelector('.revenue')?.innerText, timestamp: Date.now() }; """ } )
# 处理指标数据...
await asyncio.sleep(30)
undefined

Fill Form in Authenticated Session

在认证会话中填写表单

python
undefined
python
undefined

Navigate to form page

导航至表单页面

await use_mcp_tool( "cdp-bridge", "browser_navigate", {"tab_id": "tab_0", "url": "https://example.com/settings"} )
await use_mcp_tool( "cdp-bridge", "browser_navigate", {"tab_id": "tab_0", "url": "https://example.com/settings"} )

Wait for form to load

等待表单加载完成

await use_mcp_tool( "cdp-bridge", "browser_wait", { "tab_id": "tab_0", "condition": "document.querySelector('form#settings') !== null", "timeout": 10 } )
await use_mcp_tool( "cdp-bridge", "browser_wait", { "tab_id": "tab_0", "condition": "document.querySelector('form#settings') !== null", "timeout": 10 } )

Fill and submit

填写并提交表单

await use_mcp_tool( "cdp-bridge", "browser_execute_js", { "tab_id": "tab_0", "code": """ const form = document.querySelector('form#settings'); form.querySelector('input[name="email"]').value = 'user@example.com'; form.querySelector('input[name="notifications"]').checked = true; form.submit(); """ } )
undefined
await use_mcp_tool( "cdp-bridge", "browser_execute_js", { "tab_id": "tab_0", "code": """ const form = document.querySelector('form#settings'); form.querySelector('input[name="email"]').value = 'user@example.com'; form.querySelector('input[name="notifications"]').checked = true; form.submit(); """ } )
undefined

Troubleshooting

故障排查

Extension Shows "ERR_CONNECTION_REFUSED"

扩展显示"ERR_CONNECTION_REFUSED"

Normal behavior on first load. The extension retries connection every ~5 seconds. Once you invoke any MCP tool (e.g.,
browser_get_tabs
), the server starts and the extension connects automatically within seconds.
首次加载时属于正常现象。扩展每约5秒重试一次连接。当你调用任意MCP工具(如
browser_get_tabs
)后,服务器会启动,扩展会在几秒内自动连接。

No Tabs Appear in
browser_get_tabs

browser_get_tabs
无标签页返回

  1. Verify the extension is loaded and enabled at
    chrome://extensions/
  2. Check the extension's service worker console for connection status
  3. Ensure MCP server is running (invoke any tool to start it in stdio mode)
  4. Confirm WebSocket server is listening on
    127.0.0.1:18765
  1. 验证扩展已在
    chrome://extensions/
    中加载并启用
  2. 查看扩展的服务工作线程控制台确认连接状态
  3. 确保MCP服务器已运行(在标准输入输出模式下,调用任意工具即可启动服务器)
  4. 确认WebSocket服务器正在监听
    127.0.0.1:18765

JavaScript Execution Fails with CSP Error

JavaScript执行因CSP错误失败

The extension automatically falls back to CDP
Runtime.evaluate
when CSP blocks
chrome.scripting
. If both fail:
  • Check browser console for specific CSP violations
  • Try wrapping code in
    (function() { ... })()
  • Avoid accessing restricted APIs (e.g., cross-origin iframes)
当CSP阻止
chrome.scripting
时,扩展会自动回退到CDP的
Runtime.evaluate
。若两者均失败:
  • 查看浏览器控制台获取具体的CSP违规信息
  • 尝试将代码包裹在
    (function() { ... })()
  • 避免访问受限API(如跨源iframe)

Tab IDs Change Unexpectedly

标签页ID意外变更

Tab IDs are session-based and reset when:
  • The extension reloads
  • The browser restarts
  • WebSocket reconnects
Always call
browser_get_tabs
before targeting a specific tab if state may have changed.
标签页ID基于会话生成,在以下情况会重置:
  • 扩展重新加载
  • 浏览器重启
  • WebSocket重新连接
若状态可能发生变化,在定位特定标签页前务必调用
browser_get_tabs

High Token Usage from
browser_scan

browser_scan
导致Token消耗过高

  1. Use
    "format": "text"
    for plain text extraction (fewer tokens)
  2. Limit scope by executing JS to extract specific DOM subtrees first:
python
await use_mcp_tool(
    "cdp-bridge",
    "browser_execute_js",
    {
        "tab_id": "tab_0",
        "code": "document.body.innerHTML = document.querySelector('main').innerHTML"
    }
)
  1. 使用
    "format": "text"
    提取纯文本(Token消耗更少)
  2. 先执行JS提取特定DOM子树以缩小范围:
python
await use_mcp_tool(
    "cdp-bridge",
    "browser_execute_js",
    {
        "tab_id": "tab_0",
        "code": "document.body.innerHTML = document.querySelector('main').innerHTML"
    }
)

Then scan the reduced page

然后扫描简化后的页面

await use_mcp_tool("cdp-bridge", "browser_scan", {"tab_id": "tab_0"})
undefined
await use_mcp_tool("cdp-bridge", "browser_scan", {"tab_id": "tab_0"})
undefined

Configuration Options

配置选项

Server Launch Arguments

服务器启动参数

bash
uvx cdp-bridge@latest [OPTIONS]

Options:
  --transport [stdio|streamable-http]  Transport mode (default: stdio)
  --port INTEGER                       HTTP port for streamable-http mode (default: 8000)
bash
uvx cdp-bridge@latest [OPTIONS]

选项:
  --transport [stdio|streamable-http]  传输模式(默认:stdio)
  --port INTEGER                       流式HTTP模式下的端口(默认:8000)

Extension Configuration

扩展配置

Edit
src/cdp_bridge/tmwd_cdp_bridge/background.js
to customize:
javascript
// WebSocket server address
const WS_URL = 'ws://127.0.0.1:18765';

// Reconnection interval (ms)
const RECONNECT_INTERVAL = 5000;
编辑
src/cdp_bridge/tmwd_cdp_bridge/background.js
进行自定义:
javascript
// WebSocket服务器地址
const WS_URL = 'ws://127.0.0.1:18765';

// 重连间隔(毫秒)
const RECONNECT_INTERVAL = 5000;

Architecture Notes

架构说明

  • Transport modes:
    stdio
    for single-client (subprocess),
    streamable-http
    for multi-client (persistent server)
  • Extension communication: WebSocket (primary) + HTTP long-polling (fallback)
  • JavaScript execution: MAIN world via
    chrome.scripting
    , falls back to CDP for CSP-restricted pages
  • Session management: Each connected tab gets a unique session ID, managed by
    TMWebDriver
  • 传输模式
    stdio
    适用于单客户端(子进程),
    streamable-http
    适用于多客户端(持久化服务器)
  • 扩展通信:WebSocket(主方式)+ HTTP长轮询(回退方式)
  • JavaScript执行:通过
    chrome.scripting
    在MAIN环境执行,针对受CSP限制的页面回退到CDP
  • 会话管理:每个已连接的标签页页会获得唯一会话ID,由
    TMWebDriver
    管理

Requirements

要求

  • Python 3.10+
  • Chrome or Chromium-based browser
  • Browser extension loaded in developer mode
  • Network access to
    127.0.0.1:18765
    (WebSocket) and
    127.0.0.1:18766
    (HTTP)
  • Python 3.10+
  • Chrome或基于Chromium的浏览器
  • 在开发者模式下加载浏览器扩展
  • 可访问
    127.0.0.1:18765
    (WebSocket)和
    127.0.0.1:18766
    (HTTP)