@mcp-use/cli 3.1.5-canary.4 → 3.2.0-canary.11
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 +96 -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 +2076 -540
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2038 -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,1054 @@ 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, sessionName, headers) {
|
|
3249
|
+
if (sessionName) {
|
|
3250
|
+
const result = await getOrRestoreSession(sessionName);
|
|
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 --mcp <url> for an ad-hoc connection, or use `mcp-use client <name> screenshot` for a saved server."
|
|
3273
|
+
)
|
|
3274
|
+
);
|
|
3275
|
+
return null;
|
|
3276
|
+
}
|
|
3277
|
+
async function screenshotCommand(options, argsList, context) {
|
|
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 servers 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(
|
|
3324
|
+
options,
|
|
3325
|
+
context.sessionName,
|
|
3326
|
+
headers
|
|
3327
|
+
);
|
|
3328
|
+
if (!session) {
|
|
3329
|
+
exitCode = 1;
|
|
3330
|
+
return;
|
|
3331
|
+
}
|
|
3332
|
+
const tool = session.tools.find((t) => t.name === options.tool);
|
|
3333
|
+
if (!tool) {
|
|
3334
|
+
throw new Error(
|
|
3335
|
+
`Tool "${options.tool}" not found. Available: ${session.tools.map((t) => t.name).join(", ")}`
|
|
3336
|
+
);
|
|
3337
|
+
}
|
|
3338
|
+
const resourceUri = detectToolResourceUri(tool);
|
|
3339
|
+
if (!resourceUri) {
|
|
3340
|
+
throw new Error(
|
|
3341
|
+
`Tool "${options.tool}" does not declare a UI resource (expected _meta.ui.resourceUri or openai/outputTemplate).`
|
|
3342
|
+
);
|
|
3343
|
+
}
|
|
3344
|
+
let toolArgs = {};
|
|
3345
|
+
if (argsList && argsList.length > 0) {
|
|
3346
|
+
try {
|
|
3347
|
+
toolArgs = parseToolArgs(
|
|
3348
|
+
argsList,
|
|
3349
|
+
tool.inputSchema
|
|
3350
|
+
);
|
|
3351
|
+
} catch (err) {
|
|
3352
|
+
console.error(
|
|
3353
|
+
formatError(err instanceof Error ? err.message : String(err))
|
|
3354
|
+
);
|
|
3355
|
+
console.log("");
|
|
3356
|
+
console.log(formatInfo("Usage:"));
|
|
3357
|
+
console.log(
|
|
3358
|
+
` npx ${context.usagePrefix} --tool ${options.tool} key=value [key2=value2 ...]`
|
|
3359
|
+
);
|
|
3360
|
+
console.log(
|
|
3361
|
+
` npx ${context.usagePrefix} --tool ${options.tool} nested:='{"a":1}' # JSON value`
|
|
3362
|
+
);
|
|
3363
|
+
console.log(
|
|
3364
|
+
` npx ${context.usagePrefix} --tool ${options.tool} '{"key":"value"}' # full JSON object`
|
|
3365
|
+
);
|
|
3366
|
+
if (tool.inputSchema) {
|
|
3367
|
+
console.log("");
|
|
3368
|
+
console.log(formatInfo("Tool schema:"));
|
|
3369
|
+
console.log(formatSchema(tool.inputSchema));
|
|
3370
|
+
}
|
|
3371
|
+
exitCode = 1;
|
|
3372
|
+
return;
|
|
3373
|
+
}
|
|
3374
|
+
}
|
|
3375
|
+
const toolOutput = await session.callTool(options.tool, toolArgs);
|
|
3376
|
+
const result = await captureToolScreenshot(
|
|
3377
|
+
{
|
|
3378
|
+
session,
|
|
3379
|
+
toolName: options.tool,
|
|
3380
|
+
toolArgs,
|
|
3381
|
+
toolOutput,
|
|
3382
|
+
resourceUri
|
|
3383
|
+
},
|
|
3384
|
+
{
|
|
3385
|
+
width,
|
|
3386
|
+
height,
|
|
3387
|
+
theme: options.theme,
|
|
3388
|
+
output: options.output,
|
|
3389
|
+
waitFor: options.waitFor,
|
|
3390
|
+
delayMs,
|
|
3391
|
+
timeoutMs: navTimeout,
|
|
3392
|
+
inspector: options.inspector,
|
|
3393
|
+
quiet: options.quiet,
|
|
3394
|
+
cdpUrl: options.cdpUrl
|
|
3395
|
+
}
|
|
3396
|
+
);
|
|
3397
|
+
console.log(
|
|
3398
|
+
`Saved screenshot: ${result.outputPath} (${result.width}\xD7${result.height})`
|
|
3399
|
+
);
|
|
3400
|
+
} catch (err) {
|
|
3401
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3402
|
+
console.error(formatError(`Screenshot failed: ${msg}`));
|
|
3403
|
+
exitCode = 1;
|
|
3404
|
+
} finally {
|
|
3405
|
+
await cleanupAndExit(exitCode);
|
|
3406
|
+
}
|
|
3407
|
+
}
|
|
3408
|
+
function withCommonScreenshotOptions(cmd) {
|
|
3409
|
+
return cmd.argument(
|
|
3410
|
+
"[args...]",
|
|
3411
|
+
"Tool args as key=value pairs (use key:=<json> for nested values, or pass a single JSON object)."
|
|
3412
|
+
).option(
|
|
3413
|
+
"--tool <name>",
|
|
3414
|
+
"Tool to call. Its UI resource is rendered with the result."
|
|
3415
|
+
).option("--width <px>", "Browser viewport width in pixels.", "800").option("--height <px>", "Browser viewport height in pixels.", "600").option(
|
|
3416
|
+
"--inspector <url>",
|
|
3417
|
+
"Inspector host that serves /inspector/preview/:view. When omitted, auto-spawns `@mcp-use/inspector` on a free port."
|
|
3418
|
+
).option(
|
|
3419
|
+
"--theme <light|dark>",
|
|
3420
|
+
"Color scheme to render the view in.",
|
|
3421
|
+
"light"
|
|
3422
|
+
).option(
|
|
3423
|
+
"--output <path>",
|
|
3424
|
+
"Output PNG path. Defaults to ./<view>-<timestamp>.png in cwd."
|
|
3425
|
+
).option(
|
|
3426
|
+
"--wait-for <selector>",
|
|
3427
|
+
'Override readiness selector (default: body[data-view-ready="true"]).'
|
|
3428
|
+
).option(
|
|
3429
|
+
"--delay <ms>",
|
|
3430
|
+
"Extra wait after readiness, to let chart animations / async layouts settle.",
|
|
3431
|
+
"0"
|
|
3432
|
+
).option("--timeout <ms>", "Navigation + readiness timeout in ms.", "30000").option(
|
|
3433
|
+
"--cdp-url <url>",
|
|
3434
|
+
"Connect to an existing CDP WebSocket (ws:// or wss://) instead of spawning local Chrome. Useful for hosted browsers like Notte."
|
|
3435
|
+
).option("--quiet", "Suppress dev-server output.");
|
|
3436
|
+
}
|
|
3437
|
+
function createClientScreenshotCommand() {
|
|
3438
|
+
const cmd = withCommonScreenshotOptions(
|
|
3439
|
+
new Command("screenshot").description(
|
|
3440
|
+
"Render an MCP Apps view headlessly and save a PNG. Connects to an MCP server inline via --mcp; for a saved server, use `mcp-use client <name> screenshot`."
|
|
3441
|
+
)
|
|
3442
|
+
).option(
|
|
3443
|
+
"--mcp <url>",
|
|
3444
|
+
"Ad-hoc MCP server URL. Required for the top-level form. No authentication unless --header is supplied."
|
|
3445
|
+
).option(
|
|
3446
|
+
"-H, --header <header>",
|
|
3447
|
+
'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.',
|
|
3448
|
+
collectHeader,
|
|
3449
|
+
[]
|
|
3450
|
+
);
|
|
3451
|
+
cmd.action(async (args, opts) => {
|
|
3452
|
+
await screenshotCommand(opts, args, {
|
|
3453
|
+
usagePrefix: "mcp-use client screenshot"
|
|
3454
|
+
});
|
|
3455
|
+
});
|
|
3456
|
+
return cmd;
|
|
3457
|
+
}
|
|
3458
|
+
function createPerClientScreenshotCommand(name) {
|
|
3459
|
+
const cmd = withCommonScreenshotOptions(
|
|
3460
|
+
new Command("screenshot").description(
|
|
3461
|
+
`Render an MCP Apps view headlessly using the saved server '${name}'.`
|
|
3462
|
+
)
|
|
3463
|
+
);
|
|
3464
|
+
cmd.action(async (args, opts) => {
|
|
3465
|
+
await screenshotCommand(opts, args, {
|
|
3466
|
+
sessionName: name,
|
|
3467
|
+
usagePrefix: `mcp-use client ${name} screenshot`
|
|
3468
|
+
});
|
|
3469
|
+
});
|
|
3470
|
+
return cmd;
|
|
3471
|
+
}
|
|
3472
|
+
|
|
3473
|
+
// src/commands/client.ts
|
|
3474
|
+
var RESERVED_CLIENT_SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
3475
|
+
"connect",
|
|
3476
|
+
"list",
|
|
3477
|
+
"remove",
|
|
3478
|
+
"screenshot",
|
|
3479
|
+
"help"
|
|
3480
|
+
]);
|
|
3481
|
+
var PER_CLIENT_SCOPES = /* @__PURE__ */ new Set([
|
|
3482
|
+
"tools",
|
|
3483
|
+
"resources",
|
|
3484
|
+
"prompts",
|
|
3485
|
+
"auth",
|
|
3486
|
+
"disconnect",
|
|
3487
|
+
"interactive",
|
|
3488
|
+
"screenshot"
|
|
3489
|
+
]);
|
|
3490
|
+
async function connectCommand(name, urlOrCommand, options) {
|
|
3491
|
+
if (!name || !urlOrCommand) {
|
|
3492
|
+
const looksLikeUrl = !!name && /^https?:\/\//i.test(name);
|
|
3493
|
+
if (looksLikeUrl && !urlOrCommand && !options.stdio) {
|
|
3494
|
+
console.error(formatError("Missing server name."));
|
|
3495
|
+
console.error("");
|
|
3496
|
+
console.error(
|
|
3497
|
+
formatInfo(
|
|
3498
|
+
"Each saved server needs a short name you'll use to address it later."
|
|
3499
|
+
)
|
|
3500
|
+
);
|
|
3501
|
+
console.error("");
|
|
3502
|
+
console.error("Try:");
|
|
3503
|
+
console.error(` mcp-use client connect <name> ${name}`);
|
|
3504
|
+
console.error("");
|
|
3505
|
+
console.error("Example:");
|
|
3506
|
+
console.error(` mcp-use client connect my-server ${name}`);
|
|
3507
|
+
console.error(" mcp-use client my-server tools list");
|
|
3508
|
+
} else if (name && !urlOrCommand) {
|
|
3509
|
+
console.error(
|
|
3510
|
+
formatError(options.stdio ? "Missing <command>." : "Missing <url>.")
|
|
3511
|
+
);
|
|
3512
|
+
console.error("");
|
|
3513
|
+
console.error(formatInfo("Usage:"));
|
|
3514
|
+
console.error(
|
|
3515
|
+
options.stdio ? ` mcp-use client connect ${name} "<command>" --stdio` : ` mcp-use client connect ${name} <url>`
|
|
3516
|
+
);
|
|
3517
|
+
} else {
|
|
3518
|
+
console.error(formatError("Missing required arguments: <name> <url>."));
|
|
3519
|
+
console.error("");
|
|
3520
|
+
console.error(formatInfo("Usage:"));
|
|
3521
|
+
console.error(" mcp-use client connect <name> <url>");
|
|
3522
|
+
console.error("");
|
|
3523
|
+
console.error("Example:");
|
|
3524
|
+
console.error(
|
|
3525
|
+
" mcp-use client connect manufact https://mcp.manufact.com/mcp"
|
|
3526
|
+
);
|
|
3527
|
+
}
|
|
3528
|
+
await cleanupAndExit(1);
|
|
3529
|
+
}
|
|
3530
|
+
const sessionName = name;
|
|
3531
|
+
const target = urlOrCommand;
|
|
3532
|
+
if (PER_CLIENT_SCOPES.has(sessionName)) {
|
|
3533
|
+
console.error(
|
|
3534
|
+
formatError(
|
|
3535
|
+
`'${sessionName}' is a reserved name and can't be used for a saved server.`
|
|
3536
|
+
)
|
|
3537
|
+
);
|
|
3538
|
+
console.error("");
|
|
3539
|
+
console.error(
|
|
3540
|
+
`Reserved names: ${Array.from(PER_CLIENT_SCOPES).sort().join(", ")}`
|
|
3541
|
+
);
|
|
3542
|
+
console.error("");
|
|
3543
|
+
console.error("Pick a different name, e.g.:");
|
|
3544
|
+
console.error(` mcp-use client connect my-${sessionName} ${target}`);
|
|
3545
|
+
await cleanupAndExit(1);
|
|
2231
3546
|
}
|
|
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
3547
|
try {
|
|
2249
|
-
const
|
|
2250
|
-
const client = new MCPClient();
|
|
3548
|
+
const client = new MCPClient3();
|
|
2251
3549
|
let session;
|
|
2252
3550
|
const cliClientInfo = getCliClientInfo();
|
|
2253
3551
|
if (options.stdio) {
|
|
2254
|
-
const parts =
|
|
3552
|
+
const parts = target.split(" ");
|
|
2255
3553
|
const command = parts[0];
|
|
2256
3554
|
const args = parts.slice(1);
|
|
2257
3555
|
console.error(
|
|
@@ -2270,17 +3568,41 @@ async function connectCommand(urlOrCommand, options) {
|
|
|
2270
3568
|
lastUsed: (/* @__PURE__ */ new Date()).toISOString()
|
|
2271
3569
|
});
|
|
2272
3570
|
} else {
|
|
2273
|
-
console.error(formatInfo(`Connecting to ${
|
|
3571
|
+
console.error(formatInfo(`Connecting to ${target}...`));
|
|
3572
|
+
const wantOAuth = !options.auth && options.oauth !== false;
|
|
3573
|
+
let authProvider;
|
|
3574
|
+
if (wantOAuth) {
|
|
3575
|
+
const authTimeoutMs = options.authTimeout ? Number.parseInt(options.authTimeout, 10) : void 0;
|
|
3576
|
+
authProvider = await buildOAuthProvider(target, {
|
|
3577
|
+
...authTimeoutMs ? { authTimeoutMs } : {}
|
|
3578
|
+
});
|
|
3579
|
+
}
|
|
2274
3580
|
client.addServer(sessionName, {
|
|
2275
|
-
url:
|
|
2276
|
-
|
|
3581
|
+
url: target,
|
|
3582
|
+
...authProvider ? { authProvider } : options.auth ? { headers: { Authorization: `Bearer ${options.auth}` } } : {},
|
|
2277
3583
|
clientInfo: cliClientInfo
|
|
2278
3584
|
});
|
|
2279
|
-
|
|
3585
|
+
try {
|
|
3586
|
+
session = await client.createSession(sessionName);
|
|
3587
|
+
} catch (err) {
|
|
3588
|
+
if (authProvider && isUnauthorized(err)) {
|
|
3589
|
+
console.error(
|
|
3590
|
+
formatWarning(
|
|
3591
|
+
"Server requires authentication. Starting OAuth flow."
|
|
3592
|
+
)
|
|
3593
|
+
);
|
|
3594
|
+
await runOAuthFlow(authProvider, target);
|
|
3595
|
+
console.error(formatSuccess("Authentication successful"));
|
|
3596
|
+
session = await client.createSession(sessionName);
|
|
3597
|
+
} else {
|
|
3598
|
+
throw err;
|
|
3599
|
+
}
|
|
3600
|
+
}
|
|
2280
3601
|
await saveSession(sessionName, {
|
|
2281
3602
|
type: "http",
|
|
2282
|
-
url:
|
|
2283
|
-
|
|
3603
|
+
url: target,
|
|
3604
|
+
authMode: authProvider ? "oauth" : options.auth ? "bearer" : void 0,
|
|
3605
|
+
authToken: authProvider ? void 0 : options.auth,
|
|
2284
3606
|
lastUsed: (/* @__PURE__ */ new Date()).toISOString()
|
|
2285
3607
|
});
|
|
2286
3608
|
}
|
|
@@ -2290,7 +3612,7 @@ async function connectCommand(urlOrCommand, options) {
|
|
|
2290
3612
|
if (serverInfo) {
|
|
2291
3613
|
await updateSessionInfo(sessionName, serverInfo, capabilities);
|
|
2292
3614
|
}
|
|
2293
|
-
console.log(formatSuccess(`Connected
|
|
3615
|
+
console.log(formatSuccess(`Connected as '${sessionName}'`));
|
|
2294
3616
|
if (serverInfo) {
|
|
2295
3617
|
console.log("");
|
|
2296
3618
|
console.log(formatHeader("Server Information:"));
|
|
@@ -2316,119 +3638,173 @@ async function connectCommand(urlOrCommand, options) {
|
|
|
2316
3638
|
);
|
|
2317
3639
|
} catch (error) {
|
|
2318
3640
|
console.error(formatError(`Connection failed: ${error.message}`));
|
|
2319
|
-
|
|
3641
|
+
await cleanupAndExit(1);
|
|
2320
3642
|
}
|
|
3643
|
+
await cleanupAndExit(0);
|
|
2321
3644
|
}
|
|
2322
|
-
async function disconnectCommand(
|
|
3645
|
+
async function disconnectCommand(name) {
|
|
2323
3646
|
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);
|
|
3647
|
+
const sessionData = activeSessions.get(name);
|
|
2341
3648
|
if (sessionData) {
|
|
2342
3649
|
await sessionData.client.closeAllSessions();
|
|
2343
|
-
activeSessions.delete(
|
|
2344
|
-
console.log(formatSuccess(`Disconnected from ${
|
|
3650
|
+
activeSessions.delete(name);
|
|
3651
|
+
console.log(formatSuccess(`Disconnected from ${name}`));
|
|
2345
3652
|
} else {
|
|
2346
|
-
console.log(formatInfo(`
|
|
3653
|
+
console.log(formatInfo(`Server '${name}' is not connected`));
|
|
2347
3654
|
}
|
|
2348
3655
|
} catch (error) {
|
|
2349
3656
|
console.error(formatError(`Failed to disconnect: ${error.message}`));
|
|
2350
|
-
|
|
3657
|
+
await cleanupAndExit(1);
|
|
3658
|
+
}
|
|
3659
|
+
await cleanupAndExit(0);
|
|
3660
|
+
}
|
|
3661
|
+
async function removeClientCommand(name) {
|
|
3662
|
+
try {
|
|
3663
|
+
const config = await getSession(name);
|
|
3664
|
+
if (!config) {
|
|
3665
|
+
console.error(formatError(`Server '${name}' not found`));
|
|
3666
|
+
console.error("");
|
|
3667
|
+
console.error("See your saved servers with:");
|
|
3668
|
+
console.error(" mcp-use client list");
|
|
3669
|
+
await cleanupAndExit(1);
|
|
3670
|
+
}
|
|
3671
|
+
const sessionData = activeSessions.get(name);
|
|
3672
|
+
if (sessionData) {
|
|
3673
|
+
await sessionData.client.closeAllSessions();
|
|
3674
|
+
activeSessions.delete(name);
|
|
3675
|
+
}
|
|
3676
|
+
const isOAuthHttp = config.type === "http" && config.authMode === "oauth" && typeof config.url === "string";
|
|
3677
|
+
const sharedUrlSibling = isOAuthHttp ? (await listAllSessions()).find(
|
|
3678
|
+
(s) => s.name !== name && s.config.type === "http" && s.config.url === config.url
|
|
3679
|
+
) : void 0;
|
|
3680
|
+
await removeSession(name);
|
|
3681
|
+
console.log(formatSuccess(`Removed saved server '${name}'`));
|
|
3682
|
+
if (isOAuthHttp) {
|
|
3683
|
+
if (sharedUrlSibling) {
|
|
3684
|
+
console.log(
|
|
3685
|
+
formatInfo(
|
|
3686
|
+
`OAuth tokens for ${config.url} were kept because saved server '${sharedUrlSibling.name}' still uses that URL.`
|
|
3687
|
+
)
|
|
3688
|
+
);
|
|
3689
|
+
} else {
|
|
3690
|
+
try {
|
|
3691
|
+
const provider = await buildOAuthProvider(config.url);
|
|
3692
|
+
await provider.invalidateCredentials("all");
|
|
3693
|
+
console.log(formatInfo(`Removed OAuth tokens for ${config.url}`));
|
|
3694
|
+
} catch (error) {
|
|
3695
|
+
console.error(
|
|
3696
|
+
formatWarning(
|
|
3697
|
+
`Saved entry removed, but failed to clear OAuth tokens for ${config.url}: ${error.message}`
|
|
3698
|
+
)
|
|
3699
|
+
);
|
|
3700
|
+
}
|
|
3701
|
+
}
|
|
3702
|
+
}
|
|
3703
|
+
} catch (error) {
|
|
3704
|
+
console.error(formatError(`Failed to remove server: ${error.message}`));
|
|
3705
|
+
await cleanupAndExit(1);
|
|
2351
3706
|
}
|
|
3707
|
+
await cleanupAndExit(0);
|
|
2352
3708
|
}
|
|
2353
|
-
async function
|
|
3709
|
+
async function listClientsCommand() {
|
|
2354
3710
|
try {
|
|
2355
3711
|
const sessions = await listAllSessions();
|
|
2356
3712
|
if (sessions.length === 0) {
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
3713
|
+
if (isStdoutTty()) {
|
|
3714
|
+
console.log(formatInfo("No saved servers"));
|
|
3715
|
+
console.log(
|
|
3716
|
+
formatInfo(
|
|
3717
|
+
"Connect to a server with: npx mcp-use client connect <name> <url>"
|
|
3718
|
+
)
|
|
3719
|
+
);
|
|
3720
|
+
}
|
|
2361
3721
|
return;
|
|
2362
3722
|
}
|
|
2363
|
-
|
|
2364
|
-
|
|
3723
|
+
const tty2 = isStdoutTty();
|
|
3724
|
+
if (tty2) {
|
|
3725
|
+
console.log(formatHeader("Saved Servers:"));
|
|
3726
|
+
console.log("");
|
|
3727
|
+
}
|
|
2365
3728
|
const tableData = sessions.map((s) => ({
|
|
2366
|
-
name: s.
|
|
3729
|
+
name: s.name,
|
|
2367
3730
|
type: s.config.type,
|
|
2368
3731
|
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")
|
|
3732
|
+
server: s.config.serverInfo?.name || "unknown"
|
|
2371
3733
|
}));
|
|
2372
3734
|
console.log(
|
|
2373
3735
|
formatTable(tableData, [
|
|
2374
3736
|
{ key: "name", header: "Name" },
|
|
2375
3737
|
{ key: "type", header: "Type" },
|
|
2376
|
-
{ key: "target", header: "Target",
|
|
2377
|
-
{ key: "server", header: "Server" }
|
|
2378
|
-
{ key: "status", header: "Status" }
|
|
3738
|
+
{ key: "target", header: "Target", truncate: true },
|
|
3739
|
+
{ key: "server", header: "Server" }
|
|
2379
3740
|
])
|
|
2380
3741
|
);
|
|
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
3742
|
} catch (error) {
|
|
2393
|
-
console.error(formatError(`Failed to
|
|
2394
|
-
|
|
3743
|
+
console.error(formatError(`Failed to list servers: ${error.message}`));
|
|
3744
|
+
await cleanupAndExit(1);
|
|
2395
3745
|
}
|
|
3746
|
+
await cleanupAndExit(0);
|
|
2396
3747
|
}
|
|
2397
|
-
async function listToolsCommand(options) {
|
|
3748
|
+
async function listToolsCommand(name, options) {
|
|
2398
3749
|
try {
|
|
2399
|
-
const result = await getOrRestoreSession(
|
|
2400
|
-
if (!result)
|
|
3750
|
+
const result = await getOrRestoreSession(name);
|
|
3751
|
+
if (!result) {
|
|
3752
|
+
await cleanupAndExit(1);
|
|
3753
|
+
}
|
|
2401
3754
|
const { session } = result;
|
|
2402
3755
|
const tools = await session.listTools();
|
|
2403
3756
|
if (options.json) {
|
|
2404
3757
|
console.log(formatJson(tools));
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
3758
|
+
} else if (tools.length === 0) {
|
|
3759
|
+
if (isStdoutTty()) console.log(formatInfo("No tools available"));
|
|
3760
|
+
} else {
|
|
3761
|
+
const tty2 = isStdoutTty();
|
|
3762
|
+
if (tty2) {
|
|
3763
|
+
console.log(formatHeader(`Available Tools (${tools.length}):`));
|
|
3764
|
+
console.log("");
|
|
3765
|
+
}
|
|
3766
|
+
const tableData = tools.map((tool) => {
|
|
3767
|
+
const props = tool.inputSchema?.properties ?? {};
|
|
3768
|
+
const required = tool.inputSchema?.required ?? [];
|
|
3769
|
+
const total = Object.keys(props).length;
|
|
3770
|
+
const reqCount = Array.isArray(required) ? required.length : 0;
|
|
3771
|
+
const argsCell = total === 0 ? source_default.gray("\u2014") : `${reqCount}/${total}`;
|
|
3772
|
+
return {
|
|
3773
|
+
name: source_default.bold(tool.name),
|
|
3774
|
+
mode: formatToolMode(tool.annotations),
|
|
3775
|
+
args: argsCell,
|
|
3776
|
+
description: tool.description || source_default.gray("(no description)")
|
|
3777
|
+
};
|
|
3778
|
+
});
|
|
3779
|
+
console.log(
|
|
3780
|
+
formatTable(tableData, [
|
|
3781
|
+
{ key: "name", header: "Tool" },
|
|
3782
|
+
{ key: "mode", header: "Mode" },
|
|
3783
|
+
{ key: "args", header: "Args" },
|
|
3784
|
+
{ key: "description", header: "Description", truncate: true }
|
|
3785
|
+
])
|
|
3786
|
+
);
|
|
3787
|
+
if (tty2) {
|
|
3788
|
+
console.log("");
|
|
3789
|
+
console.log(
|
|
3790
|
+
source_default.gray(
|
|
3791
|
+
"ARGS shows required/total. Modes: read-only \xB7 write \xB7 destructive."
|
|
3792
|
+
)
|
|
3793
|
+
);
|
|
3794
|
+
}
|
|
2410
3795
|
}
|
|
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
3796
|
} catch (error) {
|
|
2424
3797
|
console.error(formatError(`Failed to list tools: ${error.message}`));
|
|
2425
|
-
|
|
3798
|
+
await cleanupAndExit(1);
|
|
2426
3799
|
}
|
|
3800
|
+
await cleanupAndExit(0);
|
|
2427
3801
|
}
|
|
2428
|
-
async function describeToolCommand(
|
|
3802
|
+
async function describeToolCommand(name, toolName) {
|
|
2429
3803
|
try {
|
|
2430
|
-
const result = await getOrRestoreSession(
|
|
2431
|
-
if (!result)
|
|
3804
|
+
const result = await getOrRestoreSession(name);
|
|
3805
|
+
if (!result) {
|
|
3806
|
+
await cleanupAndExit(1);
|
|
3807
|
+
}
|
|
2432
3808
|
const { session } = result;
|
|
2433
3809
|
const tools = session.tools;
|
|
2434
3810
|
const tool = tools.find((t) => t.name === toolName);
|
|
@@ -2437,7 +3813,7 @@ async function describeToolCommand(toolName, options) {
|
|
|
2437
3813
|
console.log("");
|
|
2438
3814
|
console.log(formatInfo("Available tools:"));
|
|
2439
3815
|
tools.forEach((t) => console.log(` \u2022 ${t.name}`));
|
|
2440
|
-
|
|
3816
|
+
await cleanupAndExit(1);
|
|
2441
3817
|
}
|
|
2442
3818
|
console.log(formatHeader(`Tool: ${tool.name}`));
|
|
2443
3819
|
console.log("");
|
|
@@ -2451,94 +3827,167 @@ async function describeToolCommand(toolName, options) {
|
|
|
2451
3827
|
}
|
|
2452
3828
|
} catch (error) {
|
|
2453
3829
|
console.error(formatError(`Failed to describe tool: ${error.message}`));
|
|
2454
|
-
|
|
3830
|
+
await cleanupAndExit(1);
|
|
2455
3831
|
}
|
|
3832
|
+
await cleanupAndExit(0);
|
|
2456
3833
|
}
|
|
2457
|
-
async function callToolCommand(toolName,
|
|
3834
|
+
async function callToolCommand(name, toolName, argsList, options) {
|
|
2458
3835
|
try {
|
|
2459
|
-
const result = await getOrRestoreSession(
|
|
2460
|
-
if (!result)
|
|
3836
|
+
const result = await getOrRestoreSession(name);
|
|
3837
|
+
if (!result) {
|
|
3838
|
+
await cleanupAndExit(1);
|
|
3839
|
+
}
|
|
2461
3840
|
const { session } = result;
|
|
3841
|
+
const tools = session.tools;
|
|
3842
|
+
const tool = tools.find((t) => t.name === toolName);
|
|
2462
3843
|
let args = {};
|
|
2463
|
-
if (
|
|
3844
|
+
if (argsList && argsList.length > 0) {
|
|
2464
3845
|
try {
|
|
2465
|
-
args =
|
|
3846
|
+
args = parseToolArgs(argsList, tool?.inputSchema);
|
|
2466
3847
|
} 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
|
-
);
|
|
3848
|
+
console.error(formatError(error.message));
|
|
2479
3849
|
console.log("");
|
|
2480
|
-
console.log(formatInfo("
|
|
3850
|
+
console.log(formatInfo("Usage:"));
|
|
2481
3851
|
console.log(
|
|
2482
|
-
` npx mcp-use client tools call ${toolName}
|
|
3852
|
+
` npx mcp-use client ${name} tools call ${toolName} key=value [key2=value2 ...]`
|
|
2483
3853
|
);
|
|
2484
|
-
console.log(
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
3854
|
+
console.log(
|
|
3855
|
+
` npx mcp-use client ${name} tools call ${toolName} nested:='{"a":1}' # JSON value`
|
|
3856
|
+
);
|
|
3857
|
+
console.log(
|
|
3858
|
+
` npx mcp-use client ${name} tools call ${toolName} '{"key":"value"}' # full JSON object`
|
|
3859
|
+
);
|
|
3860
|
+
if (tool?.inputSchema) {
|
|
3861
|
+
console.log("");
|
|
3862
|
+
console.log(formatInfo("Tool schema:"));
|
|
3863
|
+
console.log(formatSchema(tool.inputSchema));
|
|
3864
|
+
}
|
|
3865
|
+
await cleanupAndExit(1);
|
|
2488
3866
|
}
|
|
3867
|
+
} else if (tool?.inputSchema?.required && tool.inputSchema.required.length > 0) {
|
|
3868
|
+
console.error(formatError("This tool requires arguments."));
|
|
3869
|
+
console.log("");
|
|
3870
|
+
console.log(formatInfo("Provide arguments as key=value pairs:"));
|
|
3871
|
+
console.log(
|
|
3872
|
+
` npx mcp-use client ${name} tools call ${toolName} key=value [key2=value2 ...]`
|
|
3873
|
+
);
|
|
3874
|
+
console.log("");
|
|
3875
|
+
console.log(formatInfo("Tool schema:"));
|
|
3876
|
+
console.log(formatSchema(tool.inputSchema));
|
|
3877
|
+
await cleanupAndExit(1);
|
|
2489
3878
|
}
|
|
2490
3879
|
console.error(formatInfo(`Calling tool '${toolName}'...`));
|
|
2491
3880
|
const callResult = await session.callTool(toolName, args, {
|
|
2492
3881
|
timeout: options?.timeout
|
|
2493
3882
|
});
|
|
3883
|
+
let screenshot = null;
|
|
3884
|
+
let screenshotError = null;
|
|
3885
|
+
if (options?.screenshot !== false) {
|
|
3886
|
+
const tool2 = session.tools.find((t) => t.name === toolName);
|
|
3887
|
+
const resourceUri = detectToolResourceUri(tool2);
|
|
3888
|
+
if (resourceUri) {
|
|
3889
|
+
console.error(
|
|
3890
|
+
formatInfo(`Capturing widget screenshot (${resourceUri})...`)
|
|
3891
|
+
);
|
|
3892
|
+
try {
|
|
3893
|
+
const shot = await captureToolScreenshot(
|
|
3894
|
+
{
|
|
3895
|
+
session,
|
|
3896
|
+
toolName,
|
|
3897
|
+
toolArgs: args,
|
|
3898
|
+
toolOutput: callResult,
|
|
3899
|
+
resourceUri
|
|
3900
|
+
},
|
|
3901
|
+
options?.screenshotOutput ? { output: options.screenshotOutput } : {}
|
|
3902
|
+
);
|
|
3903
|
+
screenshot = {
|
|
3904
|
+
path: shot.outputPath,
|
|
3905
|
+
width: shot.width,
|
|
3906
|
+
height: shot.height,
|
|
3907
|
+
view: shot.view
|
|
3908
|
+
};
|
|
3909
|
+
} catch (err) {
|
|
3910
|
+
screenshotError = err?.message ?? String(err);
|
|
3911
|
+
}
|
|
3912
|
+
}
|
|
3913
|
+
}
|
|
2494
3914
|
if (options?.json) {
|
|
2495
3915
|
console.log(formatJson(callResult));
|
|
2496
3916
|
} else {
|
|
2497
3917
|
console.log(formatToolCall(callResult));
|
|
2498
3918
|
}
|
|
3919
|
+
if (screenshot) {
|
|
3920
|
+
console.error(
|
|
3921
|
+
formatSuccess(
|
|
3922
|
+
`Saved widget screenshot: ${screenshot.path} (${screenshot.width}\xD7${screenshot.height})`
|
|
3923
|
+
)
|
|
3924
|
+
);
|
|
3925
|
+
}
|
|
3926
|
+
if (screenshotError) {
|
|
3927
|
+
console.error(
|
|
3928
|
+
formatWarning(`Skipped widget screenshot: ${screenshotError}`)
|
|
3929
|
+
);
|
|
3930
|
+
}
|
|
3931
|
+
if (callResult.isError) {
|
|
3932
|
+
await cleanupAndExit(1);
|
|
3933
|
+
}
|
|
2499
3934
|
} catch (error) {
|
|
2500
3935
|
console.error(formatError(`Failed to call tool: ${error.message}`));
|
|
2501
|
-
|
|
3936
|
+
if (error?.data !== void 0) {
|
|
3937
|
+
console.error(
|
|
3938
|
+
source_default.gray(
|
|
3939
|
+
typeof error.data === "string" ? error.data : formatJson(error.data)
|
|
3940
|
+
)
|
|
3941
|
+
);
|
|
3942
|
+
}
|
|
3943
|
+
await cleanupAndExit(1);
|
|
2502
3944
|
}
|
|
3945
|
+
await cleanupAndExit(0);
|
|
2503
3946
|
}
|
|
2504
|
-
async function listResourcesCommand(options) {
|
|
3947
|
+
async function listResourcesCommand(name, options) {
|
|
2505
3948
|
try {
|
|
2506
|
-
const result = await getOrRestoreSession(
|
|
2507
|
-
if (!result)
|
|
3949
|
+
const result = await getOrRestoreSession(name);
|
|
3950
|
+
if (!result) {
|
|
3951
|
+
await cleanupAndExit(1);
|
|
3952
|
+
}
|
|
2508
3953
|
const { session } = result;
|
|
2509
3954
|
const resourcesResult = await session.listAllResources();
|
|
2510
3955
|
const resources = resourcesResult.resources;
|
|
2511
3956
|
if (options.json) {
|
|
2512
3957
|
console.log(formatJson(resources));
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
3958
|
+
} else if (resources.length === 0) {
|
|
3959
|
+
if (isStdoutTty()) console.log(formatInfo("No resources available"));
|
|
3960
|
+
} else {
|
|
3961
|
+
const tty2 = isStdoutTty();
|
|
3962
|
+
if (tty2) {
|
|
3963
|
+
console.log(formatHeader(`Available Resources (${resources.length}):`));
|
|
3964
|
+
console.log("");
|
|
3965
|
+
}
|
|
3966
|
+
const tableData = resources.map((resource) => ({
|
|
3967
|
+
name: source_default.bold(resource.name || "(no name)"),
|
|
3968
|
+
type: resource.mimeType || source_default.gray("unknown"),
|
|
3969
|
+
uri: resource.uri
|
|
3970
|
+
}));
|
|
3971
|
+
console.log(
|
|
3972
|
+
formatTable(tableData, [
|
|
3973
|
+
{ key: "name", header: "Name" },
|
|
3974
|
+
{ key: "type", header: "Type" },
|
|
3975
|
+
{ key: "uri", header: "URI", truncate: true }
|
|
3976
|
+
])
|
|
3977
|
+
);
|
|
2518
3978
|
}
|
|
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
3979
|
} catch (error) {
|
|
2534
3980
|
console.error(formatError(`Failed to list resources: ${error.message}`));
|
|
2535
|
-
|
|
3981
|
+
await cleanupAndExit(1);
|
|
2536
3982
|
}
|
|
3983
|
+
await cleanupAndExit(0);
|
|
2537
3984
|
}
|
|
2538
|
-
async function readResourceCommand(uri, options) {
|
|
3985
|
+
async function readResourceCommand(name, uri, options) {
|
|
2539
3986
|
try {
|
|
2540
|
-
const result = await getOrRestoreSession(
|
|
2541
|
-
if (!result)
|
|
3987
|
+
const result = await getOrRestoreSession(name);
|
|
3988
|
+
if (!result) {
|
|
3989
|
+
await cleanupAndExit(1);
|
|
3990
|
+
}
|
|
2542
3991
|
const { session } = result;
|
|
2543
3992
|
console.error(formatInfo(`Reading resource: ${uri}`));
|
|
2544
3993
|
const resource = await session.readResource(uri);
|
|
@@ -2549,13 +3998,16 @@ async function readResourceCommand(uri, options) {
|
|
|
2549
3998
|
}
|
|
2550
3999
|
} catch (error) {
|
|
2551
4000
|
console.error(formatError(`Failed to read resource: ${error.message}`));
|
|
2552
|
-
|
|
4001
|
+
await cleanupAndExit(1);
|
|
2553
4002
|
}
|
|
4003
|
+
await cleanupAndExit(0);
|
|
2554
4004
|
}
|
|
2555
|
-
async function subscribeResourceCommand(
|
|
4005
|
+
async function subscribeResourceCommand(name, uri) {
|
|
2556
4006
|
try {
|
|
2557
|
-
const result = await getOrRestoreSession(
|
|
2558
|
-
if (!result)
|
|
4007
|
+
const result = await getOrRestoreSession(name);
|
|
4008
|
+
if (!result) {
|
|
4009
|
+
await cleanupAndExit(1);
|
|
4010
|
+
}
|
|
2559
4011
|
const { session } = result;
|
|
2560
4012
|
await session.subscribeToResource(uri);
|
|
2561
4013
|
console.log(formatSuccess(`Subscribed to resource: ${uri}`));
|
|
@@ -2573,13 +4025,15 @@ async function subscribeResourceCommand(uri, options) {
|
|
|
2573
4025
|
console.error(
|
|
2574
4026
|
formatError(`Failed to subscribe to resource: ${error.message}`)
|
|
2575
4027
|
);
|
|
2576
|
-
|
|
4028
|
+
await cleanupAndExit(1);
|
|
2577
4029
|
}
|
|
2578
4030
|
}
|
|
2579
|
-
async function unsubscribeResourceCommand(
|
|
4031
|
+
async function unsubscribeResourceCommand(name, uri) {
|
|
2580
4032
|
try {
|
|
2581
|
-
const result = await getOrRestoreSession(
|
|
2582
|
-
if (!result)
|
|
4033
|
+
const result = await getOrRestoreSession(name);
|
|
4034
|
+
if (!result) {
|
|
4035
|
+
await cleanupAndExit(1);
|
|
4036
|
+
}
|
|
2583
4037
|
const { session } = result;
|
|
2584
4038
|
await session.unsubscribeFromResource(uri);
|
|
2585
4039
|
console.log(formatSuccess(`Unsubscribed from resource: ${uri}`));
|
|
@@ -2587,53 +4041,76 @@ async function unsubscribeResourceCommand(uri, options) {
|
|
|
2587
4041
|
console.error(
|
|
2588
4042
|
formatError(`Failed to unsubscribe from resource: ${error.message}`)
|
|
2589
4043
|
);
|
|
2590
|
-
|
|
4044
|
+
await cleanupAndExit(1);
|
|
2591
4045
|
}
|
|
4046
|
+
await cleanupAndExit(0);
|
|
2592
4047
|
}
|
|
2593
|
-
async function listPromptsCommand(options) {
|
|
4048
|
+
async function listPromptsCommand(name, options) {
|
|
2594
4049
|
try {
|
|
2595
|
-
const result = await getOrRestoreSession(
|
|
2596
|
-
if (!result)
|
|
4050
|
+
const result = await getOrRestoreSession(name);
|
|
4051
|
+
if (!result) {
|
|
4052
|
+
await cleanupAndExit(1);
|
|
4053
|
+
}
|
|
2597
4054
|
const { session } = result;
|
|
2598
4055
|
const promptsResult = await session.listPrompts();
|
|
2599
4056
|
const prompts = promptsResult.prompts;
|
|
2600
4057
|
if (options.json) {
|
|
2601
4058
|
console.log(formatJson(prompts));
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
4059
|
+
} else if (prompts.length === 0) {
|
|
4060
|
+
if (isStdoutTty()) console.log(formatInfo("No prompts available"));
|
|
4061
|
+
} else {
|
|
4062
|
+
const tty2 = isStdoutTty();
|
|
4063
|
+
if (tty2) {
|
|
4064
|
+
console.log(formatHeader(`Available Prompts (${prompts.length}):`));
|
|
4065
|
+
console.log("");
|
|
4066
|
+
}
|
|
4067
|
+
const tableData = prompts.map((prompt4) => {
|
|
4068
|
+
const args = prompt4.arguments ?? [];
|
|
4069
|
+
const reqCount = Array.isArray(args) ? args.filter((a) => a?.required).length : 0;
|
|
4070
|
+
const total = Array.isArray(args) ? args.length : 0;
|
|
4071
|
+
const argsCell = total === 0 ? source_default.gray("\u2014") : `${reqCount}/${total}`;
|
|
4072
|
+
return {
|
|
4073
|
+
name: source_default.bold(prompt4.name),
|
|
4074
|
+
args: argsCell,
|
|
4075
|
+
description: prompt4.description || source_default.gray("(no description)")
|
|
4076
|
+
};
|
|
4077
|
+
});
|
|
4078
|
+
console.log(
|
|
4079
|
+
formatTable(tableData, [
|
|
4080
|
+
{ key: "name", header: "Prompt" },
|
|
4081
|
+
{ key: "args", header: "Args" },
|
|
4082
|
+
{ key: "description", header: "Description", truncate: true }
|
|
4083
|
+
])
|
|
4084
|
+
);
|
|
2607
4085
|
}
|
|
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
4086
|
} catch (error) {
|
|
2621
4087
|
console.error(formatError(`Failed to list prompts: ${error.message}`));
|
|
2622
|
-
|
|
4088
|
+
await cleanupAndExit(1);
|
|
2623
4089
|
}
|
|
4090
|
+
await cleanupAndExit(0);
|
|
2624
4091
|
}
|
|
2625
|
-
async function getPromptCommand(promptName,
|
|
4092
|
+
async function getPromptCommand(name, promptName, argsList, options) {
|
|
2626
4093
|
try {
|
|
2627
|
-
const result = await getOrRestoreSession(
|
|
2628
|
-
if (!result)
|
|
4094
|
+
const result = await getOrRestoreSession(name);
|
|
4095
|
+
if (!result) {
|
|
4096
|
+
await cleanupAndExit(1);
|
|
4097
|
+
}
|
|
2629
4098
|
const { session } = result;
|
|
2630
4099
|
let args = {};
|
|
2631
|
-
if (
|
|
4100
|
+
if (argsList && argsList.length > 0) {
|
|
2632
4101
|
try {
|
|
2633
|
-
args =
|
|
4102
|
+
args = parsePromptArgs(argsList);
|
|
2634
4103
|
} catch (error) {
|
|
2635
|
-
console.error(formatError(
|
|
2636
|
-
|
|
4104
|
+
console.error(formatError(error.message));
|
|
4105
|
+
console.log("");
|
|
4106
|
+
console.log(formatInfo("Usage:"));
|
|
4107
|
+
console.log(
|
|
4108
|
+
` npx mcp-use client ${name} prompts get ${promptName} key=value [key2=value2 ...]`
|
|
4109
|
+
);
|
|
4110
|
+
console.log(
|
|
4111
|
+
` npx mcp-use client ${name} prompts get ${promptName} '{"key":"value"}' # full JSON object`
|
|
4112
|
+
);
|
|
4113
|
+
await cleanupAndExit(1);
|
|
2637
4114
|
}
|
|
2638
4115
|
}
|
|
2639
4116
|
console.error(formatInfo(`Getting prompt '${promptName}'...`));
|
|
@@ -2655,12 +4132,13 @@ async function getPromptCommand(promptName, argsJson, options) {
|
|
|
2655
4132
|
}
|
|
2656
4133
|
} catch (error) {
|
|
2657
4134
|
console.error(formatError(`Failed to get prompt: ${error.message}`));
|
|
2658
|
-
|
|
4135
|
+
await cleanupAndExit(1);
|
|
2659
4136
|
}
|
|
4137
|
+
await cleanupAndExit(0);
|
|
2660
4138
|
}
|
|
2661
|
-
async function interactiveCommand(
|
|
4139
|
+
async function interactiveCommand(name) {
|
|
2662
4140
|
try {
|
|
2663
|
-
const result = await getOrRestoreSession(
|
|
4141
|
+
const result = await getOrRestoreSession(name);
|
|
2664
4142
|
if (!result) return;
|
|
2665
4143
|
const { name: sessionName, session } = result;
|
|
2666
4144
|
console.log(formatHeader("MCP Interactive Mode"));
|
|
@@ -2683,15 +4161,11 @@ async function interactiveCommand(options) {
|
|
|
2683
4161
|
source_default.gray(" prompts list - List available prompts")
|
|
2684
4162
|
);
|
|
2685
4163
|
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
4164
|
console.log(
|
|
2691
4165
|
source_default.gray(" exit, quit - Exit interactive mode")
|
|
2692
4166
|
);
|
|
2693
4167
|
console.log("");
|
|
2694
|
-
const rl =
|
|
4168
|
+
const rl = createInterface2({
|
|
2695
4169
|
input: process.stdin,
|
|
2696
4170
|
output: process.stdout,
|
|
2697
4171
|
prompt: source_default.cyan("mcp> ")
|
|
@@ -2706,7 +4180,7 @@ async function interactiveCommand(options) {
|
|
|
2706
4180
|
if (trimmed === "exit" || trimmed === "quit") {
|
|
2707
4181
|
console.log(formatInfo("Goodbye!"));
|
|
2708
4182
|
rl.close();
|
|
2709
|
-
|
|
4183
|
+
await cleanupAndExit(0);
|
|
2710
4184
|
}
|
|
2711
4185
|
const parts = trimmed.split(" ");
|
|
2712
4186
|
const scope = parts[0];
|
|
@@ -2806,22 +4280,10 @@ async function interactiveCommand(options) {
|
|
|
2806
4280
|
)
|
|
2807
4281
|
);
|
|
2808
4282
|
}
|
|
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
4283
|
} else {
|
|
2822
4284
|
console.error(
|
|
2823
4285
|
formatError(
|
|
2824
|
-
"Unknown command. Type a valid scope: tools, resources, prompts
|
|
4286
|
+
"Unknown command. Type a valid scope: tools, resources, prompts"
|
|
2825
4287
|
)
|
|
2826
4288
|
);
|
|
2827
4289
|
}
|
|
@@ -2830,58 +4292,95 @@ async function interactiveCommand(options) {
|
|
|
2830
4292
|
}
|
|
2831
4293
|
rl.prompt();
|
|
2832
4294
|
});
|
|
2833
|
-
rl.on("close", () => {
|
|
4295
|
+
rl.on("close", async () => {
|
|
2834
4296
|
console.log("");
|
|
2835
4297
|
console.log(formatInfo("Goodbye!"));
|
|
2836
|
-
|
|
4298
|
+
await cleanupAndExit(0);
|
|
2837
4299
|
});
|
|
2838
4300
|
} catch (error) {
|
|
2839
4301
|
console.error(
|
|
2840
4302
|
formatError(`Failed to start interactive mode: ${error.message}`)
|
|
2841
4303
|
);
|
|
2842
|
-
|
|
4304
|
+
await cleanupAndExit(1);
|
|
2843
4305
|
}
|
|
2844
4306
|
}
|
|
2845
4307
|
function createClientCommand() {
|
|
2846
|
-
const clientCommand = new
|
|
2847
|
-
"Interactive MCP client for terminal usage"
|
|
4308
|
+
const clientCommand = new Command2("client").description(
|
|
4309
|
+
"Interactive MCP client for terminal usage. Use `mcp-use client <name> ...` to run commands against a saved server."
|
|
4310
|
+
).showHelpAfterError(
|
|
4311
|
+
"(Run `mcp-use client --help` to see available commands)"
|
|
4312
|
+
);
|
|
4313
|
+
clientCommand.command("connect [name] [url]").description(
|
|
4314
|
+
"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`)."
|
|
4315
|
+
).option("--stdio", "Use stdio connector instead of HTTP").option("--auth <token>", "Static Bearer token (skips OAuth)").option(
|
|
4316
|
+
"--no-oauth",
|
|
4317
|
+
"Don't auto-trigger OAuth on 401; fail with the 401 instead"
|
|
4318
|
+
).option(
|
|
4319
|
+
"--auth-timeout <ms>",
|
|
4320
|
+
"OAuth loopback wait timeout in ms (default 300000)"
|
|
4321
|
+
).action(connectCommand);
|
|
4322
|
+
clientCommand.command("list").description("List saved servers").action(listClientsCommand);
|
|
4323
|
+
clientCommand.command("remove <name>").description(
|
|
4324
|
+
"Remove a saved server. Also clears any OAuth tokens for that URL, unless another saved server still uses it."
|
|
4325
|
+
).action(removeClientCommand);
|
|
4326
|
+
clientCommand.addCommand(createClientScreenshotCommand());
|
|
4327
|
+
return clientCommand;
|
|
4328
|
+
}
|
|
4329
|
+
function createPerClientCommand(name) {
|
|
4330
|
+
const cmd = new Command2(`mcp-use client ${name}`).description(`Commands for server '${name}'`).showHelpAfterError(
|
|
4331
|
+
`(Run \`mcp-use client ${name} --help\` to see available commands)`
|
|
4332
|
+
);
|
|
4333
|
+
cmd.command("disconnect").description("Disconnect from this server").action(() => disconnectCommand(name));
|
|
4334
|
+
cmd.command("interactive").description("Start interactive REPL mode for this server").action(() => interactiveCommand(name));
|
|
4335
|
+
const toolsCommand = new Command2("tools").description("Interact with MCP tools").showHelpAfterError(
|
|
4336
|
+
`(Run \`mcp-use client ${name} tools --help\` to see available actions)`
|
|
2848
4337
|
);
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
4338
|
+
toolsCommand.command("list").description("List available tools").option("--json", "Output as JSON").action((options) => listToolsCommand(name, options));
|
|
4339
|
+
toolsCommand.command("call <tool> [args...]").description(
|
|
4340
|
+
"Call a tool. Args as key=value pairs (use key:=<json> for nested values, or pass a JSON object)"
|
|
4341
|
+
).option("--timeout <ms>", "Request timeout in milliseconds", parseInt).option("--json", "Output as JSON").option(
|
|
4342
|
+
"--no-screenshot",
|
|
4343
|
+
"Skip the auto-screenshot for tools that render a widget"
|
|
4344
|
+
).option(
|
|
4345
|
+
"--screenshot-output <path>",
|
|
4346
|
+
"Output PNG path for the widget screenshot (defaults to ./<view>-<timestamp>.png)"
|
|
4347
|
+
).action(
|
|
4348
|
+
(tool, args, options) => callToolCommand(name, tool, args, options)
|
|
2853
4349
|
);
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
"Interact with MCP tools"
|
|
4350
|
+
toolsCommand.command("describe <tool>").description("Show tool details and schema").action((tool) => describeToolCommand(name, tool));
|
|
4351
|
+
cmd.addCommand(toolsCommand);
|
|
4352
|
+
const resourcesCommand = new Command2("resources").description("Interact with MCP resources").showHelpAfterError(
|
|
4353
|
+
`(Run \`mcp-use client ${name} resources --help\` to see available actions)`
|
|
2859
4354
|
);
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
4355
|
+
resourcesCommand.command("list").description("List available resources").option("--json", "Output as JSON").action((options) => listResourcesCommand(name, options));
|
|
4356
|
+
resourcesCommand.command("read <uri>").description("Read a resource by URI").option("--json", "Output as JSON").action((uri, options) => readResourceCommand(name, uri, options));
|
|
4357
|
+
resourcesCommand.command("subscribe <uri>").description("Subscribe to resource updates").action((uri) => subscribeResourceCommand(name, uri));
|
|
4358
|
+
resourcesCommand.command("unsubscribe <uri>").description("Unsubscribe from resource updates").action((uri) => unsubscribeResourceCommand(name, uri));
|
|
4359
|
+
cmd.addCommand(resourcesCommand);
|
|
4360
|
+
const promptsCommand = new Command2("prompts").description("Interact with MCP prompts").showHelpAfterError(
|
|
4361
|
+
`(Run \`mcp-use client ${name} prompts --help\` to see available actions)`
|
|
2866
4362
|
);
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
const promptsCommand = new Command("prompts").description(
|
|
2873
|
-
"Interact with MCP prompts"
|
|
4363
|
+
promptsCommand.command("list").description("List available prompts").option("--json", "Output as JSON").action((options) => listPromptsCommand(name, options));
|
|
4364
|
+
promptsCommand.command("get <prompt> [args...]").description(
|
|
4365
|
+
"Get a prompt. Args as key=value pairs (or pass a JSON object)"
|
|
4366
|
+
).option("--json", "Output as JSON").action(
|
|
4367
|
+
(prompt4, args, options) => getPromptCommand(name, prompt4, args, options)
|
|
2874
4368
|
);
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
4369
|
+
cmd.addCommand(promptsCommand);
|
|
4370
|
+
const authCommand = new Command2("auth").description("Manage OAuth tokens for HTTP servers").showHelpAfterError(
|
|
4371
|
+
`(Run \`mcp-use client ${name} auth --help\` to see available actions)`
|
|
4372
|
+
);
|
|
4373
|
+
authCommand.command("status").description("Show OAuth token status for this server").action(() => authStatusCommand(name));
|
|
4374
|
+
authCommand.command("refresh").description("Force-refresh the OAuth access token").action(() => authRefreshCommand(name));
|
|
4375
|
+
authCommand.command("logout").description("Remove stored OAuth tokens for this server's URL").action(() => authLogoutCommand(name));
|
|
4376
|
+
cmd.addCommand(authCommand);
|
|
4377
|
+
cmd.addCommand(createPerClientScreenshotCommand(name));
|
|
4378
|
+
return cmd;
|
|
2880
4379
|
}
|
|
2881
4380
|
|
|
2882
4381
|
// src/commands/deploy.ts
|
|
2883
4382
|
import { promises as fs9 } from "fs";
|
|
2884
|
-
import
|
|
4383
|
+
import path8 from "path";
|
|
2885
4384
|
|
|
2886
4385
|
// src/utils/git.ts
|
|
2887
4386
|
import { execFile as execFile7 } from "child_process";
|
|
@@ -3036,15 +4535,15 @@ function getMcpServerUrlForCloudServer(server) {
|
|
|
3036
4535
|
|
|
3037
4536
|
// src/utils/project-link.ts
|
|
3038
4537
|
import { promises as fs8 } from "fs";
|
|
3039
|
-
import
|
|
4538
|
+
import path7 from "path";
|
|
3040
4539
|
var MCP_USE_DIR = ".mcp-use";
|
|
3041
4540
|
var MCP_USE_DIR_PROJECT = "project.json";
|
|
3042
4541
|
function getMcpUseDirectory(cwd) {
|
|
3043
|
-
return
|
|
4542
|
+
return path7.join(cwd, MCP_USE_DIR);
|
|
3044
4543
|
}
|
|
3045
4544
|
async function getProjectLink(cwd) {
|
|
3046
4545
|
try {
|
|
3047
|
-
const linkPath =
|
|
4546
|
+
const linkPath = path7.join(getMcpUseDirectory(cwd), MCP_USE_DIR_PROJECT);
|
|
3048
4547
|
const content = await fs8.readFile(linkPath, "utf-8");
|
|
3049
4548
|
return JSON.parse(content);
|
|
3050
4549
|
} catch (err) {
|
|
@@ -3055,12 +4554,12 @@ async function getProjectLink(cwd) {
|
|
|
3055
4554
|
async function saveProjectLink(cwd, link) {
|
|
3056
4555
|
const mcpUseDir = getMcpUseDirectory(cwd);
|
|
3057
4556
|
await fs8.mkdir(mcpUseDir, { recursive: true });
|
|
3058
|
-
const linkPath =
|
|
4557
|
+
const linkPath = path7.join(mcpUseDir, MCP_USE_DIR_PROJECT);
|
|
3059
4558
|
await fs8.writeFile(linkPath, JSON.stringify(link, null, 2), "utf-8");
|
|
3060
4559
|
await addToGitIgnore(cwd);
|
|
3061
4560
|
}
|
|
3062
4561
|
async function addToGitIgnore(cwd) {
|
|
3063
|
-
const gitignorePath =
|
|
4562
|
+
const gitignorePath = path7.join(cwd, ".gitignore");
|
|
3064
4563
|
try {
|
|
3065
4564
|
let content = "";
|
|
3066
4565
|
try {
|
|
@@ -3201,7 +4700,7 @@ async function buildEnvVars(options) {
|
|
|
3201
4700
|
}
|
|
3202
4701
|
async function isMcpProject(cwd = process.cwd()) {
|
|
3203
4702
|
try {
|
|
3204
|
-
const content = await fs9.readFile(
|
|
4703
|
+
const content = await fs9.readFile(path8.join(cwd, "package.json"), "utf-8");
|
|
3205
4704
|
const pkg = JSON.parse(content);
|
|
3206
4705
|
return !!(pkg.dependencies?.["mcp-use"] || pkg.dependencies?.["@modelcontextprotocol/sdk"] || pkg.devDependencies?.["mcp-use"] || pkg.devDependencies?.["@modelcontextprotocol/sdk"]);
|
|
3207
4706
|
} catch {
|
|
@@ -3210,16 +4709,16 @@ async function isMcpProject(cwd = process.cwd()) {
|
|
|
3210
4709
|
}
|
|
3211
4710
|
async function getProjectName(cwd = process.cwd()) {
|
|
3212
4711
|
try {
|
|
3213
|
-
const content = await fs9.readFile(
|
|
4712
|
+
const content = await fs9.readFile(path8.join(cwd, "package.json"), "utf-8");
|
|
3214
4713
|
const pkg = JSON.parse(content);
|
|
3215
4714
|
if (pkg.name) return pkg.name;
|
|
3216
4715
|
} catch {
|
|
3217
4716
|
}
|
|
3218
|
-
return
|
|
4717
|
+
return path8.basename(cwd);
|
|
3219
4718
|
}
|
|
3220
4719
|
async function detectBuildCommand(cwd) {
|
|
3221
4720
|
try {
|
|
3222
|
-
const content = await fs9.readFile(
|
|
4721
|
+
const content = await fs9.readFile(path8.join(cwd, "package.json"), "utf-8");
|
|
3223
4722
|
if (JSON.parse(content).scripts?.build) return "npm run build";
|
|
3224
4723
|
} catch {
|
|
3225
4724
|
}
|
|
@@ -3227,7 +4726,7 @@ async function detectBuildCommand(cwd) {
|
|
|
3227
4726
|
}
|
|
3228
4727
|
async function detectStartCommand(cwd) {
|
|
3229
4728
|
try {
|
|
3230
|
-
const content = await fs9.readFile(
|
|
4729
|
+
const content = await fs9.readFile(path8.join(cwd, "package.json"), "utf-8");
|
|
3231
4730
|
const pkg = JSON.parse(content);
|
|
3232
4731
|
if (pkg.scripts?.start) return "npm start";
|
|
3233
4732
|
if (pkg.main) return `node ${pkg.main}`;
|
|
@@ -3238,7 +4737,7 @@ async function detectStartCommand(cwd) {
|
|
|
3238
4737
|
async function detectRuntime(cwd) {
|
|
3239
4738
|
for (const f of ["requirements.txt", "pyproject.toml", "setup.py"]) {
|
|
3240
4739
|
try {
|
|
3241
|
-
await fs9.access(
|
|
4740
|
+
await fs9.access(path8.join(cwd, f));
|
|
3242
4741
|
return "python";
|
|
3243
4742
|
} catch {
|
|
3244
4743
|
continue;
|
|
@@ -3285,7 +4784,7 @@ var REQUIRED_IGNORES = [
|
|
|
3285
4784
|
".mcp-use"
|
|
3286
4785
|
];
|
|
3287
4786
|
async function ensureGitignore(cwd) {
|
|
3288
|
-
const gitignorePath =
|
|
4787
|
+
const gitignorePath = path8.join(cwd, ".gitignore");
|
|
3289
4788
|
let content = "";
|
|
3290
4789
|
try {
|
|
3291
4790
|
content = await fs9.readFile(gitignorePath, "utf-8");
|
|
@@ -3745,7 +5244,7 @@ async function deployCommand(options) {
|
|
|
3745
5244
|
console.log(source_default.green("\u2713 GitHub connected\n"));
|
|
3746
5245
|
let installationDbId;
|
|
3747
5246
|
let githubInstallationId;
|
|
3748
|
-
const projectDir = options.rootDir ?
|
|
5247
|
+
const projectDir = options.rootDir ? path8.resolve(cwd, options.rootDir) : cwd;
|
|
3749
5248
|
if (options.rootDir) {
|
|
3750
5249
|
try {
|
|
3751
5250
|
await fs9.access(projectDir);
|
|
@@ -4257,7 +5756,7 @@ async function deployCommand(options) {
|
|
|
4257
5756
|
}
|
|
4258
5757
|
|
|
4259
5758
|
// src/commands/deployments.ts
|
|
4260
|
-
import { Command as
|
|
5759
|
+
import { Command as Command3 } from "commander";
|
|
4261
5760
|
async function prompt2(question) {
|
|
4262
5761
|
const readline = await import("readline");
|
|
4263
5762
|
const rl = readline.createInterface({
|
|
@@ -4669,8 +6168,8 @@ async function startDeploymentCommand(deploymentId) {
|
|
|
4669
6168
|
}
|
|
4670
6169
|
}
|
|
4671
6170
|
function createDeploymentsCommand() {
|
|
4672
|
-
const deploymentsCommand = new
|
|
4673
|
-
"
|
|
6171
|
+
const deploymentsCommand = new Command3("deployments").description("Manage cloud deployments").showHelpAfterError(
|
|
6172
|
+
"(Run `mcp-use deployments --help` to see available commands)"
|
|
4674
6173
|
);
|
|
4675
6174
|
deploymentsCommand.command("list").alias("ls").description("List all deployments").action(listDeploymentsCommand);
|
|
4676
6175
|
deploymentsCommand.command("get").argument("<deployment-id>", "Deployment ID").description("Get deployment details").action(getDeploymentCommand);
|
|
@@ -4685,10 +6184,10 @@ function createDeploymentsCommand() {
|
|
|
4685
6184
|
}
|
|
4686
6185
|
|
|
4687
6186
|
// src/commands/servers.ts
|
|
4688
|
-
import { Command as
|
|
6187
|
+
import { Command as Command5 } from "commander";
|
|
4689
6188
|
|
|
4690
6189
|
// src/commands/env.ts
|
|
4691
|
-
import { Command as
|
|
6190
|
+
import { Command as Command4 } from "commander";
|
|
4692
6191
|
var ALL_ENVS = ["production", "preview", "development"];
|
|
4693
6192
|
function parseEnvironments(raw) {
|
|
4694
6193
|
const parts = raw.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
|
|
@@ -4833,9 +6332,7 @@ async function removeEnvCommand(varId, options) {
|
|
|
4833
6332
|
}
|
|
4834
6333
|
}
|
|
4835
6334
|
function createEnvCommand() {
|
|
4836
|
-
const envCommand = new
|
|
4837
|
-
"Manage environment variables for a server"
|
|
4838
|
-
);
|
|
6335
|
+
const envCommand = new Command4("env").description("Manage environment variables for a server").showHelpAfterError("(Run `mcp-use env --help` to see available commands)");
|
|
4839
6336
|
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
6337
|
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
6338
|
"--env <environments>",
|
|
@@ -5117,8 +6614,8 @@ async function deleteServerCommand(serverId, options) {
|
|
|
5117
6614
|
}
|
|
5118
6615
|
}
|
|
5119
6616
|
function createServersCommand() {
|
|
5120
|
-
const serversCommand = new
|
|
5121
|
-
"
|
|
6617
|
+
const serversCommand = new Command5("servers").description("Manage cloud servers (Git-backed deploy targets)").showHelpAfterError(
|
|
6618
|
+
"(Run `mcp-use servers --help` to see available commands)"
|
|
5122
6619
|
);
|
|
5123
6620
|
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
6621
|
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 +6732,8 @@ async function orgCurrentCommand() {
|
|
|
5235
6732
|
}
|
|
5236
6733
|
|
|
5237
6734
|
// src/commands/skills.ts
|
|
5238
|
-
import { Command as
|
|
5239
|
-
import { cpSync, existsSync as
|
|
6735
|
+
import { Command as Command6 } from "commander";
|
|
6736
|
+
import { cpSync, existsSync as existsSync3, mkdtempSync as mkdtempSync2, readdirSync, rmSync as rmSync2 } from "fs";
|
|
5240
6737
|
import { tmpdir } from "os";
|
|
5241
6738
|
import { join as join2, resolve } from "path";
|
|
5242
6739
|
import { Readable } from "stream";
|
|
@@ -5275,7 +6772,7 @@ function sendInstallTelemetryEvent(agents, skills) {
|
|
|
5275
6772
|
}
|
|
5276
6773
|
async function addSkillsToProject(projectPath) {
|
|
5277
6774
|
const tarballUrl = `https://codeload.github.com/${REPO_OWNER}/${REPO_NAME}/tar.gz/${REPO_BRANCH}`;
|
|
5278
|
-
const tempDir =
|
|
6775
|
+
const tempDir = mkdtempSync2(join2(tmpdir(), "mcp-use-skills-"));
|
|
5279
6776
|
try {
|
|
5280
6777
|
const response = await fetch(tarballUrl);
|
|
5281
6778
|
if (!response.ok) {
|
|
@@ -5285,12 +6782,12 @@ async function addSkillsToProject(projectPath) {
|
|
|
5285
6782
|
Readable.fromWeb(response.body),
|
|
5286
6783
|
extract({
|
|
5287
6784
|
cwd: tempDir,
|
|
5288
|
-
filter: (
|
|
6785
|
+
filter: (path12) => path12.includes("/skills/"),
|
|
5289
6786
|
strip: 1
|
|
5290
6787
|
})
|
|
5291
6788
|
);
|
|
5292
6789
|
const skillsPath = join2(tempDir, "skills");
|
|
5293
|
-
if (!
|
|
6790
|
+
if (!existsSync3(skillsPath)) {
|
|
5294
6791
|
throw new Error("Skills folder not found in repository");
|
|
5295
6792
|
}
|
|
5296
6793
|
for (const preset of ALL_PRESETS) {
|
|
@@ -5301,16 +6798,16 @@ async function addSkillsToProject(projectPath) {
|
|
|
5301
6798
|
const skillNames = readdirSync(skillsPath, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
5302
6799
|
sendInstallTelemetryEvent(ALL_PRESETS.join(","), skillNames.join(","));
|
|
5303
6800
|
} finally {
|
|
5304
|
-
|
|
6801
|
+
rmSync2(tempDir, { recursive: true, force: true });
|
|
5305
6802
|
}
|
|
5306
6803
|
}
|
|
5307
6804
|
function createSkillsCommand() {
|
|
5308
|
-
const skills = new
|
|
5309
|
-
"
|
|
6805
|
+
const skills = new Command6("skills").description("Manage mcp-use AI agent skills").showHelpAfterError(
|
|
6806
|
+
"(Run `mcp-use skills --help` to see available commands)"
|
|
5310
6807
|
);
|
|
5311
6808
|
const installAction = async (options) => {
|
|
5312
6809
|
const projectPath = resolve(options.path);
|
|
5313
|
-
if (!
|
|
6810
|
+
if (!existsSync3(projectPath)) {
|
|
5314
6811
|
console.error(source_default.red(`Directory not found: ${projectPath}`));
|
|
5315
6812
|
process.exit(1);
|
|
5316
6813
|
}
|
|
@@ -5351,12 +6848,12 @@ function createSkillsCommand() {
|
|
|
5351
6848
|
}
|
|
5352
6849
|
|
|
5353
6850
|
// src/utils/next-shims.ts
|
|
5354
|
-
import { existsSync as
|
|
5355
|
-
import
|
|
6851
|
+
import { existsSync as existsSync4, promises as fs10 } from "fs";
|
|
6852
|
+
import path9 from "path";
|
|
5356
6853
|
import { fileURLToPath as fileURLToPath3, pathToFileURL } from "url";
|
|
5357
6854
|
async function detectNextJsProject(projectPath) {
|
|
5358
6855
|
try {
|
|
5359
|
-
const pkgPath =
|
|
6856
|
+
const pkgPath = path9.join(projectPath, "package.json");
|
|
5360
6857
|
const content = await fs10.readFile(pkgPath, "utf-8");
|
|
5361
6858
|
const pkg = JSON.parse(content);
|
|
5362
6859
|
const deps = pkg.dependencies ?? {};
|
|
@@ -5375,7 +6872,7 @@ async function loadNextJsEnvFiles(projectPath) {
|
|
|
5375
6872
|
];
|
|
5376
6873
|
const dotenv = await import("dotenv");
|
|
5377
6874
|
for (const file of files) {
|
|
5378
|
-
const abs =
|
|
6875
|
+
const abs = path9.join(projectPath, file);
|
|
5379
6876
|
try {
|
|
5380
6877
|
await fs10.access(abs);
|
|
5381
6878
|
} catch {
|
|
@@ -5387,20 +6884,20 @@ async function loadNextJsEnvFiles(projectPath) {
|
|
|
5387
6884
|
function getThisDir() {
|
|
5388
6885
|
if (typeof __dirname === "string") return __dirname;
|
|
5389
6886
|
const url = import.meta.url;
|
|
5390
|
-
return
|
|
6887
|
+
return path9.dirname(fileURLToPath3(url));
|
|
5391
6888
|
}
|
|
5392
6889
|
function resolveShimPath(filename) {
|
|
5393
6890
|
const thisDir = getThisDir();
|
|
5394
6891
|
const candidates = [
|
|
5395
6892
|
// Production: `dist/` next to this module
|
|
5396
|
-
|
|
6893
|
+
path9.join(thisDir, "shims", filename),
|
|
5397
6894
|
// Test / dev: one level up (e.g., from `dist/utils/` back to `src/shims/`)
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
6895
|
+
path9.join(thisDir, "..", "shims", filename),
|
|
6896
|
+
path9.join(thisDir, "..", "..", "src", "shims", filename),
|
|
6897
|
+
path9.join(thisDir, "..", "src", "shims", filename)
|
|
5401
6898
|
];
|
|
5402
6899
|
for (const candidate of candidates) {
|
|
5403
|
-
if (
|
|
6900
|
+
if (existsSync4(candidate)) return candidate;
|
|
5404
6901
|
}
|
|
5405
6902
|
return void 0;
|
|
5406
6903
|
}
|
|
@@ -5418,7 +6915,7 @@ async function registerNextShimsInProcess() {
|
|
|
5418
6915
|
const cjsPath = getShimCjsPreloadPath();
|
|
5419
6916
|
if (cjsPath) {
|
|
5420
6917
|
const { createRequire: createRequire3 } = await import("module");
|
|
5421
|
-
const req = createRequire3(pathToFileURL(getThisDir() +
|
|
6918
|
+
const req = createRequire3(pathToFileURL(getThisDir() + path9.sep).href);
|
|
5422
6919
|
req(cjsPath);
|
|
5423
6920
|
anyRegistered = true;
|
|
5424
6921
|
}
|
|
@@ -5426,7 +6923,7 @@ async function registerNextShimsInProcess() {
|
|
|
5426
6923
|
if (loaderPath) {
|
|
5427
6924
|
const { register } = await import("module");
|
|
5428
6925
|
const loaderUrl = pathToFileURL(loaderPath).href;
|
|
5429
|
-
register(loaderUrl, pathToFileURL(getThisDir() +
|
|
6926
|
+
register(loaderUrl, pathToFileURL(getThisDir() + path9.sep).href);
|
|
5430
6927
|
anyRegistered = true;
|
|
5431
6928
|
}
|
|
5432
6929
|
return anyRegistered;
|
|
@@ -5452,12 +6949,12 @@ function quoteNodeOption(value) {
|
|
|
5452
6949
|
|
|
5453
6950
|
// src/utils/update-check.ts
|
|
5454
6951
|
import { readFileSync } from "fs";
|
|
5455
|
-
import { mkdir as
|
|
6952
|
+
import { mkdir as mkdir3, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
5456
6953
|
import { createRequire } from "module";
|
|
5457
|
-
import
|
|
5458
|
-
import
|
|
5459
|
-
var CACHE_DIR =
|
|
5460
|
-
var CACHE_FILE =
|
|
6954
|
+
import os5 from "os";
|
|
6955
|
+
import path10 from "path";
|
|
6956
|
+
var CACHE_DIR = path10.join(os5.homedir(), ".mcp-use");
|
|
6957
|
+
var CACHE_FILE = path10.join(CACHE_DIR, "update-check.json");
|
|
5461
6958
|
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
5462
6959
|
var FETCH_TIMEOUT_MS = 3e3;
|
|
5463
6960
|
var PACKAGE_NAME = "mcp-use";
|
|
@@ -5490,7 +6987,7 @@ async function readCache() {
|
|
|
5490
6987
|
}
|
|
5491
6988
|
async function writeCache(latestVersion) {
|
|
5492
6989
|
try {
|
|
5493
|
-
await
|
|
6990
|
+
await mkdir3(CACHE_DIR, { recursive: true });
|
|
5494
6991
|
const cache = {
|
|
5495
6992
|
lastChecked: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5496
6993
|
latestVersion
|
|
@@ -5540,12 +7037,12 @@ function resolveInstalledVersion(projectPath) {
|
|
|
5540
7037
|
if (projectPath) {
|
|
5541
7038
|
attempts.push(() => {
|
|
5542
7039
|
const projectRequire = createRequire(
|
|
5543
|
-
|
|
7040
|
+
path10.join(projectPath, "package.json")
|
|
5544
7041
|
);
|
|
5545
7042
|
return projectRequire.resolve(`${PACKAGE_NAME}/package.json`);
|
|
5546
7043
|
});
|
|
5547
7044
|
}
|
|
5548
|
-
attempts.push(() =>
|
|
7045
|
+
attempts.push(() => path10.join(__dirname, "../../mcp-use/package.json"));
|
|
5549
7046
|
for (const attempt of attempts) {
|
|
5550
7047
|
try {
|
|
5551
7048
|
const pkgPath = attempt();
|
|
@@ -5581,14 +7078,14 @@ A new release of ${source_default.bold(PACKAGE_NAME)} is available: ${source_def
|
|
|
5581
7078
|
}
|
|
5582
7079
|
|
|
5583
7080
|
// src/index.ts
|
|
5584
|
-
var program = new
|
|
7081
|
+
var program = new Command7();
|
|
5585
7082
|
var packageContent = readFileSync2(
|
|
5586
|
-
|
|
7083
|
+
path11.join(__dirname, "../package.json"),
|
|
5587
7084
|
"utf-8"
|
|
5588
7085
|
);
|
|
5589
7086
|
var packageJson = JSON.parse(packageContent);
|
|
5590
7087
|
var packageVersion = packageJson.version || "unknown";
|
|
5591
|
-
program.name("mcp-use").description("Create and run MCP servers with ui resources widgets").version(packageVersion);
|
|
7088
|
+
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
7089
|
function displayPackageVersions(projectPath) {
|
|
5593
7090
|
const packages = [
|
|
5594
7091
|
{ name: "@mcp-use/cli", relativePath: "../package.json" },
|
|
@@ -5614,14 +7111,14 @@ function displayPackageVersions(projectPath) {
|
|
|
5614
7111
|
if (projectPath) {
|
|
5615
7112
|
try {
|
|
5616
7113
|
const projectRequire = createRequire2(
|
|
5617
|
-
|
|
7114
|
+
path11.join(projectPath, "package.json")
|
|
5618
7115
|
);
|
|
5619
7116
|
pkgPath = projectRequire.resolve(`${pkg.name}/package.json`);
|
|
5620
7117
|
} catch (resolveError) {
|
|
5621
|
-
pkgPath =
|
|
7118
|
+
pkgPath = path11.join(__dirname, pkg.relativePath);
|
|
5622
7119
|
}
|
|
5623
7120
|
} else {
|
|
5624
|
-
pkgPath =
|
|
7121
|
+
pkgPath = path11.join(__dirname, pkg.relativePath);
|
|
5625
7122
|
}
|
|
5626
7123
|
const pkgContent = readFileSync2(pkgPath, "utf-8");
|
|
5627
7124
|
const pkgJson = JSON.parse(pkgContent);
|
|
@@ -5678,7 +7175,7 @@ function normalizeBrowserHost(host) {
|
|
|
5678
7175
|
return host === "0.0.0.0" ? "localhost" : host;
|
|
5679
7176
|
}
|
|
5680
7177
|
function runCommand(command, args, cwd, env2, filterStderr = false) {
|
|
5681
|
-
const proc =
|
|
7178
|
+
const proc = spawn3(command, args, {
|
|
5682
7179
|
cwd,
|
|
5683
7180
|
stdio: filterStderr ? ["inherit", "inherit", "pipe"] : "inherit",
|
|
5684
7181
|
shell: process.platform === "win32",
|
|
@@ -5711,7 +7208,7 @@ async function startTunnel(port, subdomain) {
|
|
|
5711
7208
|
if (subdomain) {
|
|
5712
7209
|
tunnelArgs.push("--subdomain", subdomain);
|
|
5713
7210
|
}
|
|
5714
|
-
const proc =
|
|
7211
|
+
const proc = spawn3("npx", tunnelArgs, {
|
|
5715
7212
|
stdio: ["ignore", "pipe", "pipe"],
|
|
5716
7213
|
shell: process.platform === "win32"
|
|
5717
7214
|
});
|
|
@@ -5775,21 +7272,21 @@ async function startTunnel(port, subdomain) {
|
|
|
5775
7272
|
}
|
|
5776
7273
|
async function resolveEntryFile(projectPath, cliEntry, mcpDir) {
|
|
5777
7274
|
if (cliEntry) {
|
|
5778
|
-
await access(
|
|
7275
|
+
await access(path11.join(projectPath, cliEntry)).catch(() => {
|
|
5779
7276
|
throw new Error(`File not found: ${cliEntry}`);
|
|
5780
7277
|
});
|
|
5781
7278
|
return cliEntry;
|
|
5782
7279
|
}
|
|
5783
7280
|
if (mcpDir) {
|
|
5784
7281
|
const mcpCandidates = [
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
7282
|
+
path11.join(mcpDir, "index.ts"),
|
|
7283
|
+
path11.join(mcpDir, "index.tsx"),
|
|
7284
|
+
path11.join(mcpDir, "server.ts"),
|
|
7285
|
+
path11.join(mcpDir, "server.tsx")
|
|
5789
7286
|
];
|
|
5790
7287
|
for (const candidate of mcpCandidates) {
|
|
5791
7288
|
try {
|
|
5792
|
-
await access(
|
|
7289
|
+
await access(path11.join(projectPath, candidate));
|
|
5793
7290
|
return candidate;
|
|
5794
7291
|
} catch {
|
|
5795
7292
|
continue;
|
|
@@ -5798,17 +7295,17 @@ async function resolveEntryFile(projectPath, cliEntry, mcpDir) {
|
|
|
5798
7295
|
throw new Error(
|
|
5799
7296
|
`No entry file found inside ${mcpDir}.
|
|
5800
7297
|
|
|
5801
|
-
Expected one of: ${mcpCandidates.map((c) =>
|
|
7298
|
+
Expected one of: ${mcpCandidates.map((c) => path11.relative(projectPath, path11.join(projectPath, c))).join(", ")}
|
|
5802
7299
|
|
|
5803
7300
|
Fix this by either:
|
|
5804
|
-
1. Creating ${
|
|
7301
|
+
1. Creating ${path11.join(mcpDir, "index.ts")}, or
|
|
5805
7302
|
2. Passing --entry <file> on the command line`
|
|
5806
7303
|
);
|
|
5807
7304
|
}
|
|
5808
7305
|
const candidates = ["index.ts", "src/index.ts", "server.ts", "src/server.ts"];
|
|
5809
7306
|
for (const candidate of candidates) {
|
|
5810
7307
|
try {
|
|
5811
|
-
await access(
|
|
7308
|
+
await access(path11.join(projectPath, candidate));
|
|
5812
7309
|
return candidate;
|
|
5813
7310
|
} catch {
|
|
5814
7311
|
continue;
|
|
@@ -5826,7 +7323,7 @@ Fix this by either:
|
|
|
5826
7323
|
}
|
|
5827
7324
|
function resolveWidgetsDir(cliWidgetsDir, mcpDir) {
|
|
5828
7325
|
if (cliWidgetsDir) return cliWidgetsDir;
|
|
5829
|
-
if (mcpDir) return
|
|
7326
|
+
if (mcpDir) return path11.join(mcpDir, "resources");
|
|
5830
7327
|
return "resources";
|
|
5831
7328
|
}
|
|
5832
7329
|
function makeWidgetServerOnlyGuard(widgetName) {
|
|
@@ -5861,7 +7358,7 @@ function isBunRuntime() {
|
|
|
5861
7358
|
return typeof globalThis.Bun !== "undefined" || typeof process.versions.bun === "string";
|
|
5862
7359
|
}
|
|
5863
7360
|
async function generateToolRegistryTypesForServer(projectPath, serverFileRelative) {
|
|
5864
|
-
const serverFile =
|
|
7361
|
+
const serverFile = path11.join(projectPath, serverFileRelative);
|
|
5865
7362
|
const serverFileExists = await access(serverFile).then(() => true).catch(() => false);
|
|
5866
7363
|
if (!serverFileExists) {
|
|
5867
7364
|
throw new Error(`Server file not found: ${serverFile}`);
|
|
@@ -5887,7 +7384,7 @@ async function generateToolRegistryTypesForServer(projectPath, serverFileRelativ
|
|
|
5887
7384
|
await loadNextJsEnvFiles(projectPath);
|
|
5888
7385
|
await registerNextShimsInProcess();
|
|
5889
7386
|
}
|
|
5890
|
-
const projectTsconfigPath =
|
|
7387
|
+
const projectTsconfigPath = path11.join(projectPath, "tsconfig.json");
|
|
5891
7388
|
const hasTsconfig = await access(projectTsconfigPath).then(() => true).catch(() => false);
|
|
5892
7389
|
if (hasTsconfig) {
|
|
5893
7390
|
process.env.TSX_TSCONFIG_PATH = projectTsconfigPath;
|
|
@@ -5896,7 +7393,7 @@ async function generateToolRegistryTypesForServer(projectPath, serverFileRelativ
|
|
|
5896
7393
|
if (previousCwd !== projectPath) process.chdir(projectPath);
|
|
5897
7394
|
try {
|
|
5898
7395
|
const projectRequire = createRequire2(
|
|
5899
|
-
|
|
7396
|
+
path11.join(projectPath, "package.json")
|
|
5900
7397
|
);
|
|
5901
7398
|
const tsxEsmApiPath = projectRequire.resolve("tsx/esm/api");
|
|
5902
7399
|
const tsxEsmApi = await import(pathToFileURL2(tsxEsmApiPath).href);
|
|
@@ -5923,8 +7420,8 @@ async function generateToolRegistryTypesForServer(projectPath, serverFileRelativ
|
|
|
5923
7420
|
"No MCPServer instance found. Make sure your server file creates an MCPServer instance."
|
|
5924
7421
|
);
|
|
5925
7422
|
}
|
|
5926
|
-
const mcpUsePath =
|
|
5927
|
-
const { generateToolRegistryTypes } = await import(pathToFileURL2(
|
|
7423
|
+
const mcpUsePath = path11.join(projectPath, "node_modules", "mcp-use");
|
|
7424
|
+
const { generateToolRegistryTypes } = await import(pathToFileURL2(path11.join(mcpUsePath, "dist", "src", "server", "index.js")).href).then((mod) => mod);
|
|
5928
7425
|
if (!generateToolRegistryTypes) {
|
|
5929
7426
|
throw new Error("generateToolRegistryTypes not found in mcp-use package");
|
|
5930
7427
|
}
|
|
@@ -5942,7 +7439,7 @@ async function buildWidgets(projectPath, options = {}) {
|
|
|
5942
7439
|
const { promises: fs11 } = await import("fs");
|
|
5943
7440
|
const { build } = await import("vite");
|
|
5944
7441
|
const widgetsDirRelative = options.widgetsDir ?? "resources";
|
|
5945
|
-
const resourcesDir =
|
|
7442
|
+
const resourcesDir = path11.resolve(projectPath, widgetsDirRelative);
|
|
5946
7443
|
const mcpUrl = process.env.MCP_URL;
|
|
5947
7444
|
try {
|
|
5948
7445
|
await access(resourcesDir);
|
|
@@ -5964,10 +7461,10 @@ async function buildWidgets(projectPath, options = {}) {
|
|
|
5964
7461
|
if (dirent.isFile() && (dirent.name.endsWith(".tsx") || dirent.name.endsWith(".ts"))) {
|
|
5965
7462
|
entries.push({
|
|
5966
7463
|
name: dirent.name.replace(/\.tsx?$/, ""),
|
|
5967
|
-
path:
|
|
7464
|
+
path: path11.join(resourcesDir, dirent.name)
|
|
5968
7465
|
});
|
|
5969
7466
|
} else if (dirent.isDirectory()) {
|
|
5970
|
-
const widgetPath =
|
|
7467
|
+
const widgetPath = path11.join(resourcesDir, dirent.name, "widget.tsx");
|
|
5971
7468
|
try {
|
|
5972
7469
|
await fs11.access(widgetPath);
|
|
5973
7470
|
entries.push({
|
|
@@ -5997,14 +7494,14 @@ async function buildWidgets(projectPath, options = {}) {
|
|
|
5997
7494
|
);
|
|
5998
7495
|
const react = (await import("@vitejs/plugin-react")).default;
|
|
5999
7496
|
const tailwindcss = (await import("@tailwindcss/vite")).default;
|
|
6000
|
-
const projectTsconfigPath =
|
|
7497
|
+
const projectTsconfigPath = path11.join(projectPath, "tsconfig.json");
|
|
6001
7498
|
let hasProjectTsconfig = false;
|
|
6002
7499
|
try {
|
|
6003
7500
|
await access(projectTsconfigPath);
|
|
6004
7501
|
hasProjectTsconfig = true;
|
|
6005
7502
|
} catch {
|
|
6006
7503
|
}
|
|
6007
|
-
const packageJsonPath =
|
|
7504
|
+
const packageJsonPath = path11.join(projectPath, "package.json");
|
|
6008
7505
|
let favicon = "";
|
|
6009
7506
|
try {
|
|
6010
7507
|
const pkgContent = await fs11.readFile(packageJsonPath, "utf-8");
|
|
@@ -6016,16 +7513,16 @@ async function buildWidgets(projectPath, options = {}) {
|
|
|
6016
7513
|
const widgetName = entry.name;
|
|
6017
7514
|
const entryPath = entry.path.replace(/\\/g, "/");
|
|
6018
7515
|
console.log(source_default.gray(` - Building ${widgetName}...`));
|
|
6019
|
-
const tempDir =
|
|
7516
|
+
const tempDir = path11.join(projectPath, ".mcp-use", widgetName);
|
|
6020
7517
|
await fs11.mkdir(tempDir, { recursive: true });
|
|
6021
|
-
const relativeResourcesPath =
|
|
6022
|
-
const mcpUsePath =
|
|
6023
|
-
const relativeMcpUsePath =
|
|
6024
|
-
const projectSrcDir =
|
|
7518
|
+
const relativeResourcesPath = path11.relative(tempDir, resourcesDir).replace(/\\/g, "/");
|
|
7519
|
+
const mcpUsePath = path11.join(projectPath, "node_modules", "mcp-use");
|
|
7520
|
+
const relativeMcpUsePath = path11.relative(tempDir, mcpUsePath).replace(/\\/g, "/");
|
|
7521
|
+
const projectSrcDir = path11.join(projectPath, "src");
|
|
6025
7522
|
let projectSrcSourceLine = "";
|
|
6026
7523
|
try {
|
|
6027
7524
|
await access(projectSrcDir);
|
|
6028
|
-
const relativeProjectSrcPath =
|
|
7525
|
+
const relativeProjectSrcPath = path11.relative(tempDir, projectSrcDir).replace(/\\/g, "/");
|
|
6029
7526
|
projectSrcSourceLine = `@source "${relativeProjectSrcPath}";
|
|
6030
7527
|
`;
|
|
6031
7528
|
} catch {
|
|
@@ -6036,7 +7533,7 @@ async function buildWidgets(projectPath, options = {}) {
|
|
|
6036
7533
|
@source "${relativeResourcesPath}";
|
|
6037
7534
|
@source "${relativeMcpUsePath}/**/*.{ts,tsx,js,jsx}";
|
|
6038
7535
|
${projectSrcSourceLine}`;
|
|
6039
|
-
await fs11.writeFile(
|
|
7536
|
+
await fs11.writeFile(path11.join(tempDir, "styles.css"), cssContent, "utf8");
|
|
6040
7537
|
const entryContent = `import React from 'react'
|
|
6041
7538
|
import { createRoot } from 'react-dom/client'
|
|
6042
7539
|
import './styles.css'
|
|
@@ -6061,9 +7558,9 @@ if (container && Component) {
|
|
|
6061
7558
|
<script type="module" src="/entry.tsx"></script>
|
|
6062
7559
|
</body>
|
|
6063
7560
|
</html>`;
|
|
6064
|
-
await fs11.writeFile(
|
|
6065
|
-
await fs11.writeFile(
|
|
6066
|
-
const outDir =
|
|
7561
|
+
await fs11.writeFile(path11.join(tempDir, "entry.tsx"), entryContent, "utf8");
|
|
7562
|
+
await fs11.writeFile(path11.join(tempDir, "index.html"), htmlContent, "utf8");
|
|
7563
|
+
const outDir = path11.join(
|
|
6067
7564
|
projectPath,
|
|
6068
7565
|
"dist",
|
|
6069
7566
|
"resources",
|
|
@@ -6073,13 +7570,13 @@ if (container && Component) {
|
|
|
6073
7570
|
const baseUrl = mcpUrl ? `${mcpUrl}/${widgetName}/` : `/mcp-use/widgets/${widgetName}/`;
|
|
6074
7571
|
let widgetMetadata = {};
|
|
6075
7572
|
try {
|
|
6076
|
-
const metadataTempDir =
|
|
7573
|
+
const metadataTempDir = path11.join(
|
|
6077
7574
|
projectPath,
|
|
6078
7575
|
".mcp-use",
|
|
6079
7576
|
`${widgetName}-metadata`
|
|
6080
7577
|
);
|
|
6081
7578
|
await fs11.mkdir(metadataTempDir, { recursive: true });
|
|
6082
|
-
const { createServer } = await import("vite");
|
|
7579
|
+
const { createServer: createServer2 } = await import("vite");
|
|
6083
7580
|
const nodeStubsPlugin = {
|
|
6084
7581
|
name: "node-stubs",
|
|
6085
7582
|
enforce: "pre",
|
|
@@ -6107,9 +7604,9 @@ export default PostHog;
|
|
|
6107
7604
|
}
|
|
6108
7605
|
};
|
|
6109
7606
|
const serverOnlyGuard = makeWidgetServerOnlyGuard(widgetName);
|
|
6110
|
-
const metadataServer = await
|
|
7607
|
+
const metadataServer = await createServer2({
|
|
6111
7608
|
root: metadataTempDir,
|
|
6112
|
-
cacheDir:
|
|
7609
|
+
cacheDir: path11.join(metadataTempDir, ".vite-cache"),
|
|
6113
7610
|
plugins: [serverOnlyGuard, nodeStubsPlugin, tailwindcss(), react()],
|
|
6114
7611
|
// When the project has a tsconfig, enable Vite's native tsconfig-paths
|
|
6115
7612
|
// resolver so `@/*` (or any custom alias) resolves through the
|
|
@@ -6340,7 +7837,7 @@ export default {
|
|
|
6340
7837
|
// Inline all assets under 100MB (effectively all)
|
|
6341
7838
|
} : {},
|
|
6342
7839
|
rolldownOptions: {
|
|
6343
|
-
input:
|
|
7840
|
+
input: path11.join(tempDir, "index.html"),
|
|
6344
7841
|
external: (id) => {
|
|
6345
7842
|
return false;
|
|
6346
7843
|
}
|
|
@@ -6348,11 +7845,11 @@ export default {
|
|
|
6348
7845
|
}
|
|
6349
7846
|
});
|
|
6350
7847
|
try {
|
|
6351
|
-
const assetsDir =
|
|
7848
|
+
const assetsDir = path11.join(outDir, "assets");
|
|
6352
7849
|
const assetFiles = await fs11.readdir(assetsDir);
|
|
6353
7850
|
const jsFiles = assetFiles.filter((f) => f.endsWith(".js"));
|
|
6354
7851
|
for (const jsFile of jsFiles) {
|
|
6355
|
-
const jsPath =
|
|
7852
|
+
const jsPath = path11.join(assetsDir, jsFile);
|
|
6356
7853
|
let content = await fs11.readFile(jsPath, "utf8");
|
|
6357
7854
|
const zodConfigPatterns = [
|
|
6358
7855
|
// Non-minified: export const globalConfig = {}
|
|
@@ -6384,7 +7881,7 @@ export default {
|
|
|
6384
7881
|
const mcpServerUrl = process.env.MCP_SERVER_URL;
|
|
6385
7882
|
if (mcpServerUrl) {
|
|
6386
7883
|
try {
|
|
6387
|
-
const htmlPath =
|
|
7884
|
+
const htmlPath = path11.join(outDir, "index.html");
|
|
6388
7885
|
let html = await fs11.readFile(htmlPath, "utf8");
|
|
6389
7886
|
const injectionScript = `<script>window.__getFile = (filename) => { return "${mcpUrl}/${widgetName}/"+filename }; window.__mcpPublicUrl = "${mcpServerUrl}/mcp-use/public"; window.__mcpPublicAssetsUrl = "${mcpUrl}/public";</script>`;
|
|
6390
7887
|
if (!html.includes("window.__mcpPublicUrl")) {
|
|
@@ -6460,7 +7957,7 @@ async function collectTsFiles(projectPath, includePatterns, excludePatterns) {
|
|
|
6460
7957
|
for (const file of literalFiles) {
|
|
6461
7958
|
if (/\.tsx?$/.test(file) && !file.endsWith(".d.ts")) {
|
|
6462
7959
|
try {
|
|
6463
|
-
await access(
|
|
7960
|
+
await access(path11.join(projectPath, file));
|
|
6464
7961
|
files.push(file);
|
|
6465
7962
|
} catch {
|
|
6466
7963
|
}
|
|
@@ -6468,13 +7965,13 @@ async function collectTsFiles(projectPath, includePatterns, excludePatterns) {
|
|
|
6468
7965
|
}
|
|
6469
7966
|
const excludeSet = new Set(excludePatterns.map((e) => e.replace(/\*+/g, "")));
|
|
6470
7967
|
for (const prefix of dirPrefixes) {
|
|
6471
|
-
const dirPath =
|
|
7968
|
+
const dirPath = path11.join(projectPath, prefix);
|
|
6472
7969
|
try {
|
|
6473
7970
|
const entries = await fs11.readdir(dirPath, { recursive: true });
|
|
6474
7971
|
for (const entry of entries) {
|
|
6475
7972
|
const entryStr = String(entry);
|
|
6476
|
-
const rel =
|
|
6477
|
-
if (/\.tsx?$/.test(entryStr) && !entryStr.endsWith(".d.ts") && !excludeSet.has(rel.split(
|
|
7973
|
+
const rel = path11.join(prefix, entryStr);
|
|
7974
|
+
if (/\.tsx?$/.test(entryStr) && !entryStr.endsWith(".d.ts") && !excludeSet.has(rel.split(path11.sep)[0])) {
|
|
6478
7975
|
files.push(rel);
|
|
6479
7976
|
}
|
|
6480
7977
|
}
|
|
@@ -6486,7 +7983,7 @@ async function collectTsFiles(projectPath, includePatterns, excludePatterns) {
|
|
|
6486
7983
|
async function transpileWithEsbuild(projectPath) {
|
|
6487
7984
|
const esbuild = await import("esbuild");
|
|
6488
7985
|
const { promises: fs11 } = await import("fs");
|
|
6489
|
-
const tsconfigPath =
|
|
7986
|
+
const tsconfigPath = path11.join(projectPath, "tsconfig.json");
|
|
6490
7987
|
let tsconfig = {};
|
|
6491
7988
|
try {
|
|
6492
7989
|
const raw = await fs11.readFile(tsconfigPath, "utf-8");
|
|
@@ -6516,10 +8013,10 @@ async function transpileWithEsbuild(projectPath) {
|
|
|
6516
8013
|
const target = (compilerOptions.target || "ES2022").toLowerCase();
|
|
6517
8014
|
const moduleStr = (compilerOptions.module || "ESNext").toLowerCase();
|
|
6518
8015
|
const format = moduleStr.includes("commonjs") ? "cjs" : "esm";
|
|
6519
|
-
const outbase = compilerOptions.rootDir ?
|
|
8016
|
+
const outbase = compilerOptions.rootDir ? path11.resolve(projectPath, compilerOptions.rootDir) : projectPath;
|
|
6520
8017
|
await esbuild.build({
|
|
6521
|
-
entryPoints: files.map((f) =>
|
|
6522
|
-
outdir:
|
|
8018
|
+
entryPoints: files.map((f) => path11.join(projectPath, f)),
|
|
8019
|
+
outdir: path11.join(projectPath, outDir),
|
|
6523
8020
|
outbase,
|
|
6524
8021
|
bundle: true,
|
|
6525
8022
|
packages: "external",
|
|
@@ -6546,7 +8043,7 @@ program.command("build").description("Build TypeScript and MCP UI widgets").opti
|
|
|
6546
8043
|
"Inline all JS/CSS into HTML (required for VS Code MCP Apps)"
|
|
6547
8044
|
).option("--no-inline", "Keep JS/CSS as separate files (default)").option("--no-typecheck", "Skip TypeScript type checking (faster builds)").action(async (options) => {
|
|
6548
8045
|
try {
|
|
6549
|
-
const projectPath =
|
|
8046
|
+
const projectPath = path11.resolve(options.path);
|
|
6550
8047
|
const { promises: fs11 } = await import("fs");
|
|
6551
8048
|
displayPackageVersions(projectPath);
|
|
6552
8049
|
const mcpDir = options.mcpDir;
|
|
@@ -6606,7 +8103,7 @@ program.command("build").description("Build TypeScript and MCP UI widgets").opti
|
|
|
6606
8103
|
}
|
|
6607
8104
|
if (options.typecheck !== false && !mcpDir) {
|
|
6608
8105
|
console.log(source_default.gray("Type checking..."));
|
|
6609
|
-
const tscBin =
|
|
8106
|
+
const tscBin = path11.join(
|
|
6610
8107
|
projectPath,
|
|
6611
8108
|
"node_modules",
|
|
6612
8109
|
"typescript",
|
|
@@ -6629,7 +8126,7 @@ program.command("build").description("Build TypeScript and MCP UI widgets").opti
|
|
|
6629
8126
|
if (mcpDir) {
|
|
6630
8127
|
entryPoint = sourceServerFile;
|
|
6631
8128
|
} else {
|
|
6632
|
-
const baseName =
|
|
8129
|
+
const baseName = path11.basename(sourceServerFile, ".ts") + ".js";
|
|
6633
8130
|
const possibleOutputs = [
|
|
6634
8131
|
`dist/${baseName}`,
|
|
6635
8132
|
// rootDir set to project root or src
|
|
@@ -6640,7 +8137,7 @@ program.command("build").description("Build TypeScript and MCP UI widgets").opti
|
|
|
6640
8137
|
];
|
|
6641
8138
|
for (const candidate of possibleOutputs) {
|
|
6642
8139
|
try {
|
|
6643
|
-
await access(
|
|
8140
|
+
await access(path11.join(projectPath, candidate));
|
|
6644
8141
|
entryPoint = candidate;
|
|
6645
8142
|
break;
|
|
6646
8143
|
} catch {
|
|
@@ -6649,17 +8146,17 @@ program.command("build").description("Build TypeScript and MCP UI widgets").opti
|
|
|
6649
8146
|
}
|
|
6650
8147
|
}
|
|
6651
8148
|
}
|
|
6652
|
-
const publicDir =
|
|
8149
|
+
const publicDir = path11.join(projectPath, "public");
|
|
6653
8150
|
try {
|
|
6654
8151
|
await fs11.access(publicDir);
|
|
6655
8152
|
console.log(source_default.gray("Copying public assets..."));
|
|
6656
|
-
await fs11.cp(publicDir,
|
|
8153
|
+
await fs11.cp(publicDir, path11.join(projectPath, "dist", "public"), {
|
|
6657
8154
|
recursive: true
|
|
6658
8155
|
});
|
|
6659
8156
|
console.log(source_default.green("\u2713 Public assets copied"));
|
|
6660
8157
|
} catch {
|
|
6661
8158
|
}
|
|
6662
|
-
const manifestPath =
|
|
8159
|
+
const manifestPath = path11.join(projectPath, "dist", "mcp-use.json");
|
|
6663
8160
|
let existingManifest = {};
|
|
6664
8161
|
try {
|
|
6665
8162
|
const existingContent = await fs11.readFile(manifestPath, "utf-8");
|
|
@@ -6684,7 +8181,7 @@ program.command("build").description("Build TypeScript and MCP UI widgets").opti
|
|
|
6684
8181
|
// Server entry point for `mcp-use start`
|
|
6685
8182
|
widgets: widgetsData
|
|
6686
8183
|
};
|
|
6687
|
-
await fs11.mkdir(
|
|
8184
|
+
await fs11.mkdir(path11.dirname(manifestPath), { recursive: true });
|
|
6688
8185
|
await fs11.writeFile(
|
|
6689
8186
|
manifestPath,
|
|
6690
8187
|
JSON.stringify(manifest, null, 2),
|
|
@@ -6721,7 +8218,7 @@ program.command("dev").description("Run development server with auto-reload and
|
|
|
6721
8218
|
).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
8219
|
try {
|
|
6723
8220
|
process.env.MCP_USE_CLI_DEV = "1";
|
|
6724
|
-
const projectPath =
|
|
8221
|
+
const projectPath = path11.resolve(options.path);
|
|
6725
8222
|
let port = parseInt(options.port, 10);
|
|
6726
8223
|
const host = options.host;
|
|
6727
8224
|
const useHmr = options.hmr !== false;
|
|
@@ -6749,7 +8246,7 @@ program.command("dev").description("Run development server with auto-reload and
|
|
|
6749
8246
|
let tunnelUrl = void 0;
|
|
6750
8247
|
if (options.tunnel) {
|
|
6751
8248
|
try {
|
|
6752
|
-
const manifestPath =
|
|
8249
|
+
const manifestPath = path11.join(projectPath, "dist", "mcp-use.json");
|
|
6753
8250
|
let existingSubdomain;
|
|
6754
8251
|
try {
|
|
6755
8252
|
const manifestContent = await readFile3(manifestPath, "utf-8");
|
|
@@ -6798,7 +8295,7 @@ program.command("dev").description("Run development server with auto-reload and
|
|
|
6798
8295
|
manifest.tunnel = {};
|
|
6799
8296
|
}
|
|
6800
8297
|
manifest.tunnel.subdomain = tunnelSubdomain;
|
|
6801
|
-
await
|
|
8298
|
+
await mkdir4(path11.dirname(manifestPath), { recursive: true });
|
|
6802
8299
|
await writeFile3(
|
|
6803
8300
|
manifestPath,
|
|
6804
8301
|
JSON.stringify(manifest, null, 2),
|
|
@@ -6853,7 +8350,7 @@ program.command("dev").description("Run development server with auto-reload and
|
|
|
6853
8350
|
let args;
|
|
6854
8351
|
try {
|
|
6855
8352
|
const projectRequire = createRequire4(
|
|
6856
|
-
|
|
8353
|
+
path11.join(projectPath, "package.json")
|
|
6857
8354
|
);
|
|
6858
8355
|
const tsxPkgPath = projectRequire.resolve("tsx/package.json");
|
|
6859
8356
|
const tsxPkg = JSON.parse(await readFile3(tsxPkgPath, "utf-8"));
|
|
@@ -6865,7 +8362,7 @@ program.command("dev").description("Run development server with auto-reload and
|
|
|
6865
8362
|
} else {
|
|
6866
8363
|
throw new Error("No bin field found in tsx package.json");
|
|
6867
8364
|
}
|
|
6868
|
-
const tsxBin =
|
|
8365
|
+
const tsxBin = path11.resolve(path11.dirname(tsxPkgPath), binPath);
|
|
6869
8366
|
cmd = "node";
|
|
6870
8367
|
args = [tsxBin, "watch", serverFile];
|
|
6871
8368
|
} catch (error) {
|
|
@@ -6954,7 +8451,7 @@ program.command("dev").description("Run development server with auto-reload and
|
|
|
6954
8451
|
const chokidar = chokidarModule.default || chokidarModule;
|
|
6955
8452
|
const { fileURLToPath: fileURLToPath4 } = await import("url");
|
|
6956
8453
|
const { createRequire: createRequire3 } = await import("module");
|
|
6957
|
-
const projectTsconfigPath =
|
|
8454
|
+
const projectTsconfigPath = path11.join(projectPath, "tsconfig.json");
|
|
6958
8455
|
let tsconfigAvailable = false;
|
|
6959
8456
|
try {
|
|
6960
8457
|
await access(projectTsconfigPath);
|
|
@@ -6966,7 +8463,7 @@ program.command("dev").description("Run development server with auto-reload and
|
|
|
6966
8463
|
let tsxLoaderActive = false;
|
|
6967
8464
|
try {
|
|
6968
8465
|
const projectRequire = createRequire3(
|
|
6969
|
-
|
|
8466
|
+
path11.join(projectPath, "package.json")
|
|
6970
8467
|
);
|
|
6971
8468
|
const tsxEsmApiPath = projectRequire.resolve("tsx/esm/api");
|
|
6972
8469
|
const tsxEsmApi = await import(pathToFileURL2(tsxEsmApiPath).href);
|
|
@@ -6997,7 +8494,7 @@ program.command("dev").description("Run development server with auto-reload and
|
|
|
6997
8494
|
)
|
|
6998
8495
|
);
|
|
6999
8496
|
}
|
|
7000
|
-
const serverFilePath =
|
|
8497
|
+
const serverFilePath = path11.join(projectPath, serverFile);
|
|
7001
8498
|
const serverFileUrl = pathToFileURL2(serverFilePath).href;
|
|
7002
8499
|
globalThis.__mcpUseHmrMode = true;
|
|
7003
8500
|
const importServerModule = async () => {
|
|
@@ -7094,8 +8591,8 @@ program.command("dev").description("Run development server with auto-reload and
|
|
|
7094
8591
|
}
|
|
7095
8592
|
let watcher = chokidar.watch(".", {
|
|
7096
8593
|
cwd: projectPath,
|
|
7097
|
-
ignored: (
|
|
7098
|
-
const normalizedPath =
|
|
8594
|
+
ignored: (path12, stats) => {
|
|
8595
|
+
const normalizedPath = path12.replace(/\\/g, "/");
|
|
7099
8596
|
if (/(^|\/)\.[^/]/.test(normalizedPath)) {
|
|
7100
8597
|
return true;
|
|
7101
8598
|
}
|
|
@@ -7269,7 +8766,7 @@ program.command("dev").description("Run development server with auto-reload and
|
|
|
7269
8766
|
}
|
|
7270
8767
|
tunnelUrl = void 0;
|
|
7271
8768
|
if (withTunnel) {
|
|
7272
|
-
const manifestPath =
|
|
8769
|
+
const manifestPath = path11.join(projectPath, "dist", "mcp-use.json");
|
|
7273
8770
|
let existingSubdomain;
|
|
7274
8771
|
try {
|
|
7275
8772
|
const manifestContent = await readFile3(manifestPath, "utf-8");
|
|
@@ -7306,7 +8803,7 @@ program.command("dev").description("Run development server with auto-reload and
|
|
|
7306
8803
|
tunnelSubdomain = tunnelInfo.subdomain;
|
|
7307
8804
|
process.env.MCP_URL = tunnelUrl;
|
|
7308
8805
|
try {
|
|
7309
|
-
const mPath =
|
|
8806
|
+
const mPath = path11.join(projectPath, "dist", "mcp-use.json");
|
|
7310
8807
|
let manifest = {};
|
|
7311
8808
|
try {
|
|
7312
8809
|
manifest = JSON.parse(await readFile3(mPath, "utf-8"));
|
|
@@ -7314,7 +8811,7 @@ program.command("dev").description("Run development server with auto-reload and
|
|
|
7314
8811
|
}
|
|
7315
8812
|
if (!manifest.tunnel) manifest.tunnel = {};
|
|
7316
8813
|
manifest.tunnel.subdomain = tunnelSubdomain;
|
|
7317
|
-
await
|
|
8814
|
+
await mkdir4(path11.dirname(mPath), { recursive: true });
|
|
7318
8815
|
await writeFile3(mPath, JSON.stringify(manifest, null, 2), "utf-8");
|
|
7319
8816
|
} catch {
|
|
7320
8817
|
}
|
|
@@ -7418,7 +8915,7 @@ program.command("start").description("Start production server").option("-p, --pa
|
|
|
7418
8915
|
"Folder holding the MCP entry + resources (e.g. 'src/mcp' for Next.js apps)"
|
|
7419
8916
|
).option("--port <port>", "Server port", "3000").option("--tunnel", "Expose server through a tunnel").action(async (options) => {
|
|
7420
8917
|
try {
|
|
7421
|
-
const projectPath =
|
|
8918
|
+
const projectPath = path11.resolve(options.path);
|
|
7422
8919
|
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
8920
|
let port = portFlagProvided ? parseInt(options.port, 10) : parseInt(process.env.PORT || options.port || "3000", 10);
|
|
7424
8921
|
if (!await isPortAvailable(port)) {
|
|
@@ -7436,7 +8933,7 @@ program.command("start").description("Start production server").option("-p, --pa
|
|
|
7436
8933
|
let tunnelSubdomain = void 0;
|
|
7437
8934
|
if (options.tunnel) {
|
|
7438
8935
|
try {
|
|
7439
|
-
const manifestPath2 =
|
|
8936
|
+
const manifestPath2 = path11.join(projectPath, "dist", "mcp-use.json");
|
|
7440
8937
|
let existingSubdomain;
|
|
7441
8938
|
try {
|
|
7442
8939
|
const manifestContent = await readFile3(manifestPath2, "utf-8");
|
|
@@ -7491,7 +8988,7 @@ program.command("start").description("Start production server").option("-p, --pa
|
|
|
7491
8988
|
manifest.tunnel = {};
|
|
7492
8989
|
}
|
|
7493
8990
|
manifest.tunnel.subdomain = subdomain;
|
|
7494
|
-
await
|
|
8991
|
+
await mkdir4(path11.dirname(manifestPath2), { recursive: true });
|
|
7495
8992
|
await writeFile3(
|
|
7496
8993
|
manifestPath2,
|
|
7497
8994
|
JSON.stringify(manifest, null, 2),
|
|
@@ -7510,12 +9007,12 @@ program.command("start").description("Start production server").option("-p, --pa
|
|
|
7510
9007
|
}
|
|
7511
9008
|
}
|
|
7512
9009
|
let serverFile;
|
|
7513
|
-
const manifestPath =
|
|
9010
|
+
const manifestPath = path11.join(projectPath, "dist", "mcp-use.json");
|
|
7514
9011
|
try {
|
|
7515
9012
|
const manifestContent = await readFile3(manifestPath, "utf-8");
|
|
7516
9013
|
const manifest = JSON.parse(manifestContent);
|
|
7517
9014
|
if (manifest.entryPoint) {
|
|
7518
|
-
await access(
|
|
9015
|
+
await access(path11.join(projectPath, manifest.entryPoint));
|
|
7519
9016
|
serverFile = manifest.entryPoint;
|
|
7520
9017
|
}
|
|
7521
9018
|
} catch {
|
|
@@ -7536,7 +9033,7 @@ program.command("start").description("Start production server").option("-p, --pa
|
|
|
7536
9033
|
];
|
|
7537
9034
|
for (const candidate of serverCandidates) {
|
|
7538
9035
|
try {
|
|
7539
|
-
await access(
|
|
9036
|
+
await access(path11.join(projectPath, candidate));
|
|
7540
9037
|
serverFile = candidate;
|
|
7541
9038
|
break;
|
|
7542
9039
|
} catch {
|
|
@@ -7587,13 +9084,13 @@ Looked for:
|
|
|
7587
9084
|
if (isTsEntry) {
|
|
7588
9085
|
try {
|
|
7589
9086
|
const projectRequire = createRequire2(
|
|
7590
|
-
|
|
9087
|
+
path11.join(projectPath, "package.json")
|
|
7591
9088
|
);
|
|
7592
9089
|
const tsxPkgPath = projectRequire.resolve("tsx/package.json");
|
|
7593
9090
|
const tsxPkg = JSON.parse(await readFile3(tsxPkgPath, "utf-8"));
|
|
7594
9091
|
const binField = typeof tsxPkg.bin === "string" ? tsxPkg.bin : tsxPkg.bin?.tsx ?? Object.values(tsxPkg.bin ?? {})[0];
|
|
7595
9092
|
if (!binField) throw new Error("tsx bin entry not found");
|
|
7596
|
-
const tsxBin =
|
|
9093
|
+
const tsxBin = path11.resolve(path11.dirname(tsxPkgPath), binField);
|
|
7597
9094
|
spawnCmd = "node";
|
|
7598
9095
|
spawnArgs = [tsxBin, serverFile];
|
|
7599
9096
|
} catch (error) {
|
|
@@ -7606,7 +9103,7 @@ Looked for:
|
|
|
7606
9103
|
spawnArgs = ["tsx", serverFile];
|
|
7607
9104
|
}
|
|
7608
9105
|
}
|
|
7609
|
-
const serverProc =
|
|
9106
|
+
const serverProc = spawn3(spawnCmd, spawnArgs, {
|
|
7610
9107
|
cwd: projectPath,
|
|
7611
9108
|
stdio: "inherit",
|
|
7612
9109
|
env: env2
|
|
@@ -7742,7 +9239,7 @@ program.addCommand(createSkillsCommand());
|
|
|
7742
9239
|
program.command("generate-types").description(
|
|
7743
9240
|
"Generate TypeScript type definitions for tools (writes .mcp-use/tool-registry.d.ts)"
|
|
7744
9241
|
).option("-p, --path <path>", "Path to project directory", process.cwd()).option("--server <file>", "Server entry file", "index.ts").action(async (options) => {
|
|
7745
|
-
const projectPath =
|
|
9242
|
+
const projectPath = path11.resolve(options.path);
|
|
7746
9243
|
try {
|
|
7747
9244
|
console.log(source_default.blue("Generating tool registry types..."));
|
|
7748
9245
|
const result = await generateToolRegistryTypesForServer(
|
|
@@ -7772,5 +9269,49 @@ program.hook("preAction", async (_thisCommand, actionCommand) => {
|
|
|
7772
9269
|
const projectPath = actionCommand.opts().path;
|
|
7773
9270
|
await notifyIfUpdateAvailable(projectPath);
|
|
7774
9271
|
});
|
|
7775
|
-
|
|
9272
|
+
var argv = process.argv;
|
|
9273
|
+
var clientIdx = argv[2] === "client" ? 2 : -1;
|
|
9274
|
+
var perClientName = clientIdx !== -1 && argv.length > clientIdx + 1 && !argv[clientIdx + 1].startsWith("-") && !RESERVED_CLIENT_SUBCOMMANDS.has(argv[clientIdx + 1]) ? argv[clientIdx + 1] : null;
|
|
9275
|
+
if (perClientName) {
|
|
9276
|
+
if (PER_CLIENT_SCOPES.has(perClientName)) {
|
|
9277
|
+
const rest2 = argv.slice(clientIdx + 1).join(" ");
|
|
9278
|
+
console.error(formatError("Missing server name."));
|
|
9279
|
+
console.error("");
|
|
9280
|
+
console.error(
|
|
9281
|
+
`'${perClientName}' is a per-server subcommand, not a server name. Address it through a saved server:`
|
|
9282
|
+
);
|
|
9283
|
+
console.error("");
|
|
9284
|
+
console.error(` mcp-use client <name> ${rest2}`);
|
|
9285
|
+
console.error("");
|
|
9286
|
+
console.error("See your saved servers with:");
|
|
9287
|
+
console.error(" mcp-use client list");
|
|
9288
|
+
process.exit(1);
|
|
9289
|
+
}
|
|
9290
|
+
const rest = argv.slice(clientIdx + 2);
|
|
9291
|
+
const isHelpOnly = rest.length === 0 || rest.length === 1 && (rest[0] === "--help" || rest[0] === "-h");
|
|
9292
|
+
(async () => {
|
|
9293
|
+
if (isHelpOnly) {
|
|
9294
|
+
const config = await getSession(perClientName);
|
|
9295
|
+
if (!config) {
|
|
9296
|
+
console.error(formatError(`Server '${perClientName}' not found.`));
|
|
9297
|
+
console.error("");
|
|
9298
|
+
console.error("Connect to an MCP server and save it under this name:");
|
|
9299
|
+
console.error(` mcp-use client connect ${perClientName} <url>`);
|
|
9300
|
+
console.error("");
|
|
9301
|
+
console.error("See your saved servers with:");
|
|
9302
|
+
console.error(" mcp-use client list");
|
|
9303
|
+
process.exit(1);
|
|
9304
|
+
}
|
|
9305
|
+
}
|
|
9306
|
+
await createPerClientCommand(perClientName).parseAsync(rest, {
|
|
9307
|
+
from: "user"
|
|
9308
|
+
});
|
|
9309
|
+
})().catch((err) => {
|
|
9310
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
9311
|
+
console.error(formatError(message));
|
|
9312
|
+
process.exit(1);
|
|
9313
|
+
});
|
|
9314
|
+
} else {
|
|
9315
|
+
program.parse();
|
|
9316
|
+
}
|
|
7776
9317
|
//# sourceMappingURL=index.js.map
|