文件历史

212 次代码提交

作者 SHA1 备注 提交日期
Alex Rudenko b4546ef86b test: fix potential flakiness in tests (#2396)
release-please / release-please (push) Has been cancelled
Compile and run tests / Tests on macos-latest with node 22 (push) Has been cancelled
Compile and run tests / Tests on ubuntu-latest with node 22 (push) Has been cancelled
Compile and run tests / Tests on windows-latest with node 22 (push) Has been cancelled
Compile and run tests / Tests on macos-latest with node 24 (push) Has been cancelled
Compile and run tests / Tests on ubuntu-latest with node 24 (push) Has been cancelled
Compile and run tests / Tests on windows-latest with node 24 (push) Has been cancelled
Compile and run tests / Tests on macos-latest with node 26 (push) Has been cancelled
Compile and run tests / Tests on ubuntu-latest with node 26 (push) Has been cancelled
Compile and run tests / Tests on windows-latest with node 26 (push) Has been cancelled
Check code before submitting / [Required] Check correct format (push) Has been cancelled
Check code before submitting / [Required] Check docs updated (push) Has been cancelled
Compile and run tests / [Required] Tests passed (push) Has been cancelled
Assortment of various flakiness conditions found running tests in a loop
locally:


This PR introduces a comprehensive set of hermetic retry layers and
aggressive
timeout handlers across the test suite to insulate it from random
Chromium
startup hangs, CDP deadlocks, and Puppeteer lifecycle flakes. It
guarantees that
temporary browser infrastructure failures are automatically retried
without
failing the CI, while actual code assertion failures still fail fast.

### Test Harness & Retry Improvements

• tests/utils.ts: Rewrote withBrowser to include a 30-second internal
timeout and
a 3-attempt retry loop. If Chromium locks up or disconnects (Target
closed /
socket hang up), the browser is forcibly evicted (via SIGKILL if
browser.close()
hangs) and the test setup is cleanly retried.
• tests/index.test.ts: Wrapped withClient (used by E2E tests) in a
3-attempt
retry loop to handle the daemon/Chromium hanging during launch and
triggering the
60-second MCP client timeout.
• tests/browser.test.ts: Added a safeClose helper that imposes a
2-second timeout
before SIGKILLing browsers, and wrapped raw Puppeteer tests in
runWithRetry to
handle startup hangs.
• tests/shutdown.test.ts: Added a setupServerWithRetry helper to prevent
random
60s RPC timeouts when the server's Chrome instance hangs during boot.

### Flaky Operations & Navigation Fixes

• src/tools/performance.ts & tests/tools/performance.test.ts: Replaced
the
notoriously flaky waitUntil: ['networkidle0'] with 'load' when
navigating to
about:blank in performance_start_trace. This prevents random 10-second
Navigation
timeout exceeded errors. Also stubbed goto in the associated unit tests
for
better hermeticity.
• src/McpContext.ts: Wrapped browser.installExtension() with a 15-second
timeout
to prevent deadlocks when an extension fails to load.
• tests/tools/extensions.test.ts: Removed flaky headless UI navigations
to
chrome://extensions in favor of using the context.listExtensions() API.
• tests/tools/pages.test.js.snapshot: Synced test snapshots to reflect
updated
environment baselines.
2026-07-21 13:47:47 +00:00
Alex Rudenko 348975d808 refactor: clean up McpResponse.handle (#2392)
- remove unused toolName
- extract helpers and fetch data in parallel
2026-07-21 06:53:11 +00:00
Thomas Bachem dcbaf49518 fix: include the tab id in get_tab_id's text response (#2381)
As discussed in #2366: `get_tab_id` gates on
`--experimentalInteropTools`, but its result only went into
`structuredContent`, which is dropped unless
`--experimentalStructuredContent` is also set – so with interop alone
the tool always returned an empty text response.

This appends a `Tab ID: <id>` line to the text response and keeps
`structuredContent.tabId` as-is – `key: value` so it stays trivially
parseable.

## Testing

The existing `returns the tab id` test asserted empty `responseLines`,
which pinned the old behavior – now it asserts the `Tab ID:` line.
Verified end-to-end over stdio with `--experimentalInteropTools` alone:
the text response is `Tab ID: <id>` where it was `""` before.
2026-07-20 15:00:49 +00:00
Dominik Inführ 76fd242498 feat: Filter heap snapshot objects by native context (#2377)
get_heapsnapshot_details can filter objects in the heap snapshot by
their native context.
2026-07-17 08:55:08 +00:00
Dominik Inführ 8432cb97a2 feat: Add get_heapsnapshot_object_details MCP tool (#2374)
Add a get_heapsnapshot_object_details MCP tool which lets the agent
query all known information about a node in the heap snapshot.

Co-authored-by: Dominik Inführ <dinfuehr@chromium.org>
2026-07-16 11:23:43 +00:00
Dominik Inführ f78a911dc0 feat: Emit native contexts in snapshot summary (#2375)
Emits native contexts and their sizes with the get_heapsnapshot_summary
MCP tool. This should help the agent get an overview about which native
contexts consume the most memory.

Co-authored-by: Dominik Inführ <dinfuehr@chromium.org>
2026-07-16 11:16:00 +00:00
dependabot[bot] 9a7e04daa2 chore(deps-dev): bump chrome-devtools-frontend from 1.0.1652307 to 1.0.1660788 in the bundled-devtools group across 1 directory (#2299)
Bumps the bundled-devtools group with 1 update in the / directory:
[chrome-devtools-frontend](https://github.com/ChromeDevTools/devtools-frontend).

Updates `chrome-devtools-frontend` from 1.0.1652307 to 1.0.1656897
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/ChromeDevTools/devtools-frontend/commit/a5ae2f34eb11c5c58a460e6930b6bce270a3121b"><code>a5ae2f3</code></a>
Use lit-html in ServiceWorkersView.ts</li>
<li><a
href="https://github.com/ChromeDevTools/devtools-frontend/commit/251a23abd90438b2f2bb33961e894fe0b4452057"><code>251a23a</code></a>
Refactor ElementsPanel dependency injection and clean up
DOMModel.cancelSearch</li>
<li><a
href="https://github.com/ChromeDevTools/devtools-frontend/commit/8382f01f7b9644b98beb1fa335fac9f511f16139"><code>8382f01</code></a>
[global-state] Fix FrameManager access in DOMNode</li>
<li><a
href="https://github.com/ChromeDevTools/devtools-frontend/commit/78704b2ecc77051877bd7f805eed1bf6f88d0ff9"><code>78704b2</code></a>
[Sheriff]: Fix a couple of flaky tests.</li>
<li><a
href="https://github.com/ChromeDevTools/devtools-frontend/commit/8b198ec693b44abe00d41a5bca65639bda55180b"><code>8b198ec</code></a>
Roll browser-protocol and CfT</li>
<li><a
href="https://github.com/ChromeDevTools/devtools-frontend/commit/430a8bdfa2c06e2f4e974f0dade1e0cfd8bf4e88"><code>430a8bd</code></a>
[global-state] Remove static instance from SimpleAppProvider</li>
<li><a
href="https://github.com/ChromeDevTools/devtools-frontend/commit/137dae29bacbf164674a578eb5592cb12308e141"><code>137dae2</code></a>
[global-state] Stash CrUXManager on DevToolsContext and add to
universe</li>
<li><a
href="https://github.com/ChromeDevTools/devtools-frontend/commit/4f881fd9caf41e7416b03397892c17e3e9f93d0a"><code>4f881fd</code></a>
Update DevTools DEPS (trusted)</li>
<li><a
href="https://github.com/ChromeDevTools/devtools-frontend/commit/d46071e5459a66871e38b89ac11d5573ad2ed4fb"><code>d46071e</code></a>
Make SourceMapCache instantiable instead of a singleton</li>
<li><a
href="https://github.com/ChromeDevTools/devtools-frontend/commit/466be4a9e6a4db8867f8c04e034d3383f624d99f"><code>466be4a</code></a>
Roll browser-protocol and CfT</li>
<li>Additional commits viewable in <a
href="https://github.com/ChromeDevTools/devtools-frontend/compare/v1.0.1652307...v1.0.1656897">compare
view</a></li>
</ul>
</details>
<br />

---------

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Nikolay Vitkov <nvitkov@chromium.org>
2026-07-15 13:05:34 +00:00
Alex Rudenko 9c3542bd74 refactor: move and rename files (#2355)
- move utilities to sub-folders where applicable
- changes issue-descriptions to issuesDescriptions.ts and moves it to
devtools/
2026-07-13 10:58:14 +00:00
Alex Rudenko c53c1ec4c9 refactor: use response page in formatting (#2349) 2026-07-13 07:55:37 +00:00
Liohtml 78ccb193e0 fix: release held modifiers when press_key key event fails (#2347)
## What

`press_key` currently presses each modifier down, presses the main key,
then releases the modifiers — in three sequential steps with no
`try/finally`:

```ts
for (const modifier of modifiers) {
  await page.pptrPage.keyboard.down(modifier);
}
await page.pptrPage.keyboard.press(key);      // if this rejects…
for (const modifier of modifiers.toReversed()) {
  await page.pptrPage.keyboard.up(modifier);  // …this never runs
}
```

If `keyboard.press(key)` rejects — a CDP hiccup, a target crash, or a
dropped connection — the release loop is skipped and the modifier keys
are left **logically held down in the browser**.
`waitForEventsAfterAction` re-throws the action error, so nothing
downstream releases them either.

This is the unpaired-`keyDown` class of defect asked about in #2309
(*"whether any code path sends a keyDown without a guaranteed matching
keyUp on an error/timeout branch"*). This PR fixes the one concrete
instance of it in this repo.

## Fix

Wrap the down/press sequence in `try/finally` and track which modifiers
were actually pressed, releasing each held modifier even when the main
press throws. Only modifiers whose `keyboard.down()` succeeded are
released, so a failure *while* pressing a modifier doesn't emit a
spurious `keyUp`.

## Test

Adds a regression test (real browser, keydown/keyup logging) that
injects a `press()` failure mid-sequence and asserts both modifiers are
still released. It fails on `main` (`['dControl','dShift']` — no keyups)
and passes with the fix (`['dControl','dShift','uShift','uControl']`).

Verified locally: full `tests/tools/input.test.ts` suite passes, `npm
run typecheck`, eslint, and prettier all clean.

## Scope note re: #2309

I want to be precise about what this does and does not address. This
closes a **browser-level** stuck-key path: leaked keys here live in
Chromium's input state (CDP `Input.dispatchKeyEvent` is injected into
the renderer), so the observable effect is a modifier stuck **within the
driven page**. The report in #2309 is a bare `Space` that repeats
**system-wide** and survives physically unplugging the keyboard — that
symptom is at the OS input layer, which CDP-injected input doesn't route
through, so I don't claim this fully explains that case (details and a
non-reboot workaround are in a comment on the issue). Still, an unpaired
keyDown on an error branch is a real defect worth closing on its own,
and it's exactly the code path the issue asked to audit.

Prepared with AI assistance (Claude Code) and verified against a local
build before submission.
2026-07-12 09:56:35 +00:00
Alex Rudenko 58ba174273 refactor: move remaining McpContext getters (#2342)
Moves various getters to McpPage.
2026-07-10 12:46:01 +00:00
Alex Rudenko 5b33debf1a refactor: clean up McpContext getters (#2340) 2026-07-10 07:46:31 +00:00
Alex Rudenko 8a4ddb3256 refactor: remove isolated context getter (#2336) 2026-07-09 15:13:33 +00:00
Nikolay Vitkov 64005f924f refactor: use helper for Dialog handle (#2334)
Re-use the logic in the wait for helper to expose per dialog type
handling.
2026-07-09 15:12:48 +00:00
Alex Rudenko 9cd734b0a3 refactor: clean up more of the context interface (#2335)
- removed redundant page getters
2026-07-09 13:50:48 +00:00
Alex Rudenko 16db01f79c refactor: clean up page management (#2333)
- remove `#pages` and Puppeteer page getters
- rename methods for clarity
2026-07-09 11:42:11 +00:00
Nikolay Vitkov ca8d219ede revert: support experimental allowlist for navigate tool calls (#1935) (#2203)
This reverts commit d50255778a.
2026-07-09 11:14:55 +00:00
Dominik Inführ 15a6b789da feat: Print object count and total sizes in get_heapsnapshot_details (#2325)
This emits object count and total shallow sizes for
get_heapsnapshot_details.
2026-07-09 10:33:34 +00:00
Dominik Inführ 2812902ba9 feat: Support filter with heap snapshots aggregates (#2323)
This exposes DevTools named filters in get_heapsnapshot_details and
get_heapsnapshot_class_nodes tools. This allows the client to list e.g.
all objects retained through detached DOM objects.

Co-authored-by: Dominik Inführ <dinfuehr@chromium.org>
2026-07-09 09:08:29 +00:00
herdiyanitdev 6e56c028cf feat: support --allow-unrestricted-paths configuration (#2296)
## Summary

validatePath() in McpContext returned immediately, with no restriction
at all, whenever roots() returned undefined. roots() only returns
undefined when the connecting MCP client never negotiates the optional
roots capability during initialize, which any minimal client can trigger
simply by omitting it from its declared capabilities.

Since roots() already always appends the OS temp directory to whatever
explicit roots are configured, this change makes it return that same
default (temp directory only) instead of undefined when no roots have
been set. This removes the early return in validatePath() entirely, so
path validation now runs unconditionally rather than being conditional
on whether the connecting client happened to negotiate a capability it
was never required to declare per the MCP spec.


Any filePath-accepting tool (take_screenshot, saveFile, and the
performance/Lighthouse export tools that route through the same check)
had its only path-traversal guard silently disabled for the lifetime of
a connection whenever the client omitted the optional roots capability.
Since this server is designed to let an LLM drive a browser, and browsed
page content is not trusted input, this meant a client that simply
doesn't implement roots (a plausible, non-adversarial default for
lightweight or custom MCP clients) removed the only boundary preventing
the connected agent from writing to any path the process can reach.


Added a test that exercises the actual default state of roots (never
calling setRoots()) directly, since the existing tests always call
setRoots(), even with an empty array, before validating. Verified
locally with a minimal MCP client that declares no capabilities: before
this change, take_screenshot with a filePath outside any root wrote a
real file to an arbitrary path with no error; after this change, the
same call is rejected with the existing Access denied error. Also
verified that a client that does declare roots is unaffected, and that
writes to the OS temp directory continue to succeed with no roots
negotiated, matching prior behavior for that path.
2026-07-08 16:49:31 +00:00
Thomas Bachem 2c16ac311b fix: report when the selected page was auto-replaced by the fallback (#2308)
Follow-up to #2304 (the "note" part discussed there).

When the selected page disappears from the page list,
`createPagesSnapshot()` silently re-selects the first page. The agent
gets no signal: if its next call is `list_pages` (which is what the
closed-page error message recommends), the listing already shows the new
selection and every subsequent tool call runs against a page the agent
never picked.

This PR records the automatic fallback and surfaces it as a one-line
note in the pages section of the same response:

```
## Pages
Note: the previously selected page was closed. Page 1 is now selected.
1: about:blank [selected]
```

For a selected page that is missing from the list without being closed,
the note reads "is no longer listed" instead. If the expectation from
#2304 holds (a page stays listed as long as it is not closed), that
wording never renders; if the transient case discussed there does occur
in the wild, the note will make it visible.

The fallback behavior itself is unchanged (as discussed in #2304, closed
tabs keep the browser-like auto-selection). No note is emitted on first
connect, when nothing was selected before.

Tests: two unit tests for the fallback bookkeeping (closed page, regular
selection), one for the missing-but-open case via a stubbed page list,
and the one affected snapshot updated (`close_page` now includes the
note).

Refs: #2304
2026-07-08 07:08:26 +00:00
Alex Rudenko b703f2ce20 fix(snapshot): resolve element ids on the correct snapshot (#2295)
closes https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/2243

This probably regressed with the addition of third party developer tools
because the resolveCdpElementId was not moved from page to TextSnapshot.
2026-07-06 09:08:46 +00:00
Dominik Inführ 67a56c0557 feat: Add get_heapsnapshot_duplicate_strings MCP tool (#2280)
This PR adds a new MCP tool to find duplicated strings in the heap
snapshot.

Co-authored-by: Dominik Inführ <dinfuehr@chromium.org>
2026-07-03 10:01:26 +00:00
Piotr Paulski a38ef5e7ea test: fix flaky dialog tests by awaiting evaluate promises (#2284)
Fixes flakiness in pages.test.ts where unawaited page.evaluate()
promises triggering dialogs would resolve after the test ended, causing
TargetCloseError when the next test closed the pages.

TAG=agy
CONV=dc6c530f-5d71-4a91-9440-6a7006d168e6

Co-authored-by: Piotr Paulski <piotrpaulski@chromium.org>
2026-07-02 15:44:18 +00:00
Dominik Inführ 3f4a49a89a refactor: Merge MCP tools for comparing heap snapshots (#2281)
Instead of two separate MCP tools, we can have one instead which takes
classIndex as optional argument.

Co-authored-by: Dominik Inführ <dinfuehr@chromium.org>
2026-07-02 13:26:36 +00:00
Dominik Inführ 5d7b656050 feat: Add MCP tools for heap snapshot comparison (#2198)
This commit adds two MCP tools for comparing heap snapshots.
`compare_heapsnapshot_summary` compares two memory snapshot and returns
which classes have new/deleted objects.
`compare_heapsnapshot_class_nodes` can then be used to list the object
ids added and deleted for a specific class.

Co-authored-by: Dominik Inführ <dinfuehr@chromium.org>
2026-06-30 13:24:21 +00:00
Nikolay Vitkov fecef2698d test: reduce type casting in tests (#2251) 2026-06-24 09:45:16 +00:00
dependabot[bot] 0cafee074c chore(deps-dev): bump puppeteer from 25.1.0 to 25.2.0 in the bundled group (#2246)
Bumps the bundled group with 1 update:
[puppeteer](https://github.com/puppeteer/puppeteer).

Updates `puppeteer` from 25.1.0 to 25.2.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/puppeteer/puppeteer/releases">puppeteer's
releases</a>.</em></p>
<blockquote>
<h2>puppeteer-core: v25.2.0</h2>
<h2><a
href="https://github.com/puppeteer/puppeteer/compare/puppeteer-core-v25.1.0...puppeteer-core-v25.2.0">25.2.0</a>
(2026-06-22)</h2>
<h3>🎉 Features</h3>
<ul>
<li>add page locale emulation (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15075">#15075</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/c528f6508dcbe830a2bf7b61605ea53b14b56168">c528f65</a>)</li>
<li>add waitForFunction to webWorkers (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15100">#15100</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/0bdfa77b1244f4a397e22fc1ed4d9cf6eabe2697">0bdfa77</a>)</li>
<li>allow extensions to run over websockets (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15059">#15059</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/b84d8a18584fcf54670fff169408b8ac712d8c06">b84d8a1</a>)</li>
<li>roll to Chrome 150.0.7871.24 (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15126">#15126</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/b74ed1c81caffa7c727b8e21498dda42899467ed">b74ed1c</a>)</li>
<li>roll to Firefox 152.0 (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15125">#15125</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/87be90674742650fd0bcb138922ff61d2bf2ca98">87be906</a>)</li>
</ul>
<h3>🛠️ Fixes</h3>
<ul>
<li>apply allowlist to non-auto-attach sessions (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15136">#15136</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/5c7a0e0e09eef73177d27d4c4fa2669ebe8d65b7">5c7a0e0</a>)</li>
<li>await for Worker script exection (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15099">#15099</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/dc469b8aae9008b8ec665014d4fac9e13c8a0650">dc469b8</a>)</li>
<li>block service worker registrations (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15135">#15135</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/d03617c36148a0d9ac86c51b3b5661c07c150ef9">d03617c</a>)</li>
<li>correct screencast frame timing so playback matches real time (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15112">#15112</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/525b3843bb4920abe2142c1ae381fb2384d9d2dc">525b384</a>)</li>
<li>remove global skipDownload early return to include other configs (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15130">#15130</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/5b1cb20ce060c88c5f43f1a4bbfdcfd513db6cc3">5b1cb20</a>)</li>
<li>roll to Firefox 152.0.1 (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15134">#15134</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/fd12dd6f31604835b7476b624b8972b8c8c090b6">fd12dd6</a>)</li>
<li><strong>webmcp:</strong> invalidate webmcp tools on context
destruction (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15068">#15068</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/8e9c0fa4ac4054f3f31868f17761c73335229306">8e9c0fa</a>)</li>
</ul>
<h3>📄 Documentation</h3>
<ul>
<li>clarify the network allowlist/blocklist implementation (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15148">#15148</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/a7f124c930e6119581a4e54c3293cbca66c0f498">a7f124c</a>)</li>
<li>Update Page.target deprecation message to cover broader usage. (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15108">#15108</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/882c43d57698596921d83b56ef82b666ad0528c5">882c43d</a>)</li>
</ul>
<h3> Performance</h3>
<ul>
<li><strong>cdp:</strong> parallelize extension workers fetching (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15057">#15057</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/fdee509f47f1587f17cf25b176a50d5d8fcde38a">fdee509</a>)</li>
<li>optimize <code>headersArray</code> iteration in HTTPRequest (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15004">#15004</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/eb9579844f071c2aa8ad1a0c19443b618fac63b7">eb95798</a>)</li>
<li>optimize property descriptors iteration in JSHandle (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15094">#15094</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/2483b656cd17b99af089087094a0e6f88c6ba4fb">2483b65</a>)</li>
<li>parallelize iframe population in accessibility snapshots (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15083">#15083</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/7e3d42ec56c017978ca780af394ac4e5cf5391fd">7e3d42e</a>)</li>
</ul>
<h3>🏗️ Refactor</h3>
<ul>
<li>sync accessing debugger (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15066">#15066</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/904623947f270e50243c3f7fb74f4aef3245c22d">9046239</a>)</li>
</ul>
<h3>Dependencies</h3>
<ul>
<li>The following workspace dependencies were updated
<ul>
<li>dependencies
<ul>
<li><code>@​puppeteer/browsers</code> bumped from 3.0.4 to 3.0.5</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2>puppeteer: v25.2.0</h2>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/puppeteer/puppeteer/blob/main/CHANGELOG.md">puppeteer's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/puppeteer/puppeteer/compare/puppeteer-v25.1.0...puppeteer-v25.2.0">25.2.0</a>
(2026-06-22)</h2>
<h3>🎉 Features</h3>
<ul>
<li>add page locale emulation (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15075">#15075</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/c528f6508dcbe830a2bf7b61605ea53b14b56168">c528f65</a>)</li>
<li>add waitForFunction to webWorkers (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15100">#15100</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/0bdfa77b1244f4a397e22fc1ed4d9cf6eabe2697">0bdfa77</a>)</li>
<li>allow extensions to run over websockets (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15059">#15059</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/b84d8a18584fcf54670fff169408b8ac712d8c06">b84d8a1</a>)</li>
<li>roll to Chrome 150.0.7871.24 (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15126">#15126</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/b74ed1c81caffa7c727b8e21498dda42899467ed">b74ed1c</a>)</li>
<li>roll to Firefox 152.0 (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15125">#15125</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/87be90674742650fd0bcb138922ff61d2bf2ca98">87be906</a>)</li>
</ul>
<h3>🛠️ Fixes</h3>
<ul>
<li>apply allowlist to non-auto-attach sessions (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15136">#15136</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/5c7a0e0e09eef73177d27d4c4fa2669ebe8d65b7">5c7a0e0</a>)</li>
<li>await for Worker script exection (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15099">#15099</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/dc469b8aae9008b8ec665014d4fac9e13c8a0650">dc469b8</a>)</li>
<li>block service worker registrations (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15135">#15135</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/d03617c36148a0d9ac86c51b3b5661c07c150ef9">d03617c</a>)</li>
<li>correct screencast frame timing so playback matches real time (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15112">#15112</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/525b3843bb4920abe2142c1ae381fb2384d9d2dc">525b384</a>)</li>
<li>remove global skipDownload early return to include other configs (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15130">#15130</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/5b1cb20ce060c88c5f43f1a4bbfdcfd513db6cc3">5b1cb20</a>)</li>
<li>roll to Firefox 152.0.1 (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15134">#15134</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/fd12dd6f31604835b7476b624b8972b8c8c090b6">fd12dd6</a>)</li>
<li><strong>webmcp:</strong> invalidate webmcp tools on context
destruction (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15068">#15068</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/8e9c0fa4ac4054f3f31868f17761c73335229306">8e9c0fa</a>)</li>
</ul>
<h3>Dependencies</h3>
<ul>
<li>The following workspace dependencies were updated
<ul>
<li>dependencies
<ul>
<li><code>@​puppeteer/browsers</code> bumped from 3.0.4 to 3.0.5</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3>📄 Documentation</h3>
<ul>
<li>clarify the network allowlist/blocklist implementation (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15148">#15148</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/a7f124c930e6119581a4e54c3293cbca66c0f498">a7f124c</a>)</li>
<li>Update Page.target deprecation message to cover broader usage. (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15108">#15108</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/882c43d57698596921d83b56ef82b666ad0528c5">882c43d</a>)</li>
</ul>
<h3> Performance</h3>
<ul>
<li><strong>cdp:</strong> parallelize extension workers fetching (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15057">#15057</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/fdee509f47f1587f17cf25b176a50d5d8fcde38a">fdee509</a>)</li>
<li>optimize <code>headersArray</code> iteration in HTTPRequest (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15004">#15004</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/eb9579844f071c2aa8ad1a0c19443b618fac63b7">eb95798</a>)</li>
<li>optimize property descriptors iteration in JSHandle (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15094">#15094</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/2483b656cd17b99af089087094a0e6f88c6ba4fb">2483b65</a>)</li>
<li>parallelize iframe population in accessibility snapshots (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15083">#15083</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/7e3d42ec56c017978ca780af394ac4e5cf5391fd">7e3d42e</a>)</li>
</ul>
<h3>🏗️ Refactor</h3>
<ul>
<li>sync accessing debugger (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15066">#15066</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/904623947f270e50243c3f7fb74f4aef3245c22d">9046239</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/puppeteer/puppeteer/commit/049614305e94400629112b66141a4852d2b0d147"><code>0496143</code></a>
chore: release main (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15064">#15064</a>)</li>
<li><a
href="https://github.com/puppeteer/puppeteer/commit/a7f124c930e6119581a4e54c3293cbca66c0f498"><code>a7f124c</code></a>
docs: clarify the network allowlist/blocklist implementation (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15148">#15148</a>)</li>
<li><a
href="https://github.com/puppeteer/puppeteer/commit/036503e7b7eb10bc64d1b98927fe5d12cdb78f7c"><code>036503e</code></a>
test: add iframe tests (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15149">#15149</a>)</li>
<li><a
href="https://github.com/puppeteer/puppeteer/commit/2d71ccc1ca4597a6d9cdb979bc1852a31c8e5dc6"><code>2d71ccc</code></a>
chore: fix regression of debug logs (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15147">#15147</a>)</li>
<li><a
href="https://github.com/puppeteer/puppeteer/commit/5b1cb20ce060c88c5f43f1a4bbfdcfd513db6cc3"><code>5b1cb20</code></a>
fix: remove global skipDownload early return to include other configs
(<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15130">#15130</a>)</li>
<li><a
href="https://github.com/puppeteer/puppeteer/commit/1106b6e4ed2ae8c785bcd540ac9305e6bd8f02d1"><code>1106b6e</code></a>
chore(deps): bump the all group in /website with 2 updates (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15143">#15143</a>)</li>
<li><a
href="https://github.com/puppeteer/puppeteer/commit/f14e4adf67387afe4599f8b166fd6f67de3192ec"><code>f14e4ad</code></a>
chore(deps): bump actions/checkout from 6.0.3 to 7.0.0 in the all group
(<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15144">#15144</a>)</li>
<li><a
href="https://github.com/puppeteer/puppeteer/commit/6d120e64e4cf4910a3d4a29f4f58e6179084ef1a"><code>6d120e6</code></a>
chore(deps): bump node from <code>40ad9f3</code> to <code>032e78d</code>
in /docker in the all grou...</li>
<li><a
href="https://github.com/puppeteer/puppeteer/commit/5c7a0e0e09eef73177d27d4c4fa2669ebe8d65b7"><code>5c7a0e0</code></a>
fix: apply allowlist to non-auto-attach sessions (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15136">#15136</a>)</li>
<li><a
href="https://github.com/puppeteer/puppeteer/commit/77c4ffde99a7cbbd85e91bdb5687254f32cb76ef"><code>77c4ffd</code></a>
chore(webmcp): Update WebMCP to Chrome 150 implementation (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15069">#15069</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/puppeteer/puppeteer/compare/puppeteer-v25.1.0...puppeteer-v25.2.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=puppeteer&package-manager=npm_and_yarn&previous-version=25.1.0&new-version=25.2.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Nikolay Vitkov <nvitkov@chromium.org>
2026-06-22 16:49:45 +00:00
Nebrass Lamouchi ba80096521 fix: handle screencast file extensions case-insensitively and clean up temp dir on failure (#2207)
Fixes #2206

### Problem

`screencast_start` matched the requested file extension with a
**case-sensitive** `endsWith()` against `['.webm', '.mp4']` and
**silently fell back to `.mp4`** when nothing matched. Combined with
`ensureExtension()` (which replaces the extension), a request for
`demo.WEBM` was recorded as **MP4** to **`demo.mp4`** — a different
format *and* path than requested — and any unsupported extension (e.g.
`recording.avi`) silently became `.mp4`.

Separately, when `screencast_start` is called without a `filePath`, it
creates a temp directory via `mkdtemp()`. If `page.screencast()` then
throws (e.g. ffmpeg missing), that directory was leaked.

### Changes

Two commits:

1. **`fix: match screencast extension case-insensitively and reject
unsupported ones`** — match via `path.extname().toLowerCase()`; reject
an explicitly requested but unsupported extension with an explicit error
listing the supported formats; a missing extension still defaults to
`.mp4`.
2. **`fix: clean up screencast temp directory when recording fails to
start`** — remove the generated temp dir in the `catch` handler, but
only when we own the generated path (never when the caller supplied
`filePath`).

| requested       | before          | after          |
| --------------- | --------------- | -------------- |
| `demo.WEBM`     | mp4 → `demo.mp4`| webm → `demo.webm` |
| `recording.avi` | mp4 → `recording.mp4` | error (rejected) |
| `demo.webm`     | webm → `demo.webm` | unchanged |
| *(no filePath)* | mp4 temp        | unchanged      |

The matched extension is normalized to lower case (`demo.WEBM` →
`demo.webm`).

### Testing

Added three regression tests to `tests/tools/screencast.test.ts` using
the existing `sinon`/`withMcpContext` harness. Verified locally against
Chrome for Testing 149 (`PUPPETEER_EXECUTABLE_PATH`):

- With the fix reverted, the two extension tests fail (uppercase `.WEBM`
→ mp4, `.avi` not rejected) and the cleanup test fails (temp dir left
behind) — i.e. they fail for the right reason.
- With the fix applied, the full `screencast.test.ts` suite passes
(11/11).
- `tsc --noEmit` and `npm run check-format` (eslint + prettier) are
clean.

> Note: I ran the `screencast` test file (which stubs `page.screencast`)
plus typecheck/lint locally; the rest of the browser-based suite I left
to CI.

### Notes for reviewers

- I chose to **`throw`** for an unsupported explicit extension
(consistent with the ffmpeg-missing `throw` in the same handler and with
the issue's "reject with an explicit error"). Happy to switch to the
softer `appendResponseLine(...) + return` style used by the in-progress
guard if you'd prefer.
- The two commits are independent and can be split if you'd rather take
them separately.
- I left the pre-existing `as \`${string}.webm\`` assertion on
`resolvedPath` untouched to keep the diff focused, though it's slightly
misleading now that the default is `.mp4`.

---------

Co-authored-by: Nicholas Roscino <nroscino@google.com>
2026-06-18 08:16:02 +00:00
François Beaufort 163a581cc1 chore(webmcp): s/enable-features=WebMCPTesting/enable-features=WebMCP/g (#2223)
Following https://github.com/puppeteer/puppeteer/pull/15121, this PR
updates WebMCP documentation and tests to switch to the blink feature
WebMCP instead of WebMCPTesting which is removed in
https://chromium-review.googlesource.com/c/chromium/src/+/7921035/comment/1f0982d8_fb3efb91/
2026-06-17 12:46:01 +00:00
Antoine Kingue 55c8a541d4 feat(screenshot): add CLI options to cap screenshot size at the source (#1823)
## Summary

Adds **opt-in** CLI flags so operators can cap the size of screenshots
returned by `take_screenshot` before they are embedded in the MCP
response. Refs #879.

The flags address two related symptoms reported when MCP clients display
screenshots inline:

1. **Per-image dimension limit**: hosted LLM APIs commonly reject images
exceeding per-image dimension constraints (typical caps are in the
2000-8000 px range, sometimes scaling down further when many images are
in the same request). This is the exact error reported in #879.
2. **Cumulative request size**: after many captures, the cumulative
base64 payload eventually pushes a request over the per-call body size
limit imposed by the LLM API.

Both can be mitigated at the source by reducing format/quality and
downscaling the capture.

## New flags (all opt-in)

- `--screenshot-format <jpeg|png|webp>`: override the default format
used by `take_screenshot` when the caller does not specify one
- `--screenshot-quality <0-100>`: override the default JPEG/WebP
quality. Ignored for PNG
- `--screenshot-max-width <px>`: downscale screenshots wider than this
before they are returned
- `--screenshot-max-height <px>`: downscale screenshots taller than
this. Combines with `--screenshot-max-width`; the smaller scale wins so
both bounds are respected while preserving aspect ratio

For the exact error in #879, the recipe is `--screenshot-max-width=8000
--screenshot-max-height=8000` (or a smaller value such as `2000` if many
images may end up in the same request, depending on the operator's
chosen API).

## Implementation

- Resizing leverages Puppeteer's `clip.scale` (CDP
`Page.captureScreenshot`), so **no new dependencies**.
- Source dimensions per capture mode:
  - viewport: `page.viewport()`
- full page: `document.documentElement.scrollWidth/scrollHeight` via
`page.evaluate()`
  - element (`uid`): `elementHandle.boundingBox()`
- For element and full-page captures with a downscale clip, the call
routes through `page.screenshot({clip})` so the scale parameter applies.
`captureBeyondViewport` is left to Puppeteer's default (`true` when a
clip is set), preserving correct behavior for elements below the fold
and full-page captures.
- ~150 lines of source code, ~200 lines of new tests.

## Backwards compatibility

**Fully opt-in**: when no flags are set, `take_screenshot` returns the
exact same bytes as before. No behavioral change for existing users.

## Design alignment

- Aligned with the **"Reference over Value"** principle in
`docs/design-principles.md`: the existing 2 MB threshold still routes
oversized screenshots to a temporary file. This change only reduces the
size of the **inline base64 fallback path**, which the principles
document calls out as an acceptable exception when MCP clients display
images natively.
- The MCP server **hardcodes no LLM-specific size limits**. Operators
pick the values that match their client/model combination. This keeps
the maintenance surface here minimal as model limits evolve, and is
intended as a **complement to, not a replacement for**, fixes in the MCP
client itself.

## Addressing concerns raised in #879

> "It's not feasible for us to maintain this. Limits will change when
models change." (@natorion)

The flags are pure parameters; nothing about the upstream LLM is encoded
in the server. When a vendor raises (or lowers) a limit, no code change
is needed here, only the operator's CLI args change.

> "`filePath` / `page_resize` already work as a workaround." (@OrKoN)

`filePath` is great when the call site knows it's about to take a huge
screenshot, but as you noted earlier in the thread, an oversized image
already in the request history keeps causing failures even on subsequent
calls. `page_resize` works but mutates the page being debugged. The
resize in this PR happens **between Puppeteer and the MCP response**, so
the inspected page is untouched and the failure mode is prevented at the
source.

> "Should be fixed client side."

Agreed, this PR is intended as a complement, not a substitute. A
client-side fix (e.g. compaction evicts/downsamples old images) handles
the cumulative case for *any* MCP. A server-side cap handles the
per-call dimension limit for users who hit it before compaction can kick
in. The two address overlapping but distinct failure modes.

Happy to drop or rework any of this if the maintainers prefer a
different shape, for example making the threshold automatic from a
single `--max-image-bytes` knob, or rejecting the PR entirely in favor
of waiting for a client-side fix. Just wanted to put a concrete option
on the table.

## Tests

Added 6 new tests:

- `honors screenshotFormat default from CLI args`
- `keeps "png" as default format when no CLI override is set`
- `downscales viewport screenshot when screenshotMaxWidth is set`
- `downscales using the smaller scale when both max-width and max-height
are set`
- `does not resize when source is smaller than the max bounds`
- `downscales full page screenshot when screenshotMaxWidth is set`

All 627 tests in the suite pass. `npm run typecheck` and `npm run
check-format` are clean.

## Notes for reviewers

- The dimensions compared against `--screenshot-max-width/height` are
**CSS pixels** (`page.viewport()`), not raw bitmap pixels. With
`deviceScaleFactor > 1` (HiDPI emulation) the actual bitmap may still be
larger. Happy to clarify this in the option description if preferred.
- For element captures with a downscale clip, the call routes through
`page.screenshot({clip})` instead of `element.screenshot()`. Same-frame
elements are correct (boundingBox returns main-frame coords). I have
**not** exercised this path against cross-origin iframe elements; let me
know if you'd like a fallback there.
- The PR is currently in **Draft** state pending CLA verification and
any feedback on the framing above.

Refs #879
Closes https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/879
2026-06-17 10:40:14 +00:00
Dominik Inführ 08c234ea4b feat: Add get_heapsnapshot_dominators MCP tool (#2215)
Adding the get_heapsnapshot_dominators MCP tool to show the dominators
for a given node. In combination with get_heapsnapshot_retaining_paths
this should help understand what keeps an object reachable and thus
alive.

Co-authored-by: Dominik Inführ <dinfuehr@chromium.org>
Co-authored-by: Nicholas Roscino <nroscino@google.com>
2026-06-16 12:07:41 +00:00
bassem chagra 9e32002a69 fix: return error message when screencast_stop is called with no active recording (#2209)
## Summary

`screencast_stop` returns an empty response when no recording is active,
making it impossible for the calling agent to distinguish "stopped
successfully" from "nothing was recording."

`screencast_start` already handles its inverse case with an explicit
error (`"a screencast recording is already in progress"`), so this makes
`stop` consistent.

## Change

Added an error message when `screencast_stop` is called without an
active recording:

```ts
if (!data) {
  response.appendResponseLine(
    'Error: no active screencast recording to stop.',
  );
  return;
}
```

## Before

Empty tool response — agent cannot tell what happened.

## After

`Error: no active screencast recording to stop.`

---------

Co-authored-by: Nicholas Roscino <nroscino@google.com>
2026-06-16 12:01:11 +00:00
Wolfgang Beyer ed02047ae9 fix: Reset toolGroups before gathering toolGroups (#2200)
`getToolGroups` can be called multiple times. We want to reset the
`window.__dtmcp.toolGroups` array each time to prevent duplications.
2026-06-12 11:47:43 +00:00
Dominik Inführ 4f8eb7ad6b feat: Add the get_heapsnapshot_edges MCP tool (#2180)
This PR adds the get_heapsnapshot_edges MCP tool. Agents can use it to
look at the outgoing edges for a specifc object.

Co-authored-by: Dominik Inführ <dinfuehr@chromium.org>
2026-06-09 19:58:43 +00:00
Serhii Zghama b646feb4f3 feat: include page title in list_pages output (#2166)
## Motivation

`list_pages` shows each page's URL but not its title, which is painful
when multiple pages share a host — e.g. several tabs under
`app.example.com/u/0/`, `/u/1/`, `/u/2/`. There's no way to tell which
is which without visiting each one, even though every page has a usable
`document.title`.

Fixes #2156.
Closes #2175

## What this changes

- `list_pages` text output now shows the title before the URL when
available: `1: My Page (https://example.com) [selected]`. If the page
has no title (e.g. `about:blank`), the format is unchanged.
- The structured content entry for each page now includes a `title`
field alongside `id`, `url`, and `selected`.
- `page.title()` is awaited with a `.catch(() => '')` so a closed or
erroring page silently falls back to the URL-only format.
- `format()` is made `async` to support the `await` inside the page
loop; `createStructuredPage()` likewise becomes `async`.

## Testing

Start the MCP server with multiple tabs open. Call `list_pages` — pages
with titles now display as `id: Title (url)`. Pages without titles
(`about:blank`, data URLs) display as before.

---------

Co-authored-by: Piotr Paulski <31672205+zyzyzyryxy@users.noreply.github.com>
2026-06-09 16:04:12 +00:00
Dominik Inführ a97c642d43 feat: Add retaining paths MCP tool (#2187)
This PR adds the `get_heapsnapshot_retaining_paths` MCP tool. This can
be used to find the paths from the target object to the GC roots which
keep that object alive.

Co-authored-by: Dominik Inführ <dinfuehr@chromium.org>
2026-06-09 12:53:30 +00:00
Nicholas Roscino 29e389848f feat: implement extension service worker logs (#1915)
This PR introduces the possiblity to collect console messages from
service workers.
2026-06-08 12:30:37 +00:00
Dominik Inführ 8713b93b41 feat: Adds close_heapsnapshot MCP tool (#2174)
This commit adds the close_heapsnapshot MCP tool such that the coding
agent can close heap snapshots again.

Co-authored-by: Dominik Inführ <dinfuehr@chromium.org>
Co-authored-by: Nicholas Roscino <nroscino@google.com>
2026-06-05 12:56:34 +00:00
Wolfgang Beyer 30d59a7872 feat: Handle multiple providers of third-party developer tools (#2168)
This allows a page to have multiple providers of third-party developer
tools, which each respond to the `devtoolstooldiscovery` event.

- Multiple `ToolGroup`s
- MCP tool responses only mention third-party developer tools, if there
are any. Otherwise this part of the output is skipped.
2026-06-02 15:07:32 +00:00
Nicholas Roscino b39131172b chore: reflect emulated device in CrUX data (#2131)
CrUX data returned with the performance tool now reflects the emulated
device

Closes: #1813
2026-05-27 14:49:41 +00:00
Alex Rudenko f8fb2a9236 test: drop node20 code in tests (#2146) 2026-05-27 11:12:22 +00:00
dependabot[bot] f3ef0fb1ee chore(deps-dev): bump puppeteer from 25.0.4 to 25.1.0 in the bundled group (#2130)
Bumps the bundled group with 1 update:
[puppeteer](https://github.com/puppeteer/puppeteer).

Updates `puppeteer` from 25.0.4 to 25.1.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/puppeteer/puppeteer/releases">puppeteer's
releases</a>.</em></p>
<blockquote>
<h2>puppeteer-core: v25.1.0</h2>
<h2><a
href="https://github.com/puppeteer/puppeteer/compare/puppeteer-core-v25.0.4...puppeteer-core-v25.1.0">25.1.0</a>
(2026-05-26)</h2>
<h3>🎉 Features</h3>
<ul>
<li>roll to Chrome 149.0.7827.2 (<a
href="https://github.com/puppeteer/puppeteer/commit/af1b9be6b6a178f7ea6e197f738ca3cf99d786f7">af1b9be</a>)</li>
<li>roll to Firefox 151.0 (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15013">#15013</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/767ea549da1510f0838987f250b5d6be4e0f8960">767ea54</a>)</li>
</ul>
<h3>🛠️ Fixes</h3>
<ul>
<li>roll to Chrome 148.0.7778.178 (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15014">#15014</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/59764acccc9e1c7bca75a60472b288595d3f6cbf">59764ac</a>)</li>
</ul>
<h3>📄 Documentation</h3>
<ul>
<li>use ESM and top level await (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15030">#15030</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/34ecc62957bdd2c83820a51d5d1a6acb81e2957d">34ecc62</a>)</li>
</ul>
<h3>🏗️ Refactor</h3>
<ul>
<li>remove debug dependency (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15023">#15023</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/94d1e1c3dc84ed102d2e801a4e6343cd9fd4a9af">94d1e1c</a>)</li>
</ul>
<h3>Dependencies</h3>
<ul>
<li>The following workspace dependencies were updated
<ul>
<li>dependencies
<ul>
<li><code>@​puppeteer/browsers</code> bumped from 3.0.3 to 3.0.4</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2>puppeteer: v25.1.0</h2>
<h2><a
href="https://github.com/puppeteer/puppeteer/compare/puppeteer-v25.0.4...puppeteer-v25.1.0">25.1.0</a>
(2026-05-26)</h2>
<h3>🎉 Features</h3>
<ul>
<li>roll to Chrome 149.0.7827.2 (<a
href="https://github.com/puppeteer/puppeteer/commit/af1b9be6b6a178f7ea6e197f738ca3cf99d786f7">af1b9be</a>)</li>
</ul>
<h3>🛠️ Fixes</h3>
<ul>
<li>improve progress bar and install (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15042">#15042</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/51db32a5bb48f40cc635ac3eaa2ebb60e6f5bfd5">51db32a</a>)</li>
<li>support concurrency in progress bars (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15045">#15045</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/ab0171d8f4206bdb9dd4e93c2db8d28530bf3fb9">ab0171d</a>)</li>
</ul>
<h3>🏗️ Refactor</h3>
<ul>
<li>replace cosmiconfig with lilconfig (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15031">#15031</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/4a1c2fffe634d430228cac3f8bc7a72f4b5a4a29">4a1c2ff</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/puppeteer/puppeteer/blob/main/CHANGELOG.md">puppeteer's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/puppeteer/puppeteer/compare/puppeteer-v25.0.4...puppeteer-v25.1.0">25.1.0</a>
(2026-05-26)</h2>
<h3>🎉 Features</h3>
<ul>
<li>roll to Chrome 149.0.7827.2 (<a
href="https://github.com/puppeteer/puppeteer/commit/af1b9be6b6a178f7ea6e197f738ca3cf99d786f7">af1b9be</a>)</li>
<li>roll to Firefox 151.0 (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15013">#15013</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/767ea549da1510f0838987f250b5d6be4e0f8960">767ea54</a>)</li>
</ul>
<h3>🛠️ Fixes</h3>
<ul>
<li>roll to Chrome 148.0.7778.178 (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15014">#15014</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/59764acccc9e1c7bca75a60472b288595d3f6cbf">59764ac</a>)</li>
</ul>
<h3>🏗️ Refactor</h3>
<ul>
<li>remove debug dependency (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15023">#15023</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/94d1e1c3dc84ed102d2e801a4e6343cd9fd4a9af">94d1e1c</a>)</li>
</ul>
<h3>Dependencies</h3>
<ul>
<li>The following workspace dependencies were updated
<ul>
<li>dependencies
<ul>
<li><code>@​puppeteer/browsers</code> bumped from 3.0.3 to 3.0.4</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3>📄 Documentation</h3>
<ul>
<li>use ESM and top level await (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15030">#15030</a>)
(<a
href="https://github.com/puppeteer/puppeteer/commit/34ecc62957bdd2c83820a51d5d1a6acb81e2957d">34ecc62</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/puppeteer/puppeteer/commit/ede66693bc0a6d9a8029f66288d13e26966173ce"><code>ede6669</code></a>
chore: release main (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15056">#15056</a>)</li>
<li><a
href="https://github.com/puppeteer/puppeteer/commit/7bc09e7d30e98687668dfe33089b9a3a633cece7"><code>7bc09e7</code></a>
chore(deps): bump the all group with 5 updates (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15052">#15052</a>)</li>
<li><a
href="https://github.com/puppeteer/puppeteer/commit/8c8117047020a49d61f1dff5e302f05ab672951d"><code>8c81170</code></a>
chore(deps): bump the all group in /website with 3 updates (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15051">#15051</a>)</li>
<li><a
href="https://github.com/puppeteer/puppeteer/commit/09eced565903369338ff7537b20727b24a284495"><code>09eced5</code></a>
chore: update lock</li>
<li><a
href="https://github.com/puppeteer/puppeteer/commit/53b9fdadc131449377ed6de59162426140d29db6"><code>53b9fda</code></a>
chore(deps): bump the dependencies group with 2 updates (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15049">#15049</a>)</li>
<li><a
href="https://github.com/puppeteer/puppeteer/commit/d8424116590afac99369c0e996e0dfaf9029b34c"><code>d842411</code></a>
chore(deps): bump node from <code>050bf2b</code> to <code>8530f76</code>
in /docker in the all grou...</li>
<li><a
href="https://github.com/puppeteer/puppeteer/commit/1d2a5696d3c7b592b8d65327d76cc2264f9fba9f"><code>1d2a569</code></a>
docs: document read-only Docker directories (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15048">#15048</a>)</li>
<li><a
href="https://github.com/puppeteer/puppeteer/commit/ab0171d8f4206bdb9dd4e93c2db8d28530bf3fb9"><code>ab0171d</code></a>
fix: support concurrency in progress bars (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15045">#15045</a>)</li>
<li><a
href="https://github.com/puppeteer/puppeteer/commit/51db32a5bb48f40cc635ac3eaa2ebb60e6f5bfd5"><code>51db32a</code></a>
fix: improve progress bar and install (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15042">#15042</a>)</li>
<li><a
href="https://github.com/puppeteer/puppeteer/commit/d32384baa5ef8bcd81319ee698e9d46edba1e957"><code>d32384b</code></a>
chore(deps): bump qs and express in /website (<a
href="https://redirect.github.com/puppeteer/puppeteer/issues/15040">#15040</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/puppeteer/puppeteer/compare/puppeteer-v25.0.4...puppeteer-v25.1.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=puppeteer&package-manager=npm_and_yarn&previous-version=25.0.4&new-version=25.1.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Alex Rudenko <alexrudenko@chromium.org>
2026-05-26 14:05:27 +00:00
Wolfgang Beyer 3ade962a8d fix: Apply CPU throttling to secondary CDP session (#2092)
This addresses #1955 

CPU throttling needs to be applied to both the primary puppeteer session
and the secondary CDP session from the DevTools universe to have an
effect.

For network throttling this does not seem to be the case, I can see a
slowdown with the current implementation which only applies the network
throttling to the primary CDP session.

I also had to increase the navigation timeout to prevent timeout errors.
2026-05-22 07:17:30 +00:00
Pedro Durek 6992106d1c feat: add extraHttpHeaders emulation to emulate tool (#1176)
## Summary

Extend the existing `emulate` tool with an `extraHTTPHeaders` parameter
that calls Puppeteer's `page.setExtraHTTPHeaders()` (which uses CDP
`Network.setExtraHTTPHeaders` under the hood).

Closes #1175

## Approach

Per [feedback from
@natorion](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1175#issuecomment-4097587153),
this integrates into the existing `emulate` tool rather than adding a
standalone tool. The `emulate` tool is already the central hub for
page-level state modifications (userAgent, viewport, networkConditions,
geolocation, colorScheme), and custom HTTP headers fit naturally
alongside them. This also avoids increasing the MCP tool count and LLM
token overhead.

## Changes

- **`src/types.ts`** — Added `extraHTTPHeaders?: Record<string, string>`
to `EmulationSettings`
- **`src/tools/emulation.ts`** — Added `extraHTTPHeaders` as an optional
zod parameter on the `emulate` tool
- **`src/McpContext.ts`** — Added handler logic in the `emulate()`
method:
- Calls `page.setExtraHTTPHeaders()` when `extraHTTPHeaders` is provided
  - Clears from settings when an empty `{}` is passed
- Preserves existing headers when the param is **omitted** (unlike other
emulation settings that reset when omitted) — prevents
`emulate({colorScheme: "dark"})` from accidentally clearing
previously-set headers
- **`tests/tools/emulation.test.ts`** — Added 5 test cases:
  1. Sets extra headers on requests
  2. Clears headers with `{}`
  3. Headers persist across navigations
  4. Does not affect other emulation settings
  5. Reports correctly per-page (new page has no headers)

## Use Case

This enables setting custom HTTP headers on **all** requests — including
the initial document navigation and `<script>` tag loads — which
`initScript` cannot do since it runs after the document is already
fetched.

## Usage

```js
// Set headers
emulate({ extraHTTPHeaders: { "X-Custom": "value", "Authorization": "Bearer token" } })

// Clear headers
emulate({ extraHTTPHeaders: {} })

// Combine with other emulation settings
emulate({ extraHTTPHeaders: { "X-Branch": "feature-1" }, userAgent: "MyBot/1.0" })
```

---------

Co-authored-by: Alex Rudenko <alexrudenko@chromium.org>
Co-authored-by: Nicholas Roscino <nroscino@google.com>
2026-05-20 09:37:27 +00:00
Nikolay Vitkov 32dc50d59b chore(memory): update names and exposed information (#2079)
Update the tools to all include the `heapsnapshot` term for easier
handling.
Renames UID to ID to reduce confusion with the snapshot UIDs.
Renames Id to NodeId to better differentiate from the base Id.
Remove EdgeIndex as it was not useful.

Closes:
https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1970
2026-05-18 16:49:41 +00:00
Alex Rudenko 213720b69c fix: improve geolocation emulation (#2036)
- add a message about successful configuration
- add a message about the currently emulated geolocation
- switch to comma separate format instead of `x` separator.

Tested with https://www.audero.it/demo/geolocation-api-demo.html
2026-05-18 12:13:12 +00:00
Nikolay Vitkov b2b05a0670 chore(memory): expose retainer logic (#2056)
Allows the LLM to query the heapsnapshot further to understand what the
retainer path logic.
2026-05-18 09:54:00 +00:00
@masamaru0513 90d368d343 feat: support filePath in evaluate_script (#2054)
## Summary

Adds an optional `filePath` parameter to `evaluate_script` that saves
the script output to a file instead of returning it inline.

Refs #153

## Motivation

Issue #153 requested `filePath` support for `take_snapshot` and
`evaluate_script`. `take_snapshot` was addressed in #463. PR #248
previously attempted this but was closed due to conflicts. This PR
implements the same feature on the current codebase, completing the
remaining piece.

## Changes

- Add optional `filePath` parameter to the `evaluate_script` schema
- Add `context.validatePath(filePath)` call for path validation
- Pass `{filePath, context}` options to `performEvaluation()`
- In `performEvaluation()`: when `filePath` is provided, save output via
`context.saveFile()` with `.json` extension; otherwise return inline as
before
- Update `docs/tool-reference.md` via `npm run docs:generate`
- Add unit test for file output

## Key design decisions

- **Same pattern as existing tools**: Follows the `context.saveFile()`
pattern established by `take_snapshot` (#463), `take_screenshot`,
`get_network_request` (#795), and performance tools (#686).
- **Minimal change surface**: Only `performEvaluation()` gains an
optional `options` parameter. No new interfaces or abstractions.
- **Backwards compatible**: `filePath` is optional. When omitted,
behavior is identical to before.

## Testing

**Unit test added** (`tests/tools/script.test.ts`):
- Call `evaluate_script` with `filePath` set to a temp file
- Assert response contains "Output saved to"
- Assert file content matches the JSON-serialized return value
- Clean up temp file in `finally` block

**Manual testing performed**:
- `() => document.title` with `filePath: /tmp/test.json` → file contains
`"Example Domain"`
- `() => document.title` without `filePath` → inline ```json block
returned (no regression)
- `() => Array.from({length: 100}, ...)` with `filePath` → 100-item
array saved correctly
- `filePath` pointing to non-existent directory → directory
auto-created, file saved
- Relative path (`test.json`) → resolved to CWD, absolute path shown in
response
- Function that throws → error returned, no partial file created
- Existing file as `filePath` → file overwritten completely

---------

Co-authored-by: Alex Rudenko <alexrudenko@chromium.org>
2026-05-14 08:33:27 +00:00
Nikolay Vitkov f6a12be091 refactor: waitForResult helpers (#2041) 2026-05-13 12:31:33 +00:00