orkon/testcr
367 次代码提交
| 作者 | SHA1 | 备注 | 提交日期 | |
|---|---|---|---|---|
|
|
6dcfd91ca2 | test(upload_file): cover non-file input routing | ||
|
|
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. |
||
|
|
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> |
||
|
|
a9228141ae |
fix: validate extension-enforced output paths (#2269)
Fixes output path validation so tools cannot validate one path and then write to a different canonical target after extension enforcement. Changes: - Resolve dangling symlinks to their target path during canonicalization. - Validate the final extension-enforced output path before writing. - Apply the same final-path validation to heap snapshots and screencasts. - Add regression coverage for dangling symlinks that point outside configured roots. Validation: - npm run format - npm run check-format - npm run test tests/utils/files.test.ts - npm run test tests/roots.test.ts - npm run test tests/tools/memory.test.ts tests/tools/screencast.test.ts Note: I also ran the full npm test suite locally. The targeted tests above passed, but the full suite hit local WSL daemon/e2e startup timeouts while waiting for daemon.pid / server_start, which appear unrelated to this path-validation change. --------- Co-authored-by: huynhtrungcsc <huynhtrungcsc@users.noreply.github.com> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
e5bd334c97 |
chore: make TOON dependency optional (#2260)
Since toon dependency is only needed for `--experimentalToonFormat` flag, we can make it optional to decrease package size and security footprint for users that don't use it. Testing npx optional peer dependency resolution before the release: ``` npm i -g verdaccio verdaccio ``` On a separate terminal: ``` npm adduser --registry http://localhost:4873/ # follow prompts to create user and login # replace the published package in verdaccio npm unpublish chrome-devtools-mcp@1.4.0 --force --registry http://localhost:4873 npm publish --registry http://localhost:4873 # clear npx cache rm -rf ~/.npm/_npx # run the server from commandline and observe both packages being installed: npx --registry http://localhost:4873 --package chrome-devtools-mcp@latest --package @toon-format/toon chrome-devtools-mcp --experimentalToonFormat ``` Paste the following commands (each line separately) to manually interact with the mcp server and observe TOON formatted response directly ### 1. Initialize the session {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test-client","version":"1.0.0"}}} ### 2. Confirm initialization {"jsonrpc":"2.0","method":"notifications/initialized"} ### 3. Navigate to Google (this will already create snapshot in most recent versions) {"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"navigate_page","arguments":{"url":"https://google.com"}}} ### 4. Take Snapshot (if not returned by the previous command) {"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"take_snapshot","arguments":{}}} --------- Co-authored-by: Piotr Paulski <piotrpaulski@chromium.org> |
||
|
|
6a9466378c |
fix: respecte allow/block list in loadResouce (#2254)
Fixes https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/2218 Closes https://github.com/ChromeDevTools/chrome-devtools-mcp/pull/2225 |
||
|
|
fecef2698d | test: reduce type casting in tests (#2251) | ||
|
|
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 /> [](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> |
||
|
|
6225ffbaf1 |
fix: hide Windows update check consoles (#2231)
## Summary Fixes #2230. - Sets `windowsHide: true` on the detached update-check process so Windows does not flash a console window. - Also hides the `npm config get registry` subprocess used by the updater helper. - Covers the update-check spawn options in the existing tests. ## Verification - `NODE_OPTIONS=--max-old-space-size=4096 npm run build` - `npm run test:no-build -- tests/check-for-updates.test.ts` - `NODE_OPTIONS=--max-old-space-size=4096 npx eslint src/utils/check-for-updates.ts src/bin/check-latest-version.ts tests/check-for-updates.test.ts` - `npx prettier --check src/utils/check-for-updates.ts src/bin/check-latest-version.ts tests/check-for-updates.test.ts` - `git diff --check` Note: plain `npm run build` and full-repo `npm run check-format` hit the local Node heap limit in this runner; the same build passed with the heap limit raised, and touched-file lint/format checks passed. Co-authored-by: cyphercodes <cyphercodes@users.noreply.github.com> |
||
|
|
5a9d6af743 |
fix(network): keep redirect chain order consistent between text and JSON (#2221)
## Problem `get_network_request` returns, from the **same call**, both a human-readable text block (`toStringDetailed()`) and a `structuredContent.networkRequest` object (`toJSONDetailed()`) — emitted together in `McpResponse.ts`. For a request that went through HTTP redirects, the redirect chain comes out in **opposite orders** in the two representations: - `toJSONDetailed()` (`NetworkFormatter.ts`) reverses `redirectChain()` once → newest→oldest in the JSON. - the text formatter then reverses it **a second time** → oldest→newest in the text. Because each path calls `redirectChain()` separately and Puppeteer returns a fresh copy on every call (`HTTPRequest#redirectChain()` does `this._redirectChain.slice()`), the two reverses operate on different arrays and don't cancel. So a consumer reading the text and a consumer parsing the structured JSON from the same response see contradictory redirect orders. ## Solution Drop the redundant `.reverse()` in the text formatter so the rendered text uses the order already produced by `toJSONDetailed()`. Both representations are now consistent (newest→oldest), and `structuredContent` is unchanged. ## Why the existing tests didn't catch it - The existing "handles redirect chain" test uses a **single-element** chain, where reversing is a no-op. - `getMockRequest().redirectChain()` returned the **same array reference** on every call, unlike real Puppeteer — so the two reverses accidentally agreed in tests. This PR makes the mock return a fresh copy per call (matching Puppeteer) and adds a regression test with a multi-element chain that asserts the text and JSON orders match. ## Testing - `npm test` for the formatter suite passes. New test `renders the redirect chain in the same order in text and JSON` is **red** before the fix (text `[first, second]` vs JSON `[second, first]`) and **green** after, with no change to existing snapshots. - `npm run typecheck` and Prettier/ESLint are clean. No existing issue tracked this; found via code inspection and confirmed empirically. |
||
|
|
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> |
||
|
|
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/ |
||
|
|
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 |
||
|
|
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> |
||
|
|
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>
|
||
|
|
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. |
||
|
|
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> |
||
|
|
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> |
||
|
|
834b259eb2 |
chore: expose loadResouce HostBinding (#2194)
This makes sure that if any resource that is loaded via the HostBinding will be correctly checked agains the roots object. |
||
|
|
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> |
||
|
|
bede4fbdfa |
chore: install HostBindingAdapter (#2186)
This PR introduces the HostBindingAdapter to utilize the functions usually available to DevTools. Additionally I moved all the DevTools related files under a `devtools` directory to better separate the extractor logic. The patch scripts for DevTools were moved under a function to remove the side-effect nature of the file. Now gets called in a the creation of the McpContext (and a before hook in test.) |
||
|
|
aa33bff19f |
feat: add experimental TOON support for structured content output (#2042)
Adds `--experimentalToonFormat` boolean flag, controlling how structured content is formatted in text response. By default, custom shorthand format is used. With this flag, TOON format (see https://github.com/toon-format/toon) is used instead. TOON format is supposed to be more token-efficient and less error-prone for agents to understand than json, but it's not clear if it will be better on those metrics than the custom format used so far. (Evals pending) One clear benefit over custom format would be less code to maintain if we decide to fully switch to TOON, due to dropping custom formatters and reusing json formatters used for structuredContent anyways. Co-authored-by: Piotr Paulski <piotrpaulski@chromium.org> |
||
|
|
29e389848f |
feat: implement extension service worker logs (#1915)
This PR introduces the possiblity to collect console messages from service workers. |
||
|
|
02b4492ca6 |
feat: support allowedUrlPattern & blockedUrlPattern Options (#2037)
## Support for Network Blocklists and Allowlists (`--blocked-url-pattern` & `--allowed-url-pattern` arguments) This PR adds support for CLI options to restrict network access in the browser session via URL patterns. ### Key Features & How It Works - **Pattern Matching:** Utilizes the [URLPattern Standard](https://urlpattern.spec.whatwg.org/) for pattern matching. - **Target Detachment:** Silently detaches from targets (pages/tabs) whose URLs match blocked patterns (or do not match allowed patterns) upon connection. - **Runtime Blocking:** Prevents navigations and blocks runtime requests (such as fetch/XHR and subresources) if they violate the pattern rules. - **Mutual Exclusivity:** `--blocked-url-pattern` and `--allowed-url-pattern` conflict with each other and cannot be configured simultaneously. - **Browser Requirements:** - **`--allowed-url-pattern`**: Requires **Chrome 149+**. - **`--blocked-url-pattern`**: Works on Chrome versions older than 149, but **Chrome 149+ is highly recommended**. ### Important Limitations & Side Effects - **Network Emulation/Throttling Conflict:** Network throttling is disabled when a network blocklist/allowlist is configured, to avoid conflicting with Puppeteer's underlying blocking mechanisms. - Using the `emulate` tool to modify `networkConditions` (e.g. setting to `Offline`) will throw an error: *`Network throttling is not supported when network blocking (allowlist/blocklist) is configured.`* - Other emulation settings (e.g., `cpuThrottlingRate`, `geolocation`, `viewport`) are unaffected and remain fully functional. --- ### Configuration Examples #### 1. Blocking specific domains or endpoints (Blocklist) Add the `--blocked-url-pattern` options to the `args` list in your MCP settings file: ```json { "mcpServers": { "chrome-devtools": { "command": "npx", "args": [ "chrome-devtools-mcp@latest", "--blocked-url-pattern=*://*.blocked-example.com/*", "--blocked-url-pattern=*://*.another-blocked-example.com/*" ] } } } ``` #### 2. Restricting access to authorized domains (Allowlist) Add the `--allowed-url-pattern` options to restrict the browser to permitted hosts (requires Chrome 149+): ```json { "mcpServers": { "chrome-devtools": { "command": "npx", "args": [ "chrome-devtools-mcp@latest", "--allowed-url-pattern=https://*.allowed-example.com/*", "--allowed-url-pattern=https://*.another-allowed-example.com/*" ] } } } ``` --------- Co-authored-by: Natallia Harshunova <nharshunova@chromium.org> Co-authored-by: Alex Rudenko <alexrudenko@chromium.org> |
||
|
|
0217397257 |
feat: memory debugging tools (#2169)
Updates the flag for the memory tooling to remove the experimental bit (keep alias for backwards compatibility). And updates the SKILLs to reflected the update names and point to the available tools. Q: Should it be called `take_heapsnapshot` or `take_heap_snapshot`? |
||
|
|
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> |
||
|
|
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. |
||
|
|
2e039c09e1 |
refactor: use validate files on the tool level (#2152)
Fixes https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/2138 Closes #2150 |
||
|
|
b39131172b |
chore: reflect emulated device in CrUX data (#2131)
CrUX data returned with the performance tool now reflects the emulated device Closes: #1813 |
||
|
|
f8fb2a9236 | test: drop node20 code in tests (#2146) | ||
|
|
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 /> [](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> |
||
|
|
5ee13b3f21 | test: improve webmcp tests (#2139) | ||
|
|
43b934cd98 |
fix: exit on stdin EOF and SIGTERM/SIGINT/SIGHUP, closing the browser cleanly (#2117)
Fixes #2116. `chrome-devtools-mcp-main.ts` currently has no shutdown handler. After a session calls `navigate_page` (or anything else that launches Chrome), the Chrome subprocess keeps the Node event loop ref'd, so closing stdin (the stdio MCP convention for "I'm done") doesn't make the server exit. Callers that close stdin to terminate the server have to fall back to SIGTERM / SIGKILL on every page-loaded session — deterministically, not flakily. This change: 1. Adds `closeBrowser()` in `browser.ts` that calls `browser.close()` for launched instances (reaps the Chrome subprocess) and `browser.disconnect()` for attached instances (leaves the user's Chrome alive). No-op if no browser is active or the connection has already been dropped. 2. Registers shutdown handlers in `chrome-devtools-mcp-main.ts` for: - `stdin.on('end' | 'close')` — stdio MCP transport convention - `SIGTERM` / `SIGINT` / `SIGHUP` — clients that signal instead of closing stdin (`SIGHUP` for parity with `src/daemon/daemon.ts`) The handler is idempotent (guarded `shuttingDown` flag), and has an unref'd 10s timeout backstop in case Chrome teardown hangs (slow `beforeunload` handlers, many tabs, etc.). ### Note on scope This complements (does not replace) the client-side fixes filed against #1765, e.g. google-gemini/gemini-cli#13391 and anthropics/claude-code#42300. The MCP stdio convention is that closing stdin signals shutdown; a server that doesn't honor that forces every client to special-case it. The watchdog sub-process (`src/telemetry/watchdog/main.ts:145-146`) and the daemon (`src/daemon/daemon.ts:224-230`) both already implement this for the same reason — this PR extends the same pattern to the main entry point so all three execution paths behave consistently. ### Measurement Repro script in #2116, same env (chrome-devtools-mcp@1.0.1, Chrome 148.0.7778.178, Node v24.11.1, Linux), 10 iterations: | Scenario | Before | After | |---|---|---| | `tools/list only` (no navigation) | 10/10 clean, 30-37 ms | 10/10 clean, 29-40 ms | | `navigate example.com` | 10/10 SIGTERM at ~5080 ms | 10/10 clean at 145-180 ms | ### Notes - I didn't add a subprocess-based test for this; the existing `tests/utils.ts:runCli` infrastructure targets the `chrome-devtools` CLI, not the stdio MCP server, and a shutdown-timing test would introduce non-trivial Chrome-startup flakiness in CI. Happy to add one if maintainers want it — pointer to the right test directory appreciated. |
||
|
|
176eb69513 | fix: use realpath for MCP roots validation (#2127) | ||
|
|
1b51a520f2 | fix(cli): address pid file creation issues (#2124) | ||
|
|
57f32b0cd4 |
fix: Fix throttling info in performance trace output (#2096)
This addresses #1955. Throttling information needs to be passed to the parser for the output to be correct. |
||
|
|
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. |
||
|
|
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> |
||
|
|
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 |
||
|
|
a9fb555c80 |
fix: include saved image paths in CLI JSON output (#2070)
Fixes #1910 Summary: - save image content before returning structured CLI JSON output - include saved image metadata in `structuredContent.images` - add coverage for JSON output with image content Tests: - `npm test -- tests/daemon/client.test.ts` - `npx eslint src/daemon/client.ts tests/daemon/client.test.ts && npx prettier --check src/daemon/client.ts tests/daemon/client.test.ts` Co-authored-by: Nicholas Roscino <nroscino@google.com> |
||
|
|
d0e6539ef4 |
fix: disable NetworkManager in DevTools (#1834)
Co-authored-by: Alex Rudenko <alexrudenko@chromium.org> |
||
|
|
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 |
||
|
|
b2b05a0670 |
chore(memory): expose retainer logic (#2056)
Allows the LLM to query the heapsnapshot further to understand what the retainer path logic. |
||
|
|
20021469dc |
chore(deps-dev): bump puppeteer from 24.43.0 to 25.0.2 in the bundled group across 1 directory (#2045)
Bumps the bundled group with 1 update in the / directory: [puppeteer](https://github.com/puppeteer/puppeteer). Updates `puppeteer` from 24.43.0 to 25.0.2 <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.0.2</h2> <h2><a href="https://github.com/puppeteer/puppeteer/compare/puppeteer-core-v25.0.1...puppeteer-core-v25.0.2">25.0.2</a> (2026-05-15)</h2> <h3>🛠️ Fixes</h3> <ul> <li>update docs text (<a href="https://redirect.github.com/puppeteer/puppeteer/issues/14992">#14992</a>) (<a href="https://github.com/puppeteer/puppeteer/commit/36527b8b9dd856e11a372b46cba23409e5613573">36527b8</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.1 to 3.0.2</li> </ul> </li> </ul> </li> </ul> <h2>puppeteer: v25.0.2</h2> <h2><a href="https://github.com/puppeteer/puppeteer/compare/puppeteer-v25.0.1...puppeteer-v25.0.2">25.0.2</a> (2026-05-15)</h2> <h3>♻️ Chores</h3> <ul> <li><strong>puppeteer:</strong> Synchronize puppeteer versions</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.1 to 3.0.2</li> <li>puppeteer-core bumped from 25.0.1 to 25.0.2</li> </ul> </li> </ul> </li> </ul> <h2>puppeteer-core: v25.0.1</h2> <h2><a href="https://github.com/puppeteer/puppeteer/compare/puppeteer-core-v25.0.0...puppeteer-core-v25.0.1">25.0.1</a> (2026-05-13)</h2> <h3>🛠️ Fixes</h3> <ul> <li>enabled features should take precedence over disabled features (<a href="https://redirect.github.com/puppeteer/puppeteer/issues/14985">#14985</a>) (<a href="https://github.com/puppeteer/puppeteer/commit/f6fd7c29f92e6183b2fd48f0571957306c58d440">f6fd7c2</a>)</li> <li>roll to Chrome 148.0.7778.167 (<a href="https://redirect.github.com/puppeteer/puppeteer/issues/14980">#14980</a>) (<a href="https://github.com/puppeteer/puppeteer/commit/84c46fede6b02a1e9a00104b97b28c263f0c84df">84c46fe</a>)</li> <li>roll to Firefox 150.0.3 (<a href="https://redirect.github.com/puppeteer/puppeteer/issues/14983">#14983</a>) (<a href="https://github.com/puppeteer/puppeteer/commit/872f77822d6b6b9b6510752d838107a967241bdb">872f778</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.0 to 3.0.1</li> </ul> </li> </ul> </li> </ul> <h2>puppeteer: v25.0.1</h2> <h2><a href="https://github.com/puppeteer/puppeteer/compare/puppeteer-v25.0.0...puppeteer-v25.0.1">25.0.1</a> (2026-05-13)</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.0.1...puppeteer-v25.0.2">25.0.2</a> (2026-05-15)</h2> <h3>♻️ Chores</h3> <ul> <li><strong>puppeteer:</strong> Synchronize puppeteer versions</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.1 to 3.0.2</li> </ul> </li> </ul> </li> </ul> <h3>🛠️ Fixes</h3> <ul> <li>update docs text (<a href="https://redirect.github.com/puppeteer/puppeteer/issues/14992">#14992</a>) (<a href="https://github.com/puppeteer/puppeteer/commit/36527b8b9dd856e11a372b46cba23409e5613573">36527b8</a>)</li> </ul> <h2><a href="https://github.com/puppeteer/puppeteer/compare/puppeteer-v25.0.0...puppeteer-v25.0.1">25.0.1</a> (2026-05-13)</h2> <h3>♻️ Chores</h3> <ul> <li><strong>puppeteer:</strong> Synchronize puppeteer versions</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.0 to 3.0.1</li> </ul> </li> </ul> </li> </ul> <h3>🛠️ Fixes</h3> <ul> <li>enabled features should take precedence over disabled features (<a href="https://redirect.github.com/puppeteer/puppeteer/issues/14985">#14985</a>) (<a href="https://github.com/puppeteer/puppeteer/commit/f6fd7c29f92e6183b2fd48f0571957306c58d440">f6fd7c2</a>)</li> <li>roll to Chrome 148.0.7778.167 (<a href="https://redirect.github.com/puppeteer/puppeteer/issues/14980">#14980</a>) (<a href="https://github.com/puppeteer/puppeteer/commit/84c46fede6b02a1e9a00104b97b28c263f0c84df">84c46fe</a>)</li> <li>roll to Firefox 150.0.3 (<a href="https://redirect.github.com/puppeteer/puppeteer/issues/14983">#14983</a>) (<a href="https://github.com/puppeteer/puppeteer/commit/872f77822d6b6b9b6510752d838107a967241bdb">872f778</a>)</li> </ul> <h2><a href="https://github.com/puppeteer/puppeteer/compare/puppeteer-v24.43.1...puppeteer-v25.0.0">25.0.0</a> (2026-05-12)</h2> <h3>⚠ BREAKING CHANGES</h3> <ul> <li>remove deprecated Puppeteer.product (<a href="https://redirect.github.com/puppeteer/puppeteer/issues/14977">#14977</a>)</li> <li>bump min NodeJS to 22 (<a href="https://redirect.github.com/puppeteer/puppeteer/issues/14973">#14973</a>)</li> <li>return a Promise for executablePath, defaultArgs (<a href="https://redirect.github.com/puppeteer/puppeteer/issues/14965">#14965</a>)</li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/puppeteer/puppeteer/commit/3aadc38c533caa8df87f381868291ede870883e2"><code>3aadc38</code></a> chore: release main (<a href="https://redirect.github.com/puppeteer/puppeteer/issues/14993">#14993</a>)</li> <li><a href="https://github.com/puppeteer/puppeteer/commit/36527b8b9dd856e11a372b46cba23409e5613573"><code>36527b8</code></a> fix: update docs text (<a href="https://redirect.github.com/puppeteer/puppeteer/issues/14992">#14992</a>)</li> <li><a href="https://github.com/puppeteer/puppeteer/commit/3ea7bd54b97965fb0e1b83a2c017720162785c92"><code>3ea7bd5</code></a> fix: capitalize "Chrome" in troubleshooting.md (<a href="https://redirect.github.com/puppeteer/puppeteer/issues/14991">#14991</a>)</li> <li><a href="https://github.com/puppeteer/puppeteer/commit/ae72fdbec9720c982fc2ed818036d7be9b2aa1dc"><code>ae72fdb</code></a> chore: Tiny punctuation update in troubleshooting.md (<a href="https://redirect.github.com/puppeteer/puppeteer/issues/14990">#14990</a>)</li> <li><a href="https://github.com/puppeteer/puppeteer/commit/1597fde55de1e7b3c31032aa43dce0607b4307d3"><code>1597fde</code></a> chore: release main (<a href="https://redirect.github.com/puppeteer/puppeteer/issues/14982">#14982</a>)</li> <li><a href="https://github.com/puppeteer/puppeteer/commit/f6fd7c29f92e6183b2fd48f0571957306c58d440"><code>f6fd7c2</code></a> fix: enabled features should take precedence over disabled features (<a href="https://redirect.github.com/puppeteer/puppeteer/issues/14985">#14985</a>)</li> <li><a href="https://github.com/puppeteer/puppeteer/commit/bf05fb9bf5bac997e365eda2e31c6067e5ec1677"><code>bf05fb9</code></a> fix: update browsers to trigger release (<a href="https://redirect.github.com/puppeteer/puppeteer/issues/14984">#14984</a>)</li> <li><a href="https://github.com/puppeteer/puppeteer/commit/872f77822d6b6b9b6510752d838107a967241bdb"><code>872f778</code></a> fix: roll to Firefox 150.0.3 (<a href="https://redirect.github.com/puppeteer/puppeteer/issues/14983">#14983</a>)</li> <li><a href="https://github.com/puppeteer/puppeteer/commit/84c46fede6b02a1e9a00104b97b28c263f0c84df"><code>84c46fe</code></a> fix: roll to Chrome 148.0.7778.167 (<a href="https://redirect.github.com/puppeteer/puppeteer/issues/14980">#14980</a>)</li> <li><a href="https://github.com/puppeteer/puppeteer/commit/ee655d8292f37cc33f279746ef72613e5ccb5db8"><code>ee655d8</code></a> chore(main): release ng-schematics 0.8.0 (<a href="https://redirect.github.com/puppeteer/puppeteer/issues/14978">#14978</a>)</li> <li>Additional commits viewable in <a href="https://github.com/puppeteer/puppeteer/compare/puppeteer-v24.43.0...puppeteer-v25.0.2">compare view</a></li> </ul> </details> <br /> --------- 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> |
||
|
|
041b208378 |
fix: report unknown tool arguments (#2064)
## Summary - register tool input schemas as passthrough so extra named arguments reach ToolHandler validation - report unknown arguments with an explicit error that names the unknown and expected arguments - stop before invoking tool handlers when unknown arguments are present - add ToolHandler coverage for reporting an extra argument Fixes #1940 ## Tests - `npm run check-format` - `npx tsc --noEmitOnError false` *(emits build artifacts but still reports the existing `chrome-devtools-frontend` type conflict in `ModelImpl.ts`)* - `node --experimental-strip-types --no-warnings=ExperimentalWarning scripts/post-build.ts` - `NODE_TEST_REPORTER=spec npm run test:no-build -- tests/ToolHandler.test.ts` |
||
|
|
e14a74c859 |
chore: disallow underscores followed by numbers in metrics names (#2058)
Underscore followed by numbers is not encouraged in the proto style guide. See "Underscores in Identifiers" in https://protobuf.dev/programming-guides/style/. This recently became an issue because we have `list_3p_developer_tools` and `execute_3p_developer_tool` which would have been dis-allowed. This change replaces them with `list3p_developer_tools` and `execute3p_developer_tool` respectively, as suggested by the style guide. This only affects the logged version. The tool name is still the existing one. This transformation is also applied to other similar places, like flag names, tool name in error logging, and tool args. The `tool_name_metrics.json` was manually updated because we never landed the server side change because it was disallowed by proto style check. |
||
|
|
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> |