## Summary
Adds optional `pageId` routing to page-scoped tools, gated behind
`--experimental-page-id-routing`. When enabled, multi-agent callers can
target a specific page without relying on global selection state. Fully
backward-compatible: without the flag, behavior is unchanged.
### Key changes
- **`pageScoped` annotation**: tools declare `pageScoped: true`; the
server merges `pageId` into their schema at registration time (when the
flag is on)
- **`McpPage` wrapper**: consolidates per-page state (numeric id,
isolated context name, focus tracking) into a single class
- **Request-scoped page routing**: `resolvePageById()` resolves the
target page, `setRequestPage()` threads it through the handler so tools
like `getSelectedPage()` see the right page
- **`assertPageIsFocused`**: keyboard/input tools validate that the
target page holds browser focus, returning an actionable error ("call
select_page first") instead of silently dispatching to the wrong page
- **`--experimental-page-id-routing` CLI flag** (hidden): gates schema
injection and request-scoped routing so the feature can be tested before
graduating
- **Eval scenarios**: `page_id_routing_test` and
`page_focus_keyboard_test` with `serverArgs` support in the eval harness
Addresses #1019
Fixes#1029
## Summary
- Use `pathToFileURL()` from `node:url` for dynamic imports in
`eval_gemini.ts` to fix `ERR_UNSUPPORTED_ESM_URL_SCHEME` on Windows -
converts absolute paths like `D:\projects\...\console_test.ts` to proper
`file:///D:/projects/.../console_test.ts` URLs that the Node.js ESM
loader accepts on all platforms
- Move `CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS=true` from Unix-only
shell env var prefix in `package.json` into `eval_gemini.ts` code,
matching the pattern already used in `scripts/test.mjs`
- Add `.gitattributes` with `* text=auto eol=lf` to enforce LF line
endings on checkout - Windows contributors cloning the repo would
otherwise get CRLF endings, which conflicts with the project's Prettier
`endOfLine: 'lf'` config and causes spurious diffs or formatting
failures
## Summary
Adds storage-isolated browser contexts via an optional `isolatedContext`
parameter on the `new_page` tool, following the simplified design
proposed by @OrKoN in #926.
Pages created with the same `isolatedContext` name share cookies,
localStorage, and storage. Pages in different isolated contexts (or the
default context) are fully isolated — ideal for testing multi-user
real-time features like chat, notifications, or collaborative editing.
## Changes
### `new_page` tool
- New optional `isolatedContext: string` parameter
- If specified, creates/reuses a named `BrowserContext` and opens a page
in it
- If omitted, uses the default browser context (existing behavior
unchanged)
### `McpContext`
- `#isolatedContexts` Map: LLM-provided names → Puppeteer
`BrowserContext` instances
- `#pageToIsolatedContextName` WeakMap: GC-safe page → context name
reverse lookup
- Auto-discovery: externally created browser contexts get
`isolated-context-1`, `isolated-context-2`, etc.
- `getIsolatedContextName(page)`: returns the isolated context name for
a page (used by response formatting)
- `page.browserContext()` used for context membership detection (no
custom target event forwarding needed)
- No context cleanup in `dispose()` or `closePage()` — either the entire
browser is closed or we disconnect without destroying state
### `McpResponse`
- Page list includes `isolatedContext=${name}` labels (both text and
structured JSON output)
### `ToolDefinition`
- `Context` interface extended with `getIsolatedContextName(page)`
method
## What's NOT included (by design)
- **No `TargetEventEmitter`**: Puppeteer forwards target events from
`BrowserContext` → `Browser` internally
- **No context cleanup**: Browser contexts are not closed on `dispose()`
or page close, per maintainer guidance
- **No `about:blank` cleanup**: Default context and isolated contexts
coexist side-by-side
## Example
```
> new_page url="https://app.example.com/chat" isolatedContext="userA"
> new_page url="https://app.example.com/chat" isolatedContext="userB"
> list_pages
Page 1: [app.example.com/chat] isolatedContext=userA
Page 2: [app.example.com/chat] isolatedContext=userB [selected]
```
Pages in different isolated contexts have fully independent cookies,
localStorage, IndexedDB, and WebSocket connections.
## Tests
- 6 new tests covering `isolatedContext` feature in
`tests/tools/pages.test.ts`
- All existing tests pass (333+)
- Zero type errors, lint clean
Closes#926
---------
Co-authored-by: Alex Rudenko <alexrudenko@chromium.org>
We should make sure there is always an empty line after license notice,
otherwise it gets dropped during build if it happens to be before import
statement.
---------
Co-authored-by: Piotr Paulski <piotrpaulski@chromium.org>
This PR adds a tool based on the node test runner that runs a loop to
see what tools a model chooses given a prompt. The expectations are
encoding for each prompt. Run `npm run eval` to get results. Currently,
only Gemini and needs and API key.
The `self.Protocol` assignment and register call have moved to the
InspectorBackend constructor. The `self.Protocol` assignment now use
`globalThis`. The register call is necessary to make DevTools foundation
work.
A follow-up PR will add a single instance of the `UniverseManager` to
`McpContext` similarly to the *Collectors. We don't do that in this PR
yet, as we need to wait for some chrome-devtools-frontend changes to
roll.
This PR introduces a feature to disable some of the MCP server tool
categories to tailor it to specific use cases.
For example,
```
npx chrome-devtools-mcp@latest --no-category-emulation Disable tools in the emulation category
npx chrome-devtools-mcp@latest --no-category-performance Disable tools in the performance category
npx chrome-devtools-mcp@latest --no-category-network Disable tools in the network category
```
This fixes the ImageDelivery insight which throws when we try to
generate it
because of its dependency on core/i18n. The long term fix here is to
remove the reliance of models/trace on the core/i18n package, or at
least dependency inject it, but this is a quick fix to at least make
this insight available.
The irony is that we don't even use the string in the AI output; but if
we don't have the method available we cannot generate the insight.
This fixes part of #124 by making this insight not error. The other
required work for that bug is to deal with Insights that do error. That
work is done in DevTools, but we need to ship & roll a new version into
the MCP server.