From eec74c2ecdc8df54d5b55e53d21169f0712e6c10 Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Fri, 28 Aug 2026 10:37:32 +0200 Subject: [PATCH] feat: option to disable source maps --- README.md | 5 +++ src/McpContext.ts | 3 ++ src/McpPage.ts | 7 ++++- src/config/mcp-options.ts | 7 +++++ src/devtools/DevtoolsUtils.ts | 16 ++++++++++ src/index.ts | 1 + src/telemetry/flag_usage_metrics.json | 8 +++++ tests/cli.test.ts | 15 +++++++++ tests/devtools/DevtoolsUtils.test.ts | 45 +++++++++++++++++++++++++++ tests/tools/console.test.ts | 33 ++++++++++++++++++++ tests/utils.ts | 2 ++ 11 files changed, 141 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5068429ae..e279b8e82 100644 --- a/README.md +++ b/README.md @@ -768,6 +768,11 @@ The Chrome DevTools MCP server supports the following configuration option: - **Type:** boolean - **Default:** `true` +- **`--sourceMaps`/ `--source-maps`** + Whether to enable source maps in DevTools. Use --no-source-maps to disable. + - **Type:** boolean + - **Default:** `true` + - **`--screenshotFormat`/ `--screenshot-format`** Override the default output format used by take_screenshot when the caller does not specify one. JPEG and WebP are ~3-5x smaller than PNG, which helps reduce context size in AI conversations. Unset preserves the existing default ("png"). - **Type:** string diff --git a/src/McpContext.ts b/src/McpContext.ts index e1c0cab97..9730136a1 100644 --- a/src/McpContext.ts +++ b/src/McpContext.ts @@ -57,6 +57,8 @@ interface McpContextOptions { experimentalIncludeAllPages?: boolean; // Whether CrUX data should be fetched. performanceCrux: boolean; + // Whether source maps are enabled in DevTools. + sourceMaps?: boolean; // The allow list of URL patterns to allow loading resources. allowList?: string[]; // The block list of URL patterns to block loading resources. @@ -556,6 +558,7 @@ export class McpContext implements Context { page.browserContext(), ), navigationTimeout: this.#options.navigationTimeout, + sourceMaps: this.#options.sourceMaps, }); this.#mcpPages.set(page, mcpPage); await mcpPage.init(); diff --git a/src/McpPage.ts b/src/McpPage.ts index ecfcfe182..5ecbbe2d3 100644 --- a/src/McpPage.ts +++ b/src/McpPage.ts @@ -138,6 +138,7 @@ export class McpPage implements ContextPage { #hasNetworkBlockOrAllowlist: boolean; #locatorClass: typeof Locator; #navigationTimeout: number; + #sourceMaps: boolean; constructor( page: Page, @@ -147,11 +148,13 @@ export class McpPage implements ContextPage { locatorClass: typeof Locator; isolatedContextName?: string; navigationTimeout?: number; + sourceMaps?: boolean; }, ) { this.#hasNetworkBlockOrAllowlist = options.hasNetworkBlockOrAllowlist; this.#locatorClass = options.locatorClass; this.#navigationTimeout = options.navigationTimeout ?? NAVIGATION_TIMEOUT; + this.#sourceMaps = options.sourceMaps ?? true; this.pptrPage = page; this.id = id; this.isolatedContextName = options.isolatedContextName; @@ -196,7 +199,9 @@ export class McpPage implements ContextPage { } try { const session = await this.pptrPage.createCDPSession(); - this.#devtoolsUniverse = await createTargetUniverse(session); + this.#devtoolsUniverse = await createTargetUniverse(session, { + sourceMaps: this.#sourceMaps, + }); } catch (e) { logger?.('Failed to initialize DevTools universe', e); } diff --git a/src/config/mcp-options.ts b/src/config/mcp-options.ts index ac5d06c81..ee2144854 100644 --- a/src/config/mcp-options.ts +++ b/src/config/mcp-options.ts @@ -280,6 +280,12 @@ export const mcpOptions = { describe: 'Set to false to opt-out of usage statistics collection. Google collects usage data to improve the tool, handled under the Google Privacy Policy (https://policies.google.com/privacy). This is independent from Chrome browser metrics. Disabled if `CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS` or `CI` env variables are set.', }, + sourceMaps: { + type: 'boolean', + default: true, + describe: + 'Whether to enable source maps in DevTools. Use --no-source-maps to disable.', + }, clearcutEndpoint: { type: 'string', hidden: true, @@ -538,6 +544,7 @@ export function parser( '$0 --no-performance-crux', 'Disable CrUX (field data) integration in performance tools.', ], + ['$0 --no-source-maps', 'Disable source maps in DevTools.'], [ '$0 --slim', 'Only 3 tools: navigation, JavaScript execution and screenshot', diff --git a/src/devtools/DevtoolsUtils.ts b/src/devtools/DevtoolsUtils.ts index 60af36c8a..4195b3e32 100644 --- a/src/devtools/DevtoolsUtils.ts +++ b/src/devtools/DevtoolsUtils.ts @@ -137,8 +137,13 @@ export interface TargetUniverse { session: CDPSession; } +export interface CreateTargetUniverseOptions { + sourceMaps?: boolean; +} + export async function createTargetUniverse( session: CDPSession, + options?: CreateTargetUniverseOptions, ): Promise { const settingStorage = new DevTools.Common.Settings.SettingsStorage({}); const universe = new DevTools.Foundation.Universe.Universe({ @@ -156,6 +161,17 @@ export async function createTargetUniverse( supportsEmulation: false, }); + const sourceMaps = options?.sourceMaps ?? true; + const jsSourceMapsSetting = universe.settings.resolve( + DevTools.SDKSettings.jsSourceMapsEnabledSettingDescriptor, + ); + jsSourceMapsSetting.set(sourceMaps); + + const cssSourceMapsSetting = universe.settings.resolve( + DevTools.SDKSettings.cssSourceMapsEnabledSettingDescriptor, + ); + cssSourceMapsSetting.set(sourceMaps); + const setting = universe.settings.resolve( DevTools.SourceMapManager.lazyLoadingSettingDescriptor, ); diff --git a/src/index.ts b/src/index.ts index ecbd89653..d1c90348a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -175,6 +175,7 @@ export async function createMcpServer( experimentalDevToolsDebugging: devtools, experimentalIncludeAllPages: serverArgs.experimentalIncludeAllPages, performanceCrux: serverArgs.performanceCrux, + sourceMaps: serverArgs.sourceMaps, allowList: allowlist, blocklist: blocklist, allowUnrestrictedPaths: serverArgs.allowUnrestrictedPaths, diff --git a/src/telemetry/flag_usage_metrics.json b/src/telemetry/flag_usage_metrics.json index 89fbb2852..157c58598 100644 --- a/src/telemetry/flag_usage_metrics.json +++ b/src/telemetry/flag_usage_metrics.json @@ -389,5 +389,13 @@ { "name": "page_id_routing", "flagType": "boolean" + }, + { + "name": "source_maps_present", + "flagType": "boolean" + }, + { + "name": "source_maps", + "flagType": "boolean" } ] diff --git a/tests/cli.test.ts b/tests/cli.test.ts index d460ea9c2..6b44c23b8 100644 --- a/tests/cli.test.ts +++ b/tests/cli.test.ts @@ -30,6 +30,7 @@ describe('cli args parsing', () => { memoryDebugging: false, experimentalStructuredContent: false, pageIdRouting: true, + sourceMaps: true, }; it('parses with default args', async () => { @@ -338,4 +339,18 @@ describe('cli args parsing', () => { 'https://b.com/*', ]); }); + + it('parses source-maps flag', async () => { + const defaultParsed = parseArguments(['main.js']); + assert.strictEqual(defaultParsed.sourceMaps, true); + + const disabledArgs = parseArguments(['--no-source-maps']); + assert.strictEqual(disabledArgs.sourceMaps, false); + + const explicitFalseArgs = parseArguments(['--source-maps=false']); + assert.strictEqual(explicitFalseArgs.sourceMaps, false); + + const explicitTrueArgs = parseArguments(['--source-maps=true']); + assert.strictEqual(explicitTrueArgs.sourceMaps, true); + }); }); diff --git a/tests/devtools/DevtoolsUtils.test.ts b/tests/devtools/DevtoolsUtils.test.ts index 9ff88a53e..f225cebbc 100644 --- a/tests/devtools/DevtoolsUtils.test.ts +++ b/tests/devtools/DevtoolsUtils.test.ts @@ -152,6 +152,51 @@ describe('createTargetUniverse', () => { ); }); }); + + it('enables source maps by default', async () => { + await withBrowser(async (browser, page) => { + const targetUniverse = await createTargetUniverse( + await page.createCDPSession(), + ); + assert.ok(targetUniverse); + + assert.strictEqual( + targetUniverse.universe.settings + .resolve(DevTools.SDKSettings.jsSourceMapsEnabledSettingDescriptor) + .get(), + true, + ); + assert.strictEqual( + targetUniverse.universe.settings + .resolve(DevTools.SDKSettings.cssSourceMapsEnabledSettingDescriptor) + .get(), + true, + ); + }); + }); + + it('disables source maps when sourceMaps is false', async () => { + await withBrowser(async (browser, page) => { + const targetUniverse = await createTargetUniverse( + await page.createCDPSession(), + {sourceMaps: false}, + ); + assert.ok(targetUniverse); + + assert.strictEqual( + targetUniverse.universe.settings + .resolve(DevTools.SDKSettings.jsSourceMapsEnabledSettingDescriptor) + .get(), + false, + ); + assert.strictEqual( + targetUniverse.universe.settings + .resolve(DevTools.SDKSettings.cssSourceMapsEnabledSettingDescriptor) + .get(), + false, + ); + }); + }); }); describe('SymbolizedError', () => { diff --git a/tests/tools/console.test.ts b/tests/tools/console.test.ts index ade614db4..2731cd7c9 100644 --- a/tests/tools/console.test.ts +++ b/tests/tools/console.test.ts @@ -635,6 +635,39 @@ describe('console', () => { }); }); + it('does not apply source maps when sourceMaps is false', async () => { + server.addRoute('/main.min.js', (_req, res) => { + res.setHeader('Content-Type', 'text/javascript'); + res.statusCode = 200; + res.end(`function n(){throw new Error("b00m!")}function o(){n()}(function n(){o()})(); + //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJiYXIiLCJFcnJvciIsImZvbyIsIklpZmUiXSwic291cmNlcyI6WyIuL21haW4uanMiXSwic291cmNlc0NvbnRlbnQiOlsiXG5mdW5jdGlvbiBiYXIoKSB7XG4gIHRocm93IG5ldyBFcnJvcignYjAwbSEnKTtcbn1cblxuZnVuY3Rpb24gZm9vKCkge1xuICBiYXIoKTtcbn1cblxuKGZ1bmN0aW9uIElpZmUoKSB7XG4gIGZvbygpO1xufSkoKTtcblxuIl0sIm1hcHBpbmdzIjoiQUFDQSxTQUFTQSxJQUNQLE1BQU0sSUFBSUMsTUFBTSxRQUNsQixDQUVBLFNBQVNDLElBQ1BGLEdBQ0YsRUFFQSxTQUFVRyxJQUNSRCxHQUNELEVBRkQiLCJpZ25vcmVMaXN0IjpbXX0= + `); + }); + server.addHtmlRoute( + '/index.html', + ``, + ); + + await withMcpContext( + async (response, context) => { + const page = context.getSelectedMcpPage(); + await page.pptrPage.goto(server.getRoute('/index.html')); + + await getConsoleMessage.handler( + {params: {msgid: 1}, page: context.getSelectedMcpPage()}, + response, + context, + ); + const formattedResponse = await response.handle(context); + const rawText = getTextContent(formattedResponse.content[0]); + + assert.ok(rawText.includes('main.min.js')); + assert.ok(!rawText.includes('main.js')); + }, + {sourceMaps: false}, + ); + }); + it('ignores frames from ignore listed URLs', async t => { server.addHtmlRoute( '/index.html', diff --git a/tests/utils.ts b/tests/utils.ts index ebe55ba7c..20fafafde 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -159,6 +159,7 @@ export async function withMcpContext( debug?: boolean; autoOpenDevTools?: boolean; performanceCrux?: boolean; + sourceMaps?: boolean; executablePath?: string; args?: string[]; blockedUrlPattern?: string[]; @@ -181,6 +182,7 @@ export async function withMcpContext( { experimentalDevToolsDebugging: false, performanceCrux: options.performanceCrux ?? true, + sourceMaps: options.sourceMaps ?? true, allowList: options.allowedUrlPattern, blocklist: options.blockedUrlPattern, allowUnrestrictedPaths: options.allowUnrestrictedPaths ?? false,