@mcp-use/cli 3.1.5-canary.3 → 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/commands/client-auth.d.ts +4 -0
- package/dist/commands/client-auth.d.ts.map +1 -0
- package/dist/commands/client.d.ts +40 -69
- package/dist/commands/client.d.ts.map +1 -1
- package/dist/commands/deployments.d.ts.map +1 -1
- package/dist/commands/screenshot.d.ts +80 -0
- package/dist/commands/screenshot.d.ts.map +1 -0
- package/dist/commands/servers.d.ts.map +1 -1
- package/dist/commands/skills.d.ts.map +1 -1
- package/dist/index.cjs +2049 -540
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2011 -497
- package/dist/index.js.map +1 -1
- package/dist/utils/cdp-screenshot.d.ts +44 -0
- package/dist/utils/cdp-screenshot.d.ts.map +1 -0
- package/dist/utils/chrome-path.d.ts +14 -0
- package/dist/utils/chrome-path.d.ts.map +1 -0
- package/dist/utils/format.d.ts +36 -5
- package/dist/utils/format.d.ts.map +1 -1
- package/dist/utils/oauth.d.ts +22 -0
- package/dist/utils/oauth.d.ts.map +1 -0
- package/dist/utils/parse-args.d.ts +27 -0
- package/dist/utils/parse-args.d.ts.map +1 -0
- package/dist/utils/session-storage.d.ts +13 -33
- package/dist/utils/session-storage.d.ts.map +1 -1
- package/dist/utils/session.d.ts +39 -0
- package/dist/utils/session.d.ts.map +1 -0
- package/package.json +5 -3
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.
|
|
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,
|
|
200
|
+
function hasFlag(flag, argv2 = globalThis.Deno ? globalThis.Deno.args : process2.argv) {
|
|
201
201
|
const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
|
|
202
|
-
const position =
|
|
203
|
-
const terminatorPosition =
|
|
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
|
|
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
|
|
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
|
|
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) =>
|
|
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 (
|
|
700
|
-
if (/^[a-z]+:\/\//i.test(
|
|
701
|
-
return
|
|
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",
|
|
704
|
+
const { stdout } = await execFile2("wslpath", ["-aw", path12], { encoding: "utf8" });
|
|
705
705
|
return stdout.trim();
|
|
706
706
|
} catch {
|
|
707
|
-
return
|
|
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
|
|
1324
|
-
const 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
|
|
1358
|
-
return this.request(`/servers/${
|
|
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 {
|
|
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
|
-
|
|
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
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
)
|
|
1872
|
-
|
|
1873
|
-
|
|
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
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
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
|
-
|
|
1888
|
-
};
|
|
1934
|
+
}
|
|
1889
1935
|
const lines = [];
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
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(
|
|
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
|
-
|
|
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 (
|
|
1920
|
-
|
|
1921
|
-
|
|
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 <
|
|
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
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
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 {
|
|
2385
|
+
return { sessions: {} };
|
|
2116
2386
|
}
|
|
2117
2387
|
const content = await readFile(SESSION_FILE_PATH, "utf-8");
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
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
|
|
2406
|
+
async function removeSession(name) {
|
|
2139
2407
|
const storage = await loadSessions();
|
|
2140
|
-
|
|
2141
|
-
|
|
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/
|
|
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
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
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
|
-
|
|
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(`
|
|
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
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
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
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
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
|
|
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 =
|
|
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 ${
|
|
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:
|
|
2276
|
-
|
|
3555
|
+
url: target,
|
|
3556
|
+
...authProvider ? { authProvider } : options.auth ? { headers: { Authorization: `Bearer ${options.auth}` } } : {},
|
|
2277
3557
|
clientInfo: cliClientInfo
|
|
2278
3558
|
});
|
|
2279
|
-
|
|
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:
|
|
2283
|
-
|
|
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
|
|
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
|
-
|
|
3615
|
+
await cleanupAndExit(1);
|
|
2320
3616
|
}
|
|
3617
|
+
await cleanupAndExit(0);
|
|
2321
3618
|
}
|
|
2322
|
-
async function disconnectCommand(
|
|
3619
|
+
async function disconnectCommand(name) {
|
|
2323
3620
|
try {
|
|
2324
|
-
|
|
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(
|
|
2344
|
-
console.log(formatSuccess(`Disconnected from ${
|
|
3624
|
+
activeSessions.delete(name);
|
|
3625
|
+
console.log(formatSuccess(`Disconnected from ${name}`));
|
|
2345
3626
|
} else {
|
|
2346
|
-
console.log(formatInfo(`
|
|
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
|
-
|
|
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
|
|
3683
|
+
async function listClientsCommand() {
|
|
2354
3684
|
try {
|
|
2355
3685
|
const sessions = await listAllSessions();
|
|
2356
3686
|
if (sessions.length === 0) {
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
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
|
-
|
|
2364
|
-
|
|
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.
|
|
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",
|
|
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
|
|
2394
|
-
|
|
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(
|
|
2400
|
-
if (!result)
|
|
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
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
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
|
-
|
|
3772
|
+
await cleanupAndExit(1);
|
|
2426
3773
|
}
|
|
3774
|
+
await cleanupAndExit(0);
|
|
2427
3775
|
}
|
|
2428
|
-
async function describeToolCommand(
|
|
3776
|
+
async function describeToolCommand(name, toolName) {
|
|
2429
3777
|
try {
|
|
2430
|
-
const result = await getOrRestoreSession(
|
|
2431
|
-
if (!result)
|
|
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
|
-
|
|
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
|
-
|
|
3804
|
+
await cleanupAndExit(1);
|
|
2455
3805
|
}
|
|
3806
|
+
await cleanupAndExit(0);
|
|
2456
3807
|
}
|
|
2457
|
-
async function callToolCommand(toolName,
|
|
3808
|
+
async function callToolCommand(name, toolName, argsList, options) {
|
|
2458
3809
|
try {
|
|
2459
|
-
const result = await getOrRestoreSession(
|
|
2460
|
-
if (!result)
|
|
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 (
|
|
3818
|
+
if (argsList && argsList.length > 0) {
|
|
2464
3819
|
try {
|
|
2465
|
-
args =
|
|
3820
|
+
args = parseToolArgs(argsList, tool?.inputSchema);
|
|
2466
3821
|
} catch (error) {
|
|
2467
|
-
console.error(formatError(
|
|
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("
|
|
3824
|
+
console.log(formatInfo("Usage:"));
|
|
2481
3825
|
console.log(
|
|
2482
|
-
` npx mcp-use client tools call ${toolName}
|
|
3826
|
+
` npx mcp-use client ${name} tools call ${toolName} key=value [key2=value2 ...]`
|
|
2483
3827
|
);
|
|
2484
|
-
console.log(
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
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
|
-
|
|
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(
|
|
2507
|
-
if (!result)
|
|
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
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
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
|
-
|
|
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(
|
|
2541
|
-
if (!result)
|
|
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
|
-
|
|
3975
|
+
await cleanupAndExit(1);
|
|
2553
3976
|
}
|
|
3977
|
+
await cleanupAndExit(0);
|
|
2554
3978
|
}
|
|
2555
|
-
async function subscribeResourceCommand(
|
|
3979
|
+
async function subscribeResourceCommand(name, uri) {
|
|
2556
3980
|
try {
|
|
2557
|
-
const result = await getOrRestoreSession(
|
|
2558
|
-
if (!result)
|
|
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
|
-
|
|
4002
|
+
await cleanupAndExit(1);
|
|
2577
4003
|
}
|
|
2578
4004
|
}
|
|
2579
|
-
async function unsubscribeResourceCommand(
|
|
4005
|
+
async function unsubscribeResourceCommand(name, uri) {
|
|
2580
4006
|
try {
|
|
2581
|
-
const result = await getOrRestoreSession(
|
|
2582
|
-
if (!result)
|
|
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
|
-
|
|
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(
|
|
2596
|
-
if (!result)
|
|
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
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
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
|
-
|
|
4062
|
+
await cleanupAndExit(1);
|
|
2623
4063
|
}
|
|
4064
|
+
await cleanupAndExit(0);
|
|
2624
4065
|
}
|
|
2625
|
-
async function getPromptCommand(promptName,
|
|
4066
|
+
async function getPromptCommand(name, promptName, argsList, options) {
|
|
2626
4067
|
try {
|
|
2627
|
-
const result = await getOrRestoreSession(
|
|
2628
|
-
if (!result)
|
|
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 (
|
|
4074
|
+
if (argsList && argsList.length > 0) {
|
|
2632
4075
|
try {
|
|
2633
|
-
args =
|
|
4076
|
+
args = parsePromptArgs(argsList);
|
|
2634
4077
|
} catch (error) {
|
|
2635
|
-
console.error(formatError(
|
|
2636
|
-
|
|
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
|
-
|
|
4109
|
+
await cleanupAndExit(1);
|
|
2659
4110
|
}
|
|
4111
|
+
await cleanupAndExit(0);
|
|
2660
4112
|
}
|
|
2661
|
-
async function interactiveCommand(
|
|
4113
|
+
async function interactiveCommand(name) {
|
|
2662
4114
|
try {
|
|
2663
|
-
const result = await getOrRestoreSession(
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
4278
|
+
await cleanupAndExit(1);
|
|
2843
4279
|
}
|
|
2844
4280
|
}
|
|
2845
4281
|
function createClientCommand() {
|
|
2846
|
-
const clientCommand = new
|
|
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
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
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
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
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
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
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
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
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
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
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
|
|
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
|
|
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
|
|
4514
|
+
return path7.join(cwd, MCP_USE_DIR);
|
|
3044
4515
|
}
|
|
3045
4516
|
async function getProjectLink(cwd) {
|
|
3046
4517
|
try {
|
|
3047
|
-
const linkPath =
|
|
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 =
|
|
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 =
|
|
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(
|
|
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(
|
|
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
|
|
4689
|
+
return path8.basename(cwd);
|
|
3219
4690
|
}
|
|
3220
4691
|
async function detectBuildCommand(cwd) {
|
|
3221
4692
|
try {
|
|
3222
|
-
const content = await fs9.readFile(
|
|
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(
|
|
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(
|
|
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 =
|
|
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 ?
|
|
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
|
|
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
|
|
4673
|
-
"
|
|
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
|
|
6159
|
+
import { Command as Command5 } from "commander";
|
|
4689
6160
|
|
|
4690
6161
|
// src/commands/env.ts
|
|
4691
|
-
import { Command as
|
|
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
|
|
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
|
|
5121
|
-
"
|
|
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
|
|
5239
|
-
import { cpSync, existsSync as
|
|
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 =
|
|
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: (
|
|
6757
|
+
filter: (path12) => path12.includes("/skills/"),
|
|
5289
6758
|
strip: 1
|
|
5290
6759
|
})
|
|
5291
6760
|
);
|
|
5292
6761
|
const skillsPath = join2(tempDir, "skills");
|
|
5293
|
-
if (!
|
|
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
|
-
|
|
6773
|
+
rmSync2(tempDir, { recursive: true, force: true });
|
|
5305
6774
|
}
|
|
5306
6775
|
}
|
|
5307
6776
|
function createSkillsCommand() {
|
|
5308
|
-
const skills = new
|
|
5309
|
-
"
|
|
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 (!
|
|
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
|
|
5355
|
-
import
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
-
|
|
6865
|
+
path9.join(thisDir, "shims", filename),
|
|
5397
6866
|
// Test / dev: one level up (e.g., from `dist/utils/` back to `src/shims/`)
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
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 (
|
|
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() +
|
|
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() +
|
|
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
|
|
6924
|
+
import { mkdir as mkdir3, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
5456
6925
|
import { createRequire } from "module";
|
|
5457
|
-
import
|
|
5458
|
-
import
|
|
5459
|
-
var CACHE_DIR =
|
|
5460
|
-
var CACHE_FILE =
|
|
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
|
|
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
|
-
|
|
7012
|
+
path10.join(projectPath, "package.json")
|
|
5544
7013
|
);
|
|
5545
7014
|
return projectRequire.resolve(`${PACKAGE_NAME}/package.json`);
|
|
5546
7015
|
});
|
|
5547
7016
|
}
|
|
5548
|
-
attempts.push(() =>
|
|
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
|
|
7053
|
+
var program = new Command7();
|
|
5585
7054
|
var packageContent = readFileSync2(
|
|
5586
|
-
|
|
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
|
-
|
|
7086
|
+
path11.join(projectPath, "package.json")
|
|
5618
7087
|
);
|
|
5619
7088
|
pkgPath = projectRequire.resolve(`${pkg.name}/package.json`);
|
|
5620
7089
|
} catch (resolveError) {
|
|
5621
|
-
pkgPath =
|
|
7090
|
+
pkgPath = path11.join(__dirname, pkg.relativePath);
|
|
5622
7091
|
}
|
|
5623
7092
|
} else {
|
|
5624
|
-
pkgPath =
|
|
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 =
|
|
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 =
|
|
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(
|
|
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
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
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(
|
|
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) =>
|
|
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 ${
|
|
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(
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
5927
|
-
const { generateToolRegistryTypes } = await import(pathToFileURL2(
|
|
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 =
|
|
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:
|
|
7436
|
+
path: path11.join(resourcesDir, dirent.name)
|
|
5968
7437
|
});
|
|
5969
7438
|
} else if (dirent.isDirectory()) {
|
|
5970
|
-
const widgetPath =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
7488
|
+
const tempDir = path11.join(projectPath, ".mcp-use", widgetName);
|
|
6020
7489
|
await fs11.mkdir(tempDir, { recursive: true });
|
|
6021
|
-
const relativeResourcesPath =
|
|
6022
|
-
const mcpUsePath =
|
|
6023
|
-
const relativeMcpUsePath =
|
|
6024
|
-
const projectSrcDir =
|
|
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 =
|
|
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(
|
|
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(
|
|
6065
|
-
await fs11.writeFile(
|
|
6066
|
-
const outDir =
|
|
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 =
|
|
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
|
|
7579
|
+
const metadataServer = await createServer2({
|
|
6111
7580
|
root: metadataTempDir,
|
|
6112
|
-
cacheDir:
|
|
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:
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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(
|
|
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 =
|
|
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 =
|
|
6477
|
-
if (/\.tsx?$/.test(entryStr) && !entryStr.endsWith(".d.ts") && !excludeSet.has(rel.split(
|
|
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 =
|
|
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 ?
|
|
7988
|
+
const outbase = compilerOptions.rootDir ? path11.resolve(projectPath, compilerOptions.rootDir) : projectPath;
|
|
6520
7989
|
await esbuild.build({
|
|
6521
|
-
entryPoints: files.map((f) =>
|
|
6522
|
-
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 =
|
|
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 =
|
|
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 =
|
|
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(
|
|
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 =
|
|
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,
|
|
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 =
|
|
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(
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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: (
|
|
7098
|
-
const normalizedPath =
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|