@mcp-use/cli 3.1.0-canary.1 → 3.1.0-canary.3

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
@@ -509,8 +509,8 @@ import { spawn } from "child_process";
509
509
  import { readFileSync as readFileSync2 } from "fs";
510
510
  import { access, mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
511
511
  import { createRequire as createRequire2 } from "module";
512
- import path7 from "path";
513
- import { pathToFileURL } from "url";
512
+ import path8 from "path";
513
+ import { pathToFileURL as pathToFileURL2 } from "url";
514
514
 
515
515
  // ../../node_modules/.pnpm/open@11.0.0/node_modules/open/index.js
516
516
  import process9 from "process";
@@ -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 (path8) => {
700
- if (/^[a-z]+:\/\//i.test(path8)) {
701
- return path8;
699
+ var convertWslPathToWindows = async (path9) => {
700
+ if (/^[a-z]+:\/\//i.test(path9)) {
701
+ return path9;
702
702
  }
703
703
  try {
704
- const { stdout } = await execFile2("wslpath", ["-aw", path8], { encoding: "utf8" });
704
+ const { stdout } = await execFile2("wslpath", ["-aw", path9], { encoding: "utf8" });
705
705
  return stdout.trim();
706
706
  } catch {
707
- return path8;
707
+ return path9;
708
708
  }
709
709
  };
710
710
 
@@ -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 path8 = encodeURIComponent(idOrSlug);
1358
- return this.request(`/servers/${path8}`);
1357
+ const path9 = encodeURIComponent(idOrSlug);
1358
+ return this.request(`/servers/${path9}`);
1359
1359
  }
1360
1360
  async deleteServer(id) {
1361
1361
  await this.request(
@@ -1569,6 +1569,14 @@ async function pollForDeviceToken(authBaseUrl, deviceCode, intervalSeconds) {
1569
1569
  }
1570
1570
  throw new Error("Login timed out. Please try again.");
1571
1571
  }
1572
+ function resolveOrgFromOption(orgs, identifier) {
1573
+ const needle = identifier.trim();
1574
+ if (!needle) return null;
1575
+ const lower = needle.toLowerCase();
1576
+ return orgs.find(
1577
+ (o) => o.slug === needle || o.id === needle || o.name.toLowerCase() === lower
1578
+ ) ?? null;
1579
+ }
1572
1580
  async function promptOrgSelection(orgs, defaultOrgId) {
1573
1581
  if (orgs.length === 0) return null;
1574
1582
  if (orgs.length === 1) {
@@ -1615,8 +1623,8 @@ async function loginCommand(options) {
1615
1623
  console.log(source_default.green.bold("\u2713 API key saved."));
1616
1624
  try {
1617
1625
  const api2 = await McpUseAPI.create();
1618
- const authInfo = await api2.testAuth();
1619
- console.log(source_default.gray(` Authenticated as ${authInfo.email}`));
1626
+ const authInfo2 = await api2.testAuth();
1627
+ console.log(source_default.gray(` Authenticated as ${authInfo2.email}`));
1620
1628
  } catch {
1621
1629
  console.log(
1622
1630
  source_default.gray(
@@ -1668,9 +1676,19 @@ async function loginCommand(options) {
1668
1676
  const keyResp = await api.createApiKeyWithAccessToken(accessToken, "CLI");
1669
1677
  await writeConfig({ apiKey: keyResp.key });
1670
1678
  console.log(source_default.green.bold("\n\u2713 Successfully logged in!"));
1679
+ let authInfo = null;
1671
1680
  try {
1672
1681
  const freshApi = await McpUseAPI.create();
1673
- const authInfo = await freshApi.testAuth();
1682
+ authInfo = await freshApi.testAuth();
1683
+ } catch {
1684
+ console.log(
1685
+ source_default.gray(
1686
+ `
1687
+ Your API key has been saved to ${source_default.white("~/.mcp-use/config.json")}`
1688
+ )
1689
+ );
1690
+ }
1691
+ if (authInfo) {
1674
1692
  console.log(source_default.cyan.bold("\nCurrent user:\n"));
1675
1693
  console.log(source_default.white(" Email: ") + source_default.cyan(authInfo.email));
1676
1694
  console.log(source_default.white(" User ID: ") + source_default.gray(authInfo.user_id));
@@ -1682,8 +1700,19 @@ async function loginCommand(options) {
1682
1700
  const orgs = authInfo.orgs ?? [];
1683
1701
  if (orgs.length > 0) {
1684
1702
  let selectedOrg = null;
1685
- if (orgs.length === 1) {
1703
+ if (options?.org) {
1704
+ selectedOrg = resolveOrgFromOption(orgs, options.org);
1705
+ if (!selectedOrg) {
1706
+ throw new Error(
1707
+ `Organization "${options.org}" not found. Run 'npx mcp-use org list' after logging in to see available organizations.`
1708
+ );
1709
+ }
1710
+ } else if (orgs.length === 1) {
1686
1711
  selectedOrg = orgs[0];
1712
+ } else if (!process.stdin.isTTY) {
1713
+ throw new Error(
1714
+ "Multiple organizations available and no TTY for interactive selection. Re-run with --org <slug|id|name> to pick one non-interactively."
1715
+ );
1687
1716
  } else {
1688
1717
  selectedOrg = await promptOrgSelection(orgs, authInfo.default_org_id);
1689
1718
  }
@@ -1701,13 +1730,6 @@ async function loginCommand(options) {
1701
1730
  );
1702
1731
  }
1703
1732
  }
1704
- } catch {
1705
- console.log(
1706
- source_default.gray(
1707
- `
1708
- Your API key has been saved to ${source_default.white("~/.mcp-use/config.json")}`
1709
- )
1710
- );
1711
1733
  }
1712
1734
  console.log(
1713
1735
  source_default.gray(
@@ -3555,9 +3577,7 @@ async function deployCommand(options) {
3555
3577
  let resolvedOrgId;
3556
3578
  if (options.org) {
3557
3579
  const authInfo = await api.testAuth();
3558
- const match = (authInfo.orgs ?? []).find(
3559
- (o) => o.slug === options.org || o.id === options.org || o.name.toLowerCase() === options.org.toLowerCase()
3560
- );
3580
+ const match = resolveOrgFromOption(authInfo.orgs ?? [], options.org);
3561
3581
  if (match) {
3562
3582
  api.setOrgId(match.id);
3563
3583
  resolvedOrgId = match.id;
@@ -4781,9 +4801,7 @@ function pickStr(obj, key) {
4781
4801
  async function applyOrgOption(api, org) {
4782
4802
  if (!org) return;
4783
4803
  const authInfo = await api.testAuth();
4784
- const match = (authInfo.orgs ?? []).find(
4785
- (o) => o.slug === org || o.id === org || o.name.toLowerCase() === org.toLowerCase()
4786
- );
4804
+ const match = resolveOrgFromOption(authInfo.orgs ?? [], org);
4787
4805
  if (!match) {
4788
4806
  console.error(
4789
4807
  source_default.red(
@@ -5212,7 +5230,7 @@ async function addSkillsToProject(projectPath) {
5212
5230
  Readable.fromWeb(response.body),
5213
5231
  extract({
5214
5232
  cwd: tempDir,
5215
- filter: (path8) => path8.includes("/skills/"),
5233
+ filter: (path9) => path9.includes("/skills/"),
5216
5234
  strip: 1
5217
5235
  })
5218
5236
  );
@@ -5277,14 +5295,114 @@ function createSkillsCommand() {
5277
5295
  return skills;
5278
5296
  }
5279
5297
 
5298
+ // src/utils/next-shims.ts
5299
+ import { existsSync as existsSync3, promises as fs10 } from "fs";
5300
+ import path6 from "path";
5301
+ import { fileURLToPath as fileURLToPath3, pathToFileURL } from "url";
5302
+ async function detectNextJsProject(projectPath) {
5303
+ try {
5304
+ const pkgPath = path6.join(projectPath, "package.json");
5305
+ const content = await fs10.readFile(pkgPath, "utf-8");
5306
+ const pkg = JSON.parse(content);
5307
+ const deps = pkg.dependencies ?? {};
5308
+ const devDeps = pkg.devDependencies ?? {};
5309
+ return "next" in deps || "next" in devDeps;
5310
+ } catch {
5311
+ return false;
5312
+ }
5313
+ }
5314
+ async function loadNextJsEnvFiles(projectPath) {
5315
+ const files = [
5316
+ ".env",
5317
+ ".env.development",
5318
+ ".env.local",
5319
+ ".env.development.local"
5320
+ ];
5321
+ const dotenv = await import("dotenv");
5322
+ for (const file of files) {
5323
+ const abs = path6.join(projectPath, file);
5324
+ try {
5325
+ await fs10.access(abs);
5326
+ } catch {
5327
+ continue;
5328
+ }
5329
+ dotenv.config({ path: abs, override: true, quiet: true });
5330
+ }
5331
+ }
5332
+ function getThisDir() {
5333
+ if (typeof __dirname === "string") return __dirname;
5334
+ const url = import.meta.url;
5335
+ return path6.dirname(fileURLToPath3(url));
5336
+ }
5337
+ function resolveShimPath(filename) {
5338
+ const thisDir = getThisDir();
5339
+ const candidates = [
5340
+ // Production: `dist/` next to this module
5341
+ path6.join(thisDir, "shims", filename),
5342
+ // Test / dev: one level up (e.g., from `dist/utils/` back to `src/shims/`)
5343
+ path6.join(thisDir, "..", "shims", filename),
5344
+ path6.join(thisDir, "..", "..", "src", "shims", filename),
5345
+ path6.join(thisDir, "..", "src", "shims", filename)
5346
+ ];
5347
+ for (const candidate of candidates) {
5348
+ if (existsSync3(candidate)) return candidate;
5349
+ }
5350
+ return void 0;
5351
+ }
5352
+ function getShimRegisterPath() {
5353
+ return resolveShimPath("next-shims-register.mjs");
5354
+ }
5355
+ function getShimLoaderPath() {
5356
+ return resolveShimPath("next-shims-loader.mjs");
5357
+ }
5358
+ function getShimCjsPreloadPath() {
5359
+ return resolveShimPath("next-shims-cjs.cjs");
5360
+ }
5361
+ async function registerNextShimsInProcess() {
5362
+ let anyRegistered = false;
5363
+ const cjsPath = getShimCjsPreloadPath();
5364
+ if (cjsPath) {
5365
+ const { createRequire: createRequire3 } = await import("module");
5366
+ const req = createRequire3(pathToFileURL(getThisDir() + path6.sep).href);
5367
+ req(cjsPath);
5368
+ anyRegistered = true;
5369
+ }
5370
+ const loaderPath = getShimLoaderPath();
5371
+ if (loaderPath) {
5372
+ const { register } = await import("module");
5373
+ const loaderUrl = pathToFileURL(loaderPath).href;
5374
+ register(loaderUrl, pathToFileURL(getThisDir() + path6.sep).href);
5375
+ anyRegistered = true;
5376
+ }
5377
+ return anyRegistered;
5378
+ }
5379
+ function withNextShimsEnv(baseEnv) {
5380
+ const additions = [];
5381
+ const cjsPath = getShimCjsPreloadPath();
5382
+ if (cjsPath) additions.push(`-r ${quoteNodeOption(cjsPath)}`);
5383
+ const registerPath = getShimRegisterPath();
5384
+ if (registerPath)
5385
+ additions.push(`--import=${pathToFileURL(registerPath).href}`);
5386
+ if (additions.length === 0) return baseEnv;
5387
+ const existing = baseEnv.NODE_OPTIONS ?? "";
5388
+ const prepended = additions.join(" ");
5389
+ return {
5390
+ ...baseEnv,
5391
+ NODE_OPTIONS: existing ? `${prepended} ${existing}` : prepended
5392
+ };
5393
+ }
5394
+ function quoteNodeOption(value) {
5395
+ return /\s/.test(value) ? `"${value}"` : value;
5396
+ }
5397
+
5280
5398
  // src/utils/update-check.ts
5281
5399
  import { readFileSync } from "fs";
5282
5400
  import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
5283
5401
  import { createRequire } from "module";
5284
5402
  import os4 from "os";
5285
- import path6 from "path";
5286
- var CACHE_DIR = path6.join(os4.homedir(), ".mcp-use");
5287
- var CACHE_FILE = path6.join(CACHE_DIR, "update-check.json");
5403
+ import path7 from "path";
5404
+ var CACHE_DIR = path7.join(os4.homedir(), ".mcp-use");
5405
+ var CACHE_FILE = path7.join(CACHE_DIR, "update-check.json");
5288
5406
  var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
5289
5407
  var FETCH_TIMEOUT_MS = 3e3;
5290
5408
  var PACKAGE_NAME = "mcp-use";
@@ -5367,12 +5485,12 @@ function resolveInstalledVersion(projectPath) {
5367
5485
  if (projectPath) {
5368
5486
  attempts.push(() => {
5369
5487
  const projectRequire = createRequire(
5370
- path6.join(projectPath, "package.json")
5488
+ path7.join(projectPath, "package.json")
5371
5489
  );
5372
5490
  return projectRequire.resolve(`${PACKAGE_NAME}/package.json`);
5373
5491
  });
5374
5492
  }
5375
- attempts.push(() => path6.join(__dirname, "../../mcp-use/package.json"));
5493
+ attempts.push(() => path7.join(__dirname, "../../mcp-use/package.json"));
5376
5494
  for (const attempt of attempts) {
5377
5495
  try {
5378
5496
  const pkgPath = attempt();
@@ -5410,7 +5528,7 @@ A new release of ${source_default.bold(PACKAGE_NAME)} is available: ${source_def
5410
5528
  // src/index.ts
5411
5529
  var program = new Command6();
5412
5530
  var packageContent = readFileSync2(
5413
- path7.join(__dirname, "../package.json"),
5531
+ path8.join(__dirname, "../package.json"),
5414
5532
  "utf-8"
5415
5533
  );
5416
5534
  var packageJson = JSON.parse(packageContent);
@@ -5441,14 +5559,14 @@ function displayPackageVersions(projectPath) {
5441
5559
  if (projectPath) {
5442
5560
  try {
5443
5561
  const projectRequire = createRequire2(
5444
- path7.join(projectPath, "package.json")
5562
+ path8.join(projectPath, "package.json")
5445
5563
  );
5446
5564
  pkgPath = projectRequire.resolve(`${pkg.name}/package.json`);
5447
5565
  } catch (resolveError) {
5448
- pkgPath = path7.join(__dirname, pkg.relativePath);
5566
+ pkgPath = path8.join(__dirname, pkg.relativePath);
5449
5567
  }
5450
5568
  } else {
5451
- pkgPath = path7.join(__dirname, pkg.relativePath);
5569
+ pkgPath = path8.join(__dirname, pkg.relativePath);
5452
5570
  }
5453
5571
  const pkgContent = readFileSync2(pkgPath, "utf-8");
5454
5572
  const pkgJson = JSON.parse(pkgContent);
@@ -5600,20 +5718,92 @@ async function startTunnel(port, subdomain) {
5600
5718
  }, 3e4);
5601
5719
  });
5602
5720
  }
5603
- async function findServerFile(projectPath) {
5721
+ async function resolveEntryFile(projectPath, cliEntry, mcpDir) {
5722
+ if (cliEntry) {
5723
+ await access(path8.join(projectPath, cliEntry)).catch(() => {
5724
+ throw new Error(`File not found: ${cliEntry}`);
5725
+ });
5726
+ return cliEntry;
5727
+ }
5728
+ if (mcpDir) {
5729
+ const mcpCandidates = [
5730
+ path8.join(mcpDir, "index.ts"),
5731
+ path8.join(mcpDir, "index.tsx"),
5732
+ path8.join(mcpDir, "server.ts"),
5733
+ path8.join(mcpDir, "server.tsx")
5734
+ ];
5735
+ for (const candidate of mcpCandidates) {
5736
+ try {
5737
+ await access(path8.join(projectPath, candidate));
5738
+ return candidate;
5739
+ } catch {
5740
+ continue;
5741
+ }
5742
+ }
5743
+ throw new Error(
5744
+ `No entry file found inside ${mcpDir}.
5745
+
5746
+ Expected one of: ${mcpCandidates.map((c) => path8.relative(projectPath, path8.join(projectPath, c))).join(", ")}
5747
+
5748
+ Fix this by either:
5749
+ 1. Creating ${path8.join(mcpDir, "index.ts")}, or
5750
+ 2. Passing --entry <file> on the command line`
5751
+ );
5752
+ }
5604
5753
  const candidates = ["index.ts", "src/index.ts", "server.ts", "src/server.ts"];
5605
5754
  for (const candidate of candidates) {
5606
5755
  try {
5607
- await access(path7.join(projectPath, candidate));
5756
+ await access(path8.join(projectPath, candidate));
5608
5757
  return candidate;
5609
5758
  } catch {
5610
5759
  continue;
5611
5760
  }
5612
5761
  }
5613
- throw new Error("No server file found");
5762
+ throw new Error(
5763
+ `No entry file found.
5764
+
5765
+ Expected one of: ${candidates.join(", ")}
5766
+
5767
+ Fix this by either:
5768
+ 1. Creating one of the default entry files above, or
5769
+ 2. Passing --entry <file> or --mcp-dir <dir> on the command line`
5770
+ );
5771
+ }
5772
+ function resolveWidgetsDir(cliWidgetsDir, mcpDir) {
5773
+ if (cliWidgetsDir) return cliWidgetsDir;
5774
+ if (mcpDir) return path8.join(mcpDir, "resources");
5775
+ return "resources";
5776
+ }
5777
+ function makeWidgetServerOnlyGuard(widgetName) {
5778
+ const rejected = /* @__PURE__ */ new Set([
5779
+ "server-only",
5780
+ "client-only",
5781
+ "next/cache",
5782
+ "next/headers",
5783
+ "next/navigation",
5784
+ "next/server"
5785
+ ]);
5786
+ return {
5787
+ name: "mcp-use-widget-server-only-guard",
5788
+ enforce: "pre",
5789
+ resolveId(id, importer) {
5790
+ if (!rejected.has(id)) return null;
5791
+ const from = importer ? ` (imported from ${importer})` : "";
5792
+ throw new Error(
5793
+ `Widget "${widgetName}" imports "${id}"${from}, which is a Next.js server-only module. Widgets run in a browser iframe and cannot use server APIs.
5794
+
5795
+ To fix:
5796
+ \u2022 Remove the import from the widget (or from any module the widget transitively imports)
5797
+ \u2022 If the widget needs data from ${id}, read it inside an MCP tool in your server and pass the result through the widget's props`
5798
+ );
5799
+ }
5800
+ };
5801
+ }
5802
+ async function findServerFile(projectPath, cliEntry, cliMcpDir) {
5803
+ return resolveEntryFile(projectPath, cliEntry, cliMcpDir);
5614
5804
  }
5615
5805
  async function generateToolRegistryTypesForServer(projectPath, serverFileRelative) {
5616
- const serverFile = path7.join(projectPath, serverFileRelative);
5806
+ const serverFile = path8.join(projectPath, serverFileRelative);
5617
5807
  const serverFileExists = await access(serverFile).then(() => true).catch(() => false);
5618
5808
  if (!serverFileExists) {
5619
5809
  throw new Error(`Server file not found: ${serverFile}`);
@@ -5622,19 +5812,48 @@ async function generateToolRegistryTypesForServer(projectPath, serverFileRelativ
5622
5812
  try {
5623
5813
  globalThis.__mcpUseHmrMode = true;
5624
5814
  globalThis.__mcpUseLastServer = void 0;
5625
- const { tsImport } = await import("tsx/esm/api");
5626
- await tsImport(pathToFileURL(serverFile).href, {
5627
- parentURL: import.meta.url,
5628
- tsconfig: path7.join(projectPath, "tsconfig.json")
5629
- });
5815
+ if (await detectNextJsProject(projectPath)) {
5816
+ await loadNextJsEnvFiles(projectPath);
5817
+ await registerNextShimsInProcess();
5818
+ }
5819
+ const projectTsconfigPath = path8.join(projectPath, "tsconfig.json");
5820
+ const hasTsconfig = await access(projectTsconfigPath).then(() => true).catch(() => false);
5821
+ if (hasTsconfig) {
5822
+ process.env.TSX_TSCONFIG_PATH = projectTsconfigPath;
5823
+ }
5824
+ const previousCwd = process.cwd();
5825
+ if (previousCwd !== projectPath) process.chdir(projectPath);
5826
+ try {
5827
+ const projectRequire = createRequire2(
5828
+ path8.join(projectPath, "package.json")
5829
+ );
5830
+ const tsxEsmApiPath = projectRequire.resolve("tsx/esm/api");
5831
+ const tsxEsmApi = await import(pathToFileURL2(tsxEsmApiPath).href);
5832
+ if (typeof tsxEsmApi.register === "function") {
5833
+ tsxEsmApi.register({
5834
+ tsconfig: hasTsconfig ? projectTsconfigPath : void 0
5835
+ });
5836
+ }
5837
+ try {
5838
+ const tsxCjsApiPath = projectRequire.resolve("tsx/cjs/api");
5839
+ const tsxCjsApi = await import(pathToFileURL2(tsxCjsApiPath).href);
5840
+ if (typeof tsxCjsApi.register === "function") {
5841
+ tsxCjsApi.register();
5842
+ }
5843
+ } catch {
5844
+ }
5845
+ await import(`${pathToFileURL2(serverFile).href}?t=${Date.now()}`);
5846
+ } finally {
5847
+ if (process.cwd() !== previousCwd) process.chdir(previousCwd);
5848
+ }
5630
5849
  const server = globalThis.__mcpUseLastServer;
5631
5850
  if (!server) {
5632
5851
  throw new Error(
5633
5852
  "No MCPServer instance found. Make sure your server file creates an MCPServer instance."
5634
5853
  );
5635
5854
  }
5636
- const mcpUsePath = path7.join(projectPath, "node_modules", "mcp-use");
5637
- const { generateToolRegistryTypes } = await import(pathToFileURL(path7.join(mcpUsePath, "dist", "src", "server", "index.js")).href).then((mod) => mod);
5855
+ const mcpUsePath = path8.join(projectPath, "node_modules", "mcp-use");
5856
+ const { generateToolRegistryTypes } = await import(pathToFileURL2(path8.join(mcpUsePath, "dist", "src", "server", "index.js")).href).then((mod) => mod);
5638
5857
  if (!generateToolRegistryTypes) {
5639
5858
  throw new Error("generateToolRegistryTypes not found in mcp-use package");
5640
5859
  }
@@ -5649,21 +5868,24 @@ async function generateToolRegistryTypesForServer(projectPath, serverFileRelativ
5649
5868
  }
5650
5869
  async function buildWidgets(projectPath, options = {}) {
5651
5870
  const { inline = true } = options;
5652
- const { promises: fs10 } = await import("fs");
5871
+ const { promises: fs11 } = await import("fs");
5653
5872
  const { build } = await import("vite");
5654
- const resourcesDir = path7.join(projectPath, "resources");
5873
+ const widgetsDirRelative = options.widgetsDir ?? "resources";
5874
+ const resourcesDir = path8.resolve(projectPath, widgetsDirRelative);
5655
5875
  const mcpUrl = process.env.MCP_URL;
5656
5876
  try {
5657
5877
  await access(resourcesDir);
5658
5878
  } catch {
5659
5879
  console.log(
5660
- source_default.gray("No resources/ directory found - skipping widget build")
5880
+ source_default.gray(
5881
+ `No ${widgetsDirRelative}/ directory found - skipping widget build`
5882
+ )
5661
5883
  );
5662
5884
  return [];
5663
5885
  }
5664
5886
  const entries = [];
5665
5887
  try {
5666
- const files = await fs10.readdir(resourcesDir, { withFileTypes: true });
5888
+ const files = await fs11.readdir(resourcesDir, { withFileTypes: true });
5667
5889
  for (const dirent of files) {
5668
5890
  if (dirent.name.startsWith("._") || dirent.name.startsWith(".DS_Store")) {
5669
5891
  continue;
@@ -5671,12 +5893,12 @@ async function buildWidgets(projectPath, options = {}) {
5671
5893
  if (dirent.isFile() && (dirent.name.endsWith(".tsx") || dirent.name.endsWith(".ts"))) {
5672
5894
  entries.push({
5673
5895
  name: dirent.name.replace(/\.tsx?$/, ""),
5674
- path: path7.join(resourcesDir, dirent.name)
5896
+ path: path8.join(resourcesDir, dirent.name)
5675
5897
  });
5676
5898
  } else if (dirent.isDirectory()) {
5677
- const widgetPath = path7.join(resourcesDir, dirent.name, "widget.tsx");
5899
+ const widgetPath = path8.join(resourcesDir, dirent.name, "widget.tsx");
5678
5900
  try {
5679
- await fs10.access(widgetPath);
5901
+ await fs11.access(widgetPath);
5680
5902
  entries.push({
5681
5903
  name: dirent.name,
5682
5904
  path: widgetPath
@@ -5686,11 +5908,15 @@ async function buildWidgets(projectPath, options = {}) {
5686
5908
  }
5687
5909
  }
5688
5910
  } catch (error) {
5689
- console.log(source_default.gray("No widgets found in resources/ directory"));
5911
+ console.log(
5912
+ source_default.gray(`No widgets found in ${widgetsDirRelative}/ directory`)
5913
+ );
5690
5914
  return [];
5691
5915
  }
5692
5916
  if (entries.length === 0) {
5693
- console.log(source_default.gray("No widgets found in resources/ directory"));
5917
+ console.log(
5918
+ source_default.gray(`No widgets found in ${widgetsDirRelative}/ directory`)
5919
+ );
5694
5920
  return [];
5695
5921
  }
5696
5922
  console.log(
@@ -5700,10 +5926,17 @@ async function buildWidgets(projectPath, options = {}) {
5700
5926
  );
5701
5927
  const react = (await import("@vitejs/plugin-react")).default;
5702
5928
  const tailwindcss = (await import("@tailwindcss/vite")).default;
5703
- const packageJsonPath = path7.join(projectPath, "package.json");
5929
+ const projectTsconfigPath = path8.join(projectPath, "tsconfig.json");
5930
+ let hasProjectTsconfig = false;
5931
+ try {
5932
+ await access(projectTsconfigPath);
5933
+ hasProjectTsconfig = true;
5934
+ } catch {
5935
+ }
5936
+ const packageJsonPath = path8.join(projectPath, "package.json");
5704
5937
  let favicon = "";
5705
5938
  try {
5706
- const pkgContent = await fs10.readFile(packageJsonPath, "utf-8");
5939
+ const pkgContent = await fs11.readFile(packageJsonPath, "utf-8");
5707
5940
  const pkg = JSON.parse(pkgContent);
5708
5941
  favicon = pkg.mcpUse?.favicon || "";
5709
5942
  } catch {
@@ -5712,18 +5945,27 @@ async function buildWidgets(projectPath, options = {}) {
5712
5945
  const widgetName = entry.name;
5713
5946
  const entryPath = entry.path.replace(/\\/g, "/");
5714
5947
  console.log(source_default.gray(` - Building ${widgetName}...`));
5715
- const tempDir = path7.join(projectPath, ".mcp-use", widgetName);
5716
- await fs10.mkdir(tempDir, { recursive: true });
5717
- const relativeResourcesPath = path7.relative(tempDir, resourcesDir).replace(/\\/g, "/");
5718
- const mcpUsePath = path7.join(projectPath, "node_modules", "mcp-use");
5719
- const relativeMcpUsePath = path7.relative(tempDir, mcpUsePath).replace(/\\/g, "/");
5948
+ const tempDir = path8.join(projectPath, ".mcp-use", widgetName);
5949
+ await fs11.mkdir(tempDir, { recursive: true });
5950
+ const relativeResourcesPath = path8.relative(tempDir, resourcesDir).replace(/\\/g, "/");
5951
+ const mcpUsePath = path8.join(projectPath, "node_modules", "mcp-use");
5952
+ const relativeMcpUsePath = path8.relative(tempDir, mcpUsePath).replace(/\\/g, "/");
5953
+ const projectSrcDir = path8.join(projectPath, "src");
5954
+ let projectSrcSourceLine = "";
5955
+ try {
5956
+ await access(projectSrcDir);
5957
+ const relativeProjectSrcPath = path8.relative(tempDir, projectSrcDir).replace(/\\/g, "/");
5958
+ projectSrcSourceLine = `@source "${relativeProjectSrcPath}";
5959
+ `;
5960
+ } catch {
5961
+ }
5720
5962
  const cssContent = `@import "tailwindcss";
5721
5963
 
5722
5964
  /* Configure Tailwind to scan the resources directory and mcp-use package */
5723
5965
  @source "${relativeResourcesPath}";
5724
5966
  @source "${relativeMcpUsePath}/**/*.{ts,tsx,js,jsx}";
5725
- `;
5726
- await fs10.writeFile(path7.join(tempDir, "styles.css"), cssContent, "utf8");
5967
+ ${projectSrcSourceLine}`;
5968
+ await fs11.writeFile(path8.join(tempDir, "styles.css"), cssContent, "utf8");
5727
5969
  const entryContent = `import React from 'react'
5728
5970
  import { createRoot } from 'react-dom/client'
5729
5971
  import './styles.css'
@@ -5748,9 +5990,9 @@ if (container && Component) {
5748
5990
  <script type="module" src="/entry.tsx"></script>
5749
5991
  </body>
5750
5992
  </html>`;
5751
- await fs10.writeFile(path7.join(tempDir, "entry.tsx"), entryContent, "utf8");
5752
- await fs10.writeFile(path7.join(tempDir, "index.html"), htmlContent, "utf8");
5753
- const outDir = path7.join(
5993
+ await fs11.writeFile(path8.join(tempDir, "entry.tsx"), entryContent, "utf8");
5994
+ await fs11.writeFile(path8.join(tempDir, "index.html"), htmlContent, "utf8");
5995
+ const outDir = path8.join(
5754
5996
  projectPath,
5755
5997
  "dist",
5756
5998
  "resources",
@@ -5760,12 +6002,12 @@ if (container && Component) {
5760
6002
  const baseUrl = mcpUrl ? `${mcpUrl}/${widgetName}/` : `/mcp-use/widgets/${widgetName}/`;
5761
6003
  let widgetMetadata = {};
5762
6004
  try {
5763
- const metadataTempDir = path7.join(
6005
+ const metadataTempDir = path8.join(
5764
6006
  projectPath,
5765
6007
  ".mcp-use",
5766
6008
  `${widgetName}-metadata`
5767
6009
  );
5768
- await fs10.mkdir(metadataTempDir, { recursive: true });
6010
+ await fs11.mkdir(metadataTempDir, { recursive: true });
5769
6011
  const { createServer } = await import("vite");
5770
6012
  const nodeStubsPlugin = {
5771
6013
  name: "node-stubs",
@@ -5793,15 +6035,16 @@ export default PostHog;
5793
6035
  return null;
5794
6036
  }
5795
6037
  };
6038
+ const serverOnlyGuard = makeWidgetServerOnlyGuard(widgetName);
5796
6039
  const metadataServer = await createServer({
5797
6040
  root: metadataTempDir,
5798
- cacheDir: path7.join(metadataTempDir, ".vite-cache"),
5799
- plugins: [nodeStubsPlugin, tailwindcss(), react()],
5800
- resolve: {
5801
- alias: {
5802
- "@": resourcesDir
5803
- }
5804
- },
6041
+ cacheDir: path8.join(metadataTempDir, ".vite-cache"),
6042
+ plugins: [serverOnlyGuard, nodeStubsPlugin, tailwindcss(), react()],
6043
+ // When the project has a tsconfig, enable Vite's native tsconfig-paths
6044
+ // resolver so `@/*` (or any custom alias) resolves through the
6045
+ // project's own paths config. Without a tsconfig, fall back to the
6046
+ // legacy hardcoded alias.
6047
+ resolve: hasProjectTsconfig ? { tsconfigPaths: true } : { alias: { "@": resourcesDir } },
5805
6048
  server: {
5806
6049
  middlewareMode: true
5807
6050
  },
@@ -5880,7 +6123,7 @@ export default PostHog;
5880
6123
  } finally {
5881
6124
  await metadataServer.close();
5882
6125
  try {
5883
- await fs10.rm(metadataTempDir, { recursive: true, force: true });
6126
+ await fs11.rm(metadataTempDir, { recursive: true, force: true });
5884
6127
  } catch {
5885
6128
  }
5886
6129
  }
@@ -5975,12 +6218,14 @@ export default {
5975
6218
  return null;
5976
6219
  }
5977
6220
  };
6221
+ const buildServerOnlyGuard = makeWidgetServerOnlyGuard(widgetName);
5978
6222
  const buildPlugins = inline ? [
6223
+ buildServerOnlyGuard,
5979
6224
  buildNodeStubsPlugin,
5980
6225
  tailwindcss(),
5981
6226
  react(),
5982
6227
  viteSingleFile({ removeViteModuleLoader: true })
5983
- ] : [buildNodeStubsPlugin, tailwindcss(), react()];
6228
+ ] : [buildServerOnlyGuard, buildNodeStubsPlugin, tailwindcss(), react()];
5984
6229
  await build({
5985
6230
  root: tempDir,
5986
6231
  base: baseUrl,
@@ -5999,11 +6244,10 @@ export default {
5999
6244
  }
6000
6245
  }
6001
6246
  },
6002
- resolve: {
6003
- alias: {
6004
- "@": resourcesDir
6005
- }
6006
- },
6247
+ // When a tsconfig exists, enable Vite's native `resolve.tsconfigPaths`
6248
+ // so the project's path aliases resolve naturally. Otherwise fall
6249
+ // back to the legacy `@` → resourcesDir alias.
6250
+ resolve: hasProjectTsconfig ? { tsconfigPaths: true } : { alias: { "@": resourcesDir } },
6007
6251
  optimizeDeps: {
6008
6252
  // Exclude Node.js-only packages from browser bundling
6009
6253
  exclude: ["posthog-node"]
@@ -6025,7 +6269,7 @@ export default {
6025
6269
  // Inline all assets under 100MB (effectively all)
6026
6270
  } : {},
6027
6271
  rolldownOptions: {
6028
- input: path7.join(tempDir, "index.html"),
6272
+ input: path8.join(tempDir, "index.html"),
6029
6273
  external: (id) => {
6030
6274
  return false;
6031
6275
  }
@@ -6033,12 +6277,12 @@ export default {
6033
6277
  }
6034
6278
  });
6035
6279
  try {
6036
- const assetsDir = path7.join(outDir, "assets");
6037
- const assetFiles = await fs10.readdir(assetsDir);
6280
+ const assetsDir = path8.join(outDir, "assets");
6281
+ const assetFiles = await fs11.readdir(assetsDir);
6038
6282
  const jsFiles = assetFiles.filter((f) => f.endsWith(".js"));
6039
6283
  for (const jsFile of jsFiles) {
6040
- const jsPath = path7.join(assetsDir, jsFile);
6041
- let content = await fs10.readFile(jsPath, "utf8");
6284
+ const jsPath = path8.join(assetsDir, jsFile);
6285
+ let content = await fs11.readFile(jsPath, "utf8");
6042
6286
  const zodConfigPatterns = [
6043
6287
  // Non-minified: export const globalConfig = {}
6044
6288
  /export\s+const\s+globalConfig\s*=\s*\{\s*\}/g,
@@ -6057,7 +6301,7 @@ export default {
6057
6301
  }
6058
6302
  }
6059
6303
  if (patched) {
6060
- await fs10.writeFile(jsPath, content, "utf8");
6304
+ await fs11.writeFile(jsPath, content, "utf8");
6061
6305
  console.log(source_default.gray(` \u2192 Patched Zod JIT in ${jsFile}`));
6062
6306
  }
6063
6307
  }
@@ -6069,8 +6313,8 @@ export default {
6069
6313
  const mcpServerUrl = process.env.MCP_SERVER_URL;
6070
6314
  if (mcpServerUrl) {
6071
6315
  try {
6072
- const htmlPath = path7.join(outDir, "index.html");
6073
- let html = await fs10.readFile(htmlPath, "utf8");
6316
+ const htmlPath = path8.join(outDir, "index.html");
6317
+ let html = await fs11.readFile(htmlPath, "utf8");
6074
6318
  const injectionScript = `<script>window.__getFile = (filename) => { return "${mcpUrl}/${widgetName}/"+filename }; window.__mcpPublicUrl = "${mcpServerUrl}/mcp-use/public"; window.__mcpPublicAssetsUrl = "${mcpUrl}/public";</script>`;
6075
6319
  if (!html.includes("window.__mcpPublicUrl")) {
6076
6320
  html = html.replace(
@@ -6091,7 +6335,7 @@ export default {
6091
6335
  <base href="${mcpServerUrl}">`
6092
6336
  );
6093
6337
  }
6094
- await fs10.writeFile(htmlPath, html, "utf8");
6338
+ await fs11.writeFile(htmlPath, html, "utf8");
6095
6339
  console.log(
6096
6340
  source_default.gray(` \u2192 Injected MCP_SERVER_URL into ${widgetName}`)
6097
6341
  );
@@ -6105,22 +6349,32 @@ export default {
6105
6349
  }
6106
6350
  }
6107
6351
  console.log(source_default.green(` \u2713 Built ${widgetName}`));
6108
- return { name: widgetName, metadata: widgetMetadata };
6352
+ return {
6353
+ status: "built",
6354
+ name: widgetName,
6355
+ metadata: widgetMetadata
6356
+ };
6109
6357
  } catch (error) {
6110
6358
  console.error(source_default.red(` \u2717 Failed to build ${widgetName}:`), error);
6111
- return null;
6359
+ return { status: "failed", name: widgetName };
6112
6360
  }
6113
6361
  };
6114
6362
  const buildResults = await Promise.all(
6115
6363
  entries.map((entry) => buildSingleWidget(entry))
6116
6364
  );
6117
- const builtWidgets = buildResults.filter(
6118
- (result) => result !== null
6365
+ const failed = buildResults.filter((r) => r.status === "failed");
6366
+ if (failed.length > 0) {
6367
+ const names = failed.map((f) => f.name).join(", ");
6368
+ throw new Error(
6369
+ `${failed.length} widget(s) failed to build: ${names}. See errors above.`
6370
+ );
6371
+ }
6372
+ return buildResults.flatMap(
6373
+ (r) => r.status === "built" ? [{ name: r.name, metadata: r.metadata }] : []
6119
6374
  );
6120
- return builtWidgets;
6121
6375
  }
6122
6376
  async function collectTsFiles(projectPath, includePatterns, excludePatterns) {
6123
- const { promises: fs10 } = await import("fs");
6377
+ const { promises: fs11 } = await import("fs");
6124
6378
  const literalFiles = [];
6125
6379
  const dirPrefixes = [];
6126
6380
  for (const pattern of includePatterns) {
@@ -6135,7 +6389,7 @@ async function collectTsFiles(projectPath, includePatterns, excludePatterns) {
6135
6389
  for (const file of literalFiles) {
6136
6390
  if (/\.tsx?$/.test(file) && !file.endsWith(".d.ts")) {
6137
6391
  try {
6138
- await access(path7.join(projectPath, file));
6392
+ await access(path8.join(projectPath, file));
6139
6393
  files.push(file);
6140
6394
  } catch {
6141
6395
  }
@@ -6143,13 +6397,13 @@ async function collectTsFiles(projectPath, includePatterns, excludePatterns) {
6143
6397
  }
6144
6398
  const excludeSet = new Set(excludePatterns.map((e) => e.replace(/\*+/g, "")));
6145
6399
  for (const prefix of dirPrefixes) {
6146
- const dirPath = path7.join(projectPath, prefix);
6400
+ const dirPath = path8.join(projectPath, prefix);
6147
6401
  try {
6148
- const entries = await fs10.readdir(dirPath, { recursive: true });
6402
+ const entries = await fs11.readdir(dirPath, { recursive: true });
6149
6403
  for (const entry of entries) {
6150
6404
  const entryStr = String(entry);
6151
- const rel = path7.join(prefix, entryStr);
6152
- if (/\.tsx?$/.test(entryStr) && !entryStr.endsWith(".d.ts") && !excludeSet.has(rel.split(path7.sep)[0])) {
6405
+ const rel = path8.join(prefix, entryStr);
6406
+ if (/\.tsx?$/.test(entryStr) && !entryStr.endsWith(".d.ts") && !excludeSet.has(rel.split(path8.sep)[0])) {
6153
6407
  files.push(rel);
6154
6408
  }
6155
6409
  }
@@ -6160,11 +6414,11 @@ async function collectTsFiles(projectPath, includePatterns, excludePatterns) {
6160
6414
  }
6161
6415
  async function transpileWithEsbuild(projectPath) {
6162
6416
  const esbuild = await import("esbuild");
6163
- const { promises: fs10 } = await import("fs");
6164
- const tsconfigPath = path7.join(projectPath, "tsconfig.json");
6417
+ const { promises: fs11 } = await import("fs");
6418
+ const tsconfigPath = path8.join(projectPath, "tsconfig.json");
6165
6419
  let tsconfig = {};
6166
6420
  try {
6167
- const raw = await fs10.readFile(tsconfigPath, "utf-8");
6421
+ const raw = await fs11.readFile(tsconfigPath, "utf-8");
6168
6422
  tsconfig = JSON.parse(raw);
6169
6423
  } catch {
6170
6424
  }
@@ -6191,10 +6445,10 @@ async function transpileWithEsbuild(projectPath) {
6191
6445
  const target = (compilerOptions.target || "ES2022").toLowerCase();
6192
6446
  const moduleStr = (compilerOptions.module || "ESNext").toLowerCase();
6193
6447
  const format = moduleStr.includes("commonjs") ? "cjs" : "esm";
6194
- const outbase = compilerOptions.rootDir ? path7.resolve(projectPath, compilerOptions.rootDir) : projectPath;
6448
+ const outbase = compilerOptions.rootDir ? path8.resolve(projectPath, compilerOptions.rootDir) : projectPath;
6195
6449
  await esbuild.build({
6196
- entryPoints: files.map((f) => path7.join(projectPath, f)),
6197
- outdir: path7.join(projectPath, outDir),
6450
+ entryPoints: files.map((f) => path8.join(projectPath, f)),
6451
+ outdir: path8.join(projectPath, outDir),
6198
6452
  outbase,
6199
6453
  bundle: false,
6200
6454
  format,
@@ -6206,21 +6460,42 @@ async function transpileWithEsbuild(projectPath) {
6206
6460
  logLevel: "warning"
6207
6461
  });
6208
6462
  }
6209
- program.command("build").description("Build TypeScript and MCP UI widgets").option("-p, --path <path>", "Path to project directory", process.cwd()).option("--with-inspector", "Include inspector in production build").option(
6463
+ program.command("build").description("Build TypeScript and MCP UI widgets").option("-p, --path <path>", "Path to project directory", process.cwd()).option(
6464
+ "--entry <file>",
6465
+ "Path to MCP server entry file (relative to project)"
6466
+ ).option(
6467
+ "--widgets-dir <dir>",
6468
+ "Path to widgets directory (relative to project)"
6469
+ ).option(
6470
+ "--mcp-dir <dir>",
6471
+ "Folder holding the MCP entry + resources (e.g. 'src/mcp' for Next.js apps)"
6472
+ ).option("--with-inspector", "Include inspector in production build").option(
6210
6473
  "--inline",
6211
6474
  "Inline all JS/CSS into HTML (required for VS Code MCP Apps)"
6212
6475
  ).option("--no-inline", "Keep JS/CSS as separate files (default)").option("--no-typecheck", "Skip TypeScript type checking (faster builds)").action(async (options) => {
6213
6476
  try {
6214
- const projectPath = path7.resolve(options.path);
6215
- const { promises: fs10 } = await import("fs");
6477
+ const projectPath = path8.resolve(options.path);
6478
+ const { promises: fs11 } = await import("fs");
6216
6479
  displayPackageVersions(projectPath);
6480
+ const mcpDir = options.mcpDir;
6481
+ const widgetsDir = resolveWidgetsDir(options.widgetsDir, mcpDir);
6217
6482
  const builtWidgets = await buildWidgets(projectPath, {
6218
- inline: options.inline ?? false
6483
+ inline: options.inline ?? false,
6484
+ widgetsDir
6219
6485
  });
6220
6486
  let sourceServerFile;
6221
6487
  try {
6222
- sourceServerFile = await findServerFile(projectPath);
6223
- } catch {
6488
+ sourceServerFile = await findServerFile(
6489
+ projectPath,
6490
+ options.entry,
6491
+ options.mcpDir
6492
+ );
6493
+ } catch (err) {
6494
+ console.log(
6495
+ source_default.yellow(
6496
+ `\u26A0 Could not locate a server entry file: ${err instanceof Error ? err.message : String(err)}`
6497
+ )
6498
+ );
6224
6499
  }
6225
6500
  if (sourceServerFile) {
6226
6501
  console.log(source_default.gray("Generating tool registry types..."));
@@ -6238,17 +6513,25 @@ program.command("build").description("Build TypeScript and MCP UI widgets").opti
6238
6513
  );
6239
6514
  }
6240
6515
  }
6241
- console.log(source_default.gray("Building TypeScript..."));
6242
- await transpileWithEsbuild(projectPath);
6243
- console.log(source_default.green("\u2713 TypeScript build complete!"));
6244
- if (options.typecheck !== false) {
6516
+ if (!mcpDir) {
6517
+ console.log(source_default.gray("Building TypeScript..."));
6518
+ await transpileWithEsbuild(projectPath);
6519
+ console.log(source_default.green("\u2713 TypeScript build complete!"));
6520
+ } else {
6521
+ console.log(
6522
+ source_default.gray(
6523
+ "Skipping TypeScript transpile (--mcp-dir mode runs source via tsx at start time)"
6524
+ )
6525
+ );
6526
+ }
6527
+ if (options.typecheck !== false && !mcpDir) {
6245
6528
  console.log(source_default.gray("Type checking..."));
6246
6529
  try {
6247
6530
  await runCommand(
6248
6531
  "node",
6249
6532
  [
6250
6533
  "--max-old-space-size=4096",
6251
- path7.join(
6534
+ path8.join(
6252
6535
  projectPath,
6253
6536
  "node_modules",
6254
6537
  "typescript",
@@ -6269,39 +6552,43 @@ program.command("build").description("Build TypeScript and MCP UI widgets").opti
6269
6552
  }
6270
6553
  let entryPoint;
6271
6554
  if (sourceServerFile) {
6272
- const baseName = path7.basename(sourceServerFile, ".ts") + ".js";
6273
- const possibleOutputs = [
6274
- `dist/${baseName}`,
6275
- // rootDir set to project root or src
6276
- `dist/src/${baseName}`,
6277
- // no rootDir, source in src/
6278
- `dist/${sourceServerFile.replace(/\.ts$/, ".js")}`
6279
- // exact path preserved
6280
- ];
6281
- for (const candidate of possibleOutputs) {
6282
- try {
6283
- await access(path7.join(projectPath, candidate));
6284
- entryPoint = candidate;
6285
- break;
6286
- } catch {
6287
- continue;
6555
+ if (mcpDir) {
6556
+ entryPoint = sourceServerFile;
6557
+ } else {
6558
+ const baseName = path8.basename(sourceServerFile, ".ts") + ".js";
6559
+ const possibleOutputs = [
6560
+ `dist/${baseName}`,
6561
+ // rootDir set to project root or src
6562
+ `dist/src/${baseName}`,
6563
+ // no rootDir, source in src/
6564
+ `dist/${sourceServerFile.replace(/\.ts$/, ".js")}`
6565
+ // exact path preserved
6566
+ ];
6567
+ for (const candidate of possibleOutputs) {
6568
+ try {
6569
+ await access(path8.join(projectPath, candidate));
6570
+ entryPoint = candidate;
6571
+ break;
6572
+ } catch {
6573
+ continue;
6574
+ }
6288
6575
  }
6289
6576
  }
6290
6577
  }
6291
- const publicDir = path7.join(projectPath, "public");
6578
+ const publicDir = path8.join(projectPath, "public");
6292
6579
  try {
6293
- await fs10.access(publicDir);
6580
+ await fs11.access(publicDir);
6294
6581
  console.log(source_default.gray("Copying public assets..."));
6295
- await fs10.cp(publicDir, path7.join(projectPath, "dist", "public"), {
6582
+ await fs11.cp(publicDir, path8.join(projectPath, "dist", "public"), {
6296
6583
  recursive: true
6297
6584
  });
6298
6585
  console.log(source_default.green("\u2713 Public assets copied"));
6299
6586
  } catch {
6300
6587
  }
6301
- const manifestPath = path7.join(projectPath, "dist", "mcp-use.json");
6588
+ const manifestPath = path8.join(projectPath, "dist", "mcp-use.json");
6302
6589
  let existingManifest = {};
6303
6590
  try {
6304
- const existingContent = await fs10.readFile(manifestPath, "utf-8");
6591
+ const existingContent = await fs11.readFile(manifestPath, "utf-8");
6305
6592
  existingManifest = JSON.parse(existingContent);
6306
6593
  } catch {
6307
6594
  }
@@ -6323,8 +6610,8 @@ program.command("build").description("Build TypeScript and MCP UI widgets").opti
6323
6610
  // Server entry point for `mcp-use start`
6324
6611
  widgets: widgetsData
6325
6612
  };
6326
- await fs10.mkdir(path7.dirname(manifestPath), { recursive: true });
6327
- await fs10.writeFile(
6613
+ await fs11.mkdir(path8.dirname(manifestPath), { recursive: true });
6614
+ await fs11.writeFile(
6328
6615
  manifestPath,
6329
6616
  JSON.stringify(manifest, null, 2),
6330
6617
  "utf8"
@@ -6344,14 +6631,23 @@ program.command("build").description("Build TypeScript and MCP UI widgets").opti
6344
6631
  process.exit(1);
6345
6632
  }
6346
6633
  });
6347
- program.command("dev").description("Run development server with auto-reload and inspector").option("-p, --path <path>", "Path to project directory", process.cwd()).option("--port <port>", "Server port", "3000").option(
6634
+ program.command("dev").description("Run development server with auto-reload and inspector").option("-p, --path <path>", "Path to project directory", process.cwd()).option(
6635
+ "--entry <file>",
6636
+ "Path to MCP server entry file (relative to project)"
6637
+ ).option(
6638
+ "--widgets-dir <dir>",
6639
+ "Path to widgets directory (relative to project)"
6640
+ ).option(
6641
+ "--mcp-dir <dir>",
6642
+ "Folder holding the MCP entry + resources (e.g. 'src/mcp' for Next.js apps)"
6643
+ ).option("--port <port>", "Server port", "3000").option(
6348
6644
  "--host <host>",
6349
6645
  "Server host (use 0.0.0.0 to listen on all interfaces)",
6350
6646
  "0.0.0.0"
6351
6647
  ).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) => {
6352
6648
  try {
6353
6649
  process.env.MCP_USE_CLI_DEV = "1";
6354
- const projectPath = path7.resolve(options.path);
6650
+ const projectPath = path8.resolve(options.path);
6355
6651
  let port = parseInt(options.port, 10);
6356
6652
  const host = options.host;
6357
6653
  const useHmr = options.hmr !== false;
@@ -6362,13 +6658,24 @@ program.command("dev").description("Run development server with auto-reload and
6362
6658
  console.log(source_default.green.bold(`\u2713 Using port ${availablePort} instead`));
6363
6659
  port = availablePort;
6364
6660
  }
6365
- const serverFile = await findServerFile(projectPath);
6661
+ const serverFile = await findServerFile(
6662
+ projectPath,
6663
+ options.entry,
6664
+ options.mcpDir
6665
+ );
6666
+ {
6667
+ const devMcpDir = options.mcpDir;
6668
+ const devWidgetsDir = resolveWidgetsDir(options.widgetsDir, devMcpDir);
6669
+ if (devWidgetsDir !== "resources") {
6670
+ process.env.MCP_USE_WIDGETS_DIR = devWidgetsDir;
6671
+ }
6672
+ }
6366
6673
  let tunnelProcess = void 0;
6367
6674
  let tunnelSubdomain = void 0;
6368
6675
  let tunnelUrl = void 0;
6369
6676
  if (options.tunnel) {
6370
6677
  try {
6371
- const manifestPath = path7.join(projectPath, "dist", "mcp-use.json");
6678
+ const manifestPath = path8.join(projectPath, "dist", "mcp-use.json");
6372
6679
  let existingSubdomain;
6373
6680
  try {
6374
6681
  const manifestContent = await readFile3(manifestPath, "utf-8");
@@ -6417,7 +6724,7 @@ program.command("dev").description("Run development server with auto-reload and
6417
6724
  manifest.tunnel = {};
6418
6725
  }
6419
6726
  manifest.tunnel.subdomain = tunnelSubdomain;
6420
- await mkdir3(path7.dirname(manifestPath), { recursive: true });
6727
+ await mkdir3(path8.dirname(manifestPath), { recursive: true });
6421
6728
  await writeFile3(
6422
6729
  manifestPath,
6423
6730
  JSON.stringify(manifest, null, 2),
@@ -6444,22 +6751,35 @@ program.command("dev").description("Run development server with auto-reload and
6444
6751
  } else if (!process.env.MCP_URL) {
6445
6752
  process.env.MCP_URL = mcpUrl;
6446
6753
  }
6754
+ const isNextJsProject = await detectNextJsProject(projectPath);
6755
+ if (isNextJsProject) {
6756
+ console.log(
6757
+ source_default.gray(
6758
+ "Next.js detected \u2014 installing server-runtime shims (server-only, next/cache, next/headers, next/navigation, next/server)"
6759
+ )
6760
+ );
6761
+ await loadNextJsEnvFiles(projectPath);
6762
+ }
6447
6763
  if (!useHmr) {
6448
6764
  console.log(source_default.gray("HMR disabled, using tsx watch (full restart)"));
6449
6765
  const processes = [];
6450
- const env2 = {
6766
+ const baseEnv = {
6767
+ // Inherit parent env (PATH, HOME, etc.) — without it, tsx can't
6768
+ // resolve its own tooling in some setups.
6769
+ ...process.env,
6451
6770
  PORT: String(port),
6452
6771
  HOST: host,
6453
6772
  NODE_ENV: "development",
6454
6773
  // Preserve user-provided MCP_URL (e.g., for reverse proxy setups)
6455
6774
  MCP_URL: process.env.MCP_URL || mcpUrl
6456
6775
  };
6776
+ const env2 = isNextJsProject ? withNextShimsEnv(baseEnv) : baseEnv;
6457
6777
  const { createRequire: createRequire4 } = await import("module");
6458
6778
  let cmd;
6459
6779
  let args;
6460
6780
  try {
6461
6781
  const projectRequire = createRequire4(
6462
- path7.join(projectPath, "package.json")
6782
+ path8.join(projectPath, "package.json")
6463
6783
  );
6464
6784
  const tsxPkgPath = projectRequire.resolve("tsx/package.json");
6465
6785
  const tsxPkg = JSON.parse(await readFile3(tsxPkgPath, "utf-8"));
@@ -6471,7 +6791,7 @@ program.command("dev").description("Run development server with auto-reload and
6471
6791
  } else {
6472
6792
  throw new Error("No bin field found in tsx package.json");
6473
6793
  }
6474
- const tsxBin = path7.resolve(path7.dirname(tsxPkgPath), binPath);
6794
+ const tsxBin = path8.resolve(path8.dirname(tsxPkgPath), binPath);
6475
6795
  cmd = "node";
6476
6796
  args = [tsxBin, "watch", serverFile];
6477
6797
  } catch (error) {
@@ -6546,18 +6866,56 @@ program.command("dev").description("Run development server with auto-reload and
6546
6866
  "HMR enabled - changes will hot reload without dropping connections"
6547
6867
  )
6548
6868
  );
6869
+ if (isNextJsProject) {
6870
+ const registered = await registerNextShimsInProcess();
6871
+ if (!registered) {
6872
+ console.warn(
6873
+ source_default.yellow(
6874
+ "[HMR] Warning: Next.js shim loader could not be found on disk. Importing server-only / next/cache / etc. will throw. Reinstall @mcp-use/cli to fix."
6875
+ )
6876
+ );
6877
+ }
6878
+ }
6549
6879
  const chokidarModule = await import("chokidar");
6550
6880
  const chokidar = chokidarModule.default || chokidarModule;
6551
- const { fileURLToPath: fileURLToPath3 } = await import("url");
6881
+ const { fileURLToPath: fileURLToPath4 } = await import("url");
6552
6882
  const { createRequire: createRequire3 } = await import("module");
6553
- let tsImport = null;
6883
+ const projectTsconfigPath = path8.join(projectPath, "tsconfig.json");
6884
+ let tsconfigAvailable = false;
6885
+ try {
6886
+ await access(projectTsconfigPath);
6887
+ tsconfigAvailable = true;
6888
+ process.env.TSX_TSCONFIG_PATH = projectTsconfigPath;
6889
+ if (process.cwd() !== projectPath) process.chdir(projectPath);
6890
+ } catch {
6891
+ }
6892
+ let tsxLoaderActive = false;
6554
6893
  try {
6555
6894
  const projectRequire = createRequire3(
6556
- path7.join(projectPath, "package.json")
6895
+ path8.join(projectPath, "package.json")
6557
6896
  );
6558
- const tsxApiPath = projectRequire.resolve("tsx/esm/api");
6559
- const tsxApi = await import(pathToFileURL(tsxApiPath).href);
6560
- tsImport = tsxApi.tsImport;
6897
+ const tsxEsmApiPath = projectRequire.resolve("tsx/esm/api");
6898
+ const tsxEsmApi = await import(pathToFileURL2(tsxEsmApiPath).href);
6899
+ if (typeof tsxEsmApi.register === "function") {
6900
+ tsxEsmApi.register({
6901
+ tsconfig: tsconfigAvailable ? projectTsconfigPath : void 0,
6902
+ onImport: (url) => {
6903
+ const filePath = url.startsWith("file://") ? fileURLToPath4(url) : url;
6904
+ if (!filePath.includes("node_modules") && filePath.startsWith(projectPath)) {
6905
+ console.debug(`[HMR] Loaded: ${url}`);
6906
+ }
6907
+ }
6908
+ });
6909
+ tsxLoaderActive = true;
6910
+ }
6911
+ try {
6912
+ const tsxCjsApiPath = projectRequire.resolve("tsx/cjs/api");
6913
+ const tsxCjsApi = await import(pathToFileURL2(tsxCjsApiPath).href);
6914
+ if (typeof tsxCjsApi.register === "function") {
6915
+ tsxCjsApi.register();
6916
+ }
6917
+ } catch {
6918
+ }
6561
6919
  } catch {
6562
6920
  console.log(
6563
6921
  source_default.yellow(
@@ -6565,25 +6923,15 @@ program.command("dev").description("Run development server with auto-reload and
6565
6923
  )
6566
6924
  );
6567
6925
  }
6568
- const serverFilePath = path7.join(projectPath, serverFile);
6569
- const serverFileUrl = pathToFileURL(serverFilePath).href;
6926
+ const serverFilePath = path8.join(projectPath, serverFile);
6927
+ const serverFileUrl = pathToFileURL2(serverFilePath).href;
6570
6928
  globalThis.__mcpUseHmrMode = true;
6571
6929
  const importServerModule = async () => {
6572
6930
  const previousServer = globalThis.__mcpUseLastServer;
6573
6931
  globalThis.__mcpUseLastServer = null;
6574
- if (tsImport) {
6575
- await tsImport(`${serverFileUrl}?t=${Date.now()}`, {
6576
- parentURL: import.meta.url,
6577
- onImport: (file) => {
6578
- const filePath = file.startsWith("file://") ? fileURLToPath3(file) : file;
6579
- if (!filePath.includes("node_modules") && filePath.startsWith(projectPath)) {
6580
- console.debug(`[HMR] Loaded: ${file}`);
6581
- }
6582
- }
6583
- });
6584
- } else {
6585
- await import(`${serverFileUrl}?t=${Date.now()}`);
6932
+ if (!tsxLoaderActive) {
6586
6933
  }
6934
+ await import(`${serverFileUrl}?t=${Date.now()}`);
6587
6935
  const instance = globalThis.__mcpUseLastServer;
6588
6936
  if (!instance) {
6589
6937
  globalThis.__mcpUseLastServer = previousServer;
@@ -6597,7 +6945,7 @@ program.command("dev").description("Run development server with auto-reload and
6597
6945
  if (instance === previousServer) {
6598
6946
  console.warn(
6599
6947
  source_default.yellow(
6600
- "[HMR] Warning: Module re-import returned the same server instance. The module may not have been re-evaluated. " + (!tsImport ? "Install tsx as a devDependency for reliable TypeScript HMR." : "This may be a tsx caching issue.")
6948
+ "[HMR] Warning: Module re-import returned the same server instance. The module may not have been re-evaluated. " + (!tsxLoaderActive ? "Install tsx as a devDependency for reliable TypeScript HMR." : "This may be a module cache issue.")
6601
6949
  )
6602
6950
  );
6603
6951
  return null;
@@ -6672,8 +7020,8 @@ program.command("dev").description("Run development server with auto-reload and
6672
7020
  }
6673
7021
  let watcher = chokidar.watch(".", {
6674
7022
  cwd: projectPath,
6675
- ignored: (path8, stats) => {
6676
- const normalizedPath = path8.replace(/\\/g, "/");
7023
+ ignored: (path9, stats) => {
7024
+ const normalizedPath = path9.replace(/\\/g, "/");
6677
7025
  if (/(^|\/)\.[^/]/.test(normalizedPath)) {
6678
7026
  return true;
6679
7027
  }
@@ -6847,7 +7195,7 @@ program.command("dev").description("Run development server with auto-reload and
6847
7195
  }
6848
7196
  tunnelUrl = void 0;
6849
7197
  if (withTunnel) {
6850
- const manifestPath = path7.join(projectPath, "dist", "mcp-use.json");
7198
+ const manifestPath = path8.join(projectPath, "dist", "mcp-use.json");
6851
7199
  let existingSubdomain;
6852
7200
  try {
6853
7201
  const manifestContent = await readFile3(manifestPath, "utf-8");
@@ -6884,7 +7232,7 @@ program.command("dev").description("Run development server with auto-reload and
6884
7232
  tunnelSubdomain = tunnelInfo.subdomain;
6885
7233
  process.env.MCP_URL = tunnelUrl;
6886
7234
  try {
6887
- const mPath = path7.join(projectPath, "dist", "mcp-use.json");
7235
+ const mPath = path8.join(projectPath, "dist", "mcp-use.json");
6888
7236
  let manifest = {};
6889
7237
  try {
6890
7238
  manifest = JSON.parse(await readFile3(mPath, "utf-8"));
@@ -6892,7 +7240,7 @@ program.command("dev").description("Run development server with auto-reload and
6892
7240
  }
6893
7241
  if (!manifest.tunnel) manifest.tunnel = {};
6894
7242
  manifest.tunnel.subdomain = tunnelSubdomain;
6895
- await mkdir3(path7.dirname(mPath), { recursive: true });
7243
+ await mkdir3(path8.dirname(mPath), { recursive: true });
6896
7244
  await writeFile3(mPath, JSON.stringify(manifest, null, 2), "utf-8");
6897
7245
  } catch {
6898
7246
  }
@@ -6988,9 +7336,15 @@ program.command("dev").description("Run development server with auto-reload and
6988
7336
  process.exit(1);
6989
7337
  }
6990
7338
  });
6991
- program.command("start").description("Start production server").option("-p, --path <path>", "Path to project directory", process.cwd()).option("--port <port>", "Server port", "3000").option("--tunnel", "Expose server through a tunnel").action(async (options) => {
7339
+ program.command("start").description("Start production server").option("-p, --path <path>", "Path to project directory", process.cwd()).option(
7340
+ "--entry <file>",
7341
+ "Path to MCP server entry file (relative to project)"
7342
+ ).option(
7343
+ "--mcp-dir <dir>",
7344
+ "Folder holding the MCP entry + resources (e.g. 'src/mcp' for Next.js apps)"
7345
+ ).option("--port <port>", "Server port", "3000").option("--tunnel", "Expose server through a tunnel").action(async (options) => {
6992
7346
  try {
6993
- const projectPath = path7.resolve(options.path);
7347
+ const projectPath = path8.resolve(options.path);
6994
7348
  const portFlagProvided = process.argv.includes("--port") || process.argv.includes("-p") || process.argv.some((arg) => arg.startsWith("--port=")) || process.argv.some((arg) => arg.startsWith("-p="));
6995
7349
  let port = portFlagProvided ? parseInt(options.port, 10) : parseInt(process.env.PORT || options.port || "3000", 10);
6996
7350
  if (!await isPortAvailable(port)) {
@@ -7008,7 +7362,7 @@ program.command("start").description("Start production server").option("-p, --pa
7008
7362
  let tunnelSubdomain = void 0;
7009
7363
  if (options.tunnel) {
7010
7364
  try {
7011
- const manifestPath2 = path7.join(projectPath, "dist", "mcp-use.json");
7365
+ const manifestPath2 = path8.join(projectPath, "dist", "mcp-use.json");
7012
7366
  let existingSubdomain;
7013
7367
  try {
7014
7368
  const manifestContent = await readFile3(manifestPath2, "utf-8");
@@ -7063,7 +7417,7 @@ program.command("start").description("Start production server").option("-p, --pa
7063
7417
  manifest.tunnel = {};
7064
7418
  }
7065
7419
  manifest.tunnel.subdomain = subdomain;
7066
- await mkdir3(path7.dirname(manifestPath2), { recursive: true });
7420
+ await mkdir3(path8.dirname(manifestPath2), { recursive: true });
7067
7421
  await writeFile3(
7068
7422
  manifestPath2,
7069
7423
  JSON.stringify(manifest, null, 2),
@@ -7082,18 +7436,25 @@ program.command("start").description("Start production server").option("-p, --pa
7082
7436
  }
7083
7437
  }
7084
7438
  let serverFile;
7085
- const manifestPath = path7.join(projectPath, "dist", "mcp-use.json");
7439
+ const manifestPath = path8.join(projectPath, "dist", "mcp-use.json");
7086
7440
  try {
7087
7441
  const manifestContent = await readFile3(manifestPath, "utf-8");
7088
7442
  const manifest = JSON.parse(manifestContent);
7089
7443
  if (manifest.entryPoint) {
7090
- await access(path7.join(projectPath, manifest.entryPoint));
7444
+ await access(path8.join(projectPath, manifest.entryPoint));
7091
7445
  serverFile = manifest.entryPoint;
7092
7446
  }
7093
7447
  } catch {
7094
7448
  }
7095
7449
  if (!serverFile) {
7450
+ const startMcpDir = options.mcpDir;
7096
7451
  const serverCandidates = [
7452
+ ...startMcpDir ? [
7453
+ `${startMcpDir}/index.ts`,
7454
+ `${startMcpDir}/index.tsx`,
7455
+ `dist/${startMcpDir}/index.js`,
7456
+ `dist/${startMcpDir}/server.js`
7457
+ ] : [],
7097
7458
  "dist/index.js",
7098
7459
  "dist/server.js",
7099
7460
  "dist/src/index.js",
@@ -7101,7 +7462,7 @@ program.command("start").description("Start production server").option("-p, --pa
7101
7462
  ];
7102
7463
  for (const candidate of serverCandidates) {
7103
7464
  try {
7104
- await access(path7.join(projectPath, candidate));
7465
+ await access(path8.join(projectPath, candidate));
7105
7466
  serverFile = candidate;
7106
7467
  break;
7107
7468
  } catch {
@@ -7125,18 +7486,53 @@ Looked for:
7125
7486
  process.exit(1);
7126
7487
  }
7127
7488
  console.log("Starting production server...");
7128
- const env2 = {
7489
+ const isNextJsProject = await detectNextJsProject(projectPath);
7490
+ if (isNextJsProject) {
7491
+ console.log(
7492
+ source_default.gray(
7493
+ "Next.js detected \u2014 installing server-runtime shims for the production server"
7494
+ )
7495
+ );
7496
+ await loadNextJsEnvFiles(projectPath);
7497
+ }
7498
+ const baseEnv = {
7129
7499
  ...process.env,
7130
7500
  PORT: String(port),
7131
7501
  NODE_ENV: "production"
7132
7502
  };
7133
7503
  if (mcpUrl) {
7134
- env2.MCP_URL = mcpUrl;
7504
+ baseEnv.MCP_URL = mcpUrl;
7135
7505
  console.log(source_default.whiteBright(`Tunnel: ${mcpUrl}/mcp`));
7136
- } else if (!env2.MCP_URL) {
7137
- env2.MCP_URL = `http://localhost:${port}`;
7506
+ } else if (!baseEnv.MCP_URL) {
7507
+ baseEnv.MCP_URL = `http://localhost:${port}`;
7508
+ }
7509
+ const env2 = isNextJsProject ? withNextShimsEnv(baseEnv) : baseEnv;
7510
+ const isTsEntry = /\.(ts|tsx|mts|cts)$/.test(serverFile);
7511
+ let spawnCmd = "node";
7512
+ let spawnArgs = [serverFile];
7513
+ if (isTsEntry) {
7514
+ try {
7515
+ const projectRequire = createRequire2(
7516
+ path8.join(projectPath, "package.json")
7517
+ );
7518
+ const tsxPkgPath = projectRequire.resolve("tsx/package.json");
7519
+ const tsxPkg = JSON.parse(await readFile3(tsxPkgPath, "utf-8"));
7520
+ const binField = typeof tsxPkg.bin === "string" ? tsxPkg.bin : tsxPkg.bin?.tsx ?? Object.values(tsxPkg.bin ?? {})[0];
7521
+ if (!binField) throw new Error("tsx bin entry not found");
7522
+ const tsxBin = path8.resolve(path8.dirname(tsxPkgPath), binField);
7523
+ spawnCmd = "node";
7524
+ spawnArgs = [tsxBin, serverFile];
7525
+ } catch (error) {
7526
+ console.log(
7527
+ source_default.yellow(
7528
+ `Could not resolve local tsx (${error instanceof Error ? error.message : String(error)}); falling back to npx`
7529
+ )
7530
+ );
7531
+ spawnCmd = "npx";
7532
+ spawnArgs = ["tsx", serverFile];
7533
+ }
7138
7534
  }
7139
- const serverProc = spawn("node", [serverFile], {
7535
+ const serverProc = spawn(spawnCmd, spawnArgs, {
7140
7536
  cwd: projectPath,
7141
7537
  stdio: "inherit",
7142
7538
  env: env2
@@ -7202,9 +7598,9 @@ Looked for:
7202
7598
  program.command("login").description("Login to mcp-use cloud").option(
7203
7599
  "--api-key <key>",
7204
7600
  "Login with an API key directly (non-interactive, for CI/CD)"
7205
- ).action(async (opts) => {
7601
+ ).option("--org <slug|id|name>", "Select an organization non-interactively").action(async (opts) => {
7206
7602
  try {
7207
- await loginCommand({ apiKey: opts.apiKey });
7603
+ await loginCommand({ apiKey: opts.apiKey, org: opts.org });
7208
7604
  process.exit(0);
7209
7605
  } catch (error) {
7210
7606
  console.error(
@@ -7272,7 +7668,7 @@ program.addCommand(createSkillsCommand());
7272
7668
  program.command("generate-types").description(
7273
7669
  "Generate TypeScript type definitions for tools (writes .mcp-use/tool-registry.d.ts)"
7274
7670
  ).option("-p, --path <path>", "Path to project directory", process.cwd()).option("--server <file>", "Server entry file", "index.ts").action(async (options) => {
7275
- const projectPath = path7.resolve(options.path);
7671
+ const projectPath = path8.resolve(options.path);
7276
7672
  try {
7277
7673
  console.log(source_default.blue("Generating tool registry types..."));
7278
7674
  const success = await generateToolRegistryTypesForServer(