Skip to content

take_snapshot accessibility tree omits CMP-injected consent overlays — agents can't dismiss banners that are visually rendered #1957

@mtberlin2023

Description

@mtberlin2023

Description

When an LLM-driven agent uses chrome-devtools-mcp to navigate to sites that show a GDPR/cookie consent overlay, take_snapshot returns the page body (header, article text, links) but does NOT include the consent modal's DOM in its accessibility tree output — even though the banner is visually rendered and blocks user interaction.

Result: the agent decides "no banner present, just read the article", picks read_content / follow_link, and is then blocked from real interaction by an overlay it cannot see.

Reproduction

6/6 sites attempted on 2026-04-25 from an EU connection, headless Chrome via chrome-devtools-mcp (latest from npm, MCP SDK 1.27.0):

Target Snapshot included CMP modal DOM?
undavos.com no (only passive footer notice)
termly.io/resources/articles/gdpr-consent-examples/ no
theguardian.com/europe no
zeit.de no
bbc.com/news n/a — page didn't finish loading inside default patience window
cookiebot.com (CMP vendor's own site) no — 54 occurrences of "consent" in body text, zero `accept all` / `dialog` / `modal` element nodes

Across all six, the snapshot text contained either zero modal-shaped hits or only ARIA `` (page-header) role hits — no `dialog`, `accept all`, `reject all`, or modal-shaped elements.

Suspected causes (in order of likelihood)

  1. CMP scripts render the banner in the top-layer / `` element (HTML top-layer feature) which the accessibility-tree extraction may not be traversing.
  2. CMP banners are commonly Shadow-DOM encapsulated; if `take_snapshot` doesn't pierce shadow roots, encapsulated UIs are invisible.
  3. CMP scripts detect headless Chrome / automation and skip injection entirely. (Less likely — body content renders fine, suggesting JS executes; consent-specific JS may fingerprint though.)

Why it matters

Any agent workflow that involves visiting EU-served pages will hit a CMP wall on the majority of major sites. If the agent can't see the modal, it can't dismiss it; if it can't dismiss it, it's stuck on a page that visually looks blocked but the agent perceives as ready-to-read. Silent failure mode — the agent reports success on noise it pulled from headers/footers and never reaches the article.

What "fixed" would look like

`take_snapshot` returns DOM nodes from the top layer (per the HTML top-layer spec) and traverses open shadow roots. CMP overlays would then appear as `dialog` / `button` nodes with `accept all`, `reject all`, `manage cookies` accessible names, and a downstream agent could pick `click` on them.

Workaround on our side

Built a synthetic consent-banner fixture (visible `<div role="dialog">` with standard ARIA roles — no shadow DOM, no top-layer, no fingerprinting) to validate our agent's LLM-trigger leg in a controlled environment. The agent picks the right action when the modal is in the snapshot — confirming the gap is upstream in chrome-devtools-mcp's snapshot, not in the agent's reasoning.

Possibly related: #1069 (HTML `confirm()`/`alert()` dialog handling) — different bug but adjacent surface area.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions