@mcp-use/cli 3.2.0-canary.6 → 3.2.0-canary.8

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/dist/index.js CHANGED
@@ -2945,52 +2945,67 @@ function waitForDevToolsUrl(child, timeoutMs = 5e3) {
2945
2945
  });
2946
2946
  }
2947
2947
  async function captureScreenshot(opts) {
2948
- const userDataDir = mkdtempSync(path4.join(os4.tmpdir(), "mcp-use-chrome-"));
2949
- const chromeArgs = [
2950
- "--headless=new",
2951
- "--remote-debugging-port=0",
2952
- `--user-data-dir=${userDataDir}`,
2953
- "--no-first-run",
2954
- "--no-default-browser-check",
2955
- "--disable-extensions",
2956
- "--disable-gpu",
2957
- "--hide-scrollbars",
2958
- "--mute-audio",
2959
- `--window-size=${opts.width},${opts.height}`,
2960
- "about:blank"
2961
- ];
2962
- const child = spawn(opts.chromePath, chromeArgs, {
2963
- stdio: ["ignore", "pipe", "pipe"]
2964
- });
2965
- child.stdout?.resume();
2948
+ let userDataDir;
2949
+ let child;
2966
2950
  let cdp;
2967
2951
  let cleanedUp = false;
2968
2952
  const cleanup = () => {
2969
2953
  if (cleanedUp) return;
2970
2954
  cleanedUp = true;
2971
2955
  cdp?.close();
2972
- if (!child.killed) {
2956
+ if (child && !child.killed) {
2973
2957
  try {
2974
2958
  child.kill("SIGTERM");
2975
2959
  } catch {
2976
2960
  }
2961
+ const localChild = child;
2977
2962
  const killTimer = setTimeout(() => {
2978
- if (!child.killed) {
2963
+ if (!localChild.killed) {
2979
2964
  try {
2980
- child.kill("SIGKILL");
2965
+ localChild.kill("SIGKILL");
2981
2966
  } catch {
2982
2967
  }
2983
2968
  }
2984
2969
  }, 2e3);
2985
2970
  killTimer.unref();
2986
2971
  }
2987
- try {
2988
- rmSync(userDataDir, { recursive: true, force: true });
2989
- } catch {
2972
+ if (userDataDir) {
2973
+ try {
2974
+ rmSync(userDataDir, { recursive: true, force: true });
2975
+ } catch {
2976
+ }
2990
2977
  }
2991
2978
  };
2992
2979
  try {
2993
- const wsUrl = await waitForDevToolsUrl(child);
2980
+ let wsUrl;
2981
+ if (opts.cdpUrl) {
2982
+ wsUrl = opts.cdpUrl;
2983
+ } else {
2984
+ if (!opts.chromePath) {
2985
+ throw new Error(
2986
+ "captureScreenshot requires either `cdpUrl` or `chromePath`"
2987
+ );
2988
+ }
2989
+ userDataDir = mkdtempSync(path4.join(os4.tmpdir(), "mcp-use-chrome-"));
2990
+ const chromeArgs = [
2991
+ "--headless=new",
2992
+ "--remote-debugging-port=0",
2993
+ `--user-data-dir=${userDataDir}`,
2994
+ "--no-first-run",
2995
+ "--no-default-browser-check",
2996
+ "--disable-extensions",
2997
+ "--disable-gpu",
2998
+ "--hide-scrollbars",
2999
+ "--mute-audio",
3000
+ `--window-size=${opts.width},${opts.height}`,
3001
+ "about:blank"
3002
+ ];
3003
+ child = spawn(opts.chromePath, chromeArgs, {
3004
+ stdio: ["ignore", "pipe", "pipe"]
3005
+ });
3006
+ child.stdout?.resume();
3007
+ wsUrl = await waitForDevToolsUrl(child);
3008
+ }
2994
3009
  const ws = new WebSocket(wsUrl);
2995
3010
  await new Promise((resolve2, reject) => {
2996
3011
  const onOpen = () => {
@@ -3005,14 +3020,47 @@ async function captureScreenshot(opts) {
3005
3020
  ws.once("error", onError);
3006
3021
  });
3007
3022
  cdp = new CdpClient(ws);
3008
- const { targetId } = await cdp.send(
3009
- "Target.createTarget",
3010
- { url: "about:blank" }
3011
- );
3012
- const { sessionId } = await cdp.send(
3013
- "Target.attachToTarget",
3014
- { targetId, flatten: true }
3015
- );
3023
+ let sessionId;
3024
+ if (opts.cdpUrl) {
3025
+ const attachPromise = new Promise((resolve2, reject) => {
3026
+ const timer = setTimeout(
3027
+ () => reject(
3028
+ new Error(
3029
+ "Timed out waiting for Target.attachedToTarget event from remote CDP"
3030
+ )
3031
+ ),
3032
+ 1e4
3033
+ );
3034
+ const onMessage = (data) => {
3035
+ try {
3036
+ const msg = JSON.parse(data.toString());
3037
+ if (msg.method === "Target.attachedToTarget" && msg.params?.targetInfo?.type === "page" && typeof msg.params.sessionId === "string") {
3038
+ clearTimeout(timer);
3039
+ ws.off("message", onMessage);
3040
+ resolve2(msg.params.sessionId);
3041
+ }
3042
+ } catch {
3043
+ }
3044
+ };
3045
+ ws.on("message", onMessage);
3046
+ });
3047
+ await cdp.send("Target.setAutoAttach", {
3048
+ autoAttach: true,
3049
+ waitForDebuggerOnStart: false,
3050
+ flatten: true
3051
+ });
3052
+ sessionId = await attachPromise;
3053
+ } else {
3054
+ const { targetId } = await cdp.send(
3055
+ "Target.createTarget",
3056
+ { url: "about:blank" }
3057
+ );
3058
+ const attach = await cdp.send(
3059
+ "Target.attachToTarget",
3060
+ { targetId, flatten: true }
3061
+ );
3062
+ sessionId = attach.sessionId;
3063
+ }
3016
3064
  await cdp.send("Page.enable", {}, sessionId);
3017
3065
  await cdp.send(
3018
3066
  "Emulation.setDeviceMetricsOverride",
@@ -3179,6 +3227,31 @@ function resolveChromePath() {
3179
3227
  }
3180
3228
 
3181
3229
  // src/commands/screenshot.ts
3230
+ function parseHeaderArg(raw) {
3231
+ const idx = raw.indexOf(":");
3232
+ if (idx === -1) {
3233
+ throw new Error(
3234
+ `Invalid --header value "${raw}". Expected "Key: Value" (e.g. "Authorization: Bearer xyz").`
3235
+ );
3236
+ }
3237
+ const key = raw.slice(0, idx).trim();
3238
+ const value = raw.slice(idx + 1).trim();
3239
+ if (!key) {
3240
+ throw new Error(`Invalid --header value "${raw}". Header name is empty.`);
3241
+ }
3242
+ return [key, value];
3243
+ }
3244
+ function parseHeaderArgs(args) {
3245
+ const headers = {};
3246
+ for (const raw of args) {
3247
+ const [key, value] = parseHeaderArg(raw);
3248
+ headers[key] = value;
3249
+ }
3250
+ return headers;
3251
+ }
3252
+ function collectHeader(value, previous = []) {
3253
+ return previous.concat([value]);
3254
+ }
3182
3255
  function detectToolResourceUri(tool) {
3183
3256
  if (!tool) return null;
3184
3257
  const meta = tool._meta;
@@ -3192,7 +3265,7 @@ async function captureToolScreenshot(inputs, options = {}) {
3192
3265
  const theme = options.theme ?? "light";
3193
3266
  const timeoutMs = options.timeoutMs ?? 3e4;
3194
3267
  const delayMs = options.delayMs ?? 0;
3195
- const chromePath = resolveChromePath();
3268
+ const chromePath = options.cdpUrl ? void 0 : resolveChromePath();
3196
3269
  const view = extractViewName(inputs.resourceUri);
3197
3270
  const devOptions = {
3198
3271
  width: String(width),
@@ -3228,6 +3301,7 @@ async function captureToolScreenshot(inputs, options = {}) {
3228
3301
  timeoutMs,
3229
3302
  outputPath,
3230
3303
  chromePath,
3304
+ cdpUrl: options.cdpUrl,
3231
3305
  delayMs: Number.isFinite(delayMs) && delayMs > 0 ? delayMs : 0,
3232
3306
  bundle
3233
3307
  });
@@ -3373,7 +3447,7 @@ function parseDimension(raw, name) {
3373
3447
  return n;
3374
3448
  }
3375
3449
  var AD_HOC_SESSION_NAME = "__screenshot_ad_hoc__";
3376
- async function resolveSessionForScreenshot(options) {
3450
+ async function resolveSessionForScreenshot(options, headers) {
3377
3451
  if (options.session) {
3378
3452
  const result2 = await getOrRestoreSession(options.session);
3379
3453
  return result2?.session ?? null;
@@ -3382,6 +3456,7 @@ async function resolveSessionForScreenshot(options) {
3382
3456
  const client = new MCPClient2();
3383
3457
  client.addServer(AD_HOC_SESSION_NAME, {
3384
3458
  url: options.mcp,
3459
+ ...headers ? { headers } : {},
3385
3460
  clientInfo: getCliClientInfo()
3386
3461
  });
3387
3462
  try {
@@ -3409,6 +3484,27 @@ async function screenshotCommand(options, argsList) {
3409
3484
  exitCode = 1;
3410
3485
  return;
3411
3486
  }
3487
+ let headers;
3488
+ if (options.header && options.header.length > 0) {
3489
+ if (!options.mcp) {
3490
+ console.error(
3491
+ formatError(
3492
+ "--header is only supported with --mcp <url>. Saved sessions (use --session) carry their own auth from `mcp-use client connect`."
3493
+ )
3494
+ );
3495
+ exitCode = 1;
3496
+ return;
3497
+ }
3498
+ try {
3499
+ headers = parseHeaderArgs(options.header);
3500
+ } catch (err) {
3501
+ console.error(
3502
+ formatError(err instanceof Error ? err.message : String(err))
3503
+ );
3504
+ exitCode = 1;
3505
+ return;
3506
+ }
3507
+ }
3412
3508
  try {
3413
3509
  resolveChromePath();
3414
3510
  } catch (err) {
@@ -3422,7 +3518,7 @@ async function screenshotCommand(options, argsList) {
3422
3518
  const height = parseDimension(options.height, "height");
3423
3519
  const navTimeout = parseInt(options.timeout, 10) || 3e4;
3424
3520
  const delayMs = options.delay ? parseInt(options.delay, 10) : 0;
3425
- const session = await resolveSessionForScreenshot(options);
3521
+ const session = await resolveSessionForScreenshot(options, headers);
3426
3522
  if (!session) {
3427
3523
  exitCode = 1;
3428
3524
  return;
@@ -3488,7 +3584,8 @@ async function screenshotCommand(options, argsList) {
3488
3584
  delayMs,
3489
3585
  timeoutMs: navTimeout,
3490
3586
  inspector: options.inspector,
3491
- quiet: options.quiet
3587
+ quiet: options.quiet,
3588
+ cdpUrl: options.cdpUrl
3492
3589
  }
3493
3590
  );
3494
3591
  console.log(
@@ -3519,7 +3616,12 @@ function createScreenshotCommand() {
3519
3616
  "Saved session name (from `mcp-use client connect`). Defaults to the active session."
3520
3617
  ).option(
3521
3618
  "--mcp <url>",
3522
- "Ad-hoc MCP server URL (escape hatch). Used only when no --session and no active saved session. No authentication."
3619
+ "Ad-hoc MCP server URL (escape hatch). Used only when no --session and no active saved session. No authentication unless --header is supplied."
3620
+ ).option(
3621
+ "-H, --header <header>",
3622
+ 'HTTP header to send to the --mcp <url> server, formatted "Key: Value". Repeatable. Use to pass an Authorization bearer token or other auth headers when screenshotting an authenticated MCP server.',
3623
+ collectHeader,
3624
+ []
3523
3625
  ).option(
3524
3626
  "--theme <light|dark>",
3525
3627
  "Color scheme to render the view in.",
@@ -3534,7 +3636,10 @@ function createScreenshotCommand() {
3534
3636
  "--delay <ms>",
3535
3637
  "Extra wait after readiness, to let chart animations / async layouts settle.",
3536
3638
  "0"
3537
- ).option("--timeout <ms>", "Navigation + readiness timeout in ms.", "30000").option("--quiet", "Suppress dev-server output.").action(async (args, opts) => {
3639
+ ).option("--timeout <ms>", "Navigation + readiness timeout in ms.", "30000").option(
3640
+ "--cdp-url <url>",
3641
+ "Connect to an existing CDP WebSocket (ws:// or wss://) instead of spawning local Chrome. Useful for hosted browsers like Notte."
3642
+ ).option("--quiet", "Suppress dev-server output.").action(async (args, opts) => {
3538
3643
  await screenshotCommand(opts, args);
3539
3644
  });
3540
3645
  }