Skip to main content
Glama
cdt-smart-snapshot

cdt-smart-snapshot

by sinnhirl

cdt-smart-snapshot

npm version License

Token-efficient snapshot MCP server for Chrome DevTools Protocol.

Use alongside official chrome-devtools-mcp: operations (click / fill / navigate) stay on the official server; page perception (smart_snapshot / snapshot_diff / screenshot_to_disk) goes through this server.

Install

npm (recommended):

npm install -g cdt-smart-snapshot
# or run without installing: npx cdt-smart-snapshot

From source (developers):

git clone https://github.com/sinnhirl/cdt-smart-snapshot
cd cdt-smart-snapshot
npm install
npm run build

Requires Node.js ^20.19 || ^22.12 || >=23 and a running Chromium-based browser with remote debugging (Edge/Chrome on port 9222, or 9223 via portproxy).

Related MCP server: Enhanced Browser MCP Server

MCP configuration

Claude Code / Claude Desktop

npm install (recommended):

{
  "mcpServers": {
    "cdt-smart-snapshot": {
      "command": "cdt-smart-snapshot",
      "env": {
        "CDT_BROWSER_URL": "http://127.0.0.1:9222",
        "CDT_SNAPSHOT_DIR": "/tmp/cdt-snapshots"
      }
    }
  }
}

From source:

{
  "mcpServers": {
    "cdt-smart-snapshot": {
      "command": "node",
      "args": ["/absolute/path/to/cdt-smart-snapshot/build/src/index.js"],
      "env": {
        "CDT_BROWSER_URL": "http://127.0.0.1:9222",
        "CDT_SNAPSHOT_DIR": "/tmp/cdt-snapshots"
      }
    }
  }
}

Hermes

mcp_servers:
  cdt-smart-snapshot:
    command: cdt-smart-snapshot # or: node + build/src/index.js from source
    env:
      CDT_BROWSER_URL: http://127.0.0.1:9222 # default when browser is local; WSL2: see "Connecting to a browser"
      CDT_SNAPSHOT_DIR: /tmp/cdt-snapshots
    timeout: 300

Prefer CDT_WS_ENDPOINT when you already have a WebSocket debugger URL.

Connecting to a browser

This server does not launch a browser. It connects to a Chromium-based browser that is already running with remote debugging enabled. What to put in CDT_BROWSER_URL depends on where that browser runs relative to this server.

1. Start a browser with remote debugging

Pick a browser you already have installed (Edge / Chrome / Chromium):

# macOS / Linux
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
  --remote-debugging-port=9222 --user-data-dir=/tmp/cdt-profile &

