Skip to content

Commit f61c09f

Browse files
authored
Merge branch 'ChromeDevTools:main' into main
2 parents 482329b + 6a3ca98 commit f61c09f

12 files changed

Lines changed: 390 additions & 35 deletions

.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.16.0"
2+
".": "0.17.0"
33
}

CHANGELOG.md

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

3+
## [0.17.0](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.16.0...chrome-devtools-mcp-v0.17.0) (2026-02-10)
4+
5+
6+
### 🎉 Features
7+
8+
* include Error.cause chain for uncaught errors and logged Errors ([#906](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/906)) ([05b01ec](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/05b01ecaba47cf1ce38564636663222c9cab46de))
9+
* Integrate CrUX data into performance trace summaries ([#733](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/733)) ([b747f9d](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/b747f9d74a12d2119b6531476b2f88ab66be0ff8))
10+
* show message and stack trace in details when console.log'ging Error objects ([#902](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/902)) ([ffa00da](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/ffa00dab1b65b2eac8db215e0289317b8ed9b725))
11+
12+
13+
### 🛠️ Fixes
14+
15+
* console formatter hides frames from ignored scripts ([#927](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/927)) ([8e2380b](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/8e2380b434d9659ffa8a7043d2589261772fa04f))
16+
* limit stack traces to 50 lines ([#923](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/923)) ([caea23a](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/caea23a7cf33c87cd4ce426eb2a10724aba3cc71))
17+
18+
19+
### 📄 Documentation
20+
21+
* add macOS Web Bluetooth troubleshooting note ([#930](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/930)) ([3c9528b](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/3c9528b43d9bbff166fcfcfee321149ff44ddd21)), closes [#917](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/917)
22+
323
## [0.16.0](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.15.1...chrome-devtools-mcp-v0.16.0) (2026-02-04)
424

525

docs/troubleshooting.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,16 @@ This indicates that the browser could not be started. Make sure that no Chrome
5555
instances are running or close them. Make sure you have the latest stable Chrome
5656
installed and that [your system is able to run Chrome](https://support.google.com/chrome/a/answer/7100626?hl=en).
5757

58+
### Chrome crashes on macOS when opening Web Bluetooth
59+
60+
On macOS, Chrome launched by an MCP host app may crash with a macOS privacy
61+
permission violation (TCC, "Transparency, Consent, and Control") when a native
62+
Bluetooth chooser opens.
63+
64+
Grant Bluetooth permission to the MCP host app in
65+
`System Settings > Privacy & Security > Bluetooth`, then restart the MCP host
66+
app and Chrome MCP session.
67+
5868
### Remote debugging between virtual machine (VM) and host fails
5969

6070
When connecting DevTools inside a VM to Chrome running on the host, any domain is rejected by Chrome because of host header validation. Tunneling the port over SSH bypasses this restriction. In the VM, run:

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.16.0",
3+
"version": "0.17.0",
44
"description": "MCP server for Chrome DevTools",
55
"type": "module",
66
"bin": "./build/src/index.js",
@@ -54,7 +54,7 @@
5454
"@types/yargs": "^17.0.33",
5555
"@typescript-eslint/eslint-plugin": "^8.43.0",
5656
"@typescript-eslint/parser": "^8.43.0",
57-
"chrome-devtools-frontend": "1.0.1579812",
57+
"chrome-devtools-frontend": "1.0.1581449",
5858
"core-js": "3.48.0",
5959
"debug": "4.4.3",
6060
"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.16.0",
10+
"version": "0.17.0",
1111
"packages": [
1212
{
1313
"registryType": "npm",
1414
"registryBaseUrl": "https://registry.npmjs.org",
1515
"identifier": "chrome-devtools-mcp",
16-
"version": "0.16.0",
16+
"version": "0.17.0",
1717
"transport": {
1818
"type": "stdio"
1919
},

src/formatters/ConsoleFormatter.ts

Lines changed: 80 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
SymbolizedError,
1111
} from '../DevtoolsUtils.js';
1212
import {UncaughtError} from '../PageCollector.js';
13-
import type * as DevTools from '../third_party/index.js';
13+
import * as DevTools from '../third_party/index.js';
1414
import type {ConsoleMessage} from '../third_party/index.js';
1515

1616
export interface ConsoleFormatterOptions {
@@ -20,9 +20,16 @@ export interface ConsoleFormatterOptions {
2020
resolvedArgsForTesting?: unknown[];
2121
resolvedStackTraceForTesting?: DevTools.DevTools.StackTrace.StackTrace.StackTrace;
2222
resolvedCauseForTesting?: SymbolizedError;
23+
isIgnoredForTesting?: IgnoreCheck;
2324
}
2425

26+
export type IgnoreCheck = (
27+
frame: DevTools.DevTools.StackTrace.StackTrace.Frame,
28+
) => boolean;
29+
2530
export class ConsoleFormatter {
31+
static readonly #STACK_TRACE_MAX_LINES = 50;
32+
2633
readonly #id: number;
2734
readonly #type: string;
2835
readonly #text: string;
@@ -33,6 +40,8 @@ export class ConsoleFormatter {
3340
readonly #stack?: DevTools.DevTools.StackTrace.StackTrace.StackTrace;
3441
readonly #cause?: SymbolizedError;
3542

43+
readonly #isIgnored: IgnoreCheck;
44+
3645
private constructor(params: {
3746
id: number;
3847
type: string;
@@ -41,6 +50,7 @@ export class ConsoleFormatter {
4150
resolvedArgs?: unknown[];
4251
stack?: DevTools.DevTools.StackTrace.StackTrace.StackTrace;
4352
cause?: SymbolizedError;
53+
isIgnored: IgnoreCheck;
4454
}) {
4555
this.#id = params.id;
4656
this.#type = params.type;
@@ -49,12 +59,37 @@ export class ConsoleFormatter {
4959
this.#resolvedArgs = params.resolvedArgs ?? [];
5060
this.#stack = params.stack;
5161
this.#cause = params.cause;
62+
this.#isIgnored = params.isIgnored;
5263
}
5364

5465
static async from(
5566
msg: ConsoleMessage | UncaughtError,
5667
options: ConsoleFormatterOptions,
5768
): Promise<ConsoleFormatter> {
69+
const ignoreListManager = options?.devTools?.universe.context.get(
70+
DevTools.DevTools.IgnoreListManager,
71+
);
72+
const isIgnored: IgnoreCheck =
73+
options.isIgnoredForTesting ||
74+
(frame => {
75+
if (!ignoreListManager) {
76+
return false;
77+
}
78+
if (frame.uiSourceCode) {
79+
return ignoreListManager.isUserOrSourceMapIgnoreListedUISourceCode(
80+
frame.uiSourceCode,
81+
);
82+
}
83+
if (frame.url) {
84+
return ignoreListManager.isUserIgnoreListedURL(
85+
frame.url as Parameters<
86+
DevTools.DevTools.IgnoreListManager['isUserIgnoreListedURL']
87+
>[0],
88+
);
89+
}
90+
return false;
91+
});
92+
5893
if (msg instanceof UncaughtError) {
5994
const error = await SymbolizedError.fromDetails({
6095
devTools: options?.devTools,
@@ -70,6 +105,7 @@ export class ConsoleFormatter {
70105
text: error.message,
71106
stack: error.stackTrace,
72107
cause: error.cause,
108+
isIgnored,
73109
});
74110
}
75111

@@ -118,6 +154,7 @@ export class ConsoleFormatter {
118154
argCount: resolvedArgs.length || msg.args().length,
119155
resolvedArgs,
120156
stack,
157+
isIgnored,
121158
});
122159
}
123160

@@ -134,7 +171,6 @@ export class ConsoleFormatter {
134171
this.#formatArgs(),
135172
this.#formatStackTrace(this.#stack, this.#cause, {
136173
includeHeading: true,
137-
includeNote: true,
138174
}),
139175
].filter(line => !!line);
140176
return result.join('\n');
@@ -158,7 +194,6 @@ export class ConsoleFormatter {
158194
arg.message,
159195
this.#formatStackTrace(arg.stackTrace, arg.cause, {
160196
includeHeading: false,
161-
includeNote: true,
162197
}),
163198
]
164199
.filter(line => !!line)
@@ -186,38 +221,65 @@ export class ConsoleFormatter {
186221
#formatStackTrace(
187222
stackTrace: DevTools.DevTools.StackTrace.StackTrace.StackTrace | undefined,
188223
cause: SymbolizedError | undefined,
189-
opts: {includeHeading: boolean; includeNote: boolean},
224+
opts: {includeHeading: boolean},
190225
): string {
191226
if (!stackTrace) {
192227
return '';
193228
}
194229

230+
const lines = this.#formatStackTraceInner(stackTrace, cause);
231+
const includedLines = lines.slice(
232+
0,
233+
ConsoleFormatter.#STACK_TRACE_MAX_LINES,
234+
);
235+
const reminderCount = lines.length - includedLines.length;
236+
195237
return [
196238
opts.includeHeading ? '### Stack trace' : '',
197-
this.#formatFragment(stackTrace.syncFragment),
198-
...stackTrace.asyncFragments.map(this.#formatAsyncFragment.bind(this)),
199-
this.#formatCause(cause),
200-
opts.includeNote
201-
? 'Note: line and column numbers use 1-based indexing'
202-
: '',
239+
...includedLines,
240+
reminderCount > 0 ? `... and ${reminderCount} more frames` : '',
241+
'Note: line and column numbers use 1-based indexing',
203242
]
204243
.filter(line => !!line)
205244
.join('\n');
206245
}
207246

247+
#formatStackTraceInner(
248+
stackTrace: DevTools.DevTools.StackTrace.StackTrace.StackTrace | undefined,
249+
cause: SymbolizedError | undefined,
250+
): string[] {
251+
if (!stackTrace) {
252+
return [];
253+
}
254+
255+
return [
256+
...this.#formatFragment(stackTrace.syncFragment),
257+
...stackTrace.asyncFragments
258+
.map(this.#formatAsyncFragment.bind(this))
259+
.flat(),
260+
...this.#formatCause(cause),
261+
];
262+
}
263+
208264
#formatFragment(
209265
fragment: DevTools.DevTools.StackTrace.StackTrace.Fragment,
210-
): string {
211-
return fragment.frames.map(this.#formatFrame.bind(this)).join('\n');
266+
): string[] {
267+
const frames = fragment.frames.filter(frame => !this.#isIgnored(frame));
268+
return frames.map(this.#formatFrame.bind(this));
212269
}
213270

214271
#formatAsyncFragment(
215272
fragment: DevTools.DevTools.StackTrace.StackTrace.AsyncFragment,
216-
): string {
273+
): string[] {
274+
const formattedFrames = this.#formatFragment(fragment);
275+
if (formattedFrames.length === 0) {
276+
return [];
277+
}
278+
217279
const separatorLineLength = 40;
218280
const prefix = `--- ${fragment.description || 'async'} `;
219281
const separator = prefix + '-'.repeat(separatorLineLength - prefix.length);
220-
return separator + '\n' + this.#formatFragment(fragment);
282+
return [separator, ...formattedFrames];
221283
}
222284

223285
#formatFrame(frame: DevTools.DevTools.StackTrace.StackTrace.Frame): string {
@@ -231,20 +293,15 @@ export class ConsoleFormatter {
231293
return result;
232294
}
233295

234-
#formatCause(cause: SymbolizedError | undefined): string {
296+
#formatCause(cause: SymbolizedError | undefined): string[] {
235297
if (!cause) {
236-
return '';
298+
return [];
237299
}
238300

239301
return [
240302
`Caused by: ${cause.message}`,
241-
this.#formatStackTrace(cause.stackTrace, cause.cause, {
242-
includeHeading: false,
243-
includeNote: false,
244-
}),
245-
]
246-
.filter(line => !!line)
247-
.join('\n');
303+
...this.#formatStackTraceInner(cause.stackTrace, cause.cause),
304+
];
248305
}
249306

250307
toJSON(): object {

src/main.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import {tools} from './tools/tools.js';
3131

3232
// If moved update release-please config
3333
// x-release-please-start-version
34-
const VERSION = '0.16.0';
34+
const VERSION = '0.17.0';
3535
// x-release-please-end
3636

3737
export const args = parseArguments(VERSION);

0 commit comments

Comments
 (0)