@mcp-use/cli 3.5.2-canary.1 → 3.5.2-canary.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1136,7 +1136,7 @@ import os3 from "os";
1136
1136
  import path3 from "path";
1137
1137
  var CONFIG_DIR = path3.join(os3.homedir(), ".mcp-use");
1138
1138
  var CONFIG_FILE = path3.join(CONFIG_DIR, "config.json");
1139
- var DEFAULT_API_URL = process.env.MCP_API_URL ? process.env.MCP_API_URL.replace(/\/api\/v1$/, "") + "/api/v1" : "https://cloud.mcp-use.com/api/v1";
1139
+ var DEFAULT_API_URL = process.env.MCP_API_URL ? process.env.MCP_API_URL.replace(/\/api\/v1$/, "") + "/api/v1" : "https://cloud.manufact.com/api/v1";
1140
1140
  var DEFAULT_WEB_URL = process.env.MCP_WEB_URL ? process.env.MCP_WEB_URL : "https://manufact.com";
1141
1141
  async function ensureConfigDir() {
1142
1142
  try {
@@ -1550,37 +1550,51 @@ var McpUseAPI = class _McpUseAPI {
1550
1550
  }))
1551
1551
  };
1552
1552
  }
1553
- async getGitHubRepos(_refresh) {
1554
- const orgId = await this.resolveOrganizationId();
1555
- const installResp = await this.request(`/github/installations?organizationId=${orgId}`);
1556
- if (installResp.installations.length === 0) {
1557
- return { user: { login: "", id: 0, avatar_url: "" }, repos: [] };
1553
+ /**
1554
+ * Returns true if the GitHub App can access `${owner}/${repo}` via any of the
1555
+ * organization's installations.
1556
+ *
1557
+ * An organization can have multiple GitHub installations (e.g. a personal
1558
+ * account and one or more GitHub orgs), so we check across all of them.
1559
+ *
1560
+ * Each check is a single authoritative backend call (`repos.get` with the
1561
+ * installation token) rather than listing repos. The old listing approach
1562
+ * only returned the first page — so a repo on a later page was wrongly
1563
+ * reported inaccessible — and fully paginating it hung on very large orgs.
1564
+ * We try the installation whose account matches the repo owner first to
1565
+ * minimize GitHub calls.
1566
+ */
1567
+ async checkGitHubRepoAccess(owner, repo) {
1568
+ const status = await this.getGitHubConnectionStatus();
1569
+ const installations = status.installations ?? [];
1570
+ if (installations.length === 0) return false;
1571
+ const ownerLower = owner.toLowerCase();
1572
+ const ordered = [...installations].sort((a, b) => {
1573
+ const aMatch = a.account_login.toLowerCase() === ownerLower ? 0 : 1;
1574
+ const bMatch = b.account_login.toLowerCase() === ownerLower ? 0 : 1;
1575
+ return aMatch - bMatch;
1576
+ });
1577
+ for (const installation of ordered) {
1578
+ const hasAccess = await this.installationCanAccessRepo(
1579
+ installation.installation_id,
1580
+ owner,
1581
+ repo
1582
+ );
1583
+ if (hasAccess) return true;
1584
+ }
1585
+ return false;
1586
+ }
1587
+ async installationCanAccessRepo(installationId, owner, repo) {
1588
+ try {
1589
+ const resp = await this.request(
1590
+ `/github/installations/${installationId}/repos/${encodeURIComponent(
1591
+ owner
1592
+ )}/${encodeURIComponent(repo)}/access`
1593
+ );
1594
+ return resp.hasAccess;
1595
+ } catch {
1596
+ return false;
1558
1597
  }
1559
- const inst = installResp.installations[0];
1560
- const repoLists = await Promise.all(
1561
- installResp.installations.map(async (installation) => {
1562
- try {
1563
- const reposResp = await this.request(`/github/installations/${installation.installationId}/repos`);
1564
- return reposResp.repos;
1565
- } catch {
1566
- return [];
1567
- }
1568
- })
1569
- );
1570
- return {
1571
- user: {
1572
- login: inst.account?.login ?? "",
1573
- id: 0,
1574
- avatar_url: inst.account?.avatar_url ?? ""
1575
- },
1576
- repos: repoLists.flat().map((r) => ({
1577
- id: r.id,
1578
- name: r.name,
1579
- full_name: r.fullName,
1580
- private: r.private,
1581
- owner: { login: r.fullName.split("/")[0] ?? "" }
1582
- }))
1583
- };
1584
1598
  }
1585
1599
  async getGitHubAppName() {
1586
1600
  if (process.env.MCP_GITHUB_APP_NAME) return process.env.MCP_GITHUB_APP_NAME;
@@ -4017,6 +4031,71 @@ async function describeToolCommand(name, toolName) {
4017
4031
  }
4018
4032
  await cleanupAndExit(0);
4019
4033
  }
4034
+ async function processToolScreenshot(session, toolName, args, callResult, options) {
4035
+ const toolWithMeta = session.tools.find((t) => t.name === toolName);
4036
+ const resourceUri = detectToolResourceUri(toolWithMeta);
4037
+ const wantsScreenshot = options?.screenshot === true || options?.screenshotOutput !== void 0 || options?.screenshotDeviceScaleFactor !== void 0;
4038
+ let screenshot = null;
4039
+ let screenshotError = null;
4040
+ let widgetHintUri = null;
4041
+ if (resourceUri) {
4042
+ if (wantsScreenshot) {
4043
+ console.error(
4044
+ formatInfo(`Capturing widget screenshot (${resourceUri})...`)
4045
+ );
4046
+ try {
4047
+ const screenshotOpts = {};
4048
+ if (options?.screenshotOutput) {
4049
+ screenshotOpts.output = options.screenshotOutput;
4050
+ }
4051
+ if (options?.screenshotDeviceScaleFactor) {
4052
+ screenshotOpts.deviceScaleFactor = parseDeviceScaleFactor(
4053
+ options.screenshotDeviceScaleFactor
4054
+ );
4055
+ }
4056
+ const shot = await captureToolScreenshot(
4057
+ {
4058
+ session,
4059
+ toolName,
4060
+ toolArgs: args,
4061
+ toolOutput: callResult,
4062
+ resourceUri
4063
+ },
4064
+ screenshotOpts
4065
+ );
4066
+ screenshot = {
4067
+ path: shot.outputPath,
4068
+ width: shot.width,
4069
+ height: shot.height,
4070
+ view: shot.view
4071
+ };
4072
+ } catch (err) {
4073
+ screenshotError = err?.message ?? String(err);
4074
+ }
4075
+ } else {
4076
+ widgetHintUri = resourceUri;
4077
+ }
4078
+ }
4079
+ if (screenshot) {
4080
+ console.error(
4081
+ formatSuccess(
4082
+ `Saved widget screenshot: ${screenshot.path} (${screenshot.width}\xD7${screenshot.height})`
4083
+ )
4084
+ );
4085
+ }
4086
+ if (screenshotError) {
4087
+ console.error(
4088
+ formatWarning(`Skipped widget screenshot: ${screenshotError}`)
4089
+ );
4090
+ }
4091
+ if (widgetHintUri) {
4092
+ console.error(
4093
+ formatInfo(
4094
+ `This tool renders a widget (${widgetHintUri}). Re-run with --screenshot to save a PNG of it.`
4095
+ )
4096
+ );
4097
+ }
4098
+ }
4020
4099
  async function callToolCommand(name, toolName, argsList, options) {
4021
4100
  try {
4022
4101
  const result = await getOrRestoreSession(name);
@@ -4066,74 +4145,12 @@ async function callToolCommand(name, toolName, argsList, options) {
4066
4145
  const callResult = await session.callTool(toolName, args, {
4067
4146
  timeout: options?.timeout
4068
4147
  });
4069
- const toolWithMeta = session.tools.find((t) => t.name === toolName);
4070
- const resourceUri = detectToolResourceUri(toolWithMeta);
4071
- const wantsScreenshot = options?.screenshot === true || options?.screenshotOutput !== void 0 || options?.screenshotDeviceScaleFactor !== void 0;
4072
- let screenshot = null;
4073
- let screenshotError = null;
4074
- let widgetHintUri = null;
4075
- if (resourceUri) {
4076
- if (wantsScreenshot) {
4077
- console.error(
4078
- formatInfo(`Capturing widget screenshot (${resourceUri})...`)
4079
- );
4080
- try {
4081
- const screenshotOpts = {};
4082
- if (options?.screenshotOutput) {
4083
- screenshotOpts.output = options.screenshotOutput;
4084
- }
4085
- if (options?.screenshotDeviceScaleFactor) {
4086
- screenshotOpts.deviceScaleFactor = parseDeviceScaleFactor(
4087
- options.screenshotDeviceScaleFactor
4088
- );
4089
- }
4090
- const shot = await captureToolScreenshot(
4091
- {
4092
- session,
4093
- toolName,
4094
- toolArgs: args,
4095
- toolOutput: callResult,
4096
- resourceUri
4097
- },
4098
- screenshotOpts
4099
- );
4100
- screenshot = {
4101
- path: shot.outputPath,
4102
- width: shot.width,
4103
- height: shot.height,
4104
- view: shot.view
4105
- };
4106
- } catch (err) {
4107
- screenshotError = err?.message ?? String(err);
4108
- }
4109
- } else {
4110
- widgetHintUri = resourceUri;
4111
- }
4112
- }
4148
+ await processToolScreenshot(session, toolName, args, callResult, options);
4113
4149
  if (options?.json) {
4114
4150
  console.log(formatJson(callResult));
4115
4151
  } else {
4116
4152
  console.log(formatToolCall(callResult));
4117
4153
  }
4118
- if (screenshot) {
4119
- console.error(
4120
- formatSuccess(
4121
- `Saved widget screenshot: ${screenshot.path} (${screenshot.width}\xD7${screenshot.height})`
4122
- )
4123
- );
4124
- }
4125
- if (screenshotError) {
4126
- console.error(
4127
- formatWarning(`Skipped widget screenshot: ${screenshotError}`)
4128
- );
4129
- }
4130
- if (widgetHintUri) {
4131
- console.error(
4132
- formatInfo(
4133
- `This tool renders a widget (${widgetHintUri}). Re-run with --screenshot to save a PNG of it.`
4134
- )
4135
- );
4136
- }
4137
4154
  if (callResult.isError) {
4138
4155
  await cleanupAndExit(1);
4139
4156
  }
@@ -4225,6 +4242,14 @@ async function subscribeResourceCommand(name, uri) {
4225
4242
  }
4226
4243
  });
4227
4244
  console.log(formatInfo("Listening for updates... (Press Ctrl+C to stop)"));
4245
+ process.once("SIGINT", async () => {
4246
+ console.log(formatInfo("\nUnsubscribing and shutting down..."));
4247
+ try {
4248
+ await session.unsubscribeFromResource(uri);
4249
+ } catch (e) {
4250
+ }
4251
+ await cleanupAndExit(0);
4252
+ });
4228
4253
  await new Promise(() => {
4229
4254
  });
4230
4255
  } catch (error) {
@@ -4355,7 +4380,7 @@ async function interactiveCommand(name) {
4355
4380
  console.log(source_default.gray(" tools list - List available tools"));
4356
4381
  console.log(
4357
4382
  source_default.gray(
4358
- " tools call <name> - Call a tool (will prompt for args)"
4383
+ " tools call <name> [--screenshot] - Call a tool (will prompt for args)"
4359
4384
  )
4360
4385
  );
4361
4386
  console.log(source_default.gray(" tools describe <name> - Show tool details"));
@@ -4402,6 +4427,7 @@ async function interactiveCommand(name) {
4402
4427
  )
4403
4428
  );
4404
4429
  } else if (command === "call" && arg) {
4430
+ const wantsScreenshot = parts.includes("--screenshot");
4405
4431
  rl.question(
4406
4432
  "Arguments (JSON, or press Enter for none): ",
4407
4433
  async (argsInput) => {
@@ -4409,6 +4435,9 @@ async function interactiveCommand(name) {
4409
4435
  const args = argsInput.trim() ? JSON.parse(argsInput) : {};
4410
4436
  const result2 = await session.callTool(arg, args);
4411
4437
  console.log(formatToolCall(result2));
4438
+ await processToolScreenshot(session, arg, args, result2, {
4439
+ screenshot: wantsScreenshot
4440
+ });
4412
4441
  } catch (error) {
4413
4442
  console.error(formatError(error.message));
4414
4443
  }
@@ -5236,12 +5265,7 @@ async function getGitHubConnectionStatusWith401Retry(api, options, orgIdToRestor
5236
5265
  throw new Error("Unreachable");
5237
5266
  }
5238
5267
  async function checkRepoAccess(api, owner, repo) {
5239
- try {
5240
- const resp = await api.getGitHubRepos(true);
5241
- return resp.repos.some((r) => r.full_name === `${owner}/${repo}`);
5242
- } catch {
5243
- return false;
5244
- }
5268
+ return api.checkGitHubRepoAccess(owner, repo);
5245
5269
  }
5246
5270
  async function promptGitHubInstallation(api, reason, repoName, opts) {
5247
5271
  const yes = !!opts?.yes;