@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/commands/screenshot.d.ts +15 -0
- package/dist/commands/screenshot.d.ts.map +1 -1
- package/dist/index.cjs +144 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +144 -39
- package/dist/index.js.map +1 -1
- package/dist/utils/cdp-screenshot.d.ts +21 -10
- package/dist/utils/cdp-screenshot.d.ts.map +1 -1
- package/package.json +3 -3
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
|
-
|
|
2949
|
-
|
|
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 (!
|
|
2963
|
+
if (!localChild.killed) {
|
|
2979
2964
|
try {
|
|
2980
|
-
|
|
2965
|
+
localChild.kill("SIGKILL");
|
|
2981
2966
|
} catch {
|
|
2982
2967
|
}
|
|
2983
2968
|
}
|
|
2984
2969
|
}, 2e3);
|
|
2985
2970
|
killTimer.unref();
|
|
2986
2971
|
}
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2972
|
+
if (userDataDir) {
|
|
2973
|
+
try {
|
|
2974
|
+
rmSync(userDataDir, { recursive: true, force: true });
|
|
2975
|
+
} catch {
|
|
2976
|
+
}
|
|
2990
2977
|
}
|
|
2991
2978
|
};
|
|
2992
2979
|
try {
|
|
2993
|
-
|
|
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
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
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(
|
|
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
|
}
|