Both `chrome-devtools` and `chrome-devtools-mcp` now log a notification
when a newer version is detected to be available.
This detection is implemented as follows:
1. Read the latest version from a local 24-hour cache
(`~/.cache/chrome-devtools-mcp/latest.json`).
2. If the cache is stale or missing, spawn a detached background process
to fetch the latest version from the npm registry and update the cache
file.
DOM elements are non-serializable and therefore cannot be directly sent
between the inspected page and the MCP server. JSONSchema also has no
native type for DOM elements.
If an in-page tool expects a DOM element as an input parameter, it
should specify this in its input schema by adding `'x-mcp-type':
'HTMLElement'` to the object it expects to be a DOM element.
The MCP server internally refers to DOM elements by a UID (UIDs are
assigned when generating a page snapshot which is based on the page's
accessibility tree).
This change provides the mapping between DOM element and UID in both
directions:
1) The tool's input schema is rewritten internally, adding a required
UID attribute to objects with `'x-mcp-type': 'HTMLElement'`. This allows
the MCP server to call the in-page tool with UIDs where the tool expects
DOM elements.
2) In the page context, the UIDs are replaced with the corresponding DOM
elements, before the actual in-page tool is called. This means that the
in-page tool receives DOM elements as parameters where it expects them.
With this change new tools are added to the very back of the json file.
Any removed tools will receive a isDeprecated flag in the existing
entry. And the same with tool arguments.
This adds a script that generates a json file that summarizes all tool
calls and arguments for each.
- The arguments run through the blocklist filtering so arguments
containing high entropy ids are filtered out (e.g. "uid", "msgid" etc).
- It uses existing functions from clearcut logger module to transform
the arg name and value (i.e. take the length of the string, take the
size of the array, and rename the string to be "string_length", and
array "array_count" etc).
- These functions from the clearcut logger module will be later used to
sanitize the params as we start to log them.
This doesn't include the append only / deprecation logic just yet (i.e.
it's doesn't handle the case when new tools are added / removed, or
arguments of the existing tools are modified). This will be added in
following PRs.
The parent PR is #1250.
This moves the stored `ToolGroup` from `McpContext` to `McpPage`, where
per-page state should be stored in order for pageId-based routing to
work correctly.
When appending the list of in-page tools to a response, the list now
corresponds to the response's `McpPage`, and only falls back to the
selected `McpPage` for tools which are not page-specific.
---------
Co-authored-by: browser-automation-bot <133232582+browser-automation-bot@users.noreply.github.com>
Co-authored-by: Alex Rudenko <alexrudenko@chromium.org>
Co-authored-by: Tolgahan Demirbaş <49946947+bcfmtolgahan@users.noreply.github.com>
Splits out from #1244 per review feedback.
'waitForEventsAfterAction' previously lived in 'McpContext' and always
used the selected page's CPU/network throttling settings. With pageId
routing, a tool can target a different page than the selected one,
meaning wrong throttling multipliers were applied.
Moving the method to 'McpPage' fixes this: each tool now calls
'page.waitForEventsAfterAction(...)' and gets the correct page's
emulation settings.
'getNetworkMultiplierFromString' is extracted to 'WaitForHelper.ts' to
avoid a circular import (McpContext → McpPage already exists).
Unblocks #1777.
## Summary
Fix `chrome-devtools start` so it no longer implicitly enables
`isolated` when `--userDataDir` is provided.
Previously, the CLI wrapper always defaulted `isolated` to `true` for
`start`, which caused `userDataDir` and `isolated` to conflict even when
the user only specified `--userDataDir`. This made it impossible to
start the CLI daemon against a persistent browser profile.
## Changes
- Update `chrome-devtools start` default handling in
`src/bin/chrome-devtools.ts`
- only default `isolated=true` when `userDataDir` is not set
- Clarify the `isolated` CLI description to document the conditional
default
- Update `docs/cli.md` to reflect that:
- `headless` is enabled by default
- `isolated` is enabled by default unless `--userDataDir` is provided
- Fix a small error message typo
## Why
This matches the intended semantics of the flags:
- `--isolated` means use a temporary user data dir
- `--userDataDir` means use a persistent, explicit user data dir
If the user passes `--userDataDir`, the CLI should not also implicitly
enable `isolated`.
## Testing
- Ran:
- `npm test -- tests/cli.test.ts`
- `npm test -- tests/e2e/chrome-devtools.test.ts`
Added an e2e regression test in `tests/e2e/chrome-devtools.test.ts` to
verify that:
- `chrome-devtools start --userDataDir <temp dir>` succeeds
- the CLI no longer fails with `Arguments userDataDir and isolated are
mutually exclusive`
- the daemon starts successfully when `userDataDir` is provided
This allows the MCP server to call the in-page tools provided by the
inspected page.
Handling of (non-serializable) DOM elements as tool parameters or tool
output will be added in follow-ups.
This adds functions to sanitize the tool call parameters. They are not
called as of now since we don't have server side changes landed yet to
support these.
There is no mechanism which would allow a page to push an updated list
of in-page tools to the MCP server. The next best thing I can think of
is to append an updated list of in-page tools to the response for each
tool call related to page navigation (navigate_page, list_pages,
select_page, close_page, new_page).
This adds a `list_in_page_tools` MCP tool. When called, it dispatches a
`devtoolstooldiscovery` event on the active page. The page announces its
exposing tools by calling the event's `respondWith` method, which causes
the exposed tools to be stashed on the page's `window` object. This list
of in-page tools is then appended to the `list_in_page_tools` response.
Calling the exposed in-page tools from the MCP server will be handled in
a follow-up.
## Problem
After closing the currently selected page, calling `list_pages` throws
an error:
```
The selected page has been closed. Call list_pages to see open pages.
```
This creates a deadlock: the error message tells users to call
`list_pages`, but `list_pages` itself throws the same error.
## Root Cause
`list_pages` is defined with `definePageTool`, which marks it as
`pageScoped: true`. The handler dispatch in `index.ts` (line ~186) calls
`context.getSelectedMcpPage()` for all page-scoped tools **before**
invoking the handler. When the selected page is closed,
`getSelectedPptrPage()` throws.
However, `list_pages` doesn't actually use the `page` parameter — its
handler only calls `response.setIncludePages(true)`.
## Fix
Change `list_pages` from `definePageTool` to `defineTool`. This bypasses
the page-scoped check while preserving all existing behavior since the
handler never used the page reference.
## Testing
Reproduced the issue following the steps in #1138:
1. Call `list_pages` → returns page list ✅
2. Close the selected page
3. Call `list_pages` → now returns updated page list instead of throwing
✅Fixes#1138
---------
Co-authored-by: Piotr Paulski <piotrpaulski@chromium.org>
The changes in the PR intercepts the name of the MCP client right after
server initialization, coverts it into one of the existing enum values,
and append it with the telemetry request.
Note that the client name is unset if there is no client connected yet.
This means the server_start event won't have a client name.
Tested with gemini-cli.
---------
Co-authored-by: Yulun Zeng <yulunz@chromium.org>
This should reduce the flakiness and issue with finding open DevTools
tabs for the corresponding pages.
Currently we do multiple loops over all the targets, so this should have
a nice performance improvement when multiple pages are opened.
With a fallback for older versions.
Issue #1192
## Problem
In `--autoConnect` mode, `chrome-devtools-mcp` experiences a severe
memory leak (~13 MB/min) when Chrome is actively used. This eventually
leads to OOM crashes or kernel panics.
The root cause is in `src/PageCollector.ts`:
1. `NetworkCollector` overrides `splitAfterNavigation` but forgets to
call `navigations.splice(this.#maxNavigationSaved)`, causing the array
of navigations to grow infinitely.
2. Even within a single navigation (e.g., in long-lived SPA
applications), the `navigations[0]` array grows infinitely because there
is no limit on the number of items collected per navigation. Since
`HTTPRequest` objects are heavy, this quickly exhausts memory.
## Solution
1. Changed `#maxNavigationSaved` to `protected maxNavigationSaved` so
subclasses can access it.
2. Added `navigations.splice(this.maxNavigationSaved)` to
`NetworkCollector.splitAfterNavigation` to ensure old navigations are
properly discarded.
3. Introduced `protected maxItemsPerNavigation = 5000` to limit the
number of items stored per navigation. When the limit is reached, the
oldest item is removed (`shift()`), preventing unbounded memory growth
in SPAs.
Tested locally by running `npm run build` and verifying the fix.
---------
Co-authored-by: Wolfgang Beyer <woolfi.b@gmail.com>
This should reduce the flakiness and issue with finding open DevTools
tabs for the corresponding pages.
Currently we do multiple loops over all the targets, so this should have
a nice performance improvement when multiple pages are opened.
- `chrome-devtools-mcp.js` is the `npx chrome-devtools-mcp`
- `chrome-devtools.js` is the new CLI
- `-cli-options.js` is the corresponding options
- all these files are in the bin folder to indicate they are executable
This PR implements the ability to trigger an extension action by passing
it an extension id.
It uses puppeteer internals and the canary chrome version in tests since
the TriggerAction CDP command is still not available in the current
stable release.
- reduces the token usage.
- makes emulation and script compatible with CLI.
- documents .nullable() and .object() restrictions for the future.
- the emulation tools do not have nullable anymore and undefined would
clear the emulation instead. The model is thus required to provide all
emulation settings at once.
Closes https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/918
- renamed getSelectedPage to getSelectedPptrPage
- removes getSelectedPptrPage from tool interfaces
- moves dialog handling to McpPage
- makes responses to be optionally McpPage-scoped