Install
openclaw skills install chromedevUse this skill when you need to access or control a live Chrome browser through the local Chrome DevTools MCP middleware at http://127.0.0.1:8787/mcp, especially for opening pages, extracting rendered content, interacting with DOM elements, taking snapshots, or collecting data from websites.
openclaw skills install chromedevUse this skill when the user wants browser backed data from real web pages through the local chrome-devtools-mcp middleware running at http://127.0.0.1:8787/mcp.
This skill is for cases where rendered browser state matters, including JavaScript heavy sites, login state in the user's Chrome, interaction flows, screenshots, DOM snapshots, or page data that should come from the live browser instead of plain HTTP fetches.
Connection model
chromedev run is the long lived daemon. It is the layer that keeps the upstream chrome-devtools-mcp process attached to Chrome.http_mcp_call.mjs script is intentionally short lived. Each invocation only sends a bounded MCP request to the local daemon.Primary behavior
This workflow is otherwise a black box to the user, so visible progress output is required. Output is not optional in this skill.
Not connected, first inspect with chromedev doctor. If it reports stale/orphan chromedev processes or the retry fails, run one recovery cycle with chromedev stop --all, then chromedev run, and retry the failed page operation once. Only report the error to the user if the retry also fails.Do not send intermediate progress updates to Telegram or any other chat tool. Focus on producing the final extracted answer or summary directly. Current agent progress and final user facing answer must not trigger extra chat-tool delivery side effects. When the final extracted answer or summary is ready, deliver the final answer once. The final extracted answer or summary must not be streamed token by token. Buffer the final answer first, then send it as one complete final output.
Timeline guarantee
Progress message constraints
openclaw message send for this workflow.Bad pattern, do not do this
完成, 成功, 已提取, or similar status text after the final answer body.openclaw message send or any equivalent chat-tool delivery command during this workflow.npm view @mallocfeng/chromedev version. Then check the local CLI version with chromedev version. If chromedev is missing, the local version command is unavailable, or the npm version is newer than the local version, install the explicit npm version with npm install -g @mallocfeng/chromedev@<npm-version>, then continue. Do not repeat this check for every page task unless a command call fails.127.0.0.1:8787. If it is not running, start it automatically with chromedev run, then re-check the endpoint before using MCP tools.Not connected or similar connection/binding errors, run chromedev doctor first. If there are orphan/multiple chrome-devtools-mcp processes or the retry fails, run one recovery cycle: chromedev stop --all → chromedev run → retry once.Quick endpoint check
curl -i http://127.0.0.1:8787/mcp
A response like 400 Bad Request with No sessionId means the endpoint exists and is healthy.
This is expected because the daemon serves short lived stateless HTTP requests while keeping the upstream Chrome connection alive in the background.
npm view @mallocfeng/chromedev version, then compare it with chromedev version. If the local command is missing, too old to print a version, or older than npm, force install the explicit npm version with npm install -g @mallocfeng/chromedev@<npm-version>.chromedev run and then re-check http://127.0.0.1:8787/mcp.http://127.0.0.1:8787/mcp.Not connected, target closed, or similar startup/binding failures, run chromedev doctor to check for stale daemon state or multiple DevTools clients. Then try exactly one recovery cycle if needed: chromedev stop --all → chromedev run → retry the original page operation once.new_page. Do not try to recover by opening a blank page or any synthetic page.list_pages before the first new_page.new_page, assume the new tab is already the selected page. Do not call list_pages/select_page unless take_snapshot/evaluate_script fails with a "no selected page" or similar error.evaluate_script for a fast, bounded extraction (title/byline/time/body) first, then fall back to take_snapshot when the DOM is hard to parse or blocked.wait_for only when the page is still loading and you know a stable text or UI state to wait on (keep it short).When the user gives a URL, treat it as a request to read that page.
about:blank or any placeholder page.new_page for explicit URL tasks.new_page.evaluate_script (fast extract, keep output bounded).take_snapshot (slower, but robust when the DOM is messy).list_pages, then select_page.wait_for only when you have a reliable, small string to wait on.take_screenshot only when visuals matter.The npm package includes a reusable request client exposed through:
chromedev call <tool_name> [json_arguments]It connects to the local HTTP MCP endpoint and calls one tool.
Prefer chromedev call because it uses the request client bundled with the installed npm package and does not depend on the current skill directory containing a scripts folder.
This skill source also includes the same reusable script for development:
scripts/http_mcp_call.mjsThe script is not the persistent connection holder. It is only a request client for the already running local daemon.
When using chromedev call, the npm package's bundled dependencies should be used automatically. Only install @modelcontextprotocol/sdk in the current workspace if you intentionally run scripts/http_mcp_call.mjs directly and the dependency is missing.
WORKSPACE="$(git rev-parse --show-toplevel 2>/dev/null || pwd -P)"
cd "$WORKSPACE" && npm install @modelcontextprotocol/sdk
Check and install the latest published chromedev
REMOTE_VERSION="$(npm view @mallocfeng/chromedev version 2>/dev/null || true)"
LOCAL_VERSION="$(chromedev version 2>/dev/null | sed -E 's/^.*@([0-9]+\.[0-9]+\.[0-9]+).*$/\1/' || true)"
if [ -z "$REMOTE_VERSION" ]; then
echo "Could not read @mallocfeng/chromedev version from npm"
elif [ -z "$LOCAL_VERSION" ] || node -e 'const [l,r]=process.argv.slice(1); const a=l.split(".").map(Number); const b=r.split(".").map(Number); process.exit(b[0]>a[0] || (b[0]===a[0] && b[1]>a[1]) || (b[0]===a[0] && b[1]===a[1] && b[2]>a[2]) ? 0 : 1)' "$LOCAL_VERSION" "$REMOTE_VERSION"; then
npm install -g "@mallocfeng/chromedev@$REMOTE_VERSION"
hash -r 2>/dev/null || true
fi
Start the service if the endpoint is not already listening
curl -s -o /dev/null http://127.0.0.1:8787/mcp || chromedev run
Recover from a stuck/not-connected session
chromedev doctor
chromedev stop --all
chromedev run
List current pages
chromedev call list_pages
Open a page
chromedev call new_page '{"url":"<URL>","timeout":30000}'
For explicit URL tasks, do this first. Do not begin by opening about:blank or any recovery page.
Get a snapshot
chromedev call take_snapshot
Extract title and a bounded article body (fast path)
chromedev call evaluate_script '{"function":"() => {\n const pick = () => document.querySelector(\"article\") || document.querySelector(\"main\") || document.body;\n const el = pick();\n const text = (el?.innerText || \"\").replace(/\\s+\\n/g, "\\n").trim();\n return {\n title: document.title,\n url: location.href,\n byline: document.querySelector(\"[data-testid=byline]\")?.innerText || document.querySelector(\".byline\")?.innerText || \"\",\n time: document.querySelector(\"time\")?.getAttribute(\"datetime\") || document.querySelector(\"time\")?.innerText || \"\",\n text: text.slice(0, 20000),\n textLength: text.length\n };\n}"}'
Note: never forward raw URL in Telegram progress messages.
Wait for specific text
chromedev call wait_for '{"text":["<TEXT>"],"timeout":30000}'
evaluate_script to extract a bounded body string (e.g. article/main innerText) plus title/byline/time, then summarize from that.take_snapshot for readable page content and structure when extraction is blocked or the DOM is hard to parse.evaluate_script for precise fields, arrays, links, prices, tables, or JSON shaped output.wait_for only if a page is visibly still loading and you know a reliable text to wait on.127.0.0.1.