cdp-bridge-mcp-browser-control
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCDP Bridge MCP Browser Control
CDP Bridge MCP Browser Control
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 :
uvxbash
undefined该服务器可通过PyPI获取,使用运行:
uvxbash
undefinedTest 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
undefineduvx cdp-bridge@latest --transport streamable-http --port 8000
undefined2. Load Browser Extension
2. 加载浏览器扩展
- Navigate to
chrome://extensions/ - Enable "Developer mode"
- Click "Load unpacked"
- Select the folder from the repository
src/cdp_bridge/tmwd_cdp_bridge
The extension automatically attempts to connect to (WebSocket) and retries every ~5 seconds if the server isn't running yet.
127.0.0.1:18765- 访问
chrome://extensions/ - 启用「开发者模式」
- 点击「加载已解压的扩展程序」
- 选择仓库中的文件夹
src/cdp_bridge/tmwd_cdp_bridge
扩展会自动尝试连接(WebSocket),若服务器未运行,每约5秒重试一次。
127.0.0.1:187653. 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 8000Then 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
undefinedstdio
标准输入输出模式
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:**
```bashclaude mcp add cdp-bridge --transport streamable-http http://127.0.0.1:8000/mcp
**Codex:**
```bashstdio
标准输入输出模式
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
undefinedcodex mcp add cdp-bridge --transport streamable-http --url http://127.0.0.1:8000/mcp
undefinedAvailable Tools
可用工具
| Tool | Purpose |
|---|---|
| List all connected browser tabs with URLs and titles |
| Extract page content as simplified HTML or plain text |
| Run JavaScript in the active tab |
| Change which tab is active for MCP operations |
| Execute multiple CDP/extension commands atomically |
| Poll a JavaScript condition until true or timeout |
| Navigate the active tab to a URL |
| Capture page screenshot as base64 PNG |
| Read cookies for the current domain |
| 工具 | 用途 |
|---|---|
| 列出所有已连接的浏览器标签页,包含URL和标题 |
| 以简化HTML或纯文本格式提取页面内容 |
| 在活跃标签页中运行JavaScript |
| 切换MCP操作的目标活跃标签页 |
| 原子化执行多个CDP/扩展命令 |
| 轮询JavaScript条件,直到条件成立或超时 |
| 导航活跃标签页至指定URL |
| 捕获页面截图并以base64编码的PNG格式返回 |
| 读取当前域名的Cookie |
Common Usage Patterns
常见使用场景
Get Available Tabs
获取可用标签页
python
undefinedpython
undefinedWhen 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"
}
}
undefinedundefinedScan Page Content
扫描页面内容
python
undefinedpython
undefinedExtract 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
undefinedpython
undefinedExtract 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
undefinedpython
undefinedWait 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,超时则抛出错误
undefinedundefinedNavigate and Screenshot
导航页面并截图
python
undefinedpython
undefinedNavigate 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
}
)
undefinedscreenshot = await use_mcp_tool(
server_name="cdp-bridge",
tool_name="browser_screenshot",
arguments={
"tab_id": "tab_0",
"format": "png" # 返回base64编码的图片
}
)
undefinedRead Cookies
读取Cookie
python
undefinedpython
undefinedGet 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", ...}]
undefinedundefinedBatch Operations (Advanced)
批量操作(进阶)
python
undefinedpython
undefinedExecute 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
返回与命令顺序对应的结果数组
undefinedundefinedReal-World Examples
实际应用示例
Extract Article Content from Logged-In Site
从已登录站点提取文章内容
python
undefinedpython
undefinedUser 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';
"""
}
)
undefinedcontent = await use_mcp_tool(
"cdp-bridge",
"browser_execute_js",
{
"tab_id": article_tab["id"],
"code": """
const article = document.querySelector('article');
return article ? article.innerText : '未找到文章';
"""
}
)
undefinedMonitor Dashboard Data
监控仪表盘数据
python
undefinedpython
undefinedUser 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)undefinedwhile 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)undefinedFill Form in Authenticated Session
在认证会话中填写表单
python
undefinedpython
undefinedNavigate 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();
"""
}
)
undefinedawait 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();
"""
}
)
undefinedTroubleshooting
故障排查
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., ), the server starts and the extension connects automatically within seconds.
browser_get_tabs首次加载时属于正常现象。扩展每约5秒重试一次连接。当你调用任意MCP工具(如)后,服务器会启动,扩展会在几秒内自动连接。
browser_get_tabsNo Tabs Appear in browser_get_tabs
browser_get_tabsbrowser_get_tabs
无标签页返回
browser_get_tabs- Verify the extension is loaded and enabled at
chrome://extensions/ - Check the extension's service worker console for connection status
- Ensure MCP server is running (invoke any tool to start it in stdio mode)
- Confirm WebSocket server is listening on
127.0.0.1:18765
- 验证扩展已在中加载并启用
chrome://extensions/ - 查看扩展的服务工作线程控制台确认连接状态
- 确保MCP服务器已运行(在标准输入输出模式下,调用任意工具即可启动服务器)
- 确认WebSocket服务器正在监听
127.0.0.1:18765
JavaScript Execution Fails with CSP Error
JavaScript执行因CSP错误失败
The extension automatically falls back to CDP when CSP blocks . If both fail:
Runtime.evaluatechrome.scripting- Check browser console for specific CSP violations
- Try wrapping code in
(function() { ... })() - Avoid accessing restricted APIs (e.g., cross-origin iframes)
当CSP阻止时,扩展会自动回退到CDP的。若两者均失败:
chrome.scriptingRuntime.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 before targeting a specific tab if state may have changed.
browser_get_tabs标签页ID基于会话生成,在以下情况会重置:
- 扩展重新加载
- 浏览器重启
- WebSocket重新连接
若状态可能发生变化,在定位特定标签页前务必调用。
browser_get_tabsHigh Token Usage from browser_scan
browser_scanbrowser_scan
导致Token消耗过高
browser_scan- Use for plain text extraction (fewer tokens)
"format": "text" - 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"
}
)- 使用提取纯文本(Token消耗更少)
"format": "text" - 先执行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"})
undefinedawait use_mcp_tool("cdp-bridge", "browser_scan", {"tab_id": "tab_0"})
undefinedConfiguration 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 to customize:
src/cdp_bridge/tmwd_cdp_bridge/background.jsjavascript
// 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.jsjavascript
// WebSocket服务器地址
const WS_URL = 'ws://127.0.0.1:18765';
// 重连间隔(毫秒)
const RECONNECT_INTERVAL = 5000;Architecture Notes
架构说明
- Transport modes: for single-client (subprocess),
stdiofor multi-client (persistent server)streamable-http - Extension communication: WebSocket (primary) + HTTP long-polling (fallback)
- JavaScript execution: MAIN world via , falls back to CDP for CSP-restricted pages
chrome.scripting - Session management: Each connected tab gets a unique session ID, managed by
TMWebDriver
- 传输模式:适用于单客户端(子进程),
stdio适用于多客户端(持久化服务器)streamable-http - 扩展通信:WebSocket(主方式)+ HTTP长轮询(回退方式)
- JavaScript执行:通过在MAIN环境执行,针对受CSP限制的页面回退到CDP
chrome.scripting - 会话管理:每个已连接的标签页页会获得唯一会话ID,由管理
TMWebDriver
Requirements
要求
- Python 3.10+
- Chrome or Chromium-based browser
- Browser extension loaded in developer mode
- Network access to (WebSocket) and
127.0.0.1:18765(HTTP)127.0.0.1:18766
- Python 3.10+
- Chrome或基于Chromium的浏览器
- 在开发者模式下加载浏览器扩展
- 可访问(WebSocket)和
127.0.0.1:18765(HTTP)127.0.0.1:18766