Loading...
Loading...
Control and automate real browser sessions through CDP, preserving login state and cookies for LLM-driven interactions
npx skill4agent add aradotso/mcp-skills cdp-bridge-mcp-browser-controlSkill by ara.so — MCP Skills collection.
uvx# Test with stdio mode (default)
uvx cdp-bridge@latest
# Run as HTTP server (for multi-client scenarios)
uvx cdp-bridge@latest --transport streamable-http --port 8000chrome://extensions/src/cdp_bridge/tmwd_cdp_bridge127.0.0.1:18765{
"mcpServers": {
"cdp-bridge": {
"command": "uvx",
"args": ["cdp-bridge@latest"]
}
}
}uvx cdp-bridge@latest --transport streamable-http --port 8000{
"mcpServers": {
"cdp-bridge": {
"type": "streamableHttp",
"url": "http://127.0.0.1:8000/mcp"
}
}
}# stdio
claude mcp add cdp-bridge uvx cdp-bridge@latest
# streamable-http
claude mcp add cdp-bridge --transport streamable-http http://127.0.0.1:8000/mcp# stdio
codex mcp add cdp-bridge uvx cdp-bridge@latest
# streamable-http
codex mcp add cdp-bridge --transport streamable-http --url http://127.0.0.1:8000/mcp| 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 |
# 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 structure:
# {
# "tabs": [
# {"id": "tab_0", "url": "https://example.com", "title": "Example Domain"},
# {"id": "tab_1", "url": "https://github.com", "title": "GitHub"}
# ],
# "active_tab": "tab_0"
# }# Extract simplified HTML (default)
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
}
)
# Returns cleaned HTML with scripts/styles removed
# Preserves semantic structure for LLM consumptionbrowser_scan<script><style><svg><img>[Image: alt_text]# 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 contains the returned objectchrome.scripting.executeScriptRuntime.evaluate# 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
}
)
# Returns True if condition met, raises timeout error otherwise# Navigate to a URL
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
}
)
# 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
}
)# Get cookies for authenticated session analysis
result = await use_mcp_tool(
server_name="cdp-bridge",
tool_name="browser_cookies",
arguments={
"tab_id": "tab_0"
}
)
# Returns array of cookie objects:
# [{"name": "session_id", "value": "...", "domain": "example.com", ...}]# Execute multiple CDP commands in sequence
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# User is already logged in to medium.com
# 1. Find the article tab
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
await use_mcp_tool("cdp-bridge", "browser_switch_tab", {"tab_id": article_tab["id"]})
# 3. Extract article text
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';
"""
}
)# User has analytics dashboard open
# Poll for updated metrics every 30 seconds
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)# Navigate to form page
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
}
)
# 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();
"""
}
)browser_get_tabsbrowser_get_tabschrome://extensions/127.0.0.1:18765Runtime.evaluatechrome.scripting(function() { ... })()browser_get_tabsbrowser_scan"format": "text"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"})uvx cdp-bridge@latest [OPTIONS]
Options:
--transport [stdio|streamable-http] Transport mode (default: stdio)
--port INTEGER HTTP port for streamable-http mode (default: 8000)src/cdp_bridge/tmwd_cdp_bridge/background.js// WebSocket server address
const WS_URL = 'ws://127.0.0.1:18765';
// Reconnection interval (ms)
const RECONNECT_INTERVAL = 5000;stdiostreamable-httpchrome.scriptingTMWebDriver127.0.0.1:18765127.0.0.1:18766