This fixes the casing of the tool call params. We don't need any server
side fix since they are already converted to snake case in the proto
definition. This is needed nevertheless since the sanitizeParams()
function will be called when we log the params (see the next PR: #1863
1863).
## Summary
Fixes a double space in the navigate error message in
`src/tools/pages.ts`.
## Problem
Line 225 contains an error message with double space:
```
'Unable to navigate in the selected page: ...'
```
## Fix
Changed to single space:
```
'Unable to navigate in the selected page: ...'
```
Signed-off-by: Cocoon-Break
<54054995+kuishou68@users.noreply.github.com>
Signed-off-by: Cocoon-Break <54054995+kuishou68@users.noreply.github.com>
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.
## Summary
This PR fixes several documentation and skill-reference issues across
the repo to improve accuracy and reduce confusion in implementation and
troubleshooting workflows.
## Changes
- corrected CLI examples in skill docs
- fixed the documented argument order for `performance_analyze_insight`
- updated the memory leak fallback script path
- corrected generated tool reference wording by updating the
source-of-truth tool descriptions
---------
Co-authored-by: ojonesjr <50652264+ojonesjr@users.noreply.github.com>
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.
We shouldn't keep having enum type in the JSON at all since we are
checking the permitted values to deduce the type already. This fixes
input to the type deducing logic.
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.
Specify `--experimental-vision` argument for `chrome-devtools-mcp` to
enable additional tools for vision models. Currently, only
`click_at(x,y)` tool is supported. Usually you need a specialized model
that can provide accurate coordinates based on the screenshots.
Closes https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/403
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
- Add `@local/no-zod-nullable-object` ESLint rule that disallows
`.nullable()` and `.object()` usage in tool schema files
(`src/tools/**/*.ts`)
- Fix the existing `zod.object()` violation in `fill_form` by converting
the elements array from objects to `"uid=value"` formatted strings
- Remove the TODO from CONTRIBUTING.md and link the rule name to the
documented restriction
## Context
Per the [PR #1073
review](https://github.com/ChromeDevTools/chrome-devtools-mcp/pull/1073#discussion_r2872188785)
and the CONTRIBUTING.md guidelines, tool schemas should not use
`.nullable()` or `.object()` types. Complex objects should be
represented as short formatted strings instead.
The rule catches:
- `zod.object({...})` / `z.object({...})` - flags direct zod object
schema usage
- `.nullable()` - flags nullable schema usage on any expression
Closes#1076
## Test plan
- [ ] `npx eslint src/tools/` passes with no violations
- [ ] Creating a test file with `zod.object()` or `.nullable()` in
`src/tools/` triggers the rule
- [ ] TypeScript compilation passes (`npx tsc --noEmit`)
- [ ] Existing `fill_form` test updated to use new string format
---------
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Alex Rudenko <OrKoN@users.noreply.github.com>
Co-authored-by: Nikolay Vitkov <34244704+Lightning00Blade@users.noreply.github.com>
Co-authored-by: Alex Rudenko <alexrudenko@chromium.org>
## 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