@nimbus21.ai/chrome-devtools-mcp 0.17.3 → 0.17.5

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.
@@ -13,6 +13,36 @@ import { puppeteer } from './third_party/index.js';
13
13
  // Add stealth plugin
14
14
  puppeteerExtra.use(StealthPlugin());
15
15
  let browser;
16
+ /**
17
+ * Close the browser instance, killing the Chrome process if we launched it.
18
+ * Safe to call multiple times or when no browser exists.
19
+ */
20
+ export async function closeBrowser() {
21
+ if (!browser) {
22
+ return;
23
+ }
24
+ const b = browser;
25
+ browser = undefined;
26
+ try {
27
+ if (b.connected) {
28
+ // browser.close() sends a Browser.close CDP command and kills the process
29
+ // if it was launched by puppeteer. For connected browsers it just disconnects.
30
+ await b.close();
31
+ }
32
+ }
33
+ catch {
34
+ // Best-effort: if close fails, try to kill the process directly
35
+ try {
36
+ const proc = b.process();
37
+ if (proc && !proc.killed) {
38
+ proc.kill('SIGKILL');
39
+ }
40
+ }
41
+ catch {
42
+ // Nothing more we can do
43
+ }
44
+ }
45
+ }
16
46
  function makeTargetFilter() {
17
47
  const ignoredPrefixes = new Set([
18
48
  'chrome://',
@@ -125,7 +155,7 @@ export async function launch(options) {
125
155
  ];
126
156
  const ignoreDefaultArgs = options.stealth
127
157
  ? [...(options.ignoreDefaultChromeArgs ?? []), '--enable-automation']
128
- : options.ignoreDefaultChromeArgs ?? false;
158
+ : (options.ignoreDefaultChromeArgs ?? false);
129
159
  // Add stealth-enhancing arguments if stealth mode is enabled
130
160
  if (options.stealth) {
131
161
  args.push('--disable-blink-features=AutomationControlled', '--disable-features=IsolateOrigins,site-per-process', '--no-first-run', '--no-service-autorun', '--password-store=basic');
package/build/src/main.js CHANGED
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import './polyfill.js';
7
7
  import process from 'node:process';
8
- import { ensureBrowserConnected, ensureBrowserLaunched } from './browser.js';
8
+ import { closeBrowser, ensureBrowserConnected, ensureBrowserLaunched } from './browser.js';
9
9
  import { cliOptions, parseArguments } from './cli.js';
10
10
  import { loadIssueDescriptions } from './issue-descriptions.js';
11
11
  import { logger, saveLogsToFile } from './logger.js';
@@ -20,7 +20,7 @@ import { ToolCategory } from './tools/categories.js';
20
20
  import { tools } from './tools/tools.js';
21
21
  // If moved update release-please config
22
22
  // x-release-please-start-version
23
- const VERSION = '0.17.3';
23
+ const VERSION = '0.17.4';
24
24
  // x-release-please-end
25
25
  export const args = parseArguments(VERSION);
26
26
  const logFile = args.logFile ? saveLogsToFile(args.logFile) : undefined;
@@ -53,7 +53,10 @@ server.server.setRequestHandler(SetLevelRequestSchema, () => {
53
53
  });
54
54
  let context;
55
55
  async function getContext() {
56
- const chromeArgs = (args.chromeArg ?? []).map(String);
56
+ const chromeArgs = [
57
+ ...(args.chromeArg ?? []).map(String),
58
+ ...(args.chromeArgs ?? []).map(String),
59
+ ];
57
60
  const ignoreDefaultChromeArgs = (args.ignoreDefaultChromeArg ?? []).map(String);
58
61
  if (args.proxyServer) {
59
62
  chromeArgs.push(`--proxy-server=${args.proxyServer}`);
@@ -192,6 +195,43 @@ await loadIssueDescriptions();
192
195
  const transport = new StdioServerTransport();
193
196
  await server.connect(transport);
194
197
  logger('Chrome DevTools MCP Server connected');
198
+ // Graceful shutdown: kill Chrome when the MCP server exits for any reason.
199
+ let shuttingDown = false;
200
+ async function gracefulShutdown(reason) {
201
+ if (shuttingDown) {
202
+ return;
203
+ }
204
+ shuttingDown = true;
205
+ logger(`Shutting down: ${reason}`);
206
+ try {
207
+ context?.dispose();
208
+ }
209
+ catch {
210
+ // best-effort
211
+ }
212
+ try {
213
+ await closeBrowser();
214
+ }
215
+ catch {
216
+ // best-effort
217
+ }
218
+ logger('Shutdown complete');
219
+ process.exit(0);
220
+ }
221
+ // Handle OS signals (container stop, systemd, Ctrl+C)
222
+ for (const signal of ['SIGTERM', 'SIGINT', 'SIGHUP']) {
223
+ process.on(signal, () => {
224
+ void gracefulShutdown(`received ${signal}`);
225
+ });
226
+ }
227
+ // Handle MCP client disconnect (transport/server close)
228
+ server.server.onclose = () => {
229
+ void gracefulShutdown('MCP client disconnected');
230
+ };
231
+ // Handle stdin closing (parent process died)
232
+ process.stdin.on('close', () => {
233
+ void gracefulShutdown('stdin closed');
234
+ });
195
235
  logDisclaimers();
196
236
  void clearcutLogger?.logDailyActiveIfNeeded();
197
237
  void clearcutLogger?.logServerStart(computeFlagUsage(args, cliOptions));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nimbus21.ai/chrome-devtools-mcp",
3
- "version": "0.17.3",
3
+ "version": "0.17.5",
4
4
  "description": "MCP server for Chrome DevTools with stealth mode support",
5
5
  "type": "module",
6
6
  "bin": "./build/src/index.js",