Overview
MetaDock implements Chrome DevTools Protocol v1.3, giving you low-level access to browser internals. Connect Puppeteer, Playwright, pychrome, or any CDP client directly to MetaDock's browsers — with the added benefit of visual feedback in MetaDock's auto-tiling grid.
Authentication
CDP endpoints accept authentication via query parameter or Authorization header:
Query parameter — used for WebSocket connections and discovery endpoints.
Standard Bearer token in Authorization header.
Custom header authentication.
Discovery Endpoints
HTTP discovery endpoints at /json/ let you list targets, get protocol info, and create or close browsers.
/json/versionReturns browser version, protocol version, user agent, and WebSocket debugger URL.
Returns
Version info object with Browser, Protocol-Version, User-Agent, V8-Version, WebKit-Version, webSocketDebuggerUrl
curl -H "Authorization: Bearer YOUR_API_KEY" \
http://127.0.0.1:8080/json/version{
"Browser": "MetaDock/1.2.1",
"Protocol-Version": "1.3",
"User-Agent": "Mozilla/5.0 ...",
"V8-Version": "12.4.254.20",
"WebKit-Version": "537.36",
"webSocketDebuggerUrl": "ws://127.0.0.1:8080/devtools/browser"
}/json/listReturns all browser instances as CDP targets. Each target includes its WebSocket debugger URL. Also accessible via /json (alias).
Returns
Array of CDP target objects
curl -H "Authorization: Bearer YOUR_API_KEY" \
http://127.0.0.1:8080/json/list[
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"type": "page",
"title": "TradingView — AAPL",
"url": "https://www.tradingview.com/chart/",
"webSocketDebuggerUrl": "ws://127.0.0.1:8080/devtools/page/a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"devtoolsFrontendUrl": "",
"faviconUrl": "https://www.tradingview.com/favicon.ico"
},
{
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"type": "page",
"title": "Gmail — Inbox",
"url": "https://mail.google.com/mail/u/0/#inbox",
"webSocketDebuggerUrl": "ws://127.0.0.1:8080/devtools/page/b2c3d4e5-f6a7-8901-bcde-f12345678901",
"devtoolsFrontendUrl": "",
"faviconUrl": ""
}
]/json/protocolReturns the supported CDP protocol domains and their versions. Use this to discover which methods are available.
Returns
Array of domain objects with domain name and version
curl -H "Authorization: Bearer YOUR_API_KEY" \
http://127.0.0.1:8080/json/protocol/json/new?url={url}Create a new browser target. Optionally provide a URL to navigate to.
Parameters
url(string)— URL to navigate to in the new browserReturns
CDP target object for the newly created browser
curl -H "Authorization: Bearer YOUR_API_KEY" \
"http://127.0.0.1:8080/json/new?url=https://example.com"/json/close/{targetId}Close a browser target by its UUID.
Parameters
targetId(string)required— UUID of the target to closeReturns
'Target is closing' on success, 404 if target not found
curl -H "Authorization: Bearer YOUR_API_KEY" \
http://127.0.0.1:8080/json/close/a1b2c3d4-e5f6-7890-abcd-ef1234567890WebSocket Connection
Connect to a specific browser target using its WebSocket debugger URL. Each browser panel in MetaDock has its own CDP target that can be individually controlled.
Connection URL
ws://127.0.0.1:8080/devtools/page/{browserUuid}?token={api_key}Message Format (JSON-RPC 2.0)
{
"id": 1,
"method": "Page.navigate",
"params": {
"url": "https://example.com"
}
}{
"id": 1,
"result": {
"frameId": "main-frame-id",
"loaderId": "loader-id"
}
}{
"method": "Page.loadEventFired",
"params": {
"timestamp": 1234567890.123
}
}Error Codes
| Code | Meaning |
|---|---|
-32700 | Parse error — invalid JSON |
-32600 | Invalid request — missing required fields |
-32601 | Method not found — unsupported CDP method |
-32602 | Invalid params — wrong parameter types |
-32603 | Internal error — server-side failure |
-32000 | Server error — MetaDock-specific error |
Session Multiplexing
Use Target.attachToTarget to create sub-sessions with unique sessionId fields. This allows multiplexing multiple logical sessions over a single WebSocket connection.
Supported Domains (10)
MetaDock implements the following 10 CDP domains with their methods:
Page
Page navigation, lifecycle events, and capture
enablenavigatereloadstopLoadingcaptureScreenshotprintToPDFgetFrameTreegetNavigationHistorynavigateToHistoryEntrysetLifecycleEventsEnabledaddScriptToEvaluateOnNewDocumentcreateIsolatedWorldbringToFrontsetInterceptFileChooserDialoghandleJavaScriptDialoggetResourceTreeremoveScriptToEvaluateOnNewDocumentRuntime
JavaScript execution and evaluation
enableevaluatecallFunctionOngetPropertiesreleaseObjectreleaseObjectGrouprunIfWaitingForDebuggerdiscardConsoleEntriessetAsyncCallStackDepthaddBindingremoveBindingNetwork
Network monitoring, cookies, and request interception
enablesetCookiegetCookiesdeleteCookiesclearBrowserCookiessetUserAgentOverrideemulateNetworkConditionssetExtraHTTPHeadersgetCertificategetResponseBodysetRequestInterceptionsetCacheDisabledsetBypassServiceWorkerDOM
DOM access, queries, and manipulation
enablegetDocumentquerySelectorquerySelectorAllgetOuterHTMLsetOuterHTMLgetBoxModelresolveNodedescribeNoderequestChildNodessetAttributeValueremoveAttributeremoveNodefocusgetAttributessetInspectedNodehighlightNodehideHighlightEmulation
Device metrics, geolocation, touch, and media emulation
enablesetDeviceMetricsOverrideclearDeviceMetricsOverridesetGeolocationOverrideclearGeolocationOverridesetUserAgentOverridesetTouchEmulationEnabledsetEmitTouchEventsForMousesetScrollbarsHiddensetDocumentCookieDisabledsetEmulatedMediasetFocusEmulationEnabledsetAutoDarkModeOverridesetDefaultBackgroundColorOverrideInput
Mouse, keyboard, and touch input simulation
enabledispatchMouseEventdispatchKeyEventinsertTextsetIgnoreInputEventsdispatchTouchEventemulateTouchFromMouseEventsynthesizePinchGesturesynthesizeScrollGesturesynthesizeTapGestureTarget
Browser target management and session multiplexing
enablegetTargetsgetTargetInfoattachToTargetdetachFromTargetcreateTargetcloseTargetsetDiscoverTargetssetAutoAttachsetRemoteLocationsactivateTargetexposeDevToolsProtocolsendMessageToTargetBrowser
Browser-level info and window control
enablegetVersionclosegetWindowForTargetgetWindowBoundssetWindowBoundsgetHistogramsgetBrowserCommandLineLog
Console logging and violation reporting
enableclearstartViolationsReportstopViolationsReportFetch
Network request interception and modification
enablefulfillRequestfailRequestcontinueRequestcontinueWithAuthgetResponseBodyCDP Events
After enabling a domain (e.g. Page.enable), the server pushes events as they occur. Events have no id field.
| Event | Description |
|---|---|
Page.frameNavigated | A frame navigated to a new URL |
Page.frameStartedLoading | A frame started loading |
Page.frameStoppedLoading | A frame stopped loading |
Page.loadEventFired | The page load event fired |
Page.domContentEventFired | The DOMContentLoaded event fired |
Runtime.executionContextCreated | A new JavaScript execution context was created |
Log.entryAdded | A new console log entry was added |
Target.targetCreated | A new target (browser) was created |
Target.targetDestroyed | A target was closed |
Target.targetInfoChanged | Target info updated (title, URL) |
// After connecting via WebSocket and enabling Page domain:
// → Send: {"id": 1, "method": "Page.enable"}
// ← Receive: {"id": 1, "result": {}}
// Navigate to a page:
// → Send: {"id": 2, "method": "Page.navigate", "params": {"url": "https://example.com"}}
// ← Receive: {"id": 2, "result": {"frameId": "...", "loaderId": "..."}}
// Server pushes events as the page loads:
// ← {"method": "Page.frameStartedLoading", "params": {"frameId": "..."}}
// ← {"method": "Page.frameNavigated", "params": {"frame": {"id": "...", "url": "https://example.com"}}}
// ← {"method": "Page.domContentEventFired", "params": {"timestamp": 1234567890.123}}
// ← {"method": "Page.loadEventFired", "params": {"timestamp": 1234567890.456}}
// ← {"method": "Page.frameStoppedLoading", "params": {"frameId": "..."}}Puppeteer Integration
Connect Puppeteer to MetaDock's CDP endpoint. All browser actions execute in MetaDock's visual grid.
const puppeteer = require('puppeteer');
async function main() {
// 1. Discover targets
const response = await fetch("http://127.0.0.1:8080/json/list", {
headers: { "Authorization": "Bearer YOUR_API_KEY" }
});
const targets = await response.json();
// 2. Connect to first target's WebSocket URL
const browser = await puppeteer.connect({
browserWSEndpoint: targets[0].webSocketDebuggerUrl + "?token=YOUR_API_KEY"
});
// 3. Get the page
const pages = await browser.pages();
const page = pages[0];
// 4. Navigate and interact
await page.goto('https://example.com');
await page.waitForSelector('h1');
const title = await page.title();
console.log('Page title:', title);
// 5. Take a screenshot
await page.screenshot({ path: 'screenshot.png' });
// 6. Execute JavaScript
const linkCount = await page.evaluate(() => {
return document.querySelectorAll('a').length;
});
console.log('Links on page:', linkCount);
// 7. Monitor network requests
page.on('request', req => {
console.log('Request:', req.method(), req.url());
});
// 8. Intercept requests
await page.setRequestInterception(true);
page.on('request', req => {
if (req.resourceType() === 'image') {
req.abort(); // Block images
} else {
req.continue();
}
});
await page.goto('https://example.com');
}Playwright Integration
Connect Playwright via CDP for advanced browser automation with MetaDock's visual oversight.
const { chromium } = require('playwright');
async function main() {
// Connect to MetaDock via CDP
const browser = await chromium.connectOverCDP(
'http://127.0.0.1:8080'
);
// Get existing context and page
const context = browser.contexts()[0];
const page = context.pages()[0];
// Navigate
await page.goto('https://example.com');
console.log('Title:', await page.title());
// Interact with elements
await page.click('text=Learn More');
await page.waitForLoadState('networkidle');
// Fill forms
await page.fill('input[name="email"]', '[email protected]');
await page.click('button[type="submit"]');
// Evaluate JavaScript
const result = await page.evaluate(() => {
return document.querySelectorAll('a').length;
});
console.log('Links on page:', result);
// Take screenshot
await page.screenshot({ path: 'playwright-screenshot.png' });
// Route requests (intercept)
await page.route('**/*.{png,jpg,jpeg}', route => route.abort());
await page.goto('https://example.com'); // Images blocked
}Raw CDP via WebSocket
For maximum control, connect directly to the CDP WebSocket and send JSON-RPC 2.0 messages.
import asyncio
import websockets
import json
API_KEY = "your-api-key"
BROWSER_UUID = "browser-uuid-from-json-list"
async def cdp_session():
url = f"ws://127.0.0.1:8080/devtools/page/{BROWSER_UUID}?token={API_KEY}"
async with websockets.connect(url) as ws:
msg_id = 0
async def send(method, params=None):
nonlocal msg_id
msg_id += 1
message = {"id": msg_id, "method": method}
if params:
message["params"] = params
await ws.send(json.dumps(message))
response = await ws.recv()
return json.loads(response)
# Enable domains
await send("Page.enable")
await send("Runtime.enable")
await send("Network.enable")
# Navigate
result = await send("Page.navigate", {"url": "https://example.com"})
print(f"Navigated: {result}")
# Wait for load event
while True:
event = json.loads(await ws.recv())
if event.get("method") == "Page.loadEventFired":
break
# Execute JavaScript
result = await send("Runtime.evaluate", {
"expression": "document.title"
})
print(f"Title: {result['result']['result']['value']}")
# Take screenshot
result = await send("Page.captureScreenshot", {"format": "png"})
import base64
with open("screenshot.png", "wb") as f:
f.write(base64.b64decode(result["result"]["data"]))
print("Screenshot saved")
# Set geolocation
await send("Emulation.setGeolocationOverride", {
"latitude": 40.7128,
"longitude": -74.0060,
"accuracy": 100
})
# Intercept requests via Fetch domain
await send("Fetch.enable", {
"patterns": [{"urlPattern": "*", "requestStage": "Request"}]
})
asyncio.run(cdp_session())Python (pychrome)
Use pychrome for a lightweight Python CDP client.
import pychrome
# Connect to MetaDock's CDP endpoint
browser = pychrome.Browser(url="http://127.0.0.1:8080")
# List available targets
tabs = browser.list_tab()
print(f"Found {len(tabs)} targets")
tab = tabs[0]
tab.start()
# Enable domains
tab.Page.enable()
tab.Runtime.enable()
tab.Network.enable()
tab.DOM.enable()
# Navigate
tab.Page.navigate(url="https://example.com")
tab.wait(5)
# Execute JavaScript
result = tab.Runtime.evaluate(expression="document.title")
print(f"Title: {result['result']['value']}")
# Get page HTML
result = tab.Runtime.evaluate(
expression="document.documentElement.outerHTML"
)
html = result["result"]["value"]
print(f"HTML length: {len(html)}")
# Query DOM
doc = tab.DOM.getDocument()
node = tab.DOM.querySelector(
nodeId=doc["root"]["nodeId"],
selector="h1"
)
if node["nodeId"]:
outer = tab.DOM.getOuterHTML(nodeId=node["nodeId"])
print(f"H1: {outer['outerHTML']}")
# Set cookies
tab.Network.setCookie(
name="session",
value="abc123",
domain="example.com"
)
# Monitor network events
def on_request(request):
print(f"Request: {request['request']['url']}")
def on_response(response):
print(f"Response: {response['response']['status']} {response['response']['url']}")
tab.Network.requestWillBeSent = on_request
tab.Network.responseReceived = on_response
# Navigate again to see network events
tab.Page.navigate(url="https://example.com")
tab.wait(5)
# Emulate device
tab.Emulation.setDeviceMetricsOverride(
width=375,
height=812,
deviceScaleFactor=3,
mobile=True
)
# Set geolocation
tab.Emulation.setGeolocationOverride(
latitude=48.8566,
longitude=2.3522,
accuracy=100
)
tab.stop()CDP vs Other Protocols
| Use Case | Best Protocol | Why |
|---|---|---|
| Network interception | CDP | Fetch domain gives request/response control |
| DOM manipulation | CDP | DOM domain for direct node access |
| Device emulation | CDP or REST | CDP Emulation domain or REST /device endpoint |
| Simple browser automation | REST API | Easier to use, no WebSocket needed |
| AI-driven workflows | MCP | Natural language, no coding |
| Existing Selenium scripts | WebDriver | W3C standard, minimal changes |
| Real-time event monitoring | WebSocket or CDP | Both support event subscriptions |
| Screenshot/PDF capture | Any | All protocols support capture |