@mcp-use/cli 3.1.5-canary.4 → 3.2.0-canary.10

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
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- // ../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.14_tsx@4.21.0_typescript@5.9.3_yaml@2.8.3/node_modules/tsup/assets/esm_shims.js
3
+ // ../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.9_tsx@4.21.0_typescript@5.9.3_yaml@2.8.3/node_modules/tsup/assets/esm_shims.js
4
4
  import path from "path";
5
5
  import { fileURLToPath } from "url";
6
6
  var getFilename = () => fileURLToPath(import.meta.url);
@@ -197,10 +197,10 @@ var ansi_styles_default = ansiStyles;
197
197
  import process2 from "process";
198
198
  import os from "os";
199
199
  import tty from "tty";
200
- function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : process2.argv) {
200
+ function hasFlag(flag, argv2 = globalThis.Deno ? globalThis.Deno.args : process2.argv) {
201
201
  const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
202
- const position = argv.indexOf(prefix + flag);
203
- const terminatorPosition = argv.indexOf("--");
202
+ const position = argv2.indexOf(prefix + flag);
203
+ const terminatorPosition = argv2.indexOf("--");
204
204
  return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition);
205
205
  }
206
206
  var { env } = process2;
@@ -503,13 +503,13 @@ var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
503
503
  var source_default = chalk;
504
504
 
505
505
  // src/index.ts
506
- import { Command as Command6 } from "commander";
506
+ import { Command as Command7 } from "commander";
507
507
  import "dotenv/config";
508
- import { spawn } from "child_process";
508
+ import { spawn as spawn3 } from "child_process";
509
509
  import { readFileSync as readFileSync2 } from "fs";
