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>
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>
## 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.
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>
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>
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.)
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
This is a better measurement as if object self size is X and the
retainerSize Y. X will always be smaller then Y, there Y will be the
actual size on the Heap.
## Summary
Group consecutive identical console messages in `list_console_messages`,
similar to Chrome DevTools' console grouping behavior.
Fixes#904
## Changes
- Introduce `GroupedConsoleFormatter` subclass that extends
`ConsoleFormatter` and overrides `toString()` / `toJSON()` for
count-aware formatting
- Add `ConsoleFormatter.groupConsecutive()` static method that groups
consecutive messages with the same type, text, and argument count
- Apply grouping **before pagination** so grouped counts are accurate
and page sizes reflect the collapsed view
- Add unit tests for grouping logic, string formatting, and JSON output
## Key design decisions
- **`GroupedConsoleFormatter` subclass**: Keeps the existing formatter
interface clean — no new methods added to `ConsoleFormatter`.
`ConsoleFormatter` and `GroupedConsoleFormatter` are interchangeable via
the same interface.
- **Grouping before pagination** (not at format time): This was the
feedback on #963 and #1025 — grouping at format time breaks pagination
counts. This implementation groups in `McpResponse` before calling
`paginate()`.
- **No `lastId`**: Since grouped messages are truly identical, only the
first message's ID is needed.
- **`argCount` matching**: Prevents false grouping of messages with the
same text but different argument counts.
## Output example
```
msgid=1 [log] hello world (1 args) [5 times]
```
## Testing
- Unit tests in `tests/formatters/ConsoleFormatterGrouping.test.ts`
- Manual verification: identical messages (×5), mixed pattern (A,A,B,A,A
→ A×2, B×1, A×2)
Improvements for handling in-page tool responses. In order to
successfully pass an in-page tool response from the page context to the
MCP server, the response needs to be serializable. The code walks the
response object an performs the following changes:
- DOM elements are stashed onto the window object and replaced with an
ID. On the MCP server side this ID is used to map back to the
corresponding UID in the page snapshot generated from the accessibility
tree.
- Circular references are replaced with a string.
- Class instances (which can be complex or non-serializable) are
replaced with a string.
- Functions are replaced with a string.
If the in-page tool response contains DOM elements which are not part of
the page snapshot, a new snapshot is created add the missing elements
are added explicitly.
This refactors the code to extract the Id logic from the PageCollector
and provide it in the heapsnapshot.
We need to use an UID as we need the internal ClassKey to query the
heapsnapshot, but that is a strange string (usually looking like
`,ClassName`) which may get the LLM confused as we use comma separated
output.
This PR adds a CLI flag to enable redacting network headers in the same
way they are redacted in DevTools. Note that sometimes it might prevent
the agent from properly analysing network issues. Pass
`--redact-headers=false` to revert to the previous behavior.
LLM are trained to know the common status code so we can rely on that
when displaying the status code and fall back to text in case of errors
or pending reponse.
This PR wires up the default DevTools ignore listing to hide frames from
third party scripts in stack traces. By default, we ignore:
* Scripts with `/node_modules/` in their URL or where the URL starts
with `node:/`
* Scripts that are marked as ignore listed in the source map
* Content scripts from extensions
The PR adds both, unit tests and an e2e test. This is because we allow
the unit test to pass a mocked ignore checker, where-as the e2e uses the
real DevTools one.
This PR refactors the stack trace formatting logic a bit: We split the
`formatStackTrace` function into 2: One for the top-level stack trace
and the rest for the recursive ones. We also return lines separately so
we can slice the result later.
This PR implements formatting for `SymbolizedError` objects that are
part of "resolved arguments". A follow-up PR will handle the actual
conversion of `Error` remote objects to `SymbolizedError`s.
The "SymbolizedError" class represents a fully resolved error: The stack
trace is fully resolved and (in the future), the full `Error.cause`
chain is also fully resolved.
We'll use the `SymbolizedError` for both "uncaught exceptions" as well
as when logging `Error` objects to the console (e.g. `console.log(new
Error())`. This means the `resolvedArgs` array in `ConsoleFormatter`
will contain one `SymbolizedError` instance for every `Error` object
logged.
The idea is to turn formatters into instances and support both text and
JSON formatting. The structured content is output if the experimental
structured content flag is passed. For now, only the snapshots are
returned in a structured way.
Refs https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/689
If you manually inspect a network request in the DevTools UI, the Chrome
DevTools MCP server is now able to detect it and allow you to refer to
the selected request. The approach was tested in a couple of models.
In the list of the network requests we have a short indication about
which request is selected in DevTools UI. We refer to the DevTools as
"DevTools UI" to disambiguate from the DevTools MCP server.