chrome-devtools-mcp 0.20.2 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -27,6 +27,10 @@ allowing them to inspect, debug, and modify any data in the browser or DevTools.
27
27
  Avoid sharing sensitive or personal information that you don't want to share with
28
28
  MCP clients.
29
29
 
30
+ `chrome-devtools-mcp` officially supports Google Chrome and [Chrome for Testing](https://developer.chrome.com/blog/chrome-for-testing/) only.
31
+ Other Chromium-based browser may work, but this is not guaranteed, and you may encounter unexpected behavior. Use at your own discretion.
32
+ We are committed to providing fixes and support for the latest version of [Extended Stable Chrome](https://chromiumdash.appspot.com/schedule).
33
+
30
34
  Performance tools may send trace URLs to the Google CrUX API to fetch real-user
31
35
  experience data. This helps provide a holistic performance picture by
32
36
  presenting field data alongside lab data. This data is collected by the [Chrome
@@ -53,7 +57,7 @@ Collection is disabled if CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS or CI env vari
53
57
 
54
58
  - [Node.js](https://nodejs.org/) v20.19 or a newer [latest maintenance LTS](https://github.com/nodejs/Release#release-schedule) version.
55
59
  - [Chrome](https://www.google.com/chrome/) current stable version or newer.
56
- - [npm](https://www.npmjs.com/).
60
+ - [npm](https://www.npmjs.com/)
57
61
 
58
62
  ## Getting started
59
63
 
@@ -194,6 +198,17 @@ startup_timeout_ms = 20_000
194
198
 
195
199
  </details>
196
200
 
201
+ <details>
202
+ <summary>Command Code</summary>
203
+
204
+ Use the Command Code CLI to add the Chrome DevTools MCP server (<a href="https://commandcode.ai/docs/mcp">MCP guide</a>):
205
+
206
+ ```bash
207
+ cmd mcp add chrome-devtools --scope user npx chrome-devtools-mcp@latest
208
+ ```
209
+
210
+ </details>
211
+
197
212
  <details>
198
213
  <summary>Copilot CLI</summary>
199
214
 
@@ -6,52 +6,6 @@
6
6
  import { PuppeteerDevToolsConnection } from './DevToolsConnectionAdapter.js';
7
7
  import { Mutex } from './Mutex.js';
8
8
  import { DevTools } from './third_party/index.js';
9
- export function extractUrlLikeFromDevToolsTitle(title) {
10
- const match = title.match(new RegExp(`DevTools - (.*)`));
11
- return match?.[1] ?? undefined;
12
- }
13
- export function urlsEqual(url1, url2) {
14
- const normalizedUrl1 = normalizeUrl(url1);
15
- const normalizedUrl2 = normalizeUrl(url2);
16
- return normalizedUrl1 === normalizedUrl2;
17
- }
18
- /**
19
- * For the sake of the MCP server, when we determine if two URLs are equal we
20
- * remove some parts:
21
- *
22
- * 1. We do not care about the protocol.
23
- * 2. We do not care about trailing slashes.
24
- * 3. We do not care about "www".
25
- * 4. We ignore the hash parts.
26
- *
27
- * For example, if the user types "record a trace on foo.com", we would want to
28
- * match a tab in the connected Chrome instance that is showing "www.foo.com/"
29
- */
30
- function normalizeUrl(url) {
31
- let result = url.trim();
32
- // Remove protocols
33
- if (result.startsWith('https://')) {
34
- result = result.slice(8);
35
- }
36
- else if (result.startsWith('http://')) {
37
- result = result.slice(7);
38
- }
39
- // Remove 'www.'. This ensures that we find the right URL regardless of if the user adds `www` or not.
40
- if (result.startsWith('www.')) {
41
- result = result.slice(4);
42
- }
43
- // We use target URLs to locate DevTools but those often do
44
- // no include hash.
45
- const hashIdx = result.lastIndexOf('#');
46
- if (hashIdx !== -1) {
47
- result = result.slice(0, hashIdx);
48
- }
49
- // Remove trailing slash
50
- if (result.endsWith('/')) {
51
- result = result.slice(0, -1);
52
- }
53
- return result;
54
- }
55
9
  /**
56
10
  * A mock implementation of an issues manager that only implements the methods
57
11
  * that are actually used by the IssuesAggregator
@@ -5,9 +5,9 @@
5
5
  */
6
6
  import fs from 'node:fs/promises';
7
7
  import path from 'node:path';
8
- import { extractUrlLikeFromDevToolsTitle, UniverseManager, urlsEqual, } from './DevtoolsUtils.js';
8
+ import { UniverseManager } from './DevtoolsUtils.js';
9
9
  import { McpPage } from './McpPage.js';
10
- import { NetworkCollector, ConsoleCollector } from './PageCollector.js';
10
+ import { NetworkCollector, ConsoleCollector, } from './PageCollector.js';
11
11
  import { Locator } from './third_party/index.js';
12
12
  import { PredefinedNetworkConditions } from './third_party/index.js';
13
13
  import { listPages } from './tools/pages.js';
@@ -48,6 +48,7 @@ export class McpContext {
48
48
  #extensionRegistry = new ExtensionRegistry();
49
49
  #isRunningTrace = false;
50
50
  #screenRecorderData = null;
51
+ #inPageTools;
51
52
  #nextPageId = 1;
52
53
  #extensionPages = new WeakMap();
53
54
  #extensionServiceWorkerMap = new WeakMap();
@@ -328,6 +329,12 @@ export class McpContext {
328
329
  this.#selectedPage = newPage;
329
330
  this.#updateSelectedPageTimeouts();
330
331
  }
332
+ setInPageTools(toolGroup) {
333
+ this.#inPageTools = toolGroup;
334
+ }
335
+ getInPageTools() {
336
+ return this.#inPageTools;
337
+ }
331
338
  #updateSelectedPageTimeouts() {
332
339
  const page = this.#getSelectedMcpPage();
333
340
  // For waiters 5sec timeout should be sufficient.
@@ -467,38 +474,26 @@ export class McpContext {
467
474
  async detectOpenDevToolsWindows() {
468
475
  this.logger('Detecting open DevTools windows');
469
476
  const { pages } = await this.#getAllPages();
470
- // Clear all devToolsPage references before re-detecting.
471
- for (const mcpPage of this.#mcpPages.values()) {
472
- mcpPage.devToolsPage = undefined;
473
- }
474
- for (const devToolsPage of pages) {
475
- if (devToolsPage.url().startsWith('devtools://')) {
476
- try {
477
- this.logger('Calling getTargetInfo for ' + devToolsPage.url());
478
- const data = await devToolsPage
479
- // @ts-expect-error no types for _client().
480
- ._client()
481
- .send('Target.getTargetInfo');
482
- const devtoolsPageTitle = data.targetInfo.title;
483
- const urlLike = extractUrlLikeFromDevToolsTitle(devtoolsPageTitle);
484
- if (!urlLike) {
485
- continue;
486
- }
487
- // TODO: lookup without a loop.
488
- for (const page of this.#pages) {
489
- if (urlsEqual(page.url(), urlLike)) {
490
- const mcpPage = this.#mcpPages.get(page);
491
- if (mcpPage) {
492
- mcpPage.devToolsPage = devToolsPage;
493
- }
494
- }
495
- }
477
+ await Promise.all(pages.map(async (page) => {
478
+ const mcpPage = this.#mcpPages.get(page);
479
+ if (!mcpPage) {
480
+ return;
481
+ }
482
+ // Prior to Chrome 144.0.7559.59, the command fails,
483
+ // Some Electron apps still use older version
484
+ // Fall back to not exposing DevTools at all.
485
+ try {
486
+ if (await page.hasDevTools()) {
487
+ mcpPage.devToolsPage = await page.openDevTools();
496
488
  }
497
- catch (error) {
498
- this.logger('Issue occurred while trying to find DevTools', error);
489
+ else {
490
+ mcpPage.devToolsPage = undefined;
499
491
  }
500
492
  }
501
- }
493
+ catch {
494
+ mcpPage.devToolsPage = undefined;
495
+ }
496
+ }));
502
497
  }
503
498
  getExtensionServiceWorkers() {
504
499
  return this.#extensionServiceWorkers;
@@ -12,6 +12,50 @@ import { DevTools } from './third_party/index.js';
12
12
  import { handleDialog } from './tools/pages.js';
13
13
  import { getInsightOutput, getTraceSummary } from './trace-processing/parse.js';
14
14
  import { paginate } from './utils/pagination.js';
15
+ async function getToolGroup(page) {
16
+ // Check if there is a `devtoolstooldiscovery` event listener
17
+ const windowHandle = await page.pptrPage.evaluateHandle(() => window);
18
+ // @ts-expect-error internal API
19
+ const client = page.pptrPage._client();
20
+ const { listeners } = await client.send('DOMDebugger.getEventListeners', {
21
+ objectId: windowHandle.remoteObject().objectId,
22
+ });
23
+ if (listeners.find(l => l.type === 'devtoolstooldiscovery') === undefined) {
24
+ return;
25
+ }
26
+ const toolGroup = await page.pptrPage.evaluate(() => {
27
+ return new Promise(resolve => {
28
+ const event = new CustomEvent('devtoolstooldiscovery');
29
+ // @ts-expect-error Adding custom property
30
+ event.respondWith = (toolGroup) => {
31
+ if (!window.__dtmcp) {
32
+ window.__dtmcp = {};
33
+ }
34
+ window.__dtmcp.toolGroup = toolGroup;
35
+ // When receiving a toolGroup for the first time, expose a simple execution helper
36
+ if (!window.__dtmcp.executeTool) {
37
+ window.__dtmcp.executeTool = async (toolName, args) => {
38
+ if (!window.__dtmcp?.toolGroup) {
39
+ throw new Error('No tools found on the page');
40
+ }
41
+ const tool = window.__dtmcp.toolGroup.tools.find(t => t.name === toolName);
42
+ if (!tool) {
43
+ throw new Error(`Tool ${toolName} not found`);
44
+ }
45
+ return await tool.execute(args);
46
+ };
47
+ }
48
+ resolve(toolGroup);
49
+ };
50
+ window.dispatchEvent(event);
51
+ // If the page does not synchronously call `event.respondWith`, return instead of timing out
52
+ setTimeout(() => {
53
+ resolve(undefined);
54
+ }, 0);
55
+ });
56
+ });
57
+ return toolGroup;
58
+ }
15
59
  export class McpResponse {
16
60
  #includePages = false;
17
61
  #includeExtensionServiceWorkers = false;
@@ -28,6 +72,7 @@ export class McpResponse {
28
72
  #networkRequestsOptions;
29
73
  #consoleDataOptions;
30
74
  #listExtensions;
75
+ #listInPageTools;
31
76
  #devToolsData;
32
77
  #tabId;
33
78
  #args;
@@ -59,6 +104,11 @@ export class McpResponse {
59
104
  setListExtensions() {
60
105
  this.#listExtensions = true;
61
106
  }
107
+ setListInPageTools() {
108
+ if (this.#args.categoryInPageTools) {
109
+ this.#listInPageTools = true;
110
+ }
111
+ }
62
112
  setIncludeNetworkRequests(value, options) {
63
113
  if (!value) {
64
114
  this.#networkRequestsOptions = undefined;
@@ -94,8 +144,8 @@ export class McpResponse {
94
144
  includePreservedMessages: options?.includePreservedMessages,
95
145
  };
96
146
  }
97
- attachNetworkRequest(reqid, options) {
98
- this.#attachedNetworkRequestId = reqid;
147
+ attachNetworkRequest(reqId, options) {
148
+ this.#attachedNetworkRequestId = reqId;
99
149
  this.#attachedNetworkRequestOptions = options;
100
150
  }
101
151
  attachConsoleMessage(msgid) {
@@ -223,7 +273,7 @@ export class McpResponse {
223
273
  elementIdResolver: context.resolveCdpElementId.bind(context, this.#page),
224
274
  });
225
275
  if (!formatter.isValid()) {
226
- throw new Error("Can't provide detals for the msgid " + consoleMessageStableId);
276
+ throw new Error("Can't provide details for the msgid " + consoleMessageStableId);
227
277
  }
228
278
  detailedConsoleMessage = formatter;
229
279
  }
@@ -232,6 +282,11 @@ export class McpResponse {
232
282
  if (this.#listExtensions) {
233
283
  extensions = context.listExtensions();
234
284
  }
285
+ let inPageTools;
286
+ if (this.#listInPageTools) {
287
+ inPageTools = await getToolGroup(context.getSelectedMcpPage());
288
+ context.setInPageTools(inPageTools);
289
+ }
235
290
  let consoleMessages;
236
291
  if (this.#consoleDataOptions?.include) {
237
292
  if (!this.#page) {
@@ -308,6 +363,7 @@ export class McpResponse {
308
363
  traceSummary: this.#attachedTraceSummary,
309
364
  extensions,
310
365
  lighthouseResult: this.#attachedLighthouseResult,
366
+ inPageTools,
311
367
  });
312
368
  }
313
369
  format(toolName, context, data) {
@@ -498,6 +554,24 @@ Call ${handleDialog.name} to handle it before continuing.`);
498
554
  response.push(extensionsMessage);
499
555
  }
500
556
  }
557
+ if (this.#listInPageTools) {
558
+ structuredContent.inPageTools = data.inPageTools ?? undefined;
559
+ response.push('## In-page tools');
560
+ if (!data.inPageTools || !data.inPageTools.tools) {
561
+ response.push('No in-page tools available.');
562
+ }
563
+ else {
564
+ const toolGroup = data.inPageTools;
565
+ response.push(`${toolGroup.name}: ${toolGroup.description}`);
566
+ response.push('Available tools:');
567
+ const toolDefinitionsMessage = toolGroup.tools
568
+ .map(tool => {
569
+ return `name="${tool.name}", description="${tool.description}", inputSchema=${JSON.stringify(tool.inputSchema)}`;
570
+ })
571
+ .join('\n');
572
+ response.push(toolDefinitionsMessage);
573
+ }
574
+ }
501
575
  if (this.#networkRequestsOptions?.include && data.networkRequests) {
502
576
  const requests = data.networkRequests;
503
577
  response.push('## Network requests');
@@ -28,7 +28,7 @@ export class PageCollector {
28
28
  #browser;
29
29
  #listenersInitializer;
30
30
  #listeners = new WeakMap();
31
- #maxNavigationSaved = 3;
31
+ maxNavigationSaved = 3;
32
32
  /**
33
33
  * This maps a Page to a list of navigations with a sub-list
34
34
  * of all collected resources.
@@ -109,7 +109,7 @@ export class PageCollector {
109
109
  }
110
110
  // Add the latest navigation first
111
111
  navigations.unshift([]);
112
- navigations.splice(this.#maxNavigationSaved);
112
+ navigations.splice(this.maxNavigationSaved);
113
113
  }
114
114
  cleanupPageDestroyed(page) {
115
115
  const listeners = this.#listeners.get(page);
@@ -129,7 +129,7 @@ export class PageCollector {
129
129
  return navigations[0];
130
130
  }
131
131
  const data = [];
132
- for (let index = this.#maxNavigationSaved; index >= 0; index--) {
132
+ for (let index = this.maxNavigationSaved; index >= 0; index--) {
133
133
  if (navigations[index]) {
134
134
  data.push(...navigations[index]);
135
135
  }
@@ -198,10 +198,10 @@ class PageEventSubscriber {
198
198
  #resetIssueAggregator() {
199
199
  this.#issueManager = new FakeIssuesManager();
200
200
  if (this.#issueAggregator) {
201
- this.#issueAggregator.removeEventListener("AggregatedIssueUpdated" /* DevTools.IssueAggregatorEvents.AGGREGATED_ISSUE_UPDATED */, this.#onAggregatedissue);
201
+ this.#issueAggregator.removeEventListener("AggregatedIssueUpdated" /* DevTools.IssueAggregatorEvents.AGGREGATED_ISSUE_UPDATED */, this.#onAggregatedIssue);
202
202
  }
203
203
  this.#issueAggregator = new DevTools.IssueAggregator(this.#issueManager);
204
- this.#issueAggregator.addEventListener("AggregatedIssueUpdated" /* DevTools.IssueAggregatorEvents.AGGREGATED_ISSUE_UPDATED */, this.#onAggregatedissue);
204
+ this.#issueAggregator.addEventListener("AggregatedIssueUpdated" /* DevTools.IssueAggregatorEvents.AGGREGATED_ISSUE_UPDATED */, this.#onAggregatedIssue);
205
205
  }
206
206
  async subscribe() {
207
207
  this.#resetIssueAggregator();
@@ -222,13 +222,13 @@ class PageEventSubscriber {
222
222
  this.#session.off('Audits.issueAdded', this.#onIssueAdded);
223
223
  this.#session.off('Runtime.exceptionThrown', this.#onExceptionThrown);
224
224
  if (this.#issueAggregator) {
225
- this.#issueAggregator.removeEventListener("AggregatedIssueUpdated" /* DevTools.IssueAggregatorEvents.AGGREGATED_ISSUE_UPDATED */, this.#onAggregatedissue);
225
+ this.#issueAggregator.removeEventListener("AggregatedIssueUpdated" /* DevTools.IssueAggregatorEvents.AGGREGATED_ISSUE_UPDATED */, this.#onAggregatedIssue);
226
226
  }
227
227
  void this.#session.send('Audits.disable').catch(() => {
228
228
  // might fail.
229
229
  });
230
230
  }
231
- #onAggregatedissue = (event) => {
231
+ #onAggregatedIssue = (event) => {
232
232
  if (this.#seenIssues.has(event.data)) {
233
233
  return;
234
234
  }
@@ -305,5 +305,6 @@ export class NetworkCollector extends PageCollector {
305
305
  else {
306
306
  navigations.unshift([]);
307
307
  }
308
+ navigations.splice(this.maxNavigationSaved);
308
309
  }
309
310
  }
@@ -8,7 +8,7 @@ export const cliOptions = {
8
8
  autoConnect: {
9
9
  type: 'boolean',
10
10
  description: '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.',
11
- conflicts: ['isolated', 'executablePath'],
11
+ conflicts: ['isolated', 'executablePath', 'categoryExtensions'],
12
12
  default: false,
13
13
  coerce: (value) => {
14
14
  if (!value) {
@@ -21,7 +21,7 @@ export const cliOptions = {
21
21
  type: 'string',
22
22
  description: '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.',
23
23
  alias: 'u',
24
- conflicts: 'wsEndpoint',
24
+ conflicts: ['wsEndpoint', 'categoryExtensions'],
25
25
  coerce: (url) => {
26
26
  if (!url) {
27
27
  return;
@@ -39,7 +39,7 @@ export const cliOptions = {
39
39
  type: 'string',
40
40
  description: 'WebSocket endpoint to connect to a running Chrome instance (e.g., ws://127.0.0.1:9222/devtools/browser/<id>). Alternative to --browserUrl.',
41
41
  alias: 'w',
42
- conflicts: 'browserUrl',
42
+ conflicts: ['browserUrl', 'categoryExtensions'],
43
43
  coerce: (url) => {
44
44
  if (!url) {
45
45
  return;
@@ -193,9 +193,14 @@ export const cliOptions = {
193
193
  },
194
194
  categoryExtensions: {
195
195
  type: 'boolean',
196
- default: false,
197
196
  hidden: true,
198
- describe: 'Set to false to exclude tools related to extensions.',
197
+ conflicts: ['browserUrl', 'autoConnect', 'wsEndpoint'],
198
+ describe: 'Set to true to include tools related to extensions. Note: This feature is only supported with a pipe connection. autoConnect is not supported.',
199
+ },
200
+ categoryInPageTools: {
201
+ type: 'boolean',
202
+ hidden: true,
203
+ describe: 'Set to true to enable tools exposed by the inspected page itself',
199
204
  },
200
205
  performanceCrux: {
201
206
  type: 'boolean',
@@ -4,6 +4,7 @@
4
4
  * Copyright 2025 Google LLC
5
5
  * SPDX-License-Identifier: Apache-2.0
6
6
  */
7
+ process.title = 'chrome-devtools-mcp';
7
8
  import { version } from 'node:process';
8
9
  const [major, minor] = version.substring(1).split('.').map(Number);
9
10
  if (major === 20 && minor < 19) {
@@ -4,6 +4,7 @@
4
4
  * Copyright 2026 Google LLC
5
5
  * SPDX-License-Identifier: Apache-2.0
6
6
  */
7
+ process.title = 'chrome-devtools';
7
8
  import process from 'node:process';
8
9
  import { startDaemon, stopDaemon, sendCommand, handleResponse, } from '../daemon/client.js';
9
10
  import { isDaemonRunning, serializeArgs } from '../daemon/utils.js';
@@ -26,7 +27,7 @@ delete startCliOptions.autoConnect;
26
27
  // Missing CLI serialization.
27
28
  delete startCliOptions.viewport;
28
29
  // CLI is generated based on the default tool definitions. To enable conditional
29
- // tools, they needs to be enabled during CLI generation.
30
+ // tools, they need to be enabled during CLI generation.
30
31
  delete startCliOptions.experimentalPageIdRouting;
31
32
  delete startCliOptions.experimentalVision;
32
33
  delete startCliOptions.experimentalInteropTools;
@@ -42,9 +43,11 @@ if (!('default' in cliOptions.headless)) {
42
43
  throw new Error('headless cli option unexpectedly does not have a default');
43
44
  }
44
45
  if ('default' in cliOptions.isolated) {
45
- throw new Error('headless cli option unexpectedly does not have a default');
46
+ throw new Error('isolated cli option unexpectedly has a default');
46
47
  }
47
48
  startCliOptions.headless.default = true;
49
+ startCliOptions.isolated.description =
50
+ 'If specified, creates a temporary user-data-dir that is automatically cleaned up after the browser is closed. Defaults to true unless userDataDir is provided.';
48
51
  const y = yargs(hideBin(process.argv))
49
52
  .scriptName('chrome-devtools')
50
53
  .showHelpOnFail(true)
@@ -63,7 +66,7 @@ y.command('start', 'Start or restart chrome-devtools-mcp', y => y
63
66
  await stopDaemon();
64
67
  }
65
68
  // Defaults but we do not want to affect the yargs conflict resolution.
66
- if (argv.isolated === undefined) {
69
+ if (argv.isolated === undefined && argv.userDataDir === undefined) {
67
70
  argv.isolated = true;
68
71
  }
69
72
  if (argv.headless === undefined) {
@@ -503,7 +503,7 @@ export const commands = {
503
503
  },
504
504
  },
505
505
  take_memory_snapshot: {
506
- description: 'Capture a memory heapsnapshot of the currently selected page to memory leak debugging',
506
+ description: 'Capture a heap snapshot of the currently selected page. Use to analyze the memory distribution of JavaScript objects and debug memory leaks.',
507
507
  category: 'Performance',
508
508
  args: {
509
509
  filePath: {
@@ -11,7 +11,7 @@ import process from 'node:process';
11
11
  import { logger } from '../logger.js';
12
12
  import { Client, PipeTransport, StdioClientTransport, } from '../third_party/index.js';
13
13
  import { VERSION } from '../version.js';
14
- import { getDaemonPid, getPidFilePath, getSocketPath, INDEX_SCRIPT_PATH, IS_WINDOWS, isDaemonRunning, } from './utils.js';
14
+ import { DAEMON_CLIENT_NAME, getDaemonPid, getPidFilePath, getSocketPath, INDEX_SCRIPT_PATH, IS_WINDOWS, isDaemonRunning, } from './utils.js';
15
15
  const pid = getDaemonPid();
16
16
  if (isDaemonRunning(pid)) {
17
17
  logger('Another daemon process is running.');
@@ -42,7 +42,7 @@ async function setupMCPClient() {
42
42
  env: process.env,
43
43
  });
44
44
  mcpClient = new Client({
45
- name: 'chrome-devtools-cli-daemon',
45
+ name: DAEMON_CLIENT_NAME,
46
46
  version: VERSION,
47
47
  }, {
48
48
  capabilities: {},
@@ -11,6 +11,7 @@ import { logger } from '../logger.js';
11
11
  export const DAEMON_SCRIPT_PATH = path.join(import.meta.dirname, 'daemon.js');
12
12
  export const INDEX_SCRIPT_PATH = path.join(import.meta.dirname, '..', 'bin', 'chrome-devtools-mcp.js');
13
13
  const APP_NAME = 'chrome-devtools-mcp';
14
+ export const DAEMON_CLIENT_NAME = 'chrome-devtools-cli-daemon';
14
15
  // Using these paths due to strict limits on the POSIX socket path length.
15
16
  export function getSocketPath() {
16
17
  const uid = os.userInfo().uid;
@@ -36,6 +36,12 @@ export async function createMcpServer(serverArgs, options) {
36
36
  server.server.setRequestHandler(SetLevelRequestSchema, () => {
37
37
  return {};
38
38
  });
39
+ server.server.oninitialized = () => {
40
+ const clientName = server.server.getClientVersion()?.name;
41
+ if (clientName) {
42
+ clearcutLogger?.setClientName(clientName);
43
+ }
44
+ };
39
45
  let context;
40
46
  async function getContext() {
41
47
  const chromeArgs = (serverArgs.chromeArg ?? []).map(String);
@@ -95,7 +101,11 @@ export async function createMcpServer(serverArgs, options) {
95
101
  return;
96
102
  }
97
103
  if (tool.annotations.category === ToolCategory.EXTENSIONS &&
98
- serverArgs.categoryExtensions === false) {
104
+ !serverArgs.categoryExtensions) {
105
+ return;
106
+ }
107
+ if (tool.annotations.category === ToolCategory.IN_PAGE &&
108
+ !serverArgs.categoryInPageTools) {
99
109
  return;
100
110
  }
101
111
  if (tool.annotations.conditions?.includes('computerVision') &&