510
- import { access, mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
510
+ import { access, mkdir as mkdir4, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
511
511
  import { createRequire as createRequire2 } from "module";
512
- import path8 from "path";
512
+ import path11 from "path";
513
513
  import { pathToFileURL as pathToFileURL2 } from "url";
514
514
 
515
515
  // ../../node_modules/.pnpm/open@11.0.0/node_modules/open/index.js
@@ -595,7 +595,7 @@ var is_wsl_default = process3.env.__IS_WSL_TEST__ ? isWsl : isWsl();
595
595
 
596
596
  // ../../node_modules/.pnpm/powershell-utils@0.1.0/node_modules/powershell-utils/index.js
597
597
  import process4 from "process";
598
- import { Buffer } from "buffer";
598
+ import { Buffer as Buffer2 } from "buffer";
599
599
  import { promisify } from "util";
600
600
  import childProcess from "child_process";
601
601
  import fs4, { constants as fsConstants } from "fs/promises";
@@ -626,7 +626,7 @@ executePowerShell.argumentsPrefix = [
626
626
  "Bypass",
627
627
  "-EncodedCommand"
628
628
  ];
629
- executePowerShell.encodeCommand = (command) => Buffer.from(command, "utf16le").toString("base64");
629
+ executePowerShell.encodeCommand = (command) => Buffer2.from(command, "utf16le").toString("base64");
630
630
  executePowerShell.escapeArgument = (value) => `'${String(value).replaceAll("'", "''")}'`;
631
631
 
632
632
  // ../../node_modules/.pnpm/wsl-utils@0.3.1/node_modules/wsl-utils/utilities.js
@@ -696,15 +696,15 @@ var wslDefaultBrowser = async () => {
696
696
  const { stdout } = await executePowerShell(command, { powerShellPath: psPath });
697
697
  return stdout.trim();
698
698
  };
699
- var convertWslPathToWindows = async (path9) => {
700
- if (/^[a-z]+:\/\//i.test(path9)) {
701
- return path9;
699
+ var convertWslPathToWindows = async (path12) => {
700
+ if (/^[a-z]+:\/\//i.test(path12)) {
701
+ return path12;
702
702
  }
703
703
  try {
704
- const { stdout } = await execFile2("wslpath", ["-aw", path9], { encoding: "utf8" });
704
+ const { stdout } = await execFile2("wslpath", ["-aw", path12], { encoding: "utf8" });
705
705
  return stdout.trim();
706
706
  } catch {
707
- return path9;
707
+ return path12;
708
708
  }
709
709
  };
710
710
 
@@ -1320,8 +1320,8 @@ var McpUseAPI = class _McpUseAPI {
1320
1320
  // ── Organization ID resolution ──────────────────────────────────
1321
1321
  async resolveOrganizationId() {
1322
1322
  if (this.orgId) return this.orgId;
1323
- const auth = await this.testAuth();
1324
- const id = auth.default_org_id;
1323
+ const auth2 = await this.testAuth();
1324
+ const id = auth2.default_org_id;
1325
1325
  if (!id) {
1326
1326
  throw new Error(
1327
1327
  "No organization set. Run `mcp-use org switch` or use --org to specify one."
@@ -1354,8 +1354,8 @@ var McpUseAPI = class _McpUseAPI {
1354
1354
  return this.request(`/servers${q ? `?${q}` : ""}`);
1355
1355
  }
1356
1356
  async getServer(idOrSlug) {
1357
- const path9 = encodeURIComponent(idOrSlug);
1358
- return this.request(`/servers/${path9}`);
1357
+ const path12 = encodeURIComponent(idOrSlug);
1358
+ return this.request(`/servers/${path12}`);
1359
1359
  }
1360
1360
  async deleteServer(id) {
1361
1361
  await this.request(
@@ -1855,52 +1855,110 @@ async function whoamiCommand() {
1855
1855
  }
1856
1856
 
1857
1857
  // src/commands/client.ts
1858
- import { Command } from "commander";
1859
- import { MCPClient } from "mcp-use/client";
1860
- import { getPackageVersion } from "mcp-use/server";
1861
- import { createInterface } from "readline";
1858
+ import { Command as Command2 } from "commander";
1859
+ import { MCPClient as MCPClient3 } from "mcp-use/client";
1860
+ import { createInterface as createInterface2 } from "readline";
1862
1861
 
1863
1862
  // src/utils/format.ts
1864
- function formatTable(data, columns) {
1863
+ var ANSI_RE = /\x1b\[[0-9;]*m/g;
1864
+ var GUTTER = " ";
1865
+ var MIN_TRUNCATABLE_WIDTH = 8;
1866
+ var DEFAULT_MAX_WIDTH = 100;
1867
+ function stripAnsi(s) {
1868
+ return s.replace(ANSI_RE, "");
1869
+ }
1870
+ function visibleWidth(s) {
1871
+ return stripAnsi(s).length;
1872
+ }
1873
+ function padCell(s, width) {
1874
+ const w = visibleWidth(s);
1875
+ if (w >= width) return s;
1876
+ return s + " ".repeat(width - w);
1877
+ }
1878
+ function truncateCell(s, width) {
1879
+ if (width <= 0) return "";
1880
+ const plain = stripAnsi(s);
1881
+ if (plain.length <= width) return s;
1882
+ if (width === 1) return "\u2026";
1883
+ return plain.slice(0, width - 1) + "\u2026";
1884
+ }
1885
+ function formatTable(data, columns, options = {}) {
1886
+ const tsv = options.tsv ?? !process.stdout.isTTY;
1887
+ if (tsv) {
1888
+ return data.map(
1889
+ (row) => columns.map(
1890
+ (c) => stripAnsi(String(row[c.key] ?? "")).replace(/[\t\r\n]+/g, " ")
1891
+ ).join(" ")
1892
+ ).join("\n");
1893
+ }
1865
1894
  if (data.length === 0) {
1866
1895
  return source_default.gray("No items found");
1867
1896
  }
1868
- const widths = columns.map((col) => {
1869
- const maxDataWidth = Math.max(
1870
- ...data.map((row) => String(row[col.key] || "").length)
1871
- );
1872
- const headerWidth = col.header.length;
1873
- return col.width || Math.max(maxDataWidth, headerWidth, 10);
1897
+ const maxWidth = options.maxWidth ?? process.stdout.columns ?? DEFAULT_MAX_WIDTH;
1898
+ const natural = columns.map((col) => {
1899
+ const headerW = col.header.length;
1900
+ const dataW = data.reduce((m, row) => {
1901
+ return Math.max(m, visibleWidth(String(row[col.key] ?? "")));
1902
+ }, 0);
1903
+ return Math.max(headerW, dataW);
1874
1904
  });
1875
- const createRow = (values, bold = false) => {
1876
- const cells = values.map((val, i) => {
1877
- const padded = val.padEnd(widths[i]);
1878
- return bold ? source_default.bold(padded) : padded;
1879
- });
1880
- return `\u2502 ${cells.join(" \u2502 ")} \u2502`;
1881
- };
1882
- const separator = (char) => {
1883
- const parts = widths.map((w) => char.repeat(w + 2));
1884
- if (char === "\u2500") {
1885
- return `\u251C${parts.join("\u253C")}\u2524`;
1905
+ const widths = columns.map((c, i) => c.width ?? natural[i]);
1906
+ const overhead = GUTTER.length * (columns.length - 1);
1907
+ const totalWidth = () => widths.reduce((s, w) => s + w, 0) + overhead;
1908
+ if (totalWidth() > maxWidth) {
1909
+ const truncIdxs = columns.map((c, i) => c.truncate ? i : -1).filter((i) => i >= 0);
1910
+ if (truncIdxs.length > 0) {
1911
+ const fixedSum = columns.reduce(
1912
+ (s, c, i) => s + (c.truncate ? 0 : widths[i]),
1913
+ 0
1914
+ );
1915
+ const remaining = Math.max(
1916
+ truncIdxs.length * MIN_TRUNCATABLE_WIDTH,
1917
+ maxWidth - fixedSum - overhead
1918
+ );
1919
+ const truncSum = truncIdxs.reduce((s, i) => s + widths[i], 0) || 1;
1920
+ let used = 0;
1921
+ truncIdxs.forEach((i, idx) => {
1922
+ if (idx === truncIdxs.length - 1) {
1923
+ widths[i] = Math.max(MIN_TRUNCATABLE_WIDTH, remaining - used);
1924
+ } else {
1925
+ const share = Math.max(
1926
+ MIN_TRUNCATABLE_WIDTH,
1927
+ Math.floor(widths[i] / truncSum * remaining)
1928
+ );
1929
+ widths[i] = share;
1930
+ used += share;
1931
+ }
1932
+ });
1886
1933
  }
1887
- return `\u2514${parts.join("\u2534")}\u2518`;
1888
- };
1934
+ }
1889
1935
  const lines = [];
1890
- lines.push(`\u250C${widths.map((w) => "\u2500".repeat(w + 2)).join("\u252C")}\u2510`);
1891
- lines.push(
1892
- createRow(
1893
- columns.map((c) => c.header),
1894
- true
1895
- )
1896
- );
1897
- lines.push(separator("\u2500"));
1898
- data.forEach((row) => {
1899
- lines.push(createRow(columns.map((c) => String(row[c.key] || ""))));
1936
+ const headerCells = columns.map((c, i) => {
1937
+ const text = c.header.toUpperCase();
1938
+ const cell = i === columns.length - 1 ? text : padCell(text, widths[i]);
1939
+ return source_default.bold(cell);
1900
1940
  });
1901
- lines.push(separator("\u2500"));
1941
+ lines.push(headerCells.join(GUTTER).trimEnd());
1942
+ for (const row of data) {
1943
+ const cells = columns.map((c, i) => {
1944
+ let v = String(row[c.key] ?? "");
1945
+ if (visibleWidth(v) > widths[i]) {
1946
+ v = truncateCell(v, widths[i]);
1947
+ }
1948
+ return i === columns.length - 1 ? v : padCell(v, widths[i]);
1949
+ });
1950
+ lines.push(cells.join(GUTTER).trimEnd());
1951
+ }
1902
1952
  return lines.join("\n");
1903
1953
  }
1954
+ function isStdoutTty() {
1955
+ return Boolean(process.stdout.isTTY);
1956
+ }
1957
+ function formatToolMode(annotations) {
1958
+ if (annotations?.readOnlyHint === true) return source_default.green("read-only");
1959
+ if (annotations?.destructiveHint === true) return source_default.red("destructive");
1960
+ return source_default.yellow("write");
1961
+ }
1904
1962
  function formatJson(data, pretty = true) {
1905
1963
  if (pretty) {
1906
1964
  return JSON.stringify(data, null, 2);
@@ -1909,20 +1967,29 @@ function formatJson(data, pretty = true) {
1909
1967
  }
1910
1968
  function formatToolCall(result) {
1911
1969
  const lines = [];
1912
- if (result.isError) {
1970
+ const { isError, structuredContent } = result;
1971
+ const hasStructured = structuredContent !== void 0 && structuredContent !== null;
1972
+ const visibleContent = (result.content ?? []).filter(
1973
+ (item) => !hasStructured || item.type !== "text"
1974
+ );
1975
+ const hasVisibleContent = visibleContent.length > 0;
1976
+ if (isError) {
1913
1977
  lines.push(source_default.red("\u2717 Tool execution failed"));
1914
1978
  lines.push("");
1915
1979
  } else {
1916
1980
  lines.push(source_default.green("\u2713 Tool executed successfully"));
1917
1981
  lines.push("");
1918
1982
  }
1919
- if (result.content && result.content.length > 0) {
1920
- result.content.forEach((item, index) => {
1921
- if (result.content.length > 1) {
1983
+ if (hasVisibleContent) {
1984
+ if (isError) {
1985
+ lines.push(source_default.red.bold("Error details:"));
1986
+ }
1987
+ visibleContent.forEach((item, index) => {
1988
+ if (visibleContent.length > 1) {
1922
1989
  lines.push(source_default.bold(`Content ${index + 1}:`));
1923
1990
  }
1924
1991
  if (item.type === "text") {
1925
- lines.push(item.text);
1992
+ lines.push(isError ? source_default.red(item.text) : item.text);
1926
1993
  } else if (item.type === "image") {
1927
1994
  lines.push(source_default.cyan(`[Image: ${item.mimeType || "unknown type"}]`));
1928
1995
  if (item.data) {
@@ -1939,11 +2006,20 @@ function formatToolCall(result) {
1939
2006
  } else {
1940
2007
  lines.push(source_default.gray(`[Unknown content type: ${item.type}]`));
1941
2008
  }
1942
- if (index < result.content.length - 1) {
2009
+ if (index < visibleContent.length - 1) {
1943
2010
  lines.push("");
1944
2011
  }
1945
2012
  });
1946
2013
  }
2014
+ if (hasStructured) {
2015
+ if (isError) {
2016
+ lines.push(source_default.bold("Structured error data:"));
2017
+ }
2018
+ lines.push(formatJson(structuredContent));
2019
+ }
2020
+ if (isError && !hasVisibleContent && !hasStructured) {
2021
+ lines.push(source_default.gray("(no error details provided by server)"));
2022
+ }
1947
2023
  return lines.join("\n");
1948
2024
  }
1949
2025
  function formatResourceContent(content) {
@@ -2096,28 +2172,223 @@ function formatPromptMessages(messages) {
2096
2172
  return lines.join("\n");
2097
2173
  }
2098
2174
 
2175
+ // src/utils/parse-args.ts
2176
+ function parseToolArgs(rawArgs, inputSchema) {
2177
+ if (!rawArgs || rawArgs.length === 0) return {};
2178
+ if (rawArgs.length === 1) {
2179
+ const trimmed = rawArgs[0].trim();
2180
+ if (trimmed.startsWith("{")) {
2181
+ try {
2182
+ const parsed = JSON.parse(trimmed);
2183
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
2184
+ throw new Error("expected a JSON object");
2185
+ }
2186
+ return parsed;
2187
+ } catch (err) {
2188
+ throw new Error(`Invalid JSON arguments: ${err.message}`);
2189
+ }
2190
+ }
2191
+ }
2192
+ const props = inputSchema?.properties ?? {};
2193
+ const result = {};
2194
+ for (const raw of rawArgs) {
2195
+ const token = raw.startsWith("--") ? raw.slice(2) : raw;
2196
+ const jsonIdx = token.indexOf(":=");
2197
+ const eqIdx = token.indexOf("=");
2198
+ let key;
2199
+ let rawValue;
2200
+ let forceJson = false;
2201
+ if (jsonIdx !== -1 && (eqIdx === -1 || jsonIdx < eqIdx)) {
2202
+ key = token.slice(0, jsonIdx);
2203
+ rawValue = token.slice(jsonIdx + 2);
2204
+ forceJson = true;
2205
+ } else if (eqIdx !== -1) {
2206
+ key = token.slice(0, eqIdx);
2207
+ rawValue = token.slice(eqIdx + 1);
2208
+ } else {
2209
+ throw new Error(
2210
+ `Invalid argument '${raw}'. Use key=value or key:=jsonvalue.`
2211
+ );
2212
+ }
2213
+ if (!key) {
2214
+ throw new Error(`Empty key in argument '${raw}'`);
2215
+ }
2216
+ result[key] = coerceArgValue(rawValue, props[key], forceJson, key);
2217
+ }
2218
+ return result;
2219
+ }
2220
+ function coerceArgValue(value, propSchema, forceJson, key) {
2221
+ if (forceJson) {
2222
+ try {
2223
+ return JSON.parse(value);
2224
+ } catch (err) {
2225
+ throw new Error(
2226
+ `Invalid JSON value for '${key}': ${err.message}`
2227
+ );
2228
+ }
2229
+ }
2230
+ const types = Array.isArray(propSchema?.type) ? propSchema.type : propSchema?.type ? [propSchema.type] : [];
2231
+ if (types.includes("null") && (value === "null" || value === "")) {
2232
+ return null;
2233
+ }
2234
+ if (types.includes("integer")) {
2235
+ const n = Number(value);
2236
+ if (!Number.isFinite(n) || !Number.isInteger(n)) {
2237
+ throw new Error(`Expected integer for '${key}', got '${value}'`);
2238
+ }
2239
+ return n;
2240
+ }
2241
+ if (types.includes("number")) {
2242
+ const n = Number(value);
2243
+ if (!Number.isFinite(n)) {
2244
+ throw new Error(`Expected number for '${key}', got '${value}'`);
2245
+ }
2246
+ return n;
2247
+ }
2248
+ if (types.includes("boolean")) {
2249
+ if (value === "true" || value === "1") return true;
2250
+ if (value === "false" || value === "0") return false;
2251
+ throw new Error(
2252
+ `Expected boolean (true/false) for '${key}', got '${value}'`
2253
+ );
2254
+ }
2255
+ if (types.includes("array") || types.includes("object")) {
2256
+ try {
2257
+ return JSON.parse(value);
2258
+ } catch (err) {
2259
+ const wanted = types.includes("array") ? "array" : "object";
2260
+ throw new Error(
2261
+ `Expected JSON ${wanted} for '${key}'. Tip: use ${key}:=<json>. ${err.message}`
2262
+ );
2263
+ }
2264
+ }
2265
+ return value;
2266
+ }
2267
+ function parsePromptArgs(rawArgs) {
2268
+ if (!rawArgs || rawArgs.length === 0) return {};
2269
+ if (rawArgs.length === 1) {
2270
+ const trimmed = rawArgs[0].trim();
2271
+ if (trimmed.startsWith("{")) {
2272
+ try {
2273
+ const parsed = JSON.parse(trimmed);
2274
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
2275
+ throw new Error("expected a JSON object");
2276
+ }
2277
+ const out = {};
2278
+ for (const [k, v] of Object.entries(parsed)) {
2279
+ out[k] = typeof v === "string" ? v : JSON.stringify(v);
2280
+ }
2281
+ return out;
2282
+ } catch (err) {
2283
+ throw new Error(`Invalid JSON arguments: ${err.message}`);
2284
+ }
2285
+ }
2286
+ }
2287
+ const result = {};
2288
+ for (const raw of rawArgs) {
2289
+ const token = raw.startsWith("--") ? raw.slice(2) : raw;
2290
+ const eqIdx = token.indexOf("=");
2291
+ if (eqIdx === -1) {
2292
+ throw new Error(`Invalid argument '${raw}'. Use key=value.`);
2293
+ }
2294
+ const key = token.slice(0, eqIdx);
2295
+ const value = token.slice(eqIdx + 1);
2296
+ if (!key) {
2297
+ throw new Error(`Empty key in argument '${raw}'`);
2298
+ }
2299
+ result[key] = value;
2300
+ }
2301
+ return result;
2302
+ }
2303
+
2304
+ // src/utils/oauth.ts
2305
+ import {
2306
+ auth,
2307
+ NodeOAuthClientProvider,
2308
+ OAuthFlowError,
2309
+ UnauthorizedError
2310
+ } from "mcp-use/auth/node";
2311
+ import { createInterface } from "readline";
2312
+ async function buildOAuthProvider(serverUrl, options = {}) {
2313
+ return NodeOAuthClientProvider.create(serverUrl, {
2314
+ clientName: "mcp-use CLI",
2315
+ clientUri: "https://mcp-use.com",
2316
+ storageKeyPrefix: "mcp:auth",
2317
+ ...options,
2318
+ openBrowser: async (url) => {
2319
+ console.error(`
2320
+ Open this URL in a browser to authenticate:`);
2321
+ console.error(` ${url}
2322
+ `);
2323
+ }
2324
+ });
2325
+ }
2326
+ async function runOAuthFlow(provider, serverUrl, print = console.error.bind(console)) {
2327
+ print(`\u2192 OAuth authentication required.`);
2328
+ print(
2329
+ ` Listening on http://127.0.0.1:${provider.callbackPort}/callback (waiting up to 5m)`
2330
+ );
2331
+ if (!provider.hasPendingFlow) {
2332
+ const result = await auth(provider, { serverUrl });
2333
+ if (result === "AUTHORIZED") {
2334
+ return;
2335
+ }
2336
+ if (result !== "REDIRECT") {
2337
+ throw new OAuthFlowError(
2338
+ "unexpected_auth_result",
2339
+ `auth() returned ${result}`
2340
+ );
2341
+ }
2342
+ }
2343
+ const code = await provider.getAuthorizationCode();
2344
+ await auth(provider, { serverUrl, authorizationCode: code });
2345
+ }
2346
+ function isUnauthorized(err) {
2347
+ if (err instanceof UnauthorizedError) return true;
2348
+ if (err instanceof Error && err.name === "UnauthorizedError") return true;
2349
+ if (err instanceof Error && err.code === 401) {
2350
+ return true;
2351
+ }
2352
+ return false;
2353
+ }
2354
+ async function promptYesNo(question, defaultYes = true) {
2355
+ if (!process.stdin.isTTY) return false;
2356
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
2357
+ try {
2358
+ const answer = await new Promise((resolve2) => {
2359
+ rl.question(`${question} ${defaultYes ? "[Y/n] " : "[y/N] "}`, resolve2);
2360
+ });
2361
+ const trimmed = answer.trim().toLowerCase();
2362
+ if (!trimmed) return defaultYes;
2363
+ return trimmed === "y" || trimmed === "yes";
2364
+ } finally {
2365
+ rl.close();
2366
+ }
2367
+ }
2368
+
2099
2369
  // src/utils/session-storage.ts
2100
2370
  import { homedir } from "os";
2101
2371
  import { join } from "path";
2102
2372
  import { readFile, writeFile, mkdir } from "fs/promises";
2103
2373
  import { existsSync } from "fs";
2104
2374
  var SESSION_FILE_PATH = join(homedir(), ".mcp-use", "cli-sessions.json");
2375
+ var _dirEnsured = false;
2105
2376
  async function ensureSessionDir() {
2106
- const dir = join(homedir(), ".mcp-use");
2107
- if (!existsSync(dir)) {
2108
- await mkdir(dir, { recursive: true });
2109
- }
2377
+ if (_dirEnsured) return;
2378
+ await mkdir(join(homedir(), ".mcp-use"), { recursive: true });
2379
+ _dirEnsured = true;
2110
2380
  }
2111
2381
  async function loadSessions() {
2112
2382
  try {
2113
2383
  await ensureSessionDir();
2114
2384
  if (!existsSync(SESSION_FILE_PATH)) {
2115
- return { activeSession: null, sessions: {} };
2385
+ return { sessions: {} };
2116
2386
  }
2117
2387
  const content = await readFile(SESSION_FILE_PATH, "utf-8");
2118
- return JSON.parse(content);
2119
- } catch (error) {
2120
- return { activeSession: null, sessions: {} };
2388
+ const parsed = JSON.parse(content);
2389
+ return { sessions: parsed?.sessions ?? {} };
2390
+ } catch {
2391
+ return { sessions: {} };
2121
2392
  }
2122
2393
  }
2123
2394
  async function saveSessions(storage) {
@@ -2130,40 +2401,22 @@ async function saveSession(name, config) {
2130
2401
  ...config,
2131
2402
  lastUsed: (/* @__PURE__ */ new Date()).toISOString()
2132
2403
  };
2133
- if (!storage.activeSession) {
2134
- storage.activeSession = name;
2135
- }
2136
2404
  await saveSessions(storage);
2137
2405
  }
2138
- async function getActiveSession() {
2406
+ async function removeSession(name) {
2139
2407
  const storage = await loadSessions();
2140
- if (!storage.activeSession || !storage.sessions[storage.activeSession]) {
2141
- return null;
2142
- }
2143
- return {
2144
- name: storage.activeSession,
2145
- config: storage.sessions[storage.activeSession]
2146
- };
2408
+ delete storage.sessions[name];
2409
+ await saveSessions(storage);
2147
2410
  }
2148
2411
  async function getSession(name) {
2149
2412
  const storage = await loadSessions();
2150
2413
  return storage.sessions[name] || null;
2151
2414
  }
2152
- async function setActiveSession(name) {
2153
- const storage = await loadSessions();
2154
- if (!storage.sessions[name]) {
2155
- throw new Error(`Session '${name}' not found`);
2156
- }
2157
- storage.activeSession = name;
2158
- storage.sessions[name].lastUsed = (/* @__PURE__ */ new Date()).toISOString();
2159
- await saveSessions(storage);
2160
- }
2161
2415
  async function listAllSessions() {
2162
2416
  const storage = await loadSessions();
2163
2417
  return Object.entries(storage.sessions).map(([name, config]) => ({
2164
2418
  name,
2165
- config,
2166
- isActive: name === storage.activeSession
2419
+ config
2167
2420
  }));
2168
2421
  }
2169
2422
  async function updateSessionInfo(name, serverInfo, capabilities) {
@@ -2176,40 +2429,68 @@ async function updateSessionInfo(name, serverInfo, capabilities) {
2176
2429
  }
2177
2430
  }
2178
2431
 
2179
- // src/commands/client.ts
2432
+ // src/utils/session.ts
2433
+ import { MCPClient } from "mcp-use/client";
2434
+ import { getPackageVersion } from "mcp-use/server";
2180
2435
  var activeSessions = /* @__PURE__ */ new Map();
2181
- async function getOrRestoreSession(sessionName) {
2182
- if (!sessionName) {
2183
- const active = await getActiveSession();
2184
- if (!active) {
2185
- console.error(
2186
- formatError("No active session. Connect to a server first.")
2187
- );
2188
- console.error(
2189
- formatInfo("Use: npx mcp-use client connect <url> --name <name>")
2190
- );
2191
- return null;
2436
+ function getCliClientInfo() {
2437
+ return {
2438
+ name: "mcp-use CLI",
2439
+ title: "mcp-use CLI",
2440
+ version: getPackageVersion(),
2441
+ description: "mcp-use CLI - Command-line interface for MCP servers",
2442
+ icons: [
2443
+ {
2444
+ src: "https://manufact.com/logo.png"
2445
+ }
2446
+ ],
2447
+ websiteUrl: "https://manufact.com"
2448
+ };
2449
+ }
2450
+ async function cleanupAndExit(code) {
2451
+ for (const [name, { client }] of activeSessions) {
2452
+ try {
2453
+ await client.closeAllSessions();
2454
+ } catch {
2192
2455
  }
2193
- sessionName = active.name;
2456
+ activeSessions.delete(name);
2194
2457
  }
2458
+ process.exit(code);
2459
+ }
2460
+ async function getOrRestoreSession(sessionName) {
2195
2461
  if (activeSessions.has(sessionName)) {
2196
2462
  const { session } = activeSessions.get(sessionName);
2197
2463
  return { name: sessionName, session };
2198
2464
  }
2199
2465
  const config = await getSession(sessionName);
2200
2466
  if (!config) {
2201
- console.error(formatError(`Session '${sessionName}' not found`));
2467
+ console.error(formatError(`Server '${sessionName}' not found`));
2468
+ console.error(
2469
+ formatInfo(
2470
+ `Connect with: npx mcp-use client connect ${sessionName} <url>`
2471
+ )
2472
+ );
2202
2473
  return null;
2203
2474
  }
2204
2475
  try {
2205
2476
  const client = new MCPClient();
2206
2477
  const cliClientInfo = getCliClientInfo();
2478
+ let authProvider;
2207
2479
  if (config.type === "http") {
2208
- client.addServer(sessionName, {
2209
- url: config.url,
2210
- headers: config.authToken ? { Authorization: `Bearer ${config.authToken}` } : void 0,
2211
- clientInfo: cliClientInfo
2212
- });
2480
+ if (config.authMode === "oauth") {
2481
+ authProvider = await buildOAuthProvider(config.url);
2482
+ client.addServer(sessionName, {
2483
+ url: config.url,
2484
+ authProvider,
2485
+ clientInfo: cliClientInfo
2486
+ });
2487
+ } else {
2488
+ client.addServer(sessionName, {
2489
+ url: config.url,
2490
+ headers: config.authToken ? { Authorization: `Bearer ${config.authToken}` } : void 0,
2491
+ clientInfo: cliClientInfo
2492
+ });
2493
+ }
2213
2494
  } else if (config.type === "stdio") {
2214
2495
  client.addServer(sessionName, {
2215
2496
  command: config.command,
@@ -2221,37 +2502,1028 @@ async function getOrRestoreSession(sessionName) {
2221
2502
  console.error(formatError(`Unknown session type: ${config.type}`));
2222
2503
  return null;
2223
2504
  }
2224
- const session = await client.createSession(sessionName);
2225
- activeSessions.set(sessionName, { client, session });
2226
- console.error(formatInfo(`Reconnected to session '${sessionName}'`));
2227
- return { name: sessionName, session };
2228
- } catch (error) {
2229
- console.error(formatError(`Failed to restore session: ${error.message}`));
2230
- return null;
2505
+ let session;
2506
+ try {
2507
+ session = await client.createSession(sessionName);
2508
+ } catch (err) {
2509
+ if (config.type === "http" && config.authMode === "oauth" && authProvider && isUnauthorized(err)) {
2510
+ const reAuth = await promptYesNo(
2511
+ `! Tokens for server '${sessionName}' expired and could not refresh. Re-authenticate now?`,
2512
+ true
2513
+ );
2514
+ if (!reAuth) {
2515
+ console.error(formatError(`Tokens expired and could not refresh.`));
2516
+ console.error(
2517
+ formatInfo(
2518
+ `Run: mcp-use client connect ${sessionName} ${config.url}`
2519
+ )
2520
+ );
2521
+ return null;
2522
+ }
2523
+ await runOAuthFlow(authProvider, config.url);
2524
+ session = await client.createSession(sessionName);
2525
+ } else {
2526
+ throw err;
2527
+ }
2528
+ }
2529
+ activeSessions.set(sessionName, { client, session });
2530
+ return { name: sessionName, session };
2531
+ } catch (error) {
2532
+ console.error(formatError(`Failed to restore server: ${error.message}`));
2533
+ return null;
2534
+ }
2535
+ }
2536
+
2537
+ // src/commands/client-auth.ts
2538
+ async function resolveSession(name) {
2539
+ const config = await getSession(name);
2540
+ if (!config) {
2541
+ console.error(formatError(`Server '${name}' not found`));
2542
+ return null;
2543
+ }
2544
+ if (config.type !== "http") {
2545
+ console.error(formatError("Auth commands only apply to HTTP servers"));
2546
+ return null;
2547
+ }
2548
+ if (config.authMode !== "oauth") {
2549
+ console.error(
2550
+ formatError(
2551
+ `Server '${name}' was not authenticated via OAuth (authMode=${config.authMode ?? "bearer"})`
2552
+ )
2553
+ );
2554
+ return null;
2555
+ }
2556
+ return { name, url: config.url };
2557
+ }
2558
+ function formatExpiresIn(expSec) {
2559
+ const ms = expSec * 1e3 - Date.now();
2560
+ if (ms <= 0) return "expired";
2561
+ const mins = Math.round(ms / 6e4);
2562
+ if (mins < 60) return `${mins}m`;
2563
+ const hours = Math.floor(mins / 60);
2564
+ const rem = mins % 60;
2565
+ return rem ? `${hours}h${rem}m` : `${hours}h`;
2566
+ }
2567
+ function decodeJwtExp(token) {
2568
+ try {
2569
+ const parts = token.split(".");
2570
+ if (parts.length < 2) return null;
2571
+ const payload = JSON.parse(
2572
+ Buffer.from(parts[1], "base64url").toString("utf-8")
2573
+ );
2574
+ return typeof payload.exp === "number" ? payload.exp : null;
2575
+ } catch {
2576
+ return null;
2577
+ }
2578
+ }
2579
+ async function authStatusCommand(name) {
2580
+ const target = await resolveSession(name);
2581
+ if (!target) {
2582
+ process.exit(1);
2583
+ }
2584
+ const { name: serverName, url } = target;
2585
+ const provider = await buildOAuthProvider(url);
2586
+ const tokens = await provider.tokens();
2587
+ const fields = {
2588
+ server: serverName,
2589
+ url,
2590
+ tokens: tokens?.access_token ? "present" : "missing"
2591
+ };
2592
+ if (tokens?.scope) fields.scope = tokens.scope;
2593
+ if (tokens?.access_token) {
2594
+ const exp = decodeJwtExp(tokens.access_token);
2595
+ fields.expires_in = exp ? formatExpiresIn(exp) : "unknown (opaque token)";
2596
+ }
2597
+ fields.refresh = tokens?.refresh_token ? "available" : "missing";
2598
+ console.log(formatKeyValue(fields));
2599
+ if (!tokens?.access_token) process.exit(1);
2600
+ }
2601
+ async function authRefreshCommand(name) {
2602
+ const target = await resolveSession(name);
2603
+ if (!target) {
2604
+ process.exit(1);
2605
+ }
2606
+ const { name: serverName, url } = target;
2607
+ const provider = await buildOAuthProvider(url);
2608
+ const refreshed = await provider.forceRefresh();
2609
+ if (!refreshed) {
2610
+ console.error(
2611
+ formatError(
2612
+ "Refresh failed (no refresh_token, or server rejected). Re-connect to re-authenticate."
2613
+ )
2614
+ );
2615
+ console.error(
2616
+ formatInfo(`Run: mcp-use client connect ${serverName} ${url}`)
2617
+ );
2618
+ process.exit(1);
2619
+ }
2620
+ const exp = refreshed.access_token ? decodeJwtExp(refreshed.access_token) : null;
2621
+ console.log(
2622
+ formatSuccess(
2623
+ `Refreshed access token${exp ? ` (expires in ${formatExpiresIn(exp)})` : ""}`
2624
+ )
2625
+ );
2626
+ }
2627
+ async function authLogoutCommand(name) {
2628
+ const target = await resolveSession(name);
2629
+ if (!target) {
2630
+ process.exit(1);
2631
+ }
2632
+ const { name: serverName, url } = target;
2633
+ const provider = await buildOAuthProvider(url);
2634
+ await provider.invalidateCredentials("all");
2635
+ console.log(formatSuccess(`Removed tokens for ${url}`));
2636
+ console.log(
2637
+ formatInfo(
2638
+ `Server '${serverName}' kept; reconnect with \`mcp-use client connect\`.`
2639
+ )
2640
+ );
2641
+ }
2642
+
2643
+ // src/commands/screenshot.ts
2644
+ import { Command } from "commander";
2645
+ import { MCPClient as MCPClient2 } from "mcp-use/client";
2646
+ import { spawn as spawn2 } from "child_process";
2647
+ import { existsSync as existsSync2 } from "fs";
2648
+ import { mkdir as mkdir2 } from "fs/promises";
2649
+ import { createServer } from "net";
2650
+ import path6 from "path";
2651
+
2652
+ // src/utils/cdp-screenshot.ts
2653
+ import { spawn } from "child_process";
2654
+ import { mkdtempSync, rmSync, writeFileSync } from "fs";
2655
+ import os4 from "os";
2656
+ import path4 from "path";
2657
+ import WebSocket from "ws";
2658
+ var CdpClient = class {
2659
+ constructor(ws) {
2660
+ this.ws = ws;
2661
+ ws.on("message", (data) => {
2662
+ let msg;
2663
+ try {
2664
+ msg = JSON.parse(data.toString());
2665
+ } catch {
2666
+ return;
2667
+ }
2668
+ if (typeof msg.id !== "number") return;
2669
+ const cb = this.pending.get(msg.id);
2670
+ if (!cb) return;
2671
+ this.pending.delete(msg.id);
2672
+ if (msg.error) {
2673
+ cb.reject(new Error(msg.error.message ?? "CDP error"));
2674
+ } else {
2675
+ cb.resolve(msg.result ?? {});
2676
+ }
2677
+ });
2678
+ ws.on("close", () => {
2679
+ for (const cb of this.pending.values()) {
2680
+ cb.reject(new Error("CDP WebSocket closed"));
2681
+ }
2682
+ this.pending.clear();
2683
+ });
2684
+ ws.on("error", (err) => {
2685
+ for (const cb of this.pending.values()) {
2686
+ cb.reject(err);
2687
+ }
2688
+ this.pending.clear();
2689
+ });
2690
+ }
2691
+ nextId = 0;
2692
+ pending = /* @__PURE__ */ new Map();
2693
+ send(method, params = {}, sessionId) {
2694
+ const id = ++this.nextId;
2695
+ const payload = { id, method, params };
2696
+ if (sessionId) payload.sessionId = sessionId;
2697
+ return new Promise((resolve2, reject) => {
2698
+ this.pending.set(id, {
2699
+ resolve: (r) => resolve2(r),
2700
+ reject
2701
+ });
2702
+ this.ws.send(JSON.stringify(payload));
2703
+ });
2704
+ }
2705
+ close() {
2706
+ try {
2707
+ this.ws.close();
2708
+ } catch {
2709
+ }
2710
+ }
2711
+ };
2712
+ function waitForDevToolsUrl(child, timeoutMs = 5e3) {
2713
+ return new Promise((resolve2, reject) => {
2714
+ let buf = "";
2715
+ const onData = (d) => {
2716
+ buf += d.toString();
2717
+ const m = buf.match(/DevTools listening on (ws:\/\/\S+)/);
2718
+ if (m) {
2719
+ cleanup();
2720
+ resolve2(m[1]);
2721
+ }
2722
+ };
2723
+ const onExit = (code) => {
2724
+ cleanup();
2725
+ reject(
2726
+ new Error(
2727
+ `Chrome exited (code ${code}) before exposing a DevTools port. Last stderr: ${buf.slice(-500)}`
2728
+ )
2729
+ );
2730
+ };
2731
+ const cleanup = () => {
2732
+ child.stderr?.off("data", onData);
2733
+ child.off("exit", onExit);
2734
+ clearTimeout(timer);
2735
+ };
2736
+ const timer = setTimeout(() => {
2737
+ cleanup();
2738
+ reject(
2739
+ new Error(`Chrome did not expose a DevTools port within ${timeoutMs}ms`)
2740
+ );
2741
+ }, timeoutMs);
2742
+ child.stderr?.on("data", onData);
2743
+ child.on("exit", onExit);
2744
+ });
2745
+ }
2746
+ async function captureScreenshot(opts) {
2747
+ let userDataDir;
2748
+ let child;
2749
+ let cdp;
2750
+ let cleanedUp = false;
2751
+ const cleanup = () => {
2752
+ if (cleanedUp) return;
2753
+ cleanedUp = true;
2754
+ cdp?.close();
2755
+ if (child && !child.killed) {
2756
+ try {
2757
+ child.kill("SIGTERM");
2758
+ } catch {
2759
+ }
2760
+ const localChild = child;
2761
+ const killTimer = setTimeout(() => {
2762
+ if (!localChild.killed) {
2763
+ try {
2764
+ localChild.kill("SIGKILL");
2765
+ } catch {
2766
+ }
2767
+ }
2768
+ }, 2e3);
2769
+ killTimer.unref();
2770
+ }
2771
+ if (userDataDir) {
2772
+ try {
2773
+ rmSync(userDataDir, { recursive: true, force: true });
2774
+ } catch {
2775
+ }
2776
+ }
2777
+ };
2778
+ try {
2779
+ let wsUrl;
2780
+ if (opts.cdpUrl) {
2781
+ wsUrl = opts.cdpUrl;
2782
+ } else {
2783
+ if (!opts.chromePath) {
2784
+ throw new Error(
2785
+ "captureScreenshot requires either `cdpUrl` or `chromePath`"
2786
+ );
2787
+ }
2788
+ userDataDir = mkdtempSync(path4.join(os4.tmpdir(), "mcp-use-chrome-"));
2789
+ const chromeArgs = [
2790
+ "--headless=new",
2791
+ "--remote-debugging-port=0",
2792
+ `--user-data-dir=${userDataDir}`,
2793
+ "--no-first-run",
2794
+ "--no-default-browser-check",
2795
+ "--disable-extensions",
2796
+ "--disable-gpu",
2797
+ "--hide-scrollbars",
2798
+ "--mute-audio",
2799
+ `--window-size=${opts.width},${opts.height}`,
2800
+ "about:blank"
2801
+ ];
2802
+ child = spawn(opts.chromePath, chromeArgs, {
2803
+ stdio: ["ignore", "pipe", "pipe"]
2804
+ });
2805
+ child.stdout?.resume();
2806
+ wsUrl = await waitForDevToolsUrl(child);
2807
+ }
2808
+ const ws = new WebSocket(wsUrl);
2809
+ await new Promise((resolve2, reject) => {
2810
+ const onOpen = () => {
2811
+ ws.off("error", onError);
2812
+ resolve2();
2813
+ };
2814
+ const onError = (err) => {
2815
+ ws.off("open", onOpen);
2816
+ reject(err);
2817
+ };
2818
+ ws.once("open", onOpen);
2819
+ ws.once("error", onError);
2820
+ });
2821
+ cdp = new CdpClient(ws);
2822
+ let sessionId;
2823
+ if (opts.cdpUrl) {
2824
+ const attachPromise = new Promise((resolve2, reject) => {
2825
+ const timer = setTimeout(
2826
+ () => reject(
2827
+ new Error(
2828
+ "Timed out waiting for Target.attachedToTarget event from remote CDP"
2829
+ )
2830
+ ),
2831
+ 1e4
2832
+ );
2833
+ const onMessage = (data) => {
2834
+ try {
2835
+ const msg = JSON.parse(data.toString());
2836
+ if (msg.method === "Target.attachedToTarget" && msg.params?.targetInfo?.type === "page" && typeof msg.params.sessionId === "string") {
2837
+ clearTimeout(timer);
2838
+ ws.off("message", onMessage);
2839
+ resolve2(msg.params.sessionId);
2840
+ }
2841
+ } catch {
2842
+ }
2843
+ };
2844
+ ws.on("message", onMessage);
2845
+ });
2846
+ await cdp.send("Target.setAutoAttach", {
2847
+ autoAttach: true,
2848
+ waitForDebuggerOnStart: false,
2849
+ flatten: true
2850
+ });
2851
+ sessionId = await attachPromise;
2852
+ } else {
2853
+ const { targetId } = await cdp.send(
2854
+ "Target.createTarget",
2855
+ { url: "about:blank" }
2856
+ );
2857
+ const attach = await cdp.send(
2858
+ "Target.attachToTarget",
2859
+ { targetId, flatten: true }
2860
+ );
2861
+ sessionId = attach.sessionId;
2862
+ }
2863
+ await cdp.send("Page.enable", {}, sessionId);
2864
+ await cdp.send(
2865
+ "Emulation.setDeviceMetricsOverride",
2866
+ {
2867
+ width: opts.width,
2868
+ height: opts.height,
2869
+ deviceScaleFactor: 1,
2870
+ mobile: false
2871
+ },
2872
+ sessionId
2873
+ );
2874
+ await cdp.send(
2875
+ "Emulation.setEmulatedMedia",
2876
+ {
2877
+ features: [
2878
+ { name: "prefers-color-scheme", value: opts.theme },
2879
+ { name: "prefers-reduced-motion", value: "reduce" }
2880
+ ]
2881
+ },
2882
+ sessionId
2883
+ );
2884
+ if (opts.bundle !== void 0) {
2885
+ const payload = JSON.stringify(JSON.stringify(opts.bundle));
2886
+ await cdp.send(
2887
+ "Page.addScriptToEvaluateOnNewDocument",
2888
+ {
2889
+ source: `globalThis.__mcpUsePreviewBundle = JSON.parse(${payload});`,
2890
+ runImmediately: true
2891
+ },
2892
+ sessionId
2893
+ );
2894
+ }
2895
+ await cdp.send("Page.navigate", { url: opts.url }, sessionId);
2896
+ const start = Date.now();
2897
+ const exprSelector = JSON.stringify(opts.waitForSelector);
2898
+ while (true) {
2899
+ const r = await cdp.send(
2900
+ "Runtime.evaluate",
2901
+ {
2902
+ expression: `!!document.querySelector(${exprSelector})`,
2903
+ returnByValue: true
2904
+ },
2905
+ sessionId
2906
+ );
2907
+ if (r.result?.value === true) break;
2908
+ if (Date.now() - start > opts.timeoutMs) {
2909
+ throw new Error(
2910
+ `Timed out after ${opts.timeoutMs}ms waiting for selector "${opts.waitForSelector}"`
2911
+ );
2912
+ }
2913
+ await new Promise((res) => setTimeout(res, 100));
2914
+ }
2915
+ if (opts.delayMs && opts.delayMs > 0) {
2916
+ await new Promise((res) => setTimeout(res, opts.delayMs));
2917
+ }
2918
+ const shot = await cdp.send(
2919
+ "Page.captureScreenshot",
2920
+ {
2921
+ format: "png",
2922
+ clip: {
2923
+ x: 0,
2924
+ y: 0,
2925
+ width: opts.width,
2926
+ height: opts.height,
2927
+ scale: 1
2928
+ }
2929
+ },
2930
+ sessionId
2931
+ );
2932
+ writeFileSync(opts.outputPath, Buffer.from(shot.data, "base64"));
2933
+ } finally {
2934
+ cleanup();
2935
+ }
2936
+ }
2937
+
2938
+ // src/utils/chrome-path.ts
2939
+ import { accessSync, constants } from "fs";
2940
+ import path5 from "path";
2941
+ var ENV_VAR_NAMES = [
2942
+ "MCP_USE_CHROME_PATH",
2943
+ "PUPPETEER_EXECUTABLE_PATH",
2944
+ "CHROME_PATH"
2945
+ ];
2946
+ var DARWIN_PATHS = [
2947
+ "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
2948
+ "/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary",
2949
+ "/Applications/Chromium.app/Contents/MacOS/Chromium",
2950
+ "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
2951
+ "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser"
2952
+ ];
2953
+ var LINUX_BINARIES = [
2954
+ "google-chrome-stable",
2955
+ "google-chrome",
2956
+ "chromium",
2957
+ "chromium-browser",
2958
+ "microsoft-edge",
2959
+ "microsoft-edge-stable",
2960
+ "brave-browser"
2961
+ ];
2962
+ var WIN_SUBPATHS = [
2963
+ "Google\\Chrome\\Application\\chrome.exe",
2964
+ "Microsoft\\Edge\\Application\\msedge.exe",
2965
+ "BraveSoftware\\Brave-Browser\\Application\\brave.exe",
2966
+ "Chromium\\Application\\chrome.exe"
2967
+ ];
2968
+ function isAccessible(p) {
2969
+ try {
2970
+ accessSync(p, constants.F_OK);
2971
+ return true;
2972
+ } catch {
2973
+ return false;
2974
+ }
2975
+ }
2976
+ function findOnPath(binary) {
2977
+ const PATH = process.env.PATH ?? "";
2978
+ for (const dir of PATH.split(":")) {
2979
+ if (!dir) continue;
2980
+ const candidate = path5.posix.join(dir, binary);
2981
+ if (isAccessible(candidate)) return candidate;
2982
+ }
2983
+ return null;
2984
+ }
2985
+ function findChrome() {
2986
+ for (const name of ENV_VAR_NAMES) {
2987
+ const v = process.env[name];
2988
+ if (v && isAccessible(v)) return v;
2989
+ }
2990
+ if (process.platform === "darwin") {
2991
+ for (const p of DARWIN_PATHS) {
2992
+ if (isAccessible(p)) return p;
2993
+ }
2994
+ return null;
2995
+ }
2996
+ if (process.platform === "linux") {
2997
+ for (const bin of LINUX_BINARIES) {
2998
+ const p = findOnPath(bin);
2999
+ if (p) return p;
3000
+ }
3001
+ return null;
3002
+ }
3003
+ if (process.platform === "win32") {
3004
+ const dirs = [
3005
+ process.env["ProgramFiles"],
3006
+ process.env["ProgramFiles(x86)"],
3007
+ process.env["LocalAppData"]
3008
+ ].filter((d) => Boolean(d));
3009
+ for (const dir of dirs) {
3010
+ for (const sub of WIN_SUBPATHS) {
3011
+ const candidate = path5.join(dir, sub);
3012
+ if (isAccessible(candidate)) return candidate;
3013
+ }
3014
+ }
3015
+ return null;
3016
+ }
3017
+ return null;
3018
+ }
3019
+ function resolveChromePath() {
3020
+ const found = findChrome();
3021
+ if (found) return found;
3022
+ throw new Error(
3023
+ "Could not find Chrome, Chromium, Edge, or Brave on this system. Install Chrome from https://google.com/chrome, or set MCP_USE_CHROME_PATH (or PUPPETEER_EXECUTABLE_PATH / CHROME_PATH) to a browser executable."
3024
+ );
3025
+ }
3026
+
3027
+ // src/commands/screenshot.ts
3028
+ function parseHeaderArg(raw) {
3029
+ const idx = raw.indexOf(":");
3030
+ if (idx === -1) {
3031
+ throw new Error(
3032
+ `Invalid --header value "${raw}". Expected "Key: Value" (e.g. "Authorization: Bearer xyz").`
3033
+ );
3034
+ }
3035
+ const key = raw.slice(0, idx).trim();
3036
+ const value = raw.slice(idx + 1).trim();
3037
+ if (!key) {
3038
+ throw new Error(`Invalid --header value "${raw}". Header name is empty.`);
3039
+ }
3040
+ return [key, value];
3041
+ }
3042
+ function parseHeaderArgs(args) {
3043
+ const headers = {};
3044
+ for (const raw of args) {
3045
+ const [key, value] = parseHeaderArg(raw);
3046
+ headers[key] = value;
3047
+ }
3048
+ return headers;
3049
+ }
3050
+ function collectHeader(value, previous = []) {
3051
+ return previous.concat([value]);
3052
+ }
3053
+ function detectToolResourceUri(tool) {
3054
+ if (!tool) return null;
3055
+ const meta = tool._meta;
3056
+ if (!meta) return null;
3057
+ const uiMeta = meta.ui ?? void 0;
3058
+ return uiMeta?.resourceUri ?? meta["openai/outputTemplate"] ?? null;
3059
+ }
3060
+ async function captureToolScreenshot(inputs, options = {}) {
3061
+ const width = options.width ?? 800;
3062
+ const height = options.height ?? 600;
3063
+ const theme = options.theme ?? "light";
3064
+ const timeoutMs = options.timeoutMs ?? 3e4;
3065
+ const delayMs = options.delayMs ?? 0;
3066
+ const chromePath = options.cdpUrl ? void 0 : resolveChromePath();
3067
+ const view = extractViewName(inputs.resourceUri);
3068
+ const devOptions = {
3069
+ width: String(width),
3070
+ height: String(height),
3071
+ theme,
3072
+ timeout: String(timeoutMs),
3073
+ inspector: options.inspector,
3074
+ quiet: options.quiet
3075
+ };
3076
+ let devHandle;
3077
+ try {
3078
+ devHandle = await ensureDevServer(devOptions);
3079
+ const resourceContents = await inputs.session.readResource(
3080
+ inputs.resourceUri
3081
+ );
3082
+ const bundle = {
3083
+ resourceUri: inputs.resourceUri,
3084
+ resourceContents,
3085
+ toolInput: inputs.toolArgs,
3086
+ toolOutput: inputs.toolOutput
3087
+ };
3088
+ const previewUrl = new URL(`/inspector/preview/${view}`, devHandle.url);
3089
+ previewUrl.searchParams.set("theme", theme);
3090
+ const ts = timestampSuffix();
3091
+ const outputPath = path6.resolve(options.output ?? `./${view}-${ts}.png`);
3092
+ await mkdir2(path6.dirname(outputPath), { recursive: true });
3093
+ await captureScreenshot({
3094
+ url: previewUrl.toString(),
3095
+ width,
3096
+ height,
3097
+ theme,
3098
+ waitForSelector: options.waitFor ?? 'body[data-view-ready="true"]',
3099
+ timeoutMs,
3100
+ outputPath,
3101
+ chromePath,
3102
+ cdpUrl: options.cdpUrl,
3103
+ delayMs: Number.isFinite(delayMs) && delayMs > 0 ? delayMs : 0,
3104
+ bundle
3105
+ });
3106
+ return { outputPath, width, height, view };
3107
+ } finally {
3108
+ killChild(devHandle?.child);
3109
+ }
3110
+ }
3111
+ function getFreePort() {
3112
+ return new Promise((resolve2, reject) => {
3113
+ const srv = createServer();
3114
+ srv.unref();
3115
+ srv.on("error", reject);
3116
+ srv.listen(0, () => {
3117
+ const addr = srv.address();
3118
+ if (typeof addr === "object" && addr) {
3119
+ const port = addr.port;
3120
+ srv.close(() => resolve2(port));
3121
+ } else {
3122
+ srv.close(() => reject(new Error("Failed to allocate free port")));
3123
+ }
3124
+ });
3125
+ });
3126
+ }
3127
+ async function probeServer(url, timeoutMs = 1500) {
3128
+ const controller = new AbortController();
3129
+ const t = setTimeout(() => controller.abort(), timeoutMs);
3130
+ try {
3131
+ const u = new URL("/inspector/health", url);
3132
+ const res = await fetch(u, { signal: controller.signal });
3133
+ if (!res.ok) return false;
3134
+ const ct = res.headers.get("content-type") ?? "";
3135
+ if (!ct.includes("application/json")) return false;
3136
+ const body = await res.json();
3137
+ return body?.status === "ok";
3138
+ } catch {
3139
+ return false;
3140
+ } finally {
3141
+ clearTimeout(t);
3142
+ }
3143
+ }
3144
+ async function waitForHealth(url, timeoutMs = 15e3) {
3145
+ const deadline = Date.now() + timeoutMs;
3146
+ while (Date.now() < deadline) {
3147
+ if (await probeServer(url)) return true;
3148
+ await new Promise((r) => setTimeout(r, 200));
3149
+ }
3150
+ return false;
3151
+ }
3152
+ function resolveInspectorCli() {
3153
+ const candidateRoots = /* @__PURE__ */ new Set();
3154
+ const moduleDir = typeof __dirname !== "undefined" ? __dirname : path6.dirname(new URL(import.meta.url).pathname);
3155
+ candidateRoots.add(moduleDir);
3156
+ candidateRoots.add(process.cwd());
3157
+ for (const start of candidateRoots) {
3158
+ let dir = start;
3159
+ while (true) {
3160
+ const candidate = path6.join(
3161
+ dir,
3162
+ "node_modules",
3163
+ "@mcp-use",
3164
+ "inspector",
3165
+ "dist",
3166
+ "cli.js"
3167
+ );
3168
+ if (existsSync2(candidate)) return candidate;
3169
+ const parent = path6.dirname(dir);
3170
+ if (parent === dir) break;
3171
+ dir = parent;
3172
+ }
3173
+ }
3174
+ throw new Error(
3175
+ "Could not locate `@mcp-use/inspector` in node_modules. Install the inspector package or pass --inspector <url> to use an existing instance."
3176
+ );
3177
+ }
3178
+ async function ensureDevServer(options) {
3179
+ if (options.inspector) {
3180
+ const ok = await probeServer(options.inspector);
3181
+ if (!ok) {
3182
+ throw new Error(
3183
+ `Inspector at ${options.inspector} did not respond on /inspector/health with status:"ok"`
3184
+ );
3185
+ }
3186
+ return { url: options.inspector };
3187
+ }
3188
+ const port = await getFreePort();
3189
+ const url = `http://localhost:${port}`;
3190
+ if (!options.quiet) {
3191
+ console.error(formatInfo(`Starting inspector on port ${port}\u2026`));
3192
+ }
3193
+ const inspectorCli = resolveInspectorCli();
3194
+ const child = spawn2(
3195
+ process.execPath,
3196
+ [inspectorCli, "--port", String(port), "--no-open"],
3197
+ {
3198
+ cwd: process.cwd(),
3199
+ stdio: ["ignore", "pipe", "pipe"],
3200
+ env: { ...process.env, MCP_INSPECTOR_MODE: "standalone" }
3201
+ }
3202
+ );
3203
+ const prefix = source_default.gray("[inspector]");
3204
+ if (!options.quiet) {
3205
+ child.stdout?.on("data", (d) => {
3206
+ process.stderr.write(`${prefix} ${d}`);
3207
+ });
3208
+ child.stderr?.on("data", (d) => {
3209
+ process.stderr.write(`${prefix} ${d}`);
3210
+ });
3211
+ } else {
3212
+ child.stdout?.resume();
3213
+ child.stderr?.resume();
3214
+ }
3215
+ const ready = await waitForHealth(url);
3216
+ if (!ready) {
3217
+ child.kill("SIGTERM");
3218
+ throw new Error(`Inspector failed to come up on ${url} within 15s.`);
3219
+ }
3220
+ return { url, child };
3221
+ }
3222
+ function killChild(child) {
3223
+ if (!child || child.killed) return;
3224
+ try {
3225
+ child.kill("SIGTERM");
3226
+ } catch {
3227
+ }
3228
+ }
3229
+ function timestampSuffix(date = /* @__PURE__ */ new Date()) {
3230
+ const pad = (n) => String(n).padStart(2, "0");
3231
+ const datePart = `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
3232
+ const timePart = `${pad(date.getHours())}-${pad(date.getMinutes())}-${pad(date.getSeconds())}`;
3233
+ return `${datePart}_${timePart}`;
3234
+ }
3235
+ function extractViewName(resourceUri) {
3236
+ const m = resourceUri.match(/^ui:\/\/widget\/(.+)$/);
3237
+ if (!m) return resourceUri;
3238
+ return m[1].replace(/\.html$/, "").replace(/\.[0-9a-f]+$/i, "");
3239
+ }
3240
+ function parseDimension(raw, name) {
3241
+ const n = parseInt(raw, 10);
3242
+ if (!Number.isFinite(n) || n <= 0) {
3243
+ throw new Error(`--${name} must be a positive integer (got "${raw}")`);
3244
+ }
3245
+ return n;
3246
+ }
3247
+ var AD_HOC_SESSION_NAME = "__screenshot_ad_hoc__";
3248
+ async function resolveSessionForScreenshot(options, headers) {
3249
+ if (options.session) {
3250
+ const result = await getOrRestoreSession(options.session);
3251
+ return result?.session ?? null;
3252
+ }
3253
+ if (options.mcp) {
3254
+ const client = new MCPClient2();
3255
+ client.addServer(AD_HOC_SESSION_NAME, {
3256
+ url: options.mcp,
3257
+ ...headers ? { headers } : {},
3258
+ clientInfo: getCliClientInfo()
3259
+ });
3260
+ try {
3261
+ const session = await client.createSession(AD_HOC_SESSION_NAME);
3262
+ activeSessions.set(AD_HOC_SESSION_NAME, { client, session });
3263
+ return session;
3264
+ } catch (err) {
3265
+ const msg = err instanceof Error ? err.message : String(err);
3266
+ console.error(formatError(`Failed to connect to ${options.mcp}: ${msg}`));
3267
+ return null;
3268
+ }
3269
+ }
3270
+ console.error(
3271
+ formatError(
3272
+ "No MCP target. Pass --session <name> (a saved server) or --mcp <url> (ad-hoc)."
3273
+ )
3274
+ );
3275
+ return null;
3276
+ }
3277
+ async function screenshotCommand(options, argsList) {
3278
+ let exitCode = 0;
3279
+ try {
3280
+ if (!options.tool) {
3281
+ console.error(
3282
+ formatError(
3283
+ "--tool <name> is required (optionally with key=value args)."
3284
+ )
3285
+ );
3286
+ exitCode = 1;
3287
+ return;
3288
+ }
3289
+ let headers;
3290
+ if (options.header && options.header.length > 0) {
3291
+ if (!options.mcp) {
3292
+ console.error(
3293
+ formatError(
3294
+ "--header is only supported with --mcp <url>. Saved sessions (use --session) carry their own auth from `mcp-use client connect`."
3295
+ )
3296
+ );
3297
+ exitCode = 1;
3298
+ return;
3299
+ }
3300
+ try {
3301
+ headers = parseHeaderArgs(options.header);
3302
+ } catch (err) {
3303
+ console.error(
3304
+ formatError(err instanceof Error ? err.message : String(err))
3305
+ );
3306
+ exitCode = 1;
3307
+ return;
3308
+ }
3309
+ }
3310
+ try {
3311
+ resolveChromePath();
3312
+ } catch (err) {
3313
+ console.error(
3314
+ formatError(err instanceof Error ? err.message : String(err))
3315
+ );
3316
+ exitCode = 1;
3317
+ return;
3318
+ }
3319
+ const width = parseDimension(options.width, "width");
3320
+ const height = parseDimension(options.height, "height");
3321
+ const navTimeout = parseInt(options.timeout, 10) || 3e4;
3322
+ const delayMs = options.delay ? parseInt(options.delay, 10) : 0;
3323
+ const session = await resolveSessionForScreenshot(options, headers);
3324
+ if (!session) {
3325
+ exitCode = 1;
3326
+ return;
3327
+ }
3328
+ const tool = session.tools.find((t) => t.name === options.tool);
3329
+ if (!tool) {
3330
+ throw new Error(
3331
+ `Tool "${options.tool}" not found. Available: ${session.tools.map((t) => t.name).join(", ")}`
3332
+ );
3333
+ }
3334
+ const resourceUri = detectToolResourceUri(tool);
3335
+ if (!resourceUri) {
3336
+ throw new Error(
3337
+ `Tool "${options.tool}" does not declare a UI resource (expected _meta.ui.resourceUri or openai/outputTemplate).`
3338
+ );
3339
+ }
3340
+ let toolArgs = {};
3341
+ if (argsList && argsList.length > 0) {
3342
+ try {
3343
+ toolArgs = parseToolArgs(
3344
+ argsList,
3345
+ tool.inputSchema
3346
+ );
3347
+ } catch (err) {
3348
+ console.error(
3349
+ formatError(err instanceof Error ? err.message : String(err))
3350
+ );
3351
+ console.log("");
3352
+ console.log(formatInfo("Usage:"));
3353
+ console.log(
3354
+ ` npx mcp-use screenshot --tool ${options.tool} key=value [key2=value2 ...]`
3355
+ );
3356
+ console.log(
3357
+ ` npx mcp-use screenshot --tool ${options.tool} nested:='{"a":1}' # JSON value`
3358
+ );
3359
+ console.log(
3360
+ ` npx mcp-use screenshot --tool ${options.tool} '{"key":"value"}' # full JSON object`
3361
+ );
3362
+ if (tool.inputSchema) {
3363
+ console.log("");
3364
+ console.log(formatInfo("Tool schema:"));
3365
+ console.log(formatSchema(tool.inputSchema));
3366
+ }
3367
+ exitCode = 1;
3368
+ return;
3369
+ }
3370
+ }
3371
+ const toolOutput = await session.callTool(options.tool, toolArgs);
3372
+ const result = await captureToolScreenshot(
3373
+ {
3374
+ session,
3375
+ toolName: options.tool,
3376
+ toolArgs,
3377
+ toolOutput,
3378
+ resourceUri
3379
+ },
3380
+ {
3381
+ width,
3382
+ height,
3383
+ theme: options.theme,
3384
+ output: options.output,
3385
+ waitFor: options.waitFor,
3386
+ delayMs,
3387
+ timeoutMs: navTimeout,
3388
+ inspector: options.inspector,
3389
+ quiet: options.quiet,
3390
+ cdpUrl: options.cdpUrl
3391
+ }
3392
+ );
3393
+ console.log(
3394
+ `Saved screenshot: ${result.outputPath} (${result.width}\xD7${result.height})`
3395
+ );
3396
+ } catch (err) {
3397
+ const msg = err instanceof Error ? err.message : String(err);
3398
+ console.error(formatError(`Screenshot failed: ${msg}`));
3399
+ exitCode = 1;
3400
+ } finally {
3401
+ await cleanupAndExit(exitCode);
3402
+ }
3403
+ }
3404
+ function createScreenshotCommand() {
3405
+ return new Command("screenshot").description(
3406
+ "Render an MCP Apps view headlessly and save a PNG by calling a tool and rendering its UI resource with the result."
3407
+ ).argument(
3408
+ "[args...]",
3409
+ "Tool args as key=value pairs (use key:=<json> for nested values, or pass a single JSON object)."
3410
+ ).option(
3411
+ "--tool <name>",
3412
+ "Tool to call. Its UI resource is rendered with the result."
3413
+ ).option("--width <px>", "Browser viewport width in pixels.", "800").option("--height <px>", "Browser viewport height in pixels.", "600").option(
3414
+ "--inspector <url>",
3415
+ "Inspector host that serves /inspector/preview/:view. When omitted, probes localhost:3000 then auto-spawns `mcp-use dev`."
3416
+ ).option(
3417
+ "--session <name>",
3418
+ "Saved server name (from `mcp-use client connect <name> <url>`)."
3419
+ ).option(
3420
+ "--mcp <url>",
3421
+ "Ad-hoc MCP server URL (escape hatch). Use when you don't have a saved server. No authentication unless --header is supplied."
3422
+ ).option(
3423
+ "-H, --header <header>",
3424
+ '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.',
3425
+ collectHeader,
3426
+ []
3427
+ ).option(
3428
+ "--theme <light|dark>",
3429
+ "Color scheme to render the view in.",
3430
+ "light"
3431
+ ).option(
3432
+ "--output <path>",
3433
+ "Output PNG path. Defaults to ./<view>-<timestamp>.png in cwd."
3434
+ ).option(
3435
+ "--wait-for <selector>",
3436
+ 'Override readiness selector (default: body[data-view-ready="true"]).'
3437
+ ).option(
3438
+ "--delay <ms>",
3439
+ "Extra wait after readiness, to let chart animations / async layouts settle.",
3440
+ "0"
3441
+ ).option("--timeout <ms>", "Navigation + readiness timeout in ms.", "30000").option(
3442
+ "--cdp-url <url>",
3443
+ "Connect to an existing CDP WebSocket (ws:// or wss://) instead of spawning local Chrome. Useful for hosted browsers like Notte."
3444
+ ).option("--quiet", "Suppress dev-server output.").action(async (args, opts) => {
3445
+ await screenshotCommand(opts, args);
3446
+ });
3447
+ }
3448
+
3449
+ // src/commands/client.ts
3450
+ var RESERVED_CLIENT_SUBCOMMANDS = /* @__PURE__ */ new Set([
3451
+ "connect",
3452
+ "list",
3453
+ "remove",
3454
+ "help"
3455
+ ]);
3456
+ var PER_CLIENT_SCOPES = /* @__PURE__ */ new Set([
3457
+ "tools",
3458
+ "resources",
3459
+ "prompts",
3460
+ "auth",
3461
+ "disconnect",
3462
+ "interactive"
3463
+ ]);
3464
+ async function connectCommand(name, urlOrCommand, options) {
3465
+ if (!name || !urlOrCommand) {
3466
+ const looksLikeUrl = !!name && /^https?:\/\//i.test(name);
3467
+ if (looksLikeUrl && !urlOrCommand && !options.stdio) {
3468
+ console.error(formatError("Missing server name."));
3469
+ console.error("");
3470
+ console.error(
3471
+ formatInfo(
3472
+ "Each saved server needs a short name you'll use to address it later."
3473
+ )
3474
+ );
3475
+ console.error("");
3476
+ console.error("Try:");
3477
+ console.error(` mcp-use client connect <name> ${name}`);
3478
+ console.error("");
3479
+ console.error("Example:");
3480
+ console.error(` mcp-use client connect my-server ${name}`);
3481
+ console.error(" mcp-use client my-server tools list");
3482
+ } else if (name && !urlOrCommand) {
3483
+ console.error(
3484
+ formatError(options.stdio ? "Missing <command>." : "Missing <url>.")
3485
+ );
3486
+ console.error("");
3487
+ console.error(formatInfo("Usage:"));
3488
+ console.error(
3489
+ options.stdio ? ` mcp-use client connect ${name} "<command>" --stdio` : ` mcp-use client connect ${name} <url>`
3490
+ );
3491
+ } else {
3492
+ console.error(formatError("Missing required arguments: <name> <url>."));
3493
+ console.error("");
3494
+ console.error(formatInfo("Usage:"));
3495
+ console.error(" mcp-use client connect <name> <url>");
3496
+ console.error("");
3497
+ console.error("Example:");
3498
+ console.error(
3499
+ " mcp-use client connect manufact https://mcp.manufact.com/mcp"
3500
+ );
3501
+ }
3502
+ await cleanupAndExit(1);
3503
+ }
3504
+ const sessionName = name;
3505
+ const target = urlOrCommand;
3506
+ if (PER_CLIENT_SCOPES.has(sessionName)) {
3507
+ console.error(
3508
+ formatError(
3509
+ `'${sessionName}' is a reserved name and can't be used for a saved server.`
3510
+ )
3511
+ );
3512
+ console.error("");
3513
+ console.error(
3514
+ `Reserved names: ${Array.from(PER_CLIENT_SCOPES).sort().join(", ")}`
3515
+ );
3516
+ console.error("");
3517
+ console.error("Pick a different name, e.g.:");
3518
+ console.error(` mcp-use client connect my-${sessionName} ${target}`);
3519
+ await cleanupAndExit(1);
2231
3520
  }
2232
- }
2233
- function getCliClientInfo() {
2234
- return {
2235
- name: "mcp-use CLI",
2236
- title: "mcp-use CLI",
2237
- version: getPackageVersion(),
2238
- description: "mcp-use CLI - Command-line interface for MCP servers",
2239
- icons: [
2240
- {
2241
- src: "https://manufact.com/logo.png"
2242
- }
2243
- ],
2244
- websiteUrl: "https://manufact.com"
2245
- };
2246
- }
2247
- async function connectCommand(urlOrCommand, options) {
2248
3521
  try {
2249
- const sessionName = options.name || `session-${Date.now()}`;
2250
- const client = new MCPClient();
3522
+ const client = new MCPClient3();
2251
3523
  let session;
2252
3524
  const cliClientInfo = getCliClientInfo();
2253
3525
  if (options.stdio) {
2254
- const parts = urlOrCommand.split(" ");
3526
+ const parts = target.split(" ");
2255
3527
  const command = parts[0];
2256
3528
  const args = parts.slice(1);
2257
3529
  console.error(
@@ -2270,17 +3542,41 @@ async function connectCommand(urlOrCommand, options) {
2270
3542
  lastUsed: (/* @__PURE__ */ new Date()).toISOString()
2271
3543
  });
2272
3544
  } else {
2273
- console.error(formatInfo(`Connecting to ${urlOrCommand}...`));
3545
+ console.error(formatInfo(`Connecting to ${target}...`));
3546
+ const wantOAuth = !options.auth && options.oauth !== false;
3547
+ let authProvider;
3548
+ if (wantOAuth) {
3549
+ const authTimeoutMs = options.authTimeout ? Number.parseInt(options.authTimeout, 10) : void 0;
3550
+ authProvider = await buildOAuthProvider(target, {
3551
+ ...authTimeoutMs ? { authTimeoutMs } : {}
3552
+ });
3553
+ }
2274
3554
  client.addServer(sessionName, {
2275
- url: urlOrCommand,
2276
- headers: options.auth ? { Authorization: `Bearer ${options.auth}` } : void 0,
3555
+ url: target,
3556
+ ...authProvider ? { authProvider } : options.auth ? { headers: { Authorization: `Bearer ${options.auth}` } } : {},
2277
3557
  clientInfo: cliClientInfo
2278
3558
  });
2279
- session = await client.createSession(sessionName);
3559
+ try {
3560
+ session = await client.createSession(sessionName);
3561
+ } catch (err) {
3562
+ if (authProvider && isUnauthorized(err)) {
3563
+ console.error(
3564
+ formatWarning(
3565
+ "Server requires authentication. Starting OAuth flow."
3566
+ )
3567
+ );
3568
+ await runOAuthFlow(authProvider, target);
3569
+ console.error(formatSuccess("Authentication successful"));
3570
+ session = await client.createSession(sessionName);
3571
+ } else {
3572
+ throw err;
3573
+ }
3574
+ }
2280
3575
  await saveSession(sessionName, {
2281
3576
  type: "http",
2282
- url: urlOrCommand,
2283
- authToken: options.auth,
3577
+ url: target,
3578
+ authMode: authProvider ? "oauth" : options.auth ? "bearer" : void 0,
3579
+ authToken: authProvider ? void 0 : options.auth,
2284
3580
  lastUsed: (/* @__PURE__ */ new Date()).toISOString()
2285
3581
  });
2286
3582
  }
@@ -2290,7 +3586,7 @@ async function connectCommand(urlOrCommand, options) {
2290
3586
  if (serverInfo) {
2291
3587
  await updateSessionInfo(sessionName, serverInfo, capabilities);
2292
3588
  }
2293
- console.log(formatSuccess(`Connected to ${sessionName}`));
3589
+ console.log(formatSuccess(`Connected as '${sessionName}'`));
2294
3590
  if (serverInfo) {
2295
3591
  console.log("");
2296
3592
  console.log(formatHeader("Server Information:"));
@@ -2316,119 +3612,173 @@ async function connectCommand(urlOrCommand, options) {
2316
3612
  );
2317
3613
  } catch (error) {
2318
3614
  console.error(formatError(`Connection failed: ${error.message}`));
2319
- process.exit(1);
3615
+ await cleanupAndExit(1);
2320
3616
  }
3617
+ await cleanupAndExit(0);
2321
3618
  }
2322
- async function disconnectCommand(sessionName, options) {
3619
+ async function disconnectCommand(name) {
2323
3620
  try {
2324
- if (options?.all) {
2325
- for (const [name, { client }] of activeSessions.entries()) {
2326
- await client.closeAllSessions();
2327
- activeSessions.delete(name);
2328
- console.log(formatSuccess(`Disconnected from ${name}`));
2329
- }
2330
- return;
2331
- }
2332
- if (!sessionName) {
2333
- const active = await getActiveSession();
2334
- if (!active) {
2335
- console.error(formatError("No active session to disconnect"));
2336
- return;
2337
- }
2338
- sessionName = active.name;
2339
- }
2340
- const sessionData = activeSessions.get(sessionName);
3621
+ const sessionData = activeSessions.get(name);
2341
3622
  if (sessionData) {
2342
3623
  await sessionData.client.closeAllSessions();
2343
- activeSessions.delete(sessionName);
2344
- console.log(formatSuccess(`Disconnected from ${sessionName}`));
3624
+ activeSessions.delete(name);
3625
+ console.log(formatSuccess(`Disconnected from ${name}`));
2345
3626
  } else {
2346
- console.log(formatInfo(`Session '${sessionName}' is not connected`));
3627
+ console.log(formatInfo(`Server '${name}' is not connected`));
2347
3628
  }
2348
3629
  } catch (error) {
2349
3630
  console.error(formatError(`Failed to disconnect: ${error.message}`));
2350
- process.exit(1);
3631
+ await cleanupAndExit(1);
3632
+ }
3633
+ await cleanupAndExit(0);
3634
+ }
3635
+ async function removeClientCommand(name) {
3636
+ try {
3637
+ const config = await getSession(name);
3638
+ if (!config) {
3639
+ console.error(formatError(`Server '${name}' not found`));
3640
+ console.error("");
3641
+ console.error("See your saved servers with:");
3642
+ console.error(" mcp-use client list");
3643
+ await cleanupAndExit(1);
3644
+ }
3645
+ const sessionData = activeSessions.get(name);
3646
+ if (sessionData) {
3647
+ await sessionData.client.closeAllSessions();
3648
+ activeSessions.delete(name);
3649
+ }
3650
+ const isOAuthHttp = config.type === "http" && config.authMode === "oauth" && typeof config.url === "string";
3651
+ const sharedUrlSibling = isOAuthHttp ? (await listAllSessions()).find(
3652
+ (s) => s.name !== name && s.config.type === "http" && s.config.url === config.url
3653
+ ) : void 0;
3654
+ await removeSession(name);
3655
+ console.log(formatSuccess(`Removed saved server '${name}'`));
3656
+ if (isOAuthHttp) {
3657
+ if (sharedUrlSibling) {
3658
+ console.log(
3659
+ formatInfo(
3660
+ `OAuth tokens for ${config.url} were kept because saved server '${sharedUrlSibling.name}' still uses that URL.`
3661
+ )
3662
+ );
3663
+ } else {
3664
+ try {
3665
+ const provider = await buildOAuthProvider(config.url);
3666
+ await provider.invalidateCredentials("all");
3667
+ console.log(formatInfo(`Removed OAuth tokens for ${config.url}`));
3668
+ } catch (error) {
3669
+ console.error(
3670
+ formatWarning(
3671
+ `Saved entry removed, but failed to clear OAuth tokens for ${config.url}: ${error.message}`
3672
+ )
3673
+ );
3674
+ }
3675
+ }
3676
+ }
3677
+ } catch (error) {
3678
+ console.error(formatError(`Failed to remove server: ${error.message}`));
3679
+ await cleanupAndExit(1);
2351
3680
  }
3681
+ await cleanupAndExit(0);
2352
3682
  }
2353
- async function listSessionsCommand() {
3683
+ async function listClientsCommand() {
2354
3684
  try {
2355
3685
  const sessions = await listAllSessions();
2356
3686
  if (sessions.length === 0) {
2357
- console.log(formatInfo("No saved sessions"));
2358
- console.log(
2359
- formatInfo("Connect to a server with: npx mcp-use client connect <url>")
2360
- );
3687
+ if (isStdoutTty()) {
3688
+ console.log(formatInfo("No saved servers"));
3689
+ console.log(
3690
+ formatInfo(
3691
+ "Connect to a server with: npx mcp-use client connect <name> <url>"
3692
+ )
3693
+ );
3694
+ }
2361
3695
  return;
2362
3696
  }
2363
- console.log(formatHeader("Saved Sessions:"));
2364
- console.log("");
3697
+ const tty2 = isStdoutTty();
3698
+ if (tty2) {
3699
+ console.log(formatHeader("Saved Servers:"));
3700
+ console.log("");
3701
+ }
2365
3702
  const tableData = sessions.map((s) => ({
2366
- name: s.isActive ? source_default.green.bold(`${s.name} *`) : s.name,
3703
+ name: s.name,
2367
3704
  type: s.config.type,
2368
3705
  target: s.config.type === "http" ? s.config.url || "" : `${s.config.command} ${(s.config.args || []).join(" ")}`,
2369
- server: s.config.serverInfo?.name || "unknown",
2370
- status: activeSessions.has(s.name) ? source_default.green("connected") : source_default.gray("disconnected")
3706
+ server: s.config.serverInfo?.name || "unknown"
2371
3707
  }));
2372
3708
  console.log(
2373
3709
  formatTable(tableData, [
2374
3710
  { key: "name", header: "Name" },
2375
3711
  { key: "type", header: "Type" },
2376
- { key: "target", header: "Target", width: 40 },
2377
- { key: "server", header: "Server" },
2378
- { key: "status", header: "Status" }
3712
+ { key: "target", header: "Target", truncate: true },
3713
+ { key: "server", header: "Server" }
2379
3714
  ])
2380
3715
  );
2381
- console.log("");
2382
- console.log(source_default.gray("* = active session"));
2383
- } catch (error) {
2384
- console.error(formatError(`Failed to list sessions: ${error.message}`));
2385
- process.exit(1);
2386
- }
2387
- }
2388
- async function switchSessionCommand(name) {
2389
- try {
2390
- await setActiveSession(name);
2391
- console.log(formatSuccess(`Switched to session '${name}'`));
2392
3716
  } catch (error) {
2393
- console.error(formatError(`Failed to switch session: ${error.message}`));
2394
- process.exit(1);
3717
+ console.error(formatError(`Failed to list servers: ${error.message}`));
3718
+ await cleanupAndExit(1);
2395
3719
  }
3720
+ await cleanupAndExit(0);
2396
3721
  }
2397
- async function listToolsCommand(options) {
3722
+ async function listToolsCommand(name, options) {
2398
3723
  try {
2399
- const result = await getOrRestoreSession(options.session || null);
2400
- if (!result) return;
3724
+ const result = await getOrRestoreSession(name);
3725
+ if (!result) {
3726
+ await cleanupAndExit(1);
3727
+ }
2401
3728
  const { session } = result;
2402
3729
  const tools = await session.listTools();
2403
3730
  if (options.json) {
2404
3731
  console.log(formatJson(tools));
2405
- return;
2406
- }
2407
- if (tools.length === 0) {
2408
- console.log(formatInfo("No tools available"));
2409
- return;
3732
+ } else if (tools.length === 0) {
3733
+ if (isStdoutTty()) console.log(formatInfo("No tools available"));
3734
+ } else {
3735
+ const tty2 = isStdoutTty();
3736
+ if (tty2) {
3737
+ console.log(formatHeader(`Available Tools (${tools.length}):`));
3738
+ console.log("");
3739
+ }
3740
+ const tableData = tools.map((tool) => {
3741
+ const props = tool.inputSchema?.properties ?? {};
3742
+ const required = tool.inputSchema?.required ?? [];
3743
+ const total = Object.keys(props).length;
3744
+ const reqCount = Array.isArray(required) ? required.length : 0;
3745
+ const argsCell = total === 0 ? source_default.gray("\u2014") : `${reqCount}/${total}`;
3746
+ return {
3747
+ name: source_default.bold(tool.name),
3748
+ mode: formatToolMode(tool.annotations),
3749
+ args: argsCell,
3750
+ description: tool.description || source_default.gray("(no description)")
3751
+ };
3752
+ });
3753
+ console.log(
3754
+ formatTable(tableData, [
3755
+ { key: "name", header: "Tool" },
3756
+ { key: "mode", header: "Mode" },
3757
+ { key: "args", header: "Args" },
3758
+ { key: "description", header: "Description", truncate: true }
3759
+ ])
3760
+ );
3761
+ if (tty2) {
3762
+ console.log("");
3763
+ console.log(
3764
+ source_default.gray(
3765
+ "ARGS shows required/total. Modes: read-only \xB7 write \xB7 destructive."
3766
+ )
3767
+ );
3768
+ }
2410
3769
  }
2411
- console.log(formatHeader(`Available Tools (${tools.length}):`));
2412
- console.log("");
2413
- const tableData = tools.map((tool) => ({
2414
- name: source_default.bold(tool.name),
2415
- description: tool.description || source_default.gray("No description")
2416
- }));
2417
- console.log(
2418
- formatTable(tableData, [
2419
- { key: "name", header: "Tool", width: 25 },
2420
- { key: "description", header: "Description", width: 50 }
2421
- ])
2422
- );
2423
3770
  } catch (error) {
2424
3771
  console.error(formatError(`Failed to list tools: ${error.message}`));
2425
- process.exit(1);
3772
+ await cleanupAndExit(1);
2426
3773
  }
3774
+ await cleanupAndExit(0);
2427
3775
  }
2428
- async function describeToolCommand(toolName, options) {
3776
+ async function describeToolCommand(name, toolName) {
2429
3777
  try {
2430
- const result = await getOrRestoreSession(options.session || null);
2431
- if (!result) return;
3778
+ const result = await getOrRestoreSession(name);
3779
+ if (!result) {
3780
+ await cleanupAndExit(1);
3781
+ }
2432
3782
  const { session } = result;
2433
3783
  const tools = session.tools;
2434
3784
  const tool = tools.find((t) => t.name === toolName);
@@ -2437,7 +3787,7 @@ async function describeToolCommand(toolName, options) {
2437
3787
  console.log("");
2438
3788
  console.log(formatInfo("Available tools:"));
2439
3789
  tools.forEach((t) => console.log(` \u2022 ${t.name}`));
2440
- return;
3790
+ await cleanupAndExit(1);
2441
3791
  }
2442
3792
  console.log(formatHeader(`Tool: ${tool.name}`));
2443
3793
  console.log("");
@@ -2451,94 +3801,167 @@ async function describeToolCommand(toolName, options) {
2451
3801
  }
2452
3802
  } catch (error) {
2453
3803
  console.error(formatError(`Failed to describe tool: ${error.message}`));
2454
- process.exit(1);
3804
+ await cleanupAndExit(1);
2455
3805
  }
3806
+ await cleanupAndExit(0);
2456
3807
  }
2457
- async function callToolCommand(toolName, argsJson, options) {
3808
+ async function callToolCommand(name, toolName, argsList, options) {
2458
3809
  try {
2459
- const result = await getOrRestoreSession(options?.session || null);
2460
- if (!result) return;
3810
+ const result = await getOrRestoreSession(name);
3811
+ if (!result) {
3812
+ await cleanupAndExit(1);
3813
+ }
2461
3814
  const { session } = result;
3815
+ const tools = session.tools;
3816
+ const tool = tools.find((t) => t.name === toolName);
2462
3817
  let args = {};
2463
- if (argsJson) {
3818
+ if (argsList && argsList.length > 0) {
2464
3819
  try {
2465
- args = JSON.parse(argsJson);
3820
+ args = parseToolArgs(argsList, tool?.inputSchema);
2466
3821
  } catch (error) {
2467
- console.error(formatError("Invalid JSON arguments"));
2468
- return;
2469
- }
2470
- } else {
2471
- const tools = session.tools;
2472
- const tool = tools.find((t) => t.name === toolName);
2473
- if (tool?.inputSchema?.required && tool.inputSchema.required.length > 0) {
2474
- console.error(
2475
- formatError(
2476
- "This tool requires arguments. Provide them as a JSON string."
2477
- )
2478
- );
3822
+ console.error(formatError(error.message));
2479
3823
  console.log("");
2480
- console.log(formatInfo("Example:"));
3824
+ console.log(formatInfo("Usage:"));
2481
3825
  console.log(
2482
- ` npx mcp-use client tools call ${toolName} '{"param": "value"}'`
3826
+ ` npx mcp-use client ${name} tools call ${toolName} key=value [key2=value2 ...]`
2483
3827
  );
2484
- console.log("");
2485
- console.log(formatInfo("Tool schema:"));
2486
- console.log(formatSchema(tool.inputSchema));
2487
- return;
3828
+ console.log(
3829
+ ` npx mcp-use client ${name} tools call ${toolName} nested:='{"a":1}' # JSON value`
3830
+ );
3831
+ console.log(
3832
+ ` npx mcp-use client ${name} tools call ${toolName} '{"key":"value"}' # full JSON object`
3833
+ );
3834
+ if (tool?.inputSchema) {
3835
+ console.log("");
3836
+ console.log(formatInfo("Tool schema:"));
3837
+ console.log(formatSchema(tool.inputSchema));
3838
+ }
3839
+ await cleanupAndExit(1);
2488
3840
  }
3841
+ } else if (tool?.inputSchema?.required && tool.inputSchema.required.length > 0) {
3842
+ console.error(formatError("This tool requires arguments."));
3843
+ console.log("");
3844
+ console.log(formatInfo("Provide arguments as key=value pairs:"));
3845
+ console.log(
3846
+ ` npx mcp-use client ${name} tools call ${toolName} key=value [key2=value2 ...]`
3847
+ );
3848
+ console.log("");
3849
+ console.log(formatInfo("Tool schema:"));
3850
+ console.log(formatSchema(tool.inputSchema));
3851
+ await cleanupAndExit(1);
2489
3852
  }
2490
3853
  console.error(formatInfo(`Calling tool '${toolName}'...`));
2491
3854
  const callResult = await session.callTool(toolName, args, {
2492
3855
  timeout: options?.timeout
2493
3856
  });
3857
+ let screenshot = null;
3858
+ let screenshotError = null;
3859
+ if (options?.screenshot !== false) {
3860
+ const tool2 = session.tools.find((t) => t.name === toolName);
3861
+ const resourceUri = detectToolResourceUri(tool2);
3862
+ if (resourceUri) {
3863
+ console.error(
3864
+ formatInfo(`Capturing widget screenshot (${resourceUri})...`)
3865
+ );
3866
+ try {
3867
+ const shot = await captureToolScreenshot(
3868
+ {
3869
+ session,
3870
+ toolName,
3871
+ toolArgs: args,
3872
+ toolOutput: callResult,
3873
+ resourceUri
3874
+ },
3875
+ options?.screenshotOutput ? { output: options.screenshotOutput } : {}
3876
+ );
3877
+ screenshot = {
3878
+ path: shot.outputPath,
3879
+ width: shot.width,
3880
+ height: shot.height,
3881
+ view: shot.view
3882
+ };
3883
+ } catch (err) {
3884
+ screenshotError = err?.message ?? String(err);
3885
+ }
3886
+ }
3887
+ }
2494
3888
  if (options?.json) {
2495
3889
  console.log(formatJson(callResult));
2496
3890
  } else {
2497
3891
  console.log(formatToolCall(callResult));
2498
3892
  }
3893
+ if (screenshot) {
3894
+ console.error(
3895
+ formatSuccess(
3896
+ `Saved widget screenshot: ${screenshot.path} (${screenshot.width}\xD7${screenshot.height})`
3897
+ )
3898
+ );
3899
+ }
3900
+ if (screenshotError) {
3901
+ console.error(
3902
+ formatWarning(`Skipped widget screenshot: ${screenshotError}`)
3903
+ );
3904
+ }
3905
+ if (callResult.isError) {
3906
+ await cleanupAndExit(1);
3907
+ }
2499
3908
  } catch (error) {
2500
3909
  console.error(formatError(`Failed to call tool: ${error.message}`));
2501
- process.exit(1);
3910
+ if (error?.data !== void 0) {
3911
+ console.error(
3912
+ source_default.gray(
3913
+ typeof error.data === "string" ? error.data : formatJson(error.data)
3914
+ )
3915
+ );
3916
+ }
3917
+ await cleanupAndExit(1);
2502
3918
  }
3919
+ await cleanupAndExit(0);
2503
3920
  }
2504
- async function listResourcesCommand(options) {
3921
+ async function listResourcesCommand(name, options) {
2505
3922
  try {
2506
- const result = await getOrRestoreSession(options.session || null);
2507
- if (!result) return;
3923
+ const result = await getOrRestoreSession(name);
3924
+ if (!result) {
3925
+ await cleanupAndExit(1);
3926
+ }
2508
3927
  const { session } = result;
2509
3928
  const resourcesResult = await session.listAllResources();
2510
3929
  const resources = resourcesResult.resources;
2511
3930
  if (options.json) {
2512
3931
  console.log(formatJson(resources));
2513
- return;
2514
- }
2515
- if (resources.length === 0) {
2516
- console.log(formatInfo("No resources available"));
2517
- return;
3932
+ } else if (resources.length === 0) {
3933
+ if (isStdoutTty()) console.log(formatInfo("No resources available"));
3934
+ } else {
3935
+ const tty2 = isStdoutTty();
3936
+ if (tty2) {
3937
+ console.log(formatHeader(`Available Resources (${resources.length}):`));
3938
+ console.log("");
3939
+ }
3940
+ const tableData = resources.map((resource) => ({
3941
+ name: source_default.bold(resource.name || "(no name)"),
3942
+ type: resource.mimeType || source_default.gray("unknown"),
3943
+ uri: resource.uri
3944
+ }));
3945
+ console.log(
3946
+ formatTable(tableData, [
3947
+ { key: "name", header: "Name" },
3948
+ { key: "type", header: "Type" },
3949
+ { key: "uri", header: "URI", truncate: true }
3950
+ ])
3951
+ );
2518
3952
  }
2519
- console.log(formatHeader(`Available Resources (${resources.length}):`));
2520
- console.log("");
2521
- const tableData = resources.map((resource) => ({
2522
- uri: resource.uri,
2523
- name: resource.name || source_default.gray("(no name)"),
2524
- type: resource.mimeType || source_default.gray("unknown")
2525
- }));
2526
- console.log(
2527
- formatTable(tableData, [
2528
- { key: "uri", header: "URI", width: 40 },
2529
- { key: "name", header: "Name", width: 20 },
2530
- { key: "type", header: "Type", width: 15 }
2531
- ])
2532
- );
2533
3953
  } catch (error) {
2534
3954
  console.error(formatError(`Failed to list resources: ${error.message}`));
2535
- process.exit(1);
3955
+ await cleanupAndExit(1);
2536
3956
  }
3957
+ await cleanupAndExit(0);
2537
3958
  }
2538
- async function readResourceCommand(uri, options) {
3959
+ async function readResourceCommand(name, uri, options) {
2539
3960
  try {
2540
- const result = await getOrRestoreSession(options.session || null);
2541
- if (!result) return;
3961
+ const result = await getOrRestoreSession(name);
3962
+ if (!result) {
3963
+ await cleanupAndExit(1);
3964
+ }
2542
3965
  const { session } = result;
2543
3966
  console.error(formatInfo(`Reading resource: ${uri}`));
2544
3967
  const resource = await session.readResource(uri);
@@ -2549,13 +3972,16 @@ async function readResourceCommand(uri, options) {
2549
3972
  }
2550
3973
  } catch (error) {
2551
3974
  console.error(formatError(`Failed to read resource: ${error.message}`));
2552
- process.exit(1);
3975
+ await cleanupAndExit(1);
2553
3976
  }
3977
+ await cleanupAndExit(0);
2554
3978
  }
2555
- async function subscribeResourceCommand(uri, options) {
3979
+ async function subscribeResourceCommand(name, uri) {
2556
3980
  try {
2557
- const result = await getOrRestoreSession(options.session || null);
2558
- if (!result) return;
3981
+ const result = await getOrRestoreSession(name);
3982
+ if (!result) {
3983
+ await cleanupAndExit(1);
3984
+ }
2559
3985
  const { session } = result;
2560
3986
  await session.subscribeToResource(uri);
2561
3987
  console.log(formatSuccess(`Subscribed to resource: ${uri}`));
@@ -2573,13 +3999,15 @@ async function subscribeResourceCommand(uri, options) {
2573
3999
  console.error(
2574
4000
  formatError(`Failed to subscribe to resource: ${error.message}`)
2575
4001
  );
2576
- process.exit(1);
4002
+ await cleanupAndExit(1);
2577
4003
  }
2578
4004
  }
2579
- async function unsubscribeResourceCommand(uri, options) {
4005
+ async function unsubscribeResourceCommand(name, uri) {
2580
4006
  try {
2581
- const result = await getOrRestoreSession(options.session || null);
2582
- if (!result) return;
4007
+ const result = await getOrRestoreSession(name);
4008
+ if (!result) {
4009
+ await cleanupAndExit(1);
4010
+ }
2583
4011
  const { session } = result;
2584
4012
  await session.unsubscribeFromResource(uri);
2585
4013
  console.log(formatSuccess(`Unsubscribed from resource: ${uri}`));
@@ -2587,53 +4015,76 @@ async function unsubscribeResourceCommand(uri, options) {
2587
4015
  console.error(
2588
4016
  formatError(`Failed to unsubscribe from resource: ${error.message}`)
2589
4017
  );
2590
- process.exit(1);
4018
+ await cleanupAndExit(1);
2591
4019
  }
4020
+ await cleanupAndExit(0);
2592
4021
  }
2593
- async function listPromptsCommand(options) {
4022
+ async function listPromptsCommand(name, options) {
2594
4023
  try {
2595
- const result = await getOrRestoreSession(options.session || null);
2596
- if (!result) return;
4024
+ const result = await getOrRestoreSession(name);
4025
+ if (!result) {
4026
+ await cleanupAndExit(1);
4027
+ }
2597
4028
  const { session } = result;
2598
4029
  const promptsResult = await session.listPrompts();
2599
4030
  const prompts = promptsResult.prompts;
2600
4031
  if (options.json) {
2601
4032
  console.log(formatJson(prompts));
2602
- return;
2603
- }
2604
- if (prompts.length === 0) {
2605
- console.log(formatInfo("No prompts available"));
2606
- return;
4033
+ } else if (prompts.length === 0) {
4034
+ if (isStdoutTty()) console.log(formatInfo("No prompts available"));
4035
+ } else {
4036
+ const tty2 = isStdoutTty();
4037
+ if (tty2) {
4038
+ console.log(formatHeader(`Available Prompts (${prompts.length}):`));
4039
+ console.log("");
4040
+ }
4041
+ const tableData = prompts.map((prompt4) => {
4042
+ const args = prompt4.arguments ?? [];
4043
+ const reqCount = Array.isArray(args) ? args.filter((a) => a?.required).length : 0;
4044
+ const total = Array.isArray(args) ? args.length : 0;
4045
+ const argsCell = total === 0 ? source_default.gray("\u2014") : `${reqCount}/${total}`;
4046
+ return {
4047
+ name: source_default.bold(prompt4.name),
4048
+ args: argsCell,
4049
+ description: prompt4.description || source_default.gray("(no description)")
4050
+ };
4051
+ });
4052
+ console.log(
4053
+ formatTable(tableData, [
4054
+ { key: "name", header: "Prompt" },
4055
+ { key: "args", header: "Args" },
4056
+ { key: "description", header: "Description", truncate: true }
4057
+ ])
4058
+ );
2607
4059
  }
2608
- console.log(formatHeader(`Available Prompts (${prompts.length}):`));
2609
- console.log("");
2610
- const tableData = prompts.map((prompt4) => ({
2611
- name: source_default.bold(prompt4.name),
2612
- description: prompt4.description || source_default.gray("No description")
2613
- }));
2614
- console.log(
2615
- formatTable(tableData, [
2616
- { key: "name", header: "Prompt", width: 25 },
2617
- { key: "description", header: "Description", width: 50 }
2618
- ])
2619
- );
2620
4060
  } catch (error) {
2621
4061
  console.error(formatError(`Failed to list prompts: ${error.message}`));
2622
- process.exit(1);
4062
+ await cleanupAndExit(1);
2623
4063
  }
4064
+ await cleanupAndExit(0);
2624
4065
  }
2625
- async function getPromptCommand(promptName, argsJson, options) {
4066
+ async function getPromptCommand(name, promptName, argsList, options) {
2626
4067
  try {
2627
- const result = await getOrRestoreSession(options?.session || null);
2628
- if (!result) return;
4068
+ const result = await getOrRestoreSession(name);
4069
+ if (!result) {
4070
+ await cleanupAndExit(1);
4071
+ }
2629
4072
  const { session } = result;
2630
4073
  let args = {};
2631
- if (argsJson) {
4074
+ if (argsList && argsList.length > 0) {
2632
4075
  try {
2633
- args = JSON.parse(argsJson);
4076
+ args = parsePromptArgs(argsList);
2634
4077
  } catch (error) {
2635
- console.error(formatError("Invalid JSON arguments"));
2636
- return;
4078
+ console.error(formatError(error.message));
4079
+ console.log("");
4080
+ console.log(formatInfo("Usage:"));
4081
+ console.log(
4082
+ ` npx mcp-use client ${name} prompts get ${promptName} key=value [key2=value2 ...]`
4083
+ );
4084
+ console.log(
4085
+ ` npx mcp-use client ${name} prompts get ${promptName} '{"key":"value"}' # full JSON object`
4086
+ );
4087
+ await cleanupAndExit(1);
2637
4088
  }
2638
4089
  }
2639
4090
  console.error(formatInfo(`Getting prompt '${promptName}'...`));
@@ -2655,12 +4106,13 @@ async function getPromptCommand(promptName, argsJson, options) {
2655
4106
  }
2656
4107
  } catch (error) {
2657
4108
  console.error(formatError(`Failed to get prompt: ${error.message}`));
2658
- process.exit(1);
4109
+ await cleanupAndExit(1);
2659
4110
  }
4111
+ await cleanupAndExit(0);
2660
4112
  }
2661
- async function interactiveCommand(options) {
4113
+ async function interactiveCommand(name) {
2662
4114
  try {
2663
- const result = await getOrRestoreSession(options.session || null);
4115
+ const result = await getOrRestoreSession(name);
2664
4116
  if (!result) return;
2665
4117
  const { name: sessionName, session } = result;
2666
4118
  console.log(formatHeader("MCP Interactive Mode"));
@@ -2683,15 +4135,11 @@ async function interactiveCommand(options) {
2683
4135
  source_default.gray(" prompts list - List available prompts")
2684
4136
  );
2685
4137
  console.log(source_default.gray(" prompts get <name> - Get a prompt"));
2686
- console.log(source_default.gray(" sessions list - List all sessions"));
2687
- console.log(
2688
- source_default.gray(" sessions switch <name> - Switch to another session")
2689
- );
2690
4138
  console.log(
2691
4139
  source_default.gray(" exit, quit - Exit interactive mode")
2692
4140
  );
2693
4141
  console.log("");
2694
- const rl = createInterface({
4142
+ const rl = createInterface2({
2695
4143
  input: process.stdin,
2696
4144
  output: process.stdout,
2697
4145
  prompt: source_default.cyan("mcp> ")
@@ -2706,7 +4154,7 @@ async function interactiveCommand(options) {
2706
4154
  if (trimmed === "exit" || trimmed === "quit") {
2707
4155
  console.log(formatInfo("Goodbye!"));
2708
4156
  rl.close();
2709
- process.exit(0);
4157
+ await cleanupAndExit(0);
2710
4158
  }
2711
4159
  const parts = trimmed.split(" ");
2712
4160
  const scope = parts[0];
@@ -2806,22 +4254,10 @@ async function interactiveCommand(options) {
2806
4254
  )
2807
4255
  );
2808
4256
  }
2809
- } else if (scope === "sessions") {
2810
- if (command === "list") {
2811
- await listSessionsCommand();
2812
- } else if (command === "switch" && arg) {
2813
- console.log(
2814
- formatWarning(
2815
- "Session switching in interactive mode will be available in a future version"
2816
- )
2817
- );
2818
- } else {
2819
- console.error(formatError("Invalid command. Try: sessions list"));
2820
- }
2821
4257
  } else {
2822
4258
  console.error(
2823
4259
  formatError(
2824
- "Unknown command. Type a valid scope: tools, resources, prompts, sessions"
4260
+ "Unknown command. Type a valid scope: tools, resources, prompts"
2825
4261
  )
2826
4262
  );
2827
4263
  }
@@ -2830,58 +4266,93 @@ async function interactiveCommand(options) {
2830
4266
  }
2831
4267
  rl.prompt();
2832
4268
  });
2833
- rl.on("close", () => {
4269
+ rl.on("close", async () => {
2834
4270
  console.log("");
2835
4271
  console.log(formatInfo("Goodbye!"));
2836
- process.exit(0);
4272
+ await cleanupAndExit(0);
2837
4273
  });
2838
4274
  } catch (error) {
2839
4275
  console.error(
2840
4276
  formatError(`Failed to start interactive mode: ${error.message}`)
2841
4277
  );
2842
- process.exit(1);
4278
+ await cleanupAndExit(1);
2843
4279
  }
2844
4280
  }
2845
4281
  function createClientCommand() {
2846
- const clientCommand = new Command("client").description(
2847
- "Interactive MCP client for terminal usage"
4282
+ const clientCommand = new Command2("client").description(
4283
+ "Interactive MCP client for terminal usage. Use `mcp-use client <name> ...` to run commands against a saved server."
4284
+ ).showHelpAfterError(
4285
+ "(Run `mcp-use client --help` to see available commands)"
4286
+ );
4287
+ clientCommand.command("connect [name] [url]").description(
4288
+ "Connect to an MCP server and save it under a short name. Use the name to address it in later commands (e.g. `mcp-use client <name> tools list`)."
4289
+ ).option("--stdio", "Use stdio connector instead of HTTP").option("--auth <token>", "Static Bearer token (skips OAuth)").option(
4290
+ "--no-oauth",
4291
+ "Don't auto-trigger OAuth on 401; fail with the 401 instead"
4292
+ ).option(
4293
+ "--auth-timeout <ms>",
4294
+ "OAuth loopback wait timeout in ms (default 300000)"
4295
+ ).action(connectCommand);
4296
+ clientCommand.command("list").description("List saved servers").action(listClientsCommand);
4297
+ clientCommand.command("remove <name>").description(
4298
+ "Remove a saved server. Also clears any OAuth tokens for that URL, unless another saved server still uses it."
4299
+ ).action(removeClientCommand);
4300
+ return clientCommand;
4301
+ }
4302
+ function createPerClientCommand(name) {
4303
+ const cmd = new Command2(`mcp-use client ${name}`).description(`Commands for server '${name}'`).showHelpAfterError(
4304
+ `(Run \`mcp-use client ${name} --help\` to see available commands)`
4305
+ );
4306
+ cmd.command("disconnect").description("Disconnect from this server").action(() => disconnectCommand(name));
4307
+ cmd.command("interactive").description("Start interactive REPL mode for this server").action(() => interactiveCommand(name));
4308
+ const toolsCommand = new Command2("tools").description("Interact with MCP tools").showHelpAfterError(
4309
+ `(Run \`mcp-use client ${name} tools --help\` to see available actions)`
2848
4310
  );
2849
- clientCommand.command("connect <url>").description("Connect to an MCP server").option("--name <name>", "Session name").option("--stdio", "Use stdio connector instead of HTTP").option("--auth <token>", "Authentication token").action(connectCommand);
2850
- clientCommand.command("disconnect [session]").description("Disconnect from a session").option("--all", "Disconnect all sessions").action(disconnectCommand);
2851
- const sessionsCommand = new Command("sessions").description(
2852
- "Manage CLI sessions"
4311
+ toolsCommand.command("list").description("List available tools").option("--json", "Output as JSON").action((options) => listToolsCommand(name, options));
4312
+ toolsCommand.command("call <tool> [args...]").description(
4313
+ "Call a tool. Args as key=value pairs (use key:=<json> for nested values, or pass a JSON object)"
4314
+ ).option("--timeout <ms>", "Request timeout in milliseconds", parseInt).option("--json", "Output as JSON").option(
4315
+ "--no-screenshot",
4316
+ "Skip the auto-screenshot for tools that render a widget"
4317
+ ).option(
4318
+ "--screenshot-output <path>",
4319
+ "Output PNG path for the widget screenshot (defaults to ./<view>-<timestamp>.png)"
4320
+ ).action(
4321
+ (tool, args, options) => callToolCommand(name, tool, args, options)
2853
4322
  );
2854
- sessionsCommand.command("list").description("List all saved sessions").action(listSessionsCommand);
2855
- sessionsCommand.command("switch <name>").description("Switch to a different session").action(switchSessionCommand);
2856
- clientCommand.addCommand(sessionsCommand);
2857
- const toolsCommand = new Command("tools").description(
2858
- "Interact with MCP tools"
4323
+ toolsCommand.command("describe <tool>").description("Show tool details and schema").action((tool) => describeToolCommand(name, tool));
4324
+ cmd.addCommand(toolsCommand);
4325
+ const resourcesCommand = new Command2("resources").description("Interact with MCP resources").showHelpAfterError(
4326
+ `(Run \`mcp-use client ${name} resources --help\` to see available actions)`
2859
4327
  );
2860
- toolsCommand.command("list").description("List available tools").option("--session <name>", "Use specific session").option("--json", "Output as JSON").action(listToolsCommand);
2861
- toolsCommand.command("call <name> [args]").description("Call a tool with arguments (JSON string)").option("--session <name>", "Use specific session").option("--timeout <ms>", "Request timeout in milliseconds", parseInt).option("--json", "Output as JSON").action(callToolCommand);
2862
- toolsCommand.command("describe <name>").description("Show tool details and schema").option("--session <name>", "Use specific session").action(describeToolCommand);
2863
- clientCommand.addCommand(toolsCommand);
2864
- const resourcesCommand = new Command("resources").description(
2865
- "Interact with MCP resources"
4328
+ resourcesCommand.command("list").description("List available resources").option("--json", "Output as JSON").action((options) => listResourcesCommand(name, options));
4329
+ resourcesCommand.command("read <uri>").description("Read a resource by URI").option("--json", "Output as JSON").action((uri, options) => readResourceCommand(name, uri, options));
4330
+ resourcesCommand.command("subscribe <uri>").description("Subscribe to resource updates").action((uri) => subscribeResourceCommand(name, uri));
4331
+ resourcesCommand.command("unsubscribe <uri>").description("Unsubscribe from resource updates").action((uri) => unsubscribeResourceCommand(name, uri));
4332
+ cmd.addCommand(resourcesCommand);
4333
+ const promptsCommand = new Command2("prompts").description("Interact with MCP prompts").showHelpAfterError(
4334
+ `(Run \`mcp-use client ${name} prompts --help\` to see available actions)`
2866
4335
  );
2867
- resourcesCommand.command("list").description("List available resources").option("--session <name>", "Use specific session").option("--json", "Output as JSON").action(listResourcesCommand);
2868
- resourcesCommand.command("read <uri>").description("Read a resource by URI").option("--session <name>", "Use specific session").option("--json", "Output as JSON").action(readResourceCommand);
2869
- resourcesCommand.command("subscribe <uri>").description("Subscribe to resource updates").option("--session <name>", "Use specific session").action(subscribeResourceCommand);
2870
- resourcesCommand.command("unsubscribe <uri>").description("Unsubscribe from resource updates").option("--session <name>", "Use specific session").action(unsubscribeResourceCommand);
2871
- clientCommand.addCommand(resourcesCommand);
2872
- const promptsCommand = new Command("prompts").description(
2873
- "Interact with MCP prompts"
4336
+ promptsCommand.command("list").description("List available prompts").option("--json", "Output as JSON").action((options) => listPromptsCommand(name, options));
4337
+ promptsCommand.command("get <prompt> [args...]").description(
4338
+ "Get a prompt. Args as key=value pairs (or pass a JSON object)"
4339
+ ).option("--json", "Output as JSON").action(
4340
+ (prompt4, args, options) => getPromptCommand(name, prompt4, args, options)
2874
4341
  );
2875
- promptsCommand.command("list").description("List available prompts").option("--session <name>", "Use specific session").option("--json", "Output as JSON").action(listPromptsCommand);
2876
- promptsCommand.command("get <name> [args]").description("Get a prompt with arguments (JSON string)").option("--session <name>", "Use specific session").option("--json", "Output as JSON").action(getPromptCommand);
2877
- clientCommand.addCommand(promptsCommand);
2878
- clientCommand.command("interactive").description("Start interactive REPL mode").option("--session <name>", "Use specific session").action(interactiveCommand);
2879
- return clientCommand;
4342
+ cmd.addCommand(promptsCommand);
4343
+ const authCommand = new Command2("auth").description("Manage OAuth tokens for HTTP servers").showHelpAfterError(
4344
+ `(Run \`mcp-use client ${name} auth --help\` to see available actions)`
4345
+ );
4346
+ authCommand.command("status").description("Show OAuth token status for this server").action(() => authStatusCommand(name));
4347
+ authCommand.command("refresh").description("Force-refresh the OAuth access token").action(() => authRefreshCommand(name));
4348
+ authCommand.command("logout").description("Remove stored OAuth tokens for this server's URL").action(() => authLogoutCommand(name));
4349
+ cmd.addCommand(authCommand);
4350
+ return cmd;
2880
4351
  }
2881
4352
 
2882
4353
  // src/commands/deploy.ts
2883
4354
  import { promises as fs9 } from "fs";
2884
- import path5 from "path";
4355
+ import path8 from "path";
2885
4356
 
2886
4357
  // src/utils/git.ts
2887
4358
  import { execFile as execFile7 } from "child_process";
@@ -3036,15 +4507,15 @@ function getMcpServerUrlForCloudServer(server) {
3036
4507
 
3037
4508
  // src/utils/project-link.ts
3038
4509
  import { promises as fs8 } from "fs";
3039
- import path4 from "path";
4510
+ import path7 from "path";
3040
4511
  var MCP_USE_DIR = ".mcp-use";
3041
4512
  var MCP_USE_DIR_PROJECT = "project.json";
3042
4513
  function getMcpUseDirectory(cwd) {
3043
- return path4.join(cwd, MCP_USE_DIR);
4514
+ return path7.join(cwd, MCP_USE_DIR);
3044
4515
  }
3045
4516
  async function getProjectLink(cwd) {
3046
4517
  try {
3047
- const linkPath = path4.join(getMcpUseDirectory(cwd), MCP_USE_DIR_PROJECT);
4518
+ const linkPath = path7.join(getMcpUseDirectory(cwd), MCP_USE_DIR_PROJECT);
3048
4519
  const content = await fs8.readFile(linkPath, "utf-8");
3049
4520
  return JSON.parse(content);
3050
4521
  } catch (err) {
@@ -3055,12 +4526,12 @@ async function getProjectLink(cwd) {
3055
4526
  async function saveProjectLink(cwd, link) {
3056
4527
  const mcpUseDir = getMcpUseDirectory(cwd);
3057
4528
  await fs8.mkdir(mcpUseDir, { recursive: true });
3058
- const linkPath = path4.join(mcpUseDir, MCP_USE_DIR_PROJECT);
4529
+ const linkPath = path7.join(mcpUseDir, MCP_USE_DIR_PROJECT);
3059
4530
  await fs8.writeFile(linkPath, JSON.stringify(link, null, 2), "utf-8");
3060
4531
  await addToGitIgnore(cwd);
3061
4532
  }
3062
4533
  async function addToGitIgnore(cwd) {
3063
- const gitignorePath = path4.join(cwd, ".gitignore");
4534
+ const gitignorePath = path7.join(cwd, ".gitignore");
3064
4535
  try {
3065
4536
  let content = "";
3066
4537
  try {
@@ -3201,7 +4672,7 @@ async function buildEnvVars(options) {
3201
4672
  }
3202
4673
  async function isMcpProject(cwd = process.cwd()) {
3203
4674
  try {
3204
- const content = await fs9.readFile(path5.join(cwd, "package.json"), "utf-8");
4675
+ const content = await fs9.readFile(path8.join(cwd, "package.json"), "utf-8");
3205
4676
  const pkg = JSON.parse(content);
3206
4677
  return !!(pkg.dependencies?.["mcp-use"] || pkg.dependencies?.["@modelcontextprotocol/sdk"] || pkg.devDependencies?.["mcp-use"] || pkg.devDependencies?.["@modelcontextprotocol/sdk"]);
3207
4678
  } catch {
@@ -3210,16 +4681,16 @@ async function isMcpProject(cwd = process.cwd()) {
3210
4681
  }
3211
4682
  async function getProjectName(cwd = process.cwd()) {
3212
4683
  try {
3213
- const content = await fs9.readFile(path5.join(cwd, "package.json"), "utf-8");
4684
+ const content = await fs9.readFile(path8.join(cwd, "package.json"), "utf-8");
3214
4685
  const pkg = JSON.parse(content);
3215
4686
  if (pkg.name) return pkg.name;
3216
4687
  } catch {
3217
4688
  }
3218
- return path5.basename(cwd);
4689
+ return path8.basename(cwd);
3219
4690
  }
3220
4691
  async function detectBuildCommand(cwd) {
3221
4692
  try {
3222
- const content = await fs9.readFile(path5.join(cwd, "package.json"), "utf-8");
4693
+ const content = await fs9.readFile(path8.join(cwd, "package.json"), "utf-8");
3223
4694
  if (JSON.parse(content).scripts?.build) return "npm run build";
3224
4695
  } catch {
3225
4696
  }
@@ -3227,7 +4698,7 @@ async function detectBuildCommand(cwd) {
3227
4698
  }
3228
4699
  async function detectStartCommand(cwd) {
3229
4700
  try {
3230
- const content = await fs9.readFile(path5.join(cwd, "package.json"), "utf-8");
4701
+ const content = await fs9.readFile(path8.join(cwd, "package.json"), "utf-8");
3231
4702
  const pkg = JSON.parse(content);
3232
4703
  if (pkg.scripts?.start) return "npm start";
3233
4704
  if (pkg.main) return `node ${pkg.main}`;
@@ -3238,7 +4709,7 @@ async function detectStartCommand(cwd) {
3238
4709
  async function detectRuntime(cwd) {
3239
4710
  for (const f of ["requirements.txt", "pyproject.toml", "setup.py"]) {
3240
4711
  try {
3241
- await fs9.access(path5.join(cwd, f));
4712
+ await fs9.access(path8.join(cwd, f));
3242
4713
  return "python";
3243
4714
  } catch {
3244
4715
  continue;
@@ -3285,7 +4756,7 @@ var REQUIRED_IGNORES = [
3285
4756
  ".mcp-use"
3286
4757
  ];
3287
4758
  async function ensureGitignore(cwd) {
3288
- const gitignorePath = path5.join(cwd, ".gitignore");
4759
+ const gitignorePath = path8.join(cwd, ".gitignore");
3289
4760
  let content = "";
3290
4761
  try {
3291
4762
  content = await fs9.readFile(gitignorePath, "utf-8");
@@ -3745,7 +5216,7 @@ async function deployCommand(options) {
3745
5216
  console.log(source_default.green("\u2713 GitHub connected\n"));
3746
5217
  let installationDbId;
3747
5218
  let githubInstallationId;
3748
- const projectDir = options.rootDir ? path5.resolve(cwd, options.rootDir) : cwd;
5219
+ const projectDir = options.rootDir ? path8.resolve(cwd, options.rootDir) : cwd;
3749
5220
  if (options.rootDir) {
3750
5221
  try {
3751
5222
  await fs9.access(projectDir);
@@ -4257,7 +5728,7 @@ async function deployCommand(options) {
4257
5728
  }
4258
5729
 
4259
5730
  // src/commands/deployments.ts
4260
- import { Command as Command2 } from "commander";
5731
+ import { Command as Command3 } from "commander";
4261
5732
  async function prompt2(question) {
4262
5733
  const readline = await import("readline");
4263
5734
  const rl = readline.createInterface({
@@ -4669,8 +6140,8 @@ async function startDeploymentCommand(deploymentId) {
4669
6140
  }
4670
6141
  }
4671
6142
  function createDeploymentsCommand() {
4672
- const deploymentsCommand = new Command2("deployments").description(
4673
- "Manage cloud deployments"
6143
+ const deploymentsCommand = new Command3("deployments").description("Manage cloud deployments").showHelpAfterError(
6144
+ "(Run `mcp-use deployments --help` to see available commands)"
4674
6145
  );
4675
6146
  deploymentsCommand.command("list").alias("ls").description("List all deployments").action(listDeploymentsCommand);
4676
6147
  deploymentsCommand.command("get").argument("<deployment-id>", "Deployment ID").description("Get deployment details").action(getDeploymentCommand);
@@ -4685,10 +6156,10 @@ function createDeploymentsCommand() {
4685
6156
  }
4686
6157
 
4687
6158
  // src/commands/servers.ts
4688
- import { Command as Command4 } from "commander";
6159
+ import { Command as Command5 } from "commander";
4689
6160
 
4690
6161
  // src/commands/env.ts
4691
- import { Command as Command3 } from "commander";
6162
+ import { Command as Command4 } from "commander";
4692
6163
  var ALL_ENVS = ["production", "preview", "development"];
4693
6164
  function parseEnvironments(raw) {
4694
6165
  const parts = raw.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
@@ -4833,9 +6304,7 @@ async function removeEnvCommand(varId, options) {
4833
6304
  }
4834
6305
  }
4835
6306
  function createEnvCommand() {
4836
- const envCommand = new Command3("env").description(
4837
- "Manage environment variables for a server"
4838
- );
6307
+ const envCommand = new Command4("env").description("Manage environment variables for a server").showHelpAfterError("(Run `mcp-use env --help` to see available commands)");
4839
6308
  envCommand.command("list").alias("ls").description("List environment variables for a server").requiredOption("--server <id>", "Server UUID").option("--show-values", "Reveal non-sensitive values in output").action(listEnvCommand);
4840
6309
  envCommand.command("add").argument("<KEY=VALUE>", "Variable assignment, e.g. API_KEY=abc123").description("Add an environment variable to a server").requiredOption("--server <id>", "Server UUID").option(
4841
6310
  "--env <environments>",
@@ -5117,8 +6586,8 @@ async function deleteServerCommand(serverId, options) {
5117
6586
  }
5118
6587
  }
5119
6588
  function createServersCommand() {
5120
- const serversCommand = new Command4("servers").description(
5121
- "Manage cloud servers (Git-backed deploy targets)"
6589
+ const serversCommand = new Command5("servers").description("Manage cloud servers (Git-backed deploy targets)").showHelpAfterError(
6590
+ "(Run `mcp-use servers --help` to see available commands)"
5122
6591
  );
5123
6592
  serversCommand.command("list").alias("ls").description("List servers for the current organization").option("--org <slug-or-id>", "Target organization (slug, id, or name)").option("--limit <n>", "Page size (1\u2013100, default 50)").option("--skip <n>", "Offset for pagination").option("--sort <field:asc|desc>", "Sort (e.g. updatedAt:desc)").action(listServersCommand);
5124
6593
  serversCommand.command("get").argument("<id-or-slug>", "Server UUID or slug").option("--org <slug-or-id>", "Resolve org context before fetch").description("Show server details and recent deployments").action(getServerCommand);
@@ -5235,8 +6704,8 @@ async function orgCurrentCommand() {
5235
6704
  }
5236
6705
 
5237
6706
  // src/commands/skills.ts
5238
- import { Command as Command5 } from "commander";
5239
- import { cpSync, existsSync as existsSync2, mkdtempSync, readdirSync, rmSync } from "fs";
6707
+ import { Command as Command6 } from "commander";
6708
+ import { cpSync, existsSync as existsSync3, mkdtempSync as mkdtempSync2, readdirSync, rmSync as rmSync2 } from "fs";
5240
6709
  import { tmpdir } from "os";
5241
6710
  import { join as join2, resolve } from "path";
5242
6711
  import { Readable } from "stream";
@@ -5275,7 +6744,7 @@ function sendInstallTelemetryEvent(agents, skills) {
5275
6744
  }
5276
6745
  async function addSkillsToProject(projectPath) {
5277
6746
  const tarballUrl = `https://codeload.github.com/${REPO_OWNER}/${REPO_NAME}/tar.gz/${REPO_BRANCH}`;
5278
- const tempDir = mkdtempSync(join2(tmpdir(), "mcp-use-skills-"));
6747
+ const tempDir = mkdtempSync2(join2(tmpdir(), "mcp-use-skills-"));
5279
6748
  try {
5280
6749
  const response = await fetch(tarballUrl);
5281
6750
  if (!response.ok) {
@@ -5285,12 +6754,12 @@ async function addSkillsToProject(projectPath) {
5285
6754
  Readable.fromWeb(response.body),
5286
6755
  extract({
5287
6756
  cwd: tempDir,
5288
- filter: (path9) => path9.includes("/skills/"),
6757
+ filter: (path12) => path12.includes("/skills/"),
5289
6758
  strip: 1
5290
6759
  })
5291
6760
  );
5292
6761
  const skillsPath = join2(tempDir, "skills");
5293
- if (!existsSync2(skillsPath)) {
6762
+ if (!existsSync3(skillsPath)) {
5294
6763
  throw new Error("Skills folder not found in repository");
5295
6764
  }
5296
6765
  for (const preset of ALL_PRESETS) {
@@ -5301,16 +6770,16 @@ async function addSkillsToProject(projectPath) {
5301
6770
  const skillNames = readdirSync(skillsPath, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
5302
6771
  sendInstallTelemetryEvent(ALL_PRESETS.join(","), skillNames.join(","));
5303
6772
  } finally {
5304
- rmSync(tempDir, { recursive: true, force: true });
6773
+ rmSync2(tempDir, { recursive: true, force: true });
5305
6774
  }
5306
6775
  }
5307
6776
  function createSkillsCommand() {
5308
- const skills = new Command5("skills").description(
5309
- "Manage mcp-use AI agent skills"
6777
+ const skills = new Command6("skills").description("Manage mcp-use AI agent skills").showHelpAfterError(
6778
+ "(Run `mcp-use skills --help` to see available commands)"
5310
6779
  );
5311
6780
  const installAction = async (options) => {
5312
6781
  const projectPath = resolve(options.path);
5313
- if (!existsSync2(projectPath)) {
6782
+ if (!existsSync3(projectPath)) {
5314
6783
  console.error(source_default.red(`Directory not found: ${projectPath}`));
5315
6784
  process.exit(1);
5316
6785
  }
@@ -5351,12 +6820,12 @@ function createSkillsCommand() {
5351
6820
  }
5352
6821
 
5353
6822
  // src/utils/next-shims.ts
5354
- import { existsSync as existsSync3, promises as fs10 } from "fs";
5355
- import path6 from "path";
6823
+ import { existsSync as existsSync4, promises as fs10 } from "fs";
6824
+ import path9 from "path";
5356
6825
  import { fileURLToPath as fileURLToPath3, pathToFileURL } from "url";
5357
6826
  async function detectNextJsProject(projectPath) {
5358
6827
  try {
5359
- const pkgPath = path6.join(projectPath, "package.json");
6828
+ const pkgPath = path9.join(projectPath, "package.json");
5360
6829
  const content = await fs10.readFile(pkgPath, "utf-8");
5361
6830
  const pkg = JSON.parse(content);
5362
6831
  const deps = pkg.dependencies ?? {};
@@ -5375,7 +6844,7 @@ async function loadNextJsEnvFiles(projectPath) {
5375
6844
  ];
5376
6845
  const dotenv = await import("dotenv");
5377
6846
  for (const file of files) {
5378
- const abs = path6.join(projectPath, file);
6847
+ const abs = path9.join(projectPath, file);
5379
6848
  try {
5380
6849
  await fs10.access(abs);
5381
6850
  } catch {
@@ -5387,20 +6856,20 @@ async function loadNextJsEnvFiles(projectPath) {
5387
6856
  function getThisDir() {
5388
6857
  if (typeof __dirname === "string") return __dirname;
5389
6858
  const url = import.meta.url;
5390
- return path6.dirname(fileURLToPath3(url));
6859
+ return path9.dirname(fileURLToPath3(url));
5391
6860
  }
5392
6861
  function resolveShimPath(filename) {
5393
6862
  const thisDir = getThisDir();
5394
6863
  const candidates = [
5395
6864
  // Production: `dist/` next to this module
5396
- path6.join(thisDir, "shims", filename),
6865
+ path9.join(thisDir, "shims", filename),
5397
6866
  // Test / dev: one level up (e.g., from `dist/utils/` back to `src/shims/`)
5398
- path6.join(thisDir, "..", "shims", filename),
5399
- path6.join(thisDir, "..", "..", "src", "shims", filename),
5400
- path6.join(thisDir, "..", "src", "shims", filename)
6867
+ path9.join(thisDir, "..", "shims", filename),
6868
+ path9.join(thisDir, "..", "..", "src", "shims", filename),
6869
+ path9.join(thisDir, "..", "src", "shims", filename)
5401
6870
  ];
5402
6871
  for (const candidate of candidates) {
5403
- if (existsSync3(candidate)) return candidate;
6872
+ if (existsSync4(candidate)) return candidate;
5404
6873
  }
5405
6874
  return void 0;
5406
6875
  }
@@ -5418,7 +6887,7 @@ async function registerNextShimsInProcess() {
5418
6887
  const cjsPath = getShimCjsPreloadPath();
5419
6888
  if (cjsPath) {
5420
6889
  const { createRequire: createRequire3 } = await import("module");
5421
- const req = createRequire3(pathToFileURL(getThisDir() + path6.sep).href);
6890
+ const req = createRequire3(pathToFileURL(getThisDir() + path9.sep).href);
5422
6891
  req(cjsPath);
5423
6892
  anyRegistered = true;
5424
6893
  }
@@ -5426,7 +6895,7 @@ async function registerNextShimsInProcess() {
5426
6895
  if (loaderPath) {
5427
6896
  const { register } = await import("module");
5428
6897
  const loaderUrl = pathToFileURL(loaderPath).href;
5429
- register(loaderUrl, pathToFileURL(getThisDir() + path6.sep).href);
6898
+ register(loaderUrl, pathToFileURL(getThisDir() + path9.sep).href);
5430
6899
  anyRegistered = true;
5431
6900
  }
5432
6901
  return anyRegistered;
@@ -5452,12 +6921,12 @@ function quoteNodeOption(value) {
5452
6921
 
5453
6922
  // src/utils/update-check.ts
5454
6923
  import { readFileSync } from "fs";
5455
- import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
6924
+ import { mkdir as mkdir3, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
5456
6925
  import { createRequire } from "module";
5457
- import os4 from "os";
5458
- import path7 from "path";
5459
- var CACHE_DIR = path7.join(os4.homedir(), ".mcp-use");
5460
- var CACHE_FILE = path7.join(CACHE_DIR, "update-check.json");
6926
+ import os5 from "os";
6927
+ import path10 from "path";
6928
+ var CACHE_DIR = path10.join(os5.homedir(), ".mcp-use");
6929
+ var CACHE_FILE = path10.join(CACHE_DIR, "update-check.json");
5461
6930
  var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
5462
6931
  var FETCH_TIMEOUT_MS = 3e3;
5463
6932
  var PACKAGE_NAME = "mcp-use";
@@ -5490,7 +6959,7 @@ async function readCache() {
5490
6959
  }
5491
6960
  async function writeCache(latestVersion) {
5492
6961
  try {
5493
- await mkdir2(CACHE_DIR, { recursive: true });
6962
+ await mkdir3(CACHE_DIR, { recursive: true });
5494
6963
  const cache = {
5495
6964
  lastChecked: (/* @__PURE__ */ new Date()).toISOString(),
5496
6965
  latestVersion
@@ -5540,12 +7009,12 @@ function resolveInstalledVersion(projectPath) {
5540
7009
  if (projectPath) {
5541
7010
  attempts.push(() => {
5542
7011
  const projectRequire = createRequire(
5543
- path7.join(projectPath, "package.json")
7012
+ path10.join(projectPath, "package.json")
5544
7013
  );
5545
7014
  return projectRequire.resolve(`${PACKAGE_NAME}/package.json`);
5546
7015
  });
5547
7016
  }
5548
- attempts.push(() => path7.join(__dirname, "../../mcp-use/package.json"));
7017
+ attempts.push(() => path10.join(__dirname, "../../mcp-use/package.json"));
5549
7018
  for (const attempt of attempts) {
5550
7019
  try {
5551
7020
  const pkgPath = attempt();
@@ -5581,14 +7050,14 @@ A new release of ${source_default.bold(PACKAGE_NAME)} is available: ${source_def
5581
7050
  }
5582
7051
 
5583
7052
  // src/index.ts
5584
- var program = new Command6();
7053
+ var program = new Command7();
5585
7054
  var packageContent = readFileSync2(
5586
- path8.join(__dirname, "../package.json"),
7055
+ path11.join(__dirname, "../package.json"),
5587
7056
  "utf-8"
5588
7057
  );
5589
7058
  var packageJson = JSON.parse(packageContent);
5590
7059
  var packageVersion = packageJson.version || "unknown";
5591
- program.name("mcp-use").description("Create and run MCP servers with ui resources widgets").version(packageVersion);
7060
+ program.name("mcp-use").description("Create and run MCP servers with ui resources widgets").version(packageVersion).showHelpAfterError("(Run `mcp-use --help` to see available commands)");
5592
7061
  function displayPackageVersions(projectPath) {
5593
7062
  const packages = [
5594
7063
  { name: "@mcp-use/cli", relativePath: "../package.json" },
@@ -5614,14 +7083,14 @@ function displayPackageVersions(projectPath) {
5614
7083
  if (projectPath) {
5615
7084
  try {
5616
7085
  const projectRequire = createRequire2(
5617
- path8.join(projectPath, "package.json")
7086
+ path11.join(projectPath, "package.json")
5618
7087
  );
5619
7088
  pkgPath = projectRequire.resolve(`${pkg.name}/package.json`);
5620
7089
  } catch (resolveError) {
5621
- pkgPath = path8.join(__dirname, pkg.relativePath);
7090
+ pkgPath = path11.join(__dirname, pkg.relativePath);
5622
7091
  }
5623
7092
  } else {
5624
- pkgPath = path8.join(__dirname, pkg.relativePath);
7093
+ pkgPath = path11.join(__dirname, pkg.relativePath);
5625
7094
  }
5626
7095
  const pkgContent = readFileSync2(pkgPath, "utf-8");
5627
7096
  const pkgJson = JSON.parse(pkgContent);
@@ -5678,7 +7147,7 @@ function normalizeBrowserHost(host) {
5678
7147
  return host === "0.0.0.0" ? "localhost" : host;
5679
7148
  }
5680
7149
  function runCommand(command, args, cwd, env2, filterStderr = false) {
5681
- const proc = spawn(command, args, {
7150
+ const proc = spawn3(command, args, {
5682
7151
  cwd,
5683
7152
  stdio: filterStderr ? ["inherit", "inherit", "pipe"] : "inherit",
5684
7153
  shell: process.platform === "win32",
@@ -5711,7 +7180,7 @@ async function startTunnel(port, subdomain) {
5711
7180
  if (subdomain) {
5712
7181
  tunnelArgs.push("--subdomain", subdomain);
5713
7182
  }
5714
- const proc = spawn("npx", tunnelArgs, {
7183
+ const proc = spawn3("npx", tunnelArgs, {
5715
7184
  stdio: ["ignore", "pipe", "pipe"],
5716
7185
  shell: process.platform === "win32"
5717
7186
  });
@@ -5775,21 +7244,21 @@ async function startTunnel(port, subdomain) {
5775
7244
  }
5776
7245
  async function resolveEntryFile(projectPath, cliEntry, mcpDir) {
5777
7246
  if (cliEntry) {
5778
- await access(path8.join(projectPath, cliEntry)).catch(() => {
7247
+ await access(path11.join(projectPath, cliEntry)).catch(() => {
5779
7248
  throw new Error(`File not found: ${cliEntry}`);
5780
7249
  });
5781
7250
  return cliEntry;
5782
7251
  }
5783
7252
  if (mcpDir) {
5784
7253
  const mcpCandidates = [
5785
- path8.join(mcpDir, "index.ts"),
5786
- path8.join(mcpDir, "index.tsx"),
5787
- path8.join(mcpDir, "server.ts"),
5788
- path8.join(mcpDir, "server.tsx")
7254
+ path11.join(mcpDir, "index.ts"),
7255
+ path11.join(mcpDir, "index.tsx"),
7256
+ path11.join(mcpDir, "server.ts"),
7257
+ path11.join(mcpDir, "server.tsx")
5789
7258
  ];
5790
7259
  for (const candidate of mcpCandidates) {
5791
7260
  try {
5792
- await access(path8.join(projectPath, candidate));
7261
+ await access(path11.join(projectPath, candidate));
5793
7262
  return candidate;
5794
7263
  } catch {
5795
7264
  continue;
@@ -5798,17 +7267,17 @@ async function resolveEntryFile(projectPath, cliEntry, mcpDir) {
5798
7267
  throw new Error(
5799
7268
  `No entry file found inside ${mcpDir}.
5800
7269
 
5801
- Expected one of: ${mcpCandidates.map((c) => path8.relative(projectPath, path8.join(projectPath, c))).join(", ")}
7270
+ Expected one of: ${mcpCandidates.map((c) => path11.relative(projectPath, path11.join(projectPath, c))).join(", ")}
5802
7271
 
5803
7272
  Fix this by either:
5804
- 1. Creating ${path8.join(mcpDir, "index.ts")}, or
7273
+ 1. Creating ${path11.join(mcpDir, "index.ts")}, or
5805
7274
  2. Passing --entry <file> on the command line`
5806
7275
  );
5807
7276
  }
5808
7277
  const candidates = ["index.ts", "src/index.ts", "server.ts", "src/server.ts"];
5809
7278
  for (const candidate of candidates) {
5810
7279
  try {
5811
- await access(path8.join(projectPath, candidate));
7280
+ await access(path11.join(projectPath, candidate));
5812
7281
  return candidate;
5813
7282
  } catch {
5814
7283
  continue;
@@ -5826,7 +7295,7 @@ Fix this by either:
5826
7295
  }
5827
7296
  function resolveWidgetsDir(cliWidgetsDir, mcpDir) {
5828
7297
  if (cliWidgetsDir) return cliWidgetsDir;
5829
- if (mcpDir) return path8.join(mcpDir, "resources");
7298
+ if (mcpDir) return path11.join(mcpDir, "resources");
5830
7299
  return "resources";
5831
7300
  }
5832
7301
  function makeWidgetServerOnlyGuard(widgetName) {
@@ -5861,7 +7330,7 @@ function isBunRuntime() {
5861
7330
  return typeof globalThis.Bun !== "undefined" || typeof process.versions.bun === "string";
5862
7331
  }
5863
7332
  async function generateToolRegistryTypesForServer(projectPath, serverFileRelative) {
5864
- const serverFile = path8.join(projectPath, serverFileRelative);
7333
+ const serverFile = path11.join(projectPath, serverFileRelative);
5865
7334
  const serverFileExists = await access(serverFile).then(() => true).catch(() => false);
5866
7335
  if (!serverFileExists) {
5867
7336
  throw new Error(`Server file not found: ${serverFile}`);
@@ -5887,7 +7356,7 @@ async function generateToolRegistryTypesForServer(projectPath, serverFileRelativ
5887
7356
  await loadNextJsEnvFiles(projectPath);
5888
7357
  await registerNextShimsInProcess();
5889
7358
  }
5890
- const projectTsconfigPath = path8.join(projectPath, "tsconfig.json");
7359
+ const projectTsconfigPath = path11.join(projectPath, "tsconfig.json");
5891
7360
  const hasTsconfig = await access(projectTsconfigPath).then(() => true).catch(() => false);
5892
7361
  if (hasTsconfig) {
5893
7362
  process.env.TSX_TSCONFIG_PATH = projectTsconfigPath;
@@ -5896,7 +7365,7 @@ async function generateToolRegistryTypesForServer(projectPath, serverFileRelativ
5896
7365
  if (previousCwd !== projectPath) process.chdir(projectPath);
5897
7366
  try {
5898
7367
  const projectRequire = createRequire2(
5899
- path8.join(projectPath, "package.json")
7368
+ path11.join(projectPath, "package.json")
5900
7369
  );
5901
7370
  const tsxEsmApiPath = projectRequire.resolve("tsx/esm/api");
5902
7371
  const tsxEsmApi = await import(pathToFileURL2(tsxEsmApiPath).href);
@@ -5923,8 +7392,8 @@ async function generateToolRegistryTypesForServer(projectPath, serverFileRelativ
5923
7392
  "No MCPServer instance found. Make sure your server file creates an MCPServer instance."
5924
7393
  );
5925
7394
  }
5926
- const mcpUsePath = path8.join(projectPath, "node_modules", "mcp-use");
5927
- const { generateToolRegistryTypes } = await import(pathToFileURL2(path8.join(mcpUsePath, "dist", "src", "server", "index.js")).href).then((mod) => mod);
7395
+ const mcpUsePath = path11.join(projectPath, "node_modules", "mcp-use");
7396
+ const { generateToolRegistryTypes } = await import(pathToFileURL2(path11.join(mcpUsePath, "dist", "src", "server", "index.js")).href).then((mod) => mod);
5928
7397
  if (!generateToolRegistryTypes) {
5929
7398
  throw new Error("generateToolRegistryTypes not found in mcp-use package");
5930
7399
  }
@@ -5942,7 +7411,7 @@ async function buildWidgets(projectPath, options = {}) {
5942
7411
  const { promises: fs11 } = await import("fs");
5943
7412
  const { build } = await import("vite");
5944
7413
  const widgetsDirRelative = options.widgetsDir ?? "resources";
5945
- const resourcesDir = path8.resolve(projectPath, widgetsDirRelative);
7414
+ const resourcesDir = path11.resolve(projectPath, widgetsDirRelative);
5946
7415
  const mcpUrl = process.env.MCP_URL;
5947
7416
  try {
5948
7417
  await access(resourcesDir);
@@ -5964,10 +7433,10 @@ async function buildWidgets(projectPath, options = {}) {
5964
7433
  if (dirent.isFile() && (dirent.name.endsWith(".tsx") || dirent.name.endsWith(".ts"))) {
5965
7434
  entries.push({
5966
7435
  name: dirent.name.replace(/\.tsx?$/, ""),
5967
- path: path8.join(resourcesDir, dirent.name)
7436
+ path: path11.join(resourcesDir, dirent.name)
5968
7437
  });
5969
7438
  } else if (dirent.isDirectory()) {
5970
- const widgetPath = path8.join(resourcesDir, dirent.name, "widget.tsx");
7439
+ const widgetPath = path11.join(resourcesDir, dirent.name, "widget.tsx");
5971
7440
  try {
5972
7441
  await fs11.access(widgetPath);
5973
7442
  entries.push({
@@ -5997,14 +7466,14 @@ async function buildWidgets(projectPath, options = {}) {
5997
7466
  );
5998
7467
  const react = (await import("@vitejs/plugin-react")).default;
5999
7468
  const tailwindcss = (await import("@tailwindcss/vite")).default;
6000
- const projectTsconfigPath = path8.join(projectPath, "tsconfig.json");
7469
+ const projectTsconfigPath = path11.join(projectPath, "tsconfig.json");
6001
7470
  let hasProjectTsconfig = false;
6002
7471
  try {
6003
7472
  await access(projectTsconfigPath);
6004
7473
  hasProjectTsconfig = true;
6005
7474
  } catch {
6006
7475
  }
6007
- const packageJsonPath = path8.join(projectPath, "package.json");
7476
+ const packageJsonPath = path11.join(projectPath, "package.json");
6008
7477
  let favicon = "";
6009
7478
  try {
6010
7479
  const pkgContent = await fs11.readFile(packageJsonPath, "utf-8");
@@ -6016,16 +7485,16 @@ async function buildWidgets(projectPath, options = {}) {
6016
7485
  const widgetName = entry.name;
6017
7486
  const entryPath = entry.path.replace(/\\/g, "/");
6018
7487
  console.log(source_default.gray(` - Building ${widgetName}...`));
6019
- const tempDir = path8.join(projectPath, ".mcp-use", widgetName);
7488
+ const tempDir = path11.join(projectPath, ".mcp-use", widgetName);
6020
7489
  await fs11.mkdir(tempDir, { recursive: true });
6021
- const relativeResourcesPath = path8.relative(tempDir, resourcesDir).replace(/\\/g, "/");
6022
- const mcpUsePath = path8.join(projectPath, "node_modules", "mcp-use");
6023
- const relativeMcpUsePath = path8.relative(tempDir, mcpUsePath).replace(/\\/g, "/");
6024
- const projectSrcDir = path8.join(projectPath, "src");
7490
+ const relativeResourcesPath = path11.relative(tempDir, resourcesDir).replace(/\\/g, "/");
7491
+ const mcpUsePath = path11.join(projectPath, "node_modules", "mcp-use");
7492
+ const relativeMcpUsePath = path11.relative(tempDir, mcpUsePath).replace(/\\/g, "/");
7493
+ const projectSrcDir = path11.join(projectPath, "src");
6025
7494
  let projectSrcSourceLine = "";
6026
7495
  try {
6027
7496
  await access(projectSrcDir);
6028
- const relativeProjectSrcPath = path8.relative(tempDir, projectSrcDir).replace(/\\/g, "/");
7497
+ const relativeProjectSrcPath = path11.relative(tempDir, projectSrcDir).replace(/\\/g, "/");
6029
7498
  projectSrcSourceLine = `@source "${relativeProjectSrcPath}";
6030
7499
  `;
6031
7500
  } catch {
@@ -6036,7 +7505,7 @@ async function buildWidgets(projectPath, options = {}) {
6036
7505
  @source "${relativeResourcesPath}";
6037
7506
  @source "${relativeMcpUsePath}/**/*.{ts,tsx,js,jsx}";
6038
7507
  ${projectSrcSourceLine}`;
6039
- await fs11.writeFile(path8.join(tempDir, "styles.css"), cssContent, "utf8");
7508
+ await fs11.writeFile(path11.join(tempDir, "styles.css"), cssContent, "utf8");
6040
7509
  const entryContent = `import React from 'react'
6041
7510
  import { createRoot } from 'react-dom/client'
6042
7511
  import './styles.css'
@@ -6061,9 +7530,9 @@ if (container && Component) {
6061
7530
  <script type="module" src="/entry.tsx"></script>
6062
7531
  </body>
6063
7532
  </html>`;
6064
- await fs11.writeFile(path8.join(tempDir, "entry.tsx"), entryContent, "utf8");
6065
- await fs11.writeFile(path8.join(tempDir, "index.html"), htmlContent, "utf8");
6066
- const outDir = path8.join(
7533
+ await fs11.writeFile(path11.join(tempDir, "entry.tsx"), entryContent, "utf8");
7534
+ await fs11.writeFile(path11.join(tempDir, "index.html"), htmlContent, "utf8");
7535
+ const outDir = path11.join(
6067
7536
  projectPath,
6068
7537
  "dist",
6069
7538
  "resources",
@@ -6073,13 +7542,13 @@ if (container && Component) {
6073
7542
  const baseUrl = mcpUrl ? `${mcpUrl}/${widgetName}/` : `/mcp-use/widgets/${widgetName}/`;
6074
7543
  let widgetMetadata = {};
6075
7544
  try {
6076
- const metadataTempDir = path8.join(
7545
+ const metadataTempDir = path11.join(
6077
7546
  projectPath,
6078
7547
  ".mcp-use",
6079
7548
  `${widgetName}-metadata`
6080
7549
  );
6081
7550
  await fs11.mkdir(metadataTempDir, { recursive: true });
6082
- const { createServer } = await import("vite");
7551
+ const { createServer: createServer2 } = await import("vite");
6083
7552
  const nodeStubsPlugin = {
6084
7553
  name: "node-stubs",
6085
7554
  enforce: "pre",
@@ -6107,9 +7576,9 @@ export default PostHog;
6107
7576
  }
6108
7577
  };
6109
7578
  const serverOnlyGuard = makeWidgetServerOnlyGuard(widgetName);
6110
- const metadataServer = await createServer({
7579
+ const metadataServer = await createServer2({
6111
7580
  root: metadataTempDir,
6112
- cacheDir: path8.join(metadataTempDir, ".vite-cache"),
7581
+ cacheDir: path11.join(metadataTempDir, ".vite-cache"),
6113
7582
  plugins: [serverOnlyGuard, nodeStubsPlugin, tailwindcss(), react()],
6114
7583
  // When the project has a tsconfig, enable Vite's native tsconfig-paths
6115
7584
  // resolver so `@/*` (or any custom alias) resolves through the
@@ -6340,7 +7809,7 @@ export default {
6340
7809
  // Inline all assets under 100MB (effectively all)
6341
7810
  } : {},
6342
7811
  rolldownOptions: {
6343
- input: path8.join(tempDir, "index.html"),
7812
+ input: path11.join(tempDir, "index.html"),
6344
7813
  external: (id) => {
6345
7814
  return false;
6346
7815
  }
@@ -6348,11 +7817,11 @@ export default {
6348
7817
  }
6349
7818
  });
6350
7819
  try {
6351
- const assetsDir = path8.join(outDir, "assets");
7820
+ const assetsDir = path11.join(outDir, "assets");
6352
7821
  const assetFiles = await fs11.readdir(assetsDir);
6353
7822
  const jsFiles = assetFiles.filter((f) => f.endsWith(".js"));
6354
7823
  for (const jsFile of jsFiles) {
6355
- const jsPath = path8.join(assetsDir, jsFile);
7824
+ const jsPath = path11.join(assetsDir, jsFile);
6356
7825
  let content = await fs11.readFile(jsPath, "utf8");
6357
7826
  const zodConfigPatterns = [
6358
7827
  // Non-minified: export const globalConfig = {}
@@ -6384,7 +7853,7 @@ export default {
6384
7853
  const mcpServerUrl = process.env.MCP_SERVER_URL;
6385
7854
  if (mcpServerUrl) {
6386
7855
  try {
6387
- const htmlPath = path8.join(outDir, "index.html");
7856
+ const htmlPath = path11.join(outDir, "index.html");
6388
7857
  let html = await fs11.readFile(htmlPath, "utf8");
6389
7858
  const injectionScript = `<script>window.__getFile = (filename) => { return "${mcpUrl}/${widgetName}/"+filename }; window.__mcpPublicUrl = "${mcpServerUrl}/mcp-use/public"; window.__mcpPublicAssetsUrl = "${mcpUrl}/public";</script>`;
6390
7859
  if (!html.includes("window.__mcpPublicUrl")) {
@@ -6460,7 +7929,7 @@ async function collectTsFiles(projectPath, includePatterns, excludePatterns) {
6460
7929
  for (const file of literalFiles) {
6461
7930
  if (/\.tsx?$/.test(file) && !file.endsWith(".d.ts")) {
6462
7931
  try {
6463
- await access(path8.join(projectPath, file));
7932
+ await access(path11.join(projectPath, file));
6464
7933
  files.push(file);
6465
7934
  } catch {
6466
7935
  }
@@ -6468,13 +7937,13 @@ async function collectTsFiles(projectPath, includePatterns, excludePatterns) {
6468
7937
  }
6469
7938
  const excludeSet = new Set(excludePatterns.map((e) => e.replace(/\*+/g, "")));
6470
7939
  for (const prefix of dirPrefixes) {
6471
- const dirPath = path8.join(projectPath, prefix);
7940
+ const dirPath = path11.join(projectPath, prefix);
6472
7941
  try {
6473
7942
  const entries = await fs11.readdir(dirPath, { recursive: true });
6474
7943
  for (const entry of entries) {
6475
7944
  const entryStr = String(entry);
6476
- const rel = path8.join(prefix, entryStr);
6477
- if (/\.tsx?$/.test(entryStr) && !entryStr.endsWith(".d.ts") && !excludeSet.has(rel.split(path8.sep)[0])) {
7945
+ const rel = path11.join(prefix, entryStr);
7946
+ if (/\.tsx?$/.test(entryStr) && !entryStr.endsWith(".d.ts") && !excludeSet.has(rel.split(path11.sep)[0])) {
6478
7947
  files.push(rel);
6479
7948
  }
6480
7949
  }
@@ -6486,7 +7955,7 @@ async function collectTsFiles(projectPath, includePatterns, excludePatterns) {
6486
7955
  async function transpileWithEsbuild(projectPath) {
6487
7956
  const esbuild = await import("esbuild");
6488
7957
  const { promises: fs11 } = await import("fs");
6489
- const tsconfigPath = path8.join(projectPath, "tsconfig.json");
7958
+ const tsconfigPath = path11.join(projectPath, "tsconfig.json");
6490
7959
  let tsconfig = {};
6491
7960
  try {
6492
7961
  const raw = await fs11.readFile(tsconfigPath, "utf-8");
@@ -6516,10 +7985,10 @@ async function transpileWithEsbuild(projectPath) {
6516
7985
  const target = (compilerOptions.target || "ES2022").toLowerCase();
6517
7986
  const moduleStr = (compilerOptions.module || "ESNext").toLowerCase();
6518
7987
  const format = moduleStr.includes("commonjs") ? "cjs" : "esm";
6519
- const outbase = compilerOptions.rootDir ? path8.resolve(projectPath, compilerOptions.rootDir) : projectPath;
7988
+ const outbase = compilerOptions.rootDir ? path11.resolve(projectPath, compilerOptions.rootDir) : projectPath;
6520
7989
  await esbuild.build({
6521
- entryPoints: files.map((f) => path8.join(projectPath, f)),
6522
- outdir: path8.join(projectPath, outDir),
7990
+ entryPoints: files.map((f) => path11.join(projectPath, f)),
7991
+ outdir: path11.join(projectPath, outDir),
6523
7992
  outbase,
6524
7993
  bundle: true,
6525
7994
  packages: "external",
@@ -6546,7 +8015,7 @@ program.command("build").description("Build TypeScript and MCP UI widgets").opti
6546
8015
  "Inline all JS/CSS into HTML (required for VS Code MCP Apps)"
6547
8016
  ).option("--no-inline", "Keep JS/CSS as separate files (default)").option("--no-typecheck", "Skip TypeScript type checking (faster builds)").action(async (options) => {
6548
8017
  try {
6549
- const projectPath = path8.resolve(options.path);
8018
+ const projectPath = path11.resolve(options.path);
6550
8019
  const { promises: fs11 } = await import("fs");
6551
8020
  displayPackageVersions(projectPath);
6552
8021
  const mcpDir = options.mcpDir;
@@ -6606,7 +8075,7 @@ program.command("build").description("Build TypeScript and MCP UI widgets").opti
6606
8075
  }
6607
8076
  if (options.typecheck !== false && !mcpDir) {
6608
8077
  console.log(source_default.gray("Type checking..."));
6609
- const tscBin = path8.join(
8078
+ const tscBin = path11.join(
6610
8079
  projectPath,
6611
8080
  "node_modules",
6612
8081
  "typescript",
@@ -6629,7 +8098,7 @@ program.command("build").description("Build TypeScript and MCP UI widgets").opti
6629
8098
  if (mcpDir) {
6630
8099
  entryPoint = sourceServerFile;
6631
8100
  } else {
6632
- const baseName = path8.basename(sourceServerFile, ".ts") + ".js";
8101
+ const baseName = path11.basename(sourceServerFile, ".ts") + ".js";
6633
8102
  const possibleOutputs = [
6634
8103
  `dist/${baseName}`,
6635
8104
  // rootDir set to project root or src
@@ -6640,7 +8109,7 @@ program.command("build").description("Build TypeScript and MCP UI widgets").opti
6640
8109
  ];
6641
8110
  for (const candidate of possibleOutputs) {
6642
8111
  try {
6643
- await access(path8.join(projectPath, candidate));
8112
+ await access(path11.join(projectPath, candidate));
6644
8113
  entryPoint = candidate;
6645
8114
  break;
6646
8115
  } catch {
@@ -6649,17 +8118,17 @@ program.command("build").description("Build TypeScript and MCP UI widgets").opti
6649
8118
  }
6650
8119
  }
6651
8120
  }
6652
- const publicDir = path8.join(projectPath, "public");
8121
+ const publicDir = path11.join(projectPath, "public");
6653
8122
  try {
6654
8123
  await fs11.access(publicDir);
6655
8124
  console.log(source_default.gray("Copying public assets..."));
6656
- await fs11.cp(publicDir, path8.join(projectPath, "dist", "public"), {
8125
+ await fs11.cp(publicDir, path11.join(projectPath, "dist", "public"), {
6657
8126
  recursive: true
6658
8127
  });
6659
8128
  console.log(source_default.green("\u2713 Public assets copied"));
6660
8129
  } catch {
6661
8130
  }
6662
- const manifestPath = path8.join(projectPath, "dist", "mcp-use.json");
8131
+ const manifestPath = path11.join(projectPath, "dist", "mcp-use.json");
6663
8132
  let existingManifest = {};
6664
8133
  try {
6665
8134
  const existingContent = await fs11.readFile(manifestPath, "utf-8");
@@ -6684,7 +8153,7 @@ program.command("build").description("Build TypeScript and MCP UI widgets").opti
6684
8153
  // Server entry point for `mcp-use start`
6685
8154
  widgets: widgetsData
6686
8155
  };
6687
- await fs11.mkdir(path8.dirname(manifestPath), { recursive: true });
8156
+ await fs11.mkdir(path11.dirname(manifestPath), { recursive: true });
6688
8157
  await fs11.writeFile(
6689
8158
  manifestPath,
6690
8159
  JSON.stringify(manifest, null, 2),
@@ -6721,7 +8190,7 @@ program.command("dev").description("Run development server with auto-reload and
6721
8190
  ).option("--no-open", "Do not auto-open inspector").option("--no-hmr", "Disable hot module reloading (use tsx watch instead)").option("--tunnel", "Expose server through a tunnel").action(async (options) => {
6722
8191
  try {
6723
8192
  process.env.MCP_USE_CLI_DEV = "1";
6724
- const projectPath = path8.resolve(options.path);
8193
+ const projectPath = path11.resolve(options.path);
6725
8194
  let port = parseInt(options.port, 10);
6726
8195
  const host = options.host;
6727
8196
  const useHmr = options.hmr !== false;
@@ -6749,7 +8218,7 @@ program.command("dev").description("Run development server with auto-reload and
6749
8218
  let tunnelUrl = void 0;
6750
8219
  if (options.tunnel) {
6751
8220
  try {
6752
- const manifestPath = path8.join(projectPath, "dist", "mcp-use.json");
8221
+ const manifestPath = path11.join(projectPath, "dist", "mcp-use.json");
6753
8222
  let existingSubdomain;
6754
8223
  try {
6755
8224
  const manifestContent = await readFile3(manifestPath, "utf-8");
@@ -6798,7 +8267,7 @@ program.command("dev").description("Run development server with auto-reload and
6798
8267
  manifest.tunnel = {};
6799
8268
  }
6800
8269
  manifest.tunnel.subdomain = tunnelSubdomain;
6801
- await mkdir3(path8.dirname(manifestPath), { recursive: true });
8270
+ await mkdir4(path11.dirname(manifestPath), { recursive: true });
6802
8271
  await writeFile3(
6803
8272
  manifestPath,
6804
8273
  JSON.stringify(manifest, null, 2),
@@ -6853,7 +8322,7 @@ program.command("dev").description("Run development server with auto-reload and
6853
8322
  let args;
6854
8323
  try {
6855
8324
  const projectRequire = createRequire4(
6856
- path8.join(projectPath, "package.json")
8325
+ path11.join(projectPath, "package.json")
6857
8326
  );
6858
8327
  const tsxPkgPath = projectRequire.resolve("tsx/package.json");
6859
8328
  const tsxPkg = JSON.parse(await readFile3(tsxPkgPath, "utf-8"));
@@ -6865,7 +8334,7 @@ program.command("dev").description("Run development server with auto-reload and
6865
8334
  } else {
6866
8335
  throw new Error("No bin field found in tsx package.json");
6867
8336
  }
6868
- const tsxBin = path8.resolve(path8.dirname(tsxPkgPath), binPath);
8337
+ const tsxBin = path11.resolve(path11.dirname(tsxPkgPath), binPath);
6869
8338
  cmd = "node";
6870
8339
  args = [tsxBin, "watch", serverFile];
6871
8340
  } catch (error) {
@@ -6954,7 +8423,7 @@ program.command("dev").description("Run development server with auto-reload and
6954
8423
  const chokidar = chokidarModule.default || chokidarModule;
6955
8424
  const { fileURLToPath: fileURLToPath4 } = await import("url");
6956
8425
  const { createRequire: createRequire3 } = await import("module");
6957
- const projectTsconfigPath = path8.join(projectPath, "tsconfig.json");
8426
+ const projectTsconfigPath = path11.join(projectPath, "tsconfig.json");
6958
8427
  let tsconfigAvailable = false;
6959
8428
  try {
6960
8429
  await access(projectTsconfigPath);
@@ -6966,7 +8435,7 @@ program.command("dev").description("Run development server with auto-reload and
6966
8435
  let tsxLoaderActive = false;
6967
8436
  try {
6968
8437
  const projectRequire = createRequire3(
6969
- path8.join(projectPath, "package.json")
8438
+ path11.join(projectPath, "package.json")
6970
8439
  );
6971
8440
  const tsxEsmApiPath = projectRequire.resolve("tsx/esm/api");
6972
8441
  const tsxEsmApi = await import(pathToFileURL2(tsxEsmApiPath).href);
@@ -6997,7 +8466,7 @@ program.command("dev").description("Run development server with auto-reload and
6997
8466
  )
6998
8467
  );
6999
8468
  }
7000
- const serverFilePath = path8.join(projectPath, serverFile);
8469
+ const serverFilePath = path11.join(projectPath, serverFile);
7001
8470
  const serverFileUrl = pathToFileURL2(serverFilePath).href;
7002
8471
  globalThis.__mcpUseHmrMode = true;
7003
8472
  const importServerModule = async () => {
@@ -7094,8 +8563,8 @@ program.command("dev").description("Run development server with auto-reload and
7094
8563
  }
7095
8564
  let watcher = chokidar.watch(".", {
7096
8565
  cwd: projectPath,
7097
- ignored: (path9, stats) => {
7098
- const normalizedPath = path9.replace(/\\/g, "/");
8566
+ ignored: (path12, stats) => {
8567
+ const normalizedPath = path12.replace(/\\/g, "/");
7099
8568
  if (/(^|\/)\.[^/]/.test(normalizedPath)) {
7100
8569
  return true;
7101
8570
  }
@@ -7269,7 +8738,7 @@ program.command("dev").description("Run development server with auto-reload and
7269
8738
  }
7270
8739
  tunnelUrl = void 0;
7271
8740
  if (withTunnel) {
7272
- const manifestPath = path8.join(projectPath, "dist", "mcp-use.json");
8741
+ const manifestPath = path11.join(projectPath, "dist", "mcp-use.json");
7273
8742
  let existingSubdomain;
7274
8743
  try {
7275
8744
  const manifestContent = await readFile3(manifestPath, "utf-8");
@@ -7306,7 +8775,7 @@ program.command("dev").description("Run development server with auto-reload and
7306
8775
  tunnelSubdomain = tunnelInfo.subdomain;
7307
8776
  process.env.MCP_URL = tunnelUrl;
7308
8777
  try {
7309
- const mPath = path8.join(projectPath, "dist", "mcp-use.json");
8778
+ const mPath = path11.join(projectPath, "dist", "mcp-use.json");
7310
8779
  let manifest = {};
7311
8780
  try {
7312
8781
  manifest = JSON.parse(await readFile3(mPath, "utf-8"));
@@ -7314,7 +8783,7 @@ program.command("dev").description("Run development server with auto-reload and
7314
8783
  }
7315
8784
  if (!manifest.tunnel) manifest.tunnel = {};
7316
8785
  manifest.tunnel.subdomain = tunnelSubdomain;
7317
- await mkdir3(path8.dirname(mPath), { recursive: true });
8786
+ await mkdir4(path11.dirname(mPath), { recursive: true });
7318
8787
  await writeFile3(mPath, JSON.stringify(manifest, null, 2), "utf-8");
7319
8788
  } catch {
7320
8789
  }
@@ -7418,7 +8887,7 @@ program.command("start").description("Start production server").option("-p, --pa
7418
8887
  "Folder holding the MCP entry + resources (e.g. 'src/mcp' for Next.js apps)"
7419
8888
  ).option("--port <port>", "Server port", "3000").option("--tunnel", "Expose server through a tunnel").action(async (options) => {
7420
8889
  try {
7421
- const projectPath = path8.resolve(options.path);
8890
+ const projectPath = path11.resolve(options.path);
7422
8891
  const portFlagProvided = process.argv.includes("--port") || process.argv.includes("-p") || process.argv.some((arg) => arg.startsWith("--port=")) || process.argv.some((arg) => arg.startsWith("-p="));
7423
8892
  let port = portFlagProvided ? parseInt(options.port, 10) : parseInt(process.env.PORT || options.port || "3000", 10);
7424
8893
  if (!await isPortAvailable(port)) {
@@ -7436,7 +8905,7 @@ program.command("start").description("Start production server").option("-p, --pa
7436
8905
  let tunnelSubdomain = void 0;
7437
8906
  if (options.tunnel) {
7438
8907
  try {
7439
- const manifestPath2 = path8.join(projectPath, "dist", "mcp-use.json");
8908
+ const manifestPath2 = path11.join(projectPath, "dist", "mcp-use.json");
7440
8909
  let existingSubdomain;
7441
8910
  try {
7442
8911
  const manifestContent = await readFile3(manifestPath2, "utf-8");
@@ -7491,7 +8960,7 @@ program.command("start").description("Start production server").option("-p, --pa
7491
8960
  manifest.tunnel = {};
7492
8961
  }
7493
8962
  manifest.tunnel.subdomain = subdomain;
7494
- await mkdir3(path8.dirname(manifestPath2), { recursive: true });
8963
+ await mkdir4(path11.dirname(manifestPath2), { recursive: true });
7495
8964
  await writeFile3(
7496
8965
  manifestPath2,
7497
8966
  JSON.stringify(manifest, null, 2),
@@ -7510,12 +8979,12 @@ program.command("start").description("Start production server").option("-p, --pa
7510
8979
  }
7511
8980
  }
7512
8981
  let serverFile;
7513
- const manifestPath = path8.join(projectPath, "dist", "mcp-use.json");
8982
+ const manifestPath = path11.join(projectPath, "dist", "mcp-use.json");
7514
8983
  try {
7515
8984
  const manifestContent = await readFile3(manifestPath, "utf-8");
7516
8985
  const manifest = JSON.parse(manifestContent);
7517
8986
  if (manifest.entryPoint) {
7518
- await access(path8.join(projectPath, manifest.entryPoint));
8987
+ await access(path11.join(projectPath, manifest.entryPoint));
7519
8988
  serverFile = manifest.entryPoint;
7520
8989
  }
7521
8990
  } catch {
@@ -7536,7 +9005,7 @@ program.command("start").description("Start production server").option("-p, --pa
7536
9005
  ];
7537
9006
  for (const candidate of serverCandidates) {
7538
9007
  try {
7539
- await access(path8.join(projectPath, candidate));
9008
+ await access(path11.join(projectPath, candidate));
7540
9009
  serverFile = candidate;
7541
9010
  break;
7542
9011
  } catch {
@@ -7587,13 +9056,13 @@ Looked for:
7587
9056
  if (isTsEntry) {
7588
9057
  try {
7589
9058
  const projectRequire = createRequire2(
7590
- path8.join(projectPath, "package.json")
9059
+ path11.join(projectPath, "package.json")
7591
9060
  );
7592
9061
  const tsxPkgPath = projectRequire.resolve("tsx/package.json");
7593
9062
  const tsxPkg = JSON.parse(await readFile3(tsxPkgPath, "utf-8"));
7594
9063
  const binField = typeof tsxPkg.bin === "string" ? tsxPkg.bin : tsxPkg.bin?.tsx ?? Object.values(tsxPkg.bin ?? {})[0];
7595
9064
  if (!binField) throw new Error("tsx bin entry not found");
7596
- const tsxBin = path8.resolve(path8.dirname(tsxPkgPath), binField);
9065
+ const tsxBin = path11.resolve(path11.dirname(tsxPkgPath), binField);
7597
9066
  spawnCmd = "node";
7598
9067
  spawnArgs = [tsxBin, serverFile];
7599
9068
  } catch (error) {
@@ -7606,7 +9075,7 @@ Looked for:
7606
9075
  spawnArgs = ["tsx", serverFile];
7607
9076
  }
7608
9077
  }
7609
- const serverProc = spawn(spawnCmd, spawnArgs, {
9078
+ const serverProc = spawn3(spawnCmd, spawnArgs, {
7610
9079
  cwd: projectPath,
7611
9080
  stdio: "inherit",
7612
9081
  env: env2
@@ -7739,10 +9208,11 @@ program.addCommand(createClientCommand());
7739
9208
  program.addCommand(createDeploymentsCommand());
7740
9209
  program.addCommand(createServersCommand());
7741
9210
  program.addCommand(createSkillsCommand());
9211
+ program.addCommand(createScreenshotCommand());
7742
9212
  program.command("generate-types").description(
7743
9213
  "Generate TypeScript type definitions for tools (writes .mcp-use/tool-registry.d.ts)"
7744
9214
  ).option("-p, --path <path>", "Path to project directory", process.cwd()).option("--server <file>", "Server entry file", "index.ts").action(async (options) => {
7745
- const projectPath = path8.resolve(options.path);
9215
+ const projectPath = path11.resolve(options.path);
7746
9216
  try {
7747
9217
  console.log(source_default.blue("Generating tool registry types..."));
7748
9218
  const result = await generateToolRegistryTypesForServer(
@@ -7772,5 +9242,49 @@ program.hook("preAction", async (_thisCommand, actionCommand) => {
7772
9242
  const projectPath = actionCommand.opts().path;
7773
9243
  await notifyIfUpdateAvailable(projectPath);
7774
9244
  });
7775
- program.parse();
9245
+ var argv = process.argv;
9246
+ var clientIdx = argv[2] === "client" ? 2 : -1;
9247
+ var perClientName = clientIdx !== -1 && argv.length > clientIdx + 1 && !argv[clientIdx + 1].startsWith("-") && !RESERVED_CLIENT_SUBCOMMANDS.has(argv[clientIdx + 1]) ? argv[clientIdx + 1] : null;
9248
+ if (perClientName) {
9249
+ if (PER_CLIENT_SCOPES.has(perClientName)) {
9250
+ const rest2 = argv.slice(clientIdx + 1).join(" ");
9251
+ console.error(formatError("Missing server name."));
9252
+ console.error("");
9253
+ console.error(
9254
+ `'${perClientName}' is a per-server subcommand, not a server name. Address it through a saved server:`
9255
+ );
9256
+ console.error("");
9257
+ console.error(` mcp-use client <name> ${rest2}`);
9258
+ console.error("");
9259
+ console.error("See your saved servers with:");
9260
+ console.error(" mcp-use client list");
9261
+ process.exit(1);
9262
+ }
9263
+ const rest = argv.slice(clientIdx + 2);
9264
+ const isHelpOnly = rest.length === 0 || rest.length === 1 && (rest[0] === "--help" || rest[0] === "-h");
9265
+ (async () => {
9266
+ if (isHelpOnly) {
9267
+ const config = await getSession(perClientName);
9268
+ if (!config) {
9269
+ console.error(formatError(`Server '${perClientName}' not found.`));
9270
+ console.error("");
9271
+ console.error("Connect to an MCP server and save it under this name:");
9272
+ console.error(` mcp-use client connect ${perClientName} <url>`);
9273
+ console.error("");
9274
+ console.error("See your saved servers with:");
9275
+ console.error(" mcp-use client list");
9276
+ process.exit(1);
9277
+ }
9278
+ }
9279
+ await createPerClientCommand(perClientName).parseAsync(rest, {
9280
+ from: "user"
9281
+ });
9282
+ })().catch((err) => {
9283
+ const message = err instanceof Error ? err.message : String(err);
9284
+ console.error(formatError(message));
9285
+ process.exit(1);
9286
+ });
9287
+ } else {
9288
+ program.parse();
9289
+ }
7776
9290
  //# sourceMappingURL=index.js.map