# Windows (PowerShell)
& "C:\Program Files\Google\Chrome\Application\chrome.exe" `
  --remote-debugging-port=9222 --user-data-dir=C:\temp\cdt-profile

--user-data-dir uses a fresh profile so the debugging instance does not clash with your normal browser session. Log into sites inside this window; the server sees that logged-in session.

2. Decide what CDT_BROWSER_URL to use

Where the MCP server runs

CDT_BROWSER_URL

Notes

Same machine as the browser (macOS / Linux / Windows native)

http://127.0.0.1:9222

Default. Nothing to change.

WSL2 (browser runs on Windows)

http://<windows-host-ip>:9223

Needs the portproxy bridge (below).

Docker container (browser on host)

http://host.docker.internal:9222

Docker Desktop exposes the host automatically.

The default http://127.0.0.1:9222 covers the common case; only change it if the browser is somewhere else.

3. WSL2: the portproxy bridge (browser on Windows)

Chromium's debugging port binds 127.0.0.1 inside Windows. WSL2 is a separate VM — its own 127.0.0.1 is not Windows', so it cannot reach the port directly. The standard fix is a netsh portproxy that listens on all interfaces on 9223 and forwards to Windows' 127.0.0.1:9222:

netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=9223 \
  connectaddress=127.0.0.1 connectport=9222

Then find the Windows host IP from WSL and verify:

# From WSL — print the Windows host IP (gateway of the default route)
ip route show default | awk '{print $3}'
# e.g. 172.27.64.1  →  CDT_BROWSER_URL=http://172.27.64.1:9223

# Verify the bridge works
curl -s http://<windows-host-ip>:9223/json/version

Security: the debugging port is a browser master switch. Keep the portproxy bound to your own machine / trusted network; do not expose 9222 on the public internet.

Tools

Tool

Purpose

smart_snapshot

Visible + interactive semantic tree with depth limit and dedupe

snapshot_diff

Added / removed / changed nodes since the last snapshot

screenshot_to_disk

Write screenshot to disk; returns a file path (no base64)

page_search

Search the latest snapshot tree by keyword; returns matching uid+path

get_node

Details for one uid: path, value, checked, rect, css selector

element_to_selector

uid → unique CSS selector (feed to official server's click/fill)

page_status

URL/title/readyState/loading + recent console errors & failed reqs

snapshot_index

Dump the current uid index (explore / debug)

Query tools (page_search / get_node / element_to_selector) read the uid index refreshed by every smart_snapshot / snapshot_diff call, so call a snapshot tool first. If the page navigated since the last snapshot they ask you to re-snapshot.

Environment

Variable

Default

Meaning

CDT_WS_ENDPOINT

(unset)

Prefer WebSocket CDP endpoint when set

CDT_BROWSER_URL

http://127.0.0.1:9222

HTTP CDP URL for puppeteer.connect

CDT_SNAPSHOT_DIR

$TMPDIR/cdt-snapshots

Screenshot output directory

CDT_MAX_DEPTH

8

Default maxDepth

Benchmark

Measured 2026-08-05 on Edge 151 (Windows) via WSL2 + portproxy 9223. 15 diverse real sites × 3 rounds, official take_snapshot-equivalent (full AX tree, official format) vs. smart_snapshot pipeline. Network idle + retry loading; reduction is stable across rounds (max spread ≤ 6.6pp, 11/15 ≤ 2.3pp).

Per-site reduction (v0.1.6, avg of 3 rounds)

Site

Type

Official chars

Smart chars

Reduction

Amazon

e-commerce

~36K

~2.0K

94.4%

CNN

news portal

~40K

~3.3K

91.8%

Reddit

social

~32K

~3.4K

89.5%

BBC News

news portal

~27K

~3.6K

86.6%

163.com

CN portal

~32K

~5.1K

84.2%

JD.com

CN e-commerce

~11K

~3.3K

70.8%

Gmail

logged-in mail

~74K

~24K

68.0%

Stack Overflow

Q&A

~23K

~8.1K

64.8%

YouTube

video

~2.7K

~1.4K

47.0%

Bilibili

video

~5.7K

~3.0K

47.1%

Zhihu

CN Q&A

~2.7K

~1.8K

32.5%

Baidu

search

~2.0K

~1.5K

25.4%

Wikipedia

long doc

~578K

~463K

19.9%

GitHub

dev platform

~4.2K

~3.4K

18.2%

Google

search

~913

~834

8.6%

Measured 2026-08-05 (v0.1.6) on Edge 151 (Windows) via WSL2 + portproxy 9223, 15 sites × 3 complete rounds (45/45 valid). Overall average reduction 56.6% (avg official tokens 14543 → smart 8795). Raw data: bench/bench-results-3x.json.

v0.1.6 fixes: Baidu/Zhihu went from negative (-10.5% / -13.2% in v0.1.4) to +25.4% / +32.5% — self-labeling controls (link/button/...) now fold their redundant text children, which dominated these label-heavy pages. Bilibili 18.1% → 47.1%. Wikipedia 2.8% → 19.9% (v0.1.5 fix kept: only interactive roles stamped visible on large pages, body text read via evaluate).

snapshot_diff (incremental, Gmail)

Step

Output

First call

full tree (~25K chars)

No-op step

1 line, 32 chars

Change step

8 lines, ~420 chars

Reading the numbers

  • High reduction (62–94%): portals / e-commerce / news / social — the page types agents operate on most. Hidden/ads/container nodes are dropped.

  • Medium (18–48%): video / search / long-doc — nav chains collapsed and redundant text folded; long-doc body text is intentionally kept for the agent to read (read specific sections via evaluate for extreme savings).

  • Low (8–10%): GitHub / Google — official interestingOnly already trimmed most junk; the page is small so savings are bounded.

Combined with snapshot_diff, a 30-step agent session on an interactive page consumes roughly 15–20% of the tokens of repeated full take_snapshot calls.

Reproduce: node bench/multi-site-3x.mjs (requires Edge debugging mode on 9222 → portproxy 9223, set CDT_BROWSER_URL=http://<windows-host-ip>:9223).

Snapshot output

Every snapshot starts with the page root line, e.g. [RootWebArea] DeepSeek 开放平台. Named images appear as [image] "name" (Chrome's AX tree reports the role image). These lines were missing before v0.1.7 — the root was dropped by the visibility pass and named logos were filtered out — so any snapshot output you see is from v0.1.7+ unless stated.

DOM query tools (v0.2.x)

get_node / element_to_selector read live DOM state behind a uid. v0.2.0 had a bug that made every DOM lookup fail (CDP objectId bound to the wrong session); it is fixed in v0.2.1. If you're on 0.2.0, upgrade:

npm install -g cdt-smart-snapshot@latest

After upgrading, element_to_selector(uid) returns a unique CSS selector you can feed straight to the official chrome-devtools-mcp click / fill, and get_node(uid) shows the real element state (value, checked, rect, selector).

Development

npm run test
npm run typecheck
npm run check-format

License

Apache-2.0

Available Tools

8 tools
element_to_selectorA

Return a unique CSS selector for a snapshot uid (for chrome-devtools-mcp click/fill). Requires smart_snapshot or snapshot_diff first.

Inputs: uid (required).

get_nodeA

Return role, path, DOM geometry, and CSS selector for one snapshot uid. Requires a prior smart_snapshot or snapshot_diff.

Inputs: uid (required).

page_statusA

Report current page URL, title, readyState, loading flag, and recent console errors, page exceptions, and failed network requests.

Inputs: clear.

screenshot_to_diskA

Takes a screenshot, saves it to disk, and returns the file path. Saves ~3000-5000 tokens per screenshot compared to returning base64. Read the file with read_file if you need to inspect it.

Inputs: format, quality, fullPage, directory.

smart_snapshotA

Returns a token-efficient semantic snapshot of the current page: only visible, interactive and meaningful nodes, with depth limiting and deduplication. Use this instead of take_snapshot to save context.

Inputs: verbose, maxDepth, includeHidden.

snapshot_diffA

Returns only the changes between the current page state and the previous smart_snapshot call. Added/removed/changed nodes with a few lines of context. Use this on every step after the first to consume minimal tokens.

Inputs: verbose, maxDepth, includeHidden.

snapshot_indexA

List uids from the most recent snapshot (role, name, path). Useful before page_search or get_node. Requires smart_snapshot or snapshot_diff first.

Inputs: maxResults.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4.1/5.0

    Scored across 8 tools

    Disambiguation5/5

    Each tool targets a distinct aspect of snapshot management: capture, diff, status, screenshot, search, node details, selector generation, and indexing. There is no overlap that would cause an agent to misselect.

    Naming Consistency3/5

    All names use snake_case and are descriptive, but the pattern is mixed: some are verb-led (get_node), some noun-led (page_status), and some use prepositions (screenshot_to_disk, element_to_selector). This inconsistency makes the set less predictable than a uniform verb_noun convention.

    Tool Count5/5

    8 tools is well-scoped for a specialized snapshot server. Each tool serves a clear purpose with no redundancy, and the count is neither too thin nor too heavy.

    Completeness5/5

    The server covers the full snapshot lifecycle: capture (smart_snapshot), diff (snapshot_diff), inspection (snapshot_index, page_search, get_node), and action support (screenshot_to_disk, element_to_selector). No significant gaps exist for its stated purpose.

    Maintenance

    ActivityInactive
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      D
      maintenance
      An advanced MCP server for browser automation using Puppeteer, specifically optimized for token efficiency through minimal data returns and progressive enhancement. It enables agents to navigate pages, capture LLM-optimized screenshots, extract structured content, and perform batch interactions.
      3
      MIT
    • A
      license
      A
      quality
      B
      maintenance
      A token-efficient MCP server for Chrome browser automation that uses direct CDP and accessibility-tree-based references for stable element selection across DOM changes.
      25
      118 npm
      19
      MIT