Skip to content

Commit d365c4e

Browse files
committed
Merge remote-tracking branch 'origin/main' into feat/semantic-filtering
2 parents 30955ce + 41ff9bf commit d365c4e

15 files changed

Lines changed: 205 additions & 38 deletions

.github/ISSUE_TEMPLATE/02-feature.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ description: Suggest an idea for for chrome-devtools-mcp
33
title: '<short description of the feature request>'
44
type: 'Feature'
55
labels:
6-
- enhancement
6+
- feature
77
body:
88
- id: description
99
type: textarea

.release-please-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
".": "0.20.1"
2+
".": "0.20.2"
33
}

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Changelog
22

3+
## [0.20.2](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.20.1...chrome-devtools-mcp-v0.20.2) (2026-03-18)
4+
5+
6+
### 📄 Documentation
7+
8+
* add troubleshooting for Claude Code plugin HTTPS clone failures ([#1195](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1195)) ([d082ca4](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/d082ca4ecd35a023d09f9c1ff949d5fb0c3fb069))
9+
310
## [0.20.1](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.20.0...chrome-devtools-mcp-v0.20.1) (2026-03-16)
411

512

package-lock.json

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "chrome-devtools-mcp",
3-
"version": "0.20.1",
3+
"version": "0.20.2",
44
"description": "MCP server for Chrome DevTools",
55
"type": "module",
66
"bin": {
@@ -59,7 +59,7 @@
5959
"@types/yargs": "^17.0.33",
6060
"@typescript-eslint/eslint-plugin": "^8.43.0",
6161
"@typescript-eslint/parser": "^8.43.0",
62-
"chrome-devtools-frontend": "1.0.1596260",
62+
"chrome-devtools-frontend": "1.0.1599001",
6363
"core-js": "3.48.0",
6464
"debug": "4.4.3",
6565
"eslint": "^9.35.0",

server.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@
77
"url": "https://github.com/ChromeDevTools/chrome-devtools-mcp",
88
"source": "github"
99
},
10-
"version": "0.20.1",
10+
"version": "0.20.2",
1111
"packages": [
1212
{
1313
"registryType": "npm",
1414
"registryBaseUrl": "https://registry.npmjs.org",
1515
"identifier": "chrome-devtools-mcp",
16-
"version": "0.20.1",
16+
"version": "0.20.2",
1717
"transport": {
1818
"type": "stdio"
1919
},

src/DevtoolsUtils.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,61 @@ import type {
1515
Target as PuppeteerTarget,
1616
} from './third_party/index.js';
1717

18+
export function extractUrlLikeFromDevToolsTitle(
19+
title: string,
20+
): string | undefined {
21+
const match = title.match(new RegExp(`DevTools - (.*)`));
22+
return match?.[1] ?? undefined;
23+
}
24+
25+
export function urlsEqual(url1: string, url2: string): boolean {
26+
const normalizedUrl1 = normalizeUrl(url1);
27+
const normalizedUrl2 = normalizeUrl(url2);
28+
return normalizedUrl1 === normalizedUrl2;
29+
}
30+
31+
/**
32+
* For the sake of the MCP server, when we determine if two URLs are equal we
33+
* remove some parts:
34+
*
35+
* 1. We do not care about the protocol.
36+
* 2. We do not care about trailing slashes.
37+
* 3. We do not care about "www".
38+
* 4. We ignore the hash parts.
39+
*
40+
* For example, if the user types "record a trace on foo.com", we would want to
41+
* match a tab in the connected Chrome instance that is showing "www.foo.com/"
42+
*/
43+
function normalizeUrl(url: string): string {
44+
let result = url.trim();
45+
46+
// Remove protocols
47+
if (result.startsWith('https://')) {
48+
result = result.slice(8);
49+
} else if (result.startsWith('http://')) {
50+
result = result.slice(7);
51+
}
52+
53+
// Remove 'www.'. This ensures that we find the right URL regardless of if the user adds `www` or not.
54+
if (result.startsWith('www.')) {
55+
result = result.slice(4);
56+
}
57+
58+
// We use target URLs to locate DevTools but those often do
59+
// no include hash.
60+
const hashIdx = result.lastIndexOf('#');
61+
if (hashIdx !== -1) {
62+
result = result.slice(0, hashIdx);
63+
}
64+
65+
// Remove trailing slash
66+
if (result.endsWith('/')) {
67+
result = result.slice(0, -1);
68+
}
69+
70+
return result;
71+
}
72+
1873
/**
1974
* A mock implementation of an issues manager that only implements the methods
2075
* that are actually used by the IssuesAggregator

src/McpContext.ts

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@ import fs from 'node:fs/promises';
88
import path from 'node:path';
99

1010
import type {TargetUniverse} from './DevtoolsUtils.js';
11-
import {UniverseManager} from './DevtoolsUtils.js';
11+
import {
12+
extractUrlLikeFromDevToolsTitle,
13+
UniverseManager,
14+
urlsEqual,
15+
} from './DevtoolsUtils.js';
1216
import {McpPage} from './McpPage.js';
1317
import type {ListenerMap, UncaughtError} from './PageCollector.js';
1418
import {NetworkCollector, ConsoleCollector} from './PageCollector.js';
@@ -649,21 +653,37 @@ export class McpContext implements Context {
649653
async detectOpenDevToolsWindows() {
650654
this.logger('Detecting open DevTools windows');
651655
const {pages} = await this.#getAllPages();
652-
653-
await Promise.all(
654-
pages.map(async page => {
655-
const mcpPage = this.#mcpPages.get(page);
656-
if (!mcpPage) {
657-
return;
658-
}
659-
660-
if (await page.hasDevTools()) {
661-
mcpPage.devToolsPage = await page.openDevTools();
662-
} else {
663-
mcpPage.devToolsPage = undefined;
656+
// Clear all devToolsPage references before re-detecting.
657+
for (const mcpPage of this.#mcpPages.values()) {
658+
mcpPage.devToolsPage = undefined;
659+
}
660+
for (const devToolsPage of pages) {
661+
if (devToolsPage.url().startsWith('devtools://')) {
662+
try {
663+
this.logger('Calling getTargetInfo for ' + devToolsPage.url());
664+
const data = await devToolsPage
665+
// @ts-expect-error no types for _client().
666+
._client()
667+
.send('Target.getTargetInfo');
668+
const devtoolsPageTitle = data.targetInfo.title;
669+
const urlLike = extractUrlLikeFromDevToolsTitle(devtoolsPageTitle);
670+
if (!urlLike) {
671+
continue;
672+
}
673+
// TODO: lookup without a loop.
674+
for (const page of this.#pages) {
675+
if (urlsEqual(page.url(), urlLike)) {
676+
const mcpPage = this.#mcpPages.get(page);
677+
if (mcpPage) {
678+
mcpPage.devToolsPage = devToolsPage;
679+
}
680+
}
681+
}
682+
} catch (error) {
683+
this.logger('Issue occurred while trying to find DevTools', error);
664684
}
665-
}),
666-
);
685+
}
686+
}
667687
}
668688

669689
getExtensionServiceWorkers(): ExtensionServiceWorker[] {

src/bin/chrome-devtools-mcp-cli-options.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export const cliOptions = {
1212
type: 'boolean',
1313
description:
1414
'If specified, automatically connects to a browser (Chrome 144+) running locally from the user data directory identified by the channel param (default channel is stable). Requires the remoted debugging server to be started in the Chrome instance via chrome://inspect/#remote-debugging.',
15-
conflicts: ['isolated', 'executablePath'],
15+
conflicts: ['isolated', 'executablePath', 'categoryExtensions'],
1616
default: false,
1717
coerce: (value: boolean | undefined) => {
1818
if (!value) {
@@ -26,7 +26,7 @@ export const cliOptions = {
2626
description:
2727
'Connect to a running, debuggable Chrome instance (e.g. `http://127.0.0.1:9222`). For more details see: https://github.com/ChromeDevTools/chrome-devtools-mcp#connecting-to-a-running-chrome-instance.',
2828
alias: 'u',
29-
conflicts: 'wsEndpoint',
29+
conflicts: ['wsEndpoint', 'categoryExtensions'],
3030
coerce: (url: string | undefined) => {
3131
if (!url) {
3232
return;
@@ -44,7 +44,7 @@ export const cliOptions = {
4444
description:
4545
'WebSocket endpoint to connect to a running Chrome instance (e.g., ws://127.0.0.1:9222/devtools/browser/<id>). Alternative to --browserUrl.',
4646
alias: 'w',
47-
conflicts: 'browserUrl',
47+
conflicts: ['browserUrl', 'categoryExtensions'],
4848
coerce: (url: string | undefined) => {
4949
if (!url) {
5050
return;
@@ -211,9 +211,10 @@ export const cliOptions = {
211211
},
212212
categoryExtensions: {
213213
type: 'boolean',
214-
default: false,
215214
hidden: true,
216-
describe: 'Set to false to exclude tools related to extensions.',
215+
conflicts: ['browserUrl', 'autoConnect', 'wsEndpoint'],
216+
describe:
217+
'Set to true to include tools related to extensions. Note: This feature is only supported with a pipe connection. autoConnect is not supported.',
217218
},
218219
performanceCrux: {
219220
type: 'boolean',

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ export async function createMcpServer(
129129
}
130130
if (
131131
tool.annotations.category === ToolCategory.EXTENSIONS &&
132-
serverArgs.categoryExtensions === false
132+
!serverArgs.categoryExtensions
133133
) {
134134
return;
135135
}

0 commit comments

Comments
 (0)