@mcp-use/cli 3.1.0-canary.2 → 3.1.0-canary.4

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(
@@ -3575,12 +3575,16 @@ async function deployCommand(options) {
3575
3575
  }
3576
3576
  let api = await McpUseAPI.create();
3577
3577
  let resolvedOrgId;
3578
+ let resolvedOrgName;
3579
+ let resolvedOrgSlug;
3578
3580
  if (options.org) {
3579
3581
  const authInfo = await api.testAuth();
3580
3582
  const match = resolveOrgFromOption(authInfo.orgs ?? [], options.org);
3581
3583
  if (match) {
3582
3584
  api.setOrgId(match.id);
3583
3585
  resolvedOrgId = match.id;
3586
+ resolvedOrgName = match.name;
3587
+ resolvedOrgSlug = match.slug ?? void 0;
3584
3588
  const slug = match.slug ? source_default.gray(` (${match.slug})`) : "";
3585
3589
  console.log(
3586
3590
  source_default.gray("Organization: ") + source_default.cyan(match.name) + slug
@@ -3620,6 +3624,8 @@ async function deployCommand(options) {
3620
3624
  }
3621
3625
  api.setOrgId(selectedOrg.id);
3622
3626
  resolvedOrgId = selectedOrg.id;
3627
+ resolvedOrgName = selectedOrg.name;
3628
+ resolvedOrgSlug = selectedOrg.slug ?? void 0;
3623
3629
  await writeConfig({
3624
3630
  ...config,
3625
3631
  orgId: selectedOrg.id,
@@ -3631,6 +3637,8 @@ async function deployCommand(options) {
3631
3637
  );
3632
3638
  } else {
3633
3639
  resolvedOrgId = config.orgId;
3640
+ resolvedOrgName = config.orgName;
3641
+ resolvedOrgSlug = config.orgSlug;
3634
3642
  api.setOrgId(config.orgId);
3635
3643
  if (config.orgName) {
3636
3644
  const slug = config.orgSlug ? source_default.gray(` (${config.orgSlug})`) : "";
@@ -4027,6 +4035,22 @@ async function deployCommand(options) {
4027
4035
  }
4028
4036
  const existingLink = !options.new ? await getProjectLink(cwd) : null;
4029
4037
  let serverId = existingLink?.serverId;
4038
+ if (serverId && resolvedOrgId) {
4039
+ try {
4040
+ const linkedServer = await api.getServer(serverId);
4041
+ if (linkedServer.organizationId !== resolvedOrgId) {
4042
+ const target = resolvedOrgName ? `${resolvedOrgName}${resolvedOrgSlug ? ` (${resolvedOrgSlug})` : ""}` : resolvedOrgId;
4043
+ console.log(
4044
+ source_default.yellow(
4045
+ `\u26A0\uFE0F Linked server belongs to a different organization. Creating a new server in ${target}...
4046
+ `
4047
+ )
4048
+ );
4049
+ serverId = void 0;
4050
+ }
4051
+ } catch {
4052
+ }
4053
+ }
4030
4054
  if (existingLink && serverId) {
4031
4055
  try {
4032
4056
  const existingDep = await api.getDeployment(existingLink.deploymentId);
@@ -5230,7 +5254,7 @@ async function addSkillsToProject(projectPath) {
5230
5254
  Readable.fromWeb(response.body),
5231
5255
  extract({
5232
5256
  cwd: tempDir,
5233
- filter: (path8) => path8.includes("/skills/"),
5257
+ filter: (path9) => path9.includes("/skills/"),
5234
5258
  strip: 1
5235
5259
  })
5236
5260
  );
@@ -5295,14 +5319,114 @@ function createSkillsCommand() {
5295
5319
  return skills;
5296
5320
  }
5297
5321
 
5322
+ // src/utils/next-shims.ts
5323
+ import { existsSync as existsSync3, promises as fs10 } from "fs";
5324
+ import path6 from "path";
5325
+ import { fileURLToPath as fileURLToPath3, pathToFileURL } from "url";
5326
+ async function detectNextJsProject(projectPath) {
5327
+ try {
5328
+ const pkgPath = path6.join(projectPath, "package.json");
5329
+ const content = await fs10.readFile(pkgPath, "utf-8");
5330
+ const pkg = JSON.parse(content);
5331
+ const deps = pkg.dependencies ?? {};
5332
+ const devDeps = pkg.devDependencies ?? {};
5333
+ return "next" in deps || "next" in devDeps;
5334
+ } catch {
5335
+ return false;
5336
+ }
5337
+ }
5338
+ async function loadNextJsEnvFiles(projectPath) {
5339
+ const files = [
5340
+ ".env",
5341
+ ".env.development",
5342
+ ".env.local",
5343
+ ".env.development.local"
5344
+ ];
5345
+ const dotenv = await import("dotenv");
5346
+ for (const file of files) {
5347
+ const abs = path6.join(projectPath, file);
5348
+ try {
5349
+ await fs10.access(abs);
5350
+ } catch {
5351
+ continue;
5352
+ }
5353
+ dotenv.config({ path: abs, override: true, quiet: true });
5354
+ }
5355
+ }
5356
+ function getThisDir() {
5357
+ if (typeof __dirname === "string") return __dirname;
5358
+ const url = import.meta.url;
5359
+ return path6.dirname(fileURLToPath3(url));
5360
+ }
5361
+ function resolveShimPath(filename) {
5362
+ const thisDir = getThisDir();
5363
+ const candidates = [
5364
+ // Production: `dist/` next to this module
5365
+ path6.join(thisDir, "shims", filename),
5366
+ // Test / dev: one level up (e.g., from `dist/utils/` back to `src/shims/`)
5367
+ path6.join(thisDir, "..", "shims", filename),
5368
+ path6.join(thisDir, "..", "..", "src", "shims", filename),
5369
+ path6.join(thisDir, "..", "src", "shims", filename)
5370
+ ];
5371
+ for (const candidate of candidates) {
5372
+ if (existsSync3(candidate)) return candidate;
5373
+ }
5374
+ return void 0;
5375
+ }
5376
+ function getShimRegisterPath() {
5377
+ return resolveShimPath("next-shims-register.mjs");
5378
+ }
5379
+ function getShimLoaderPath() {
5380
+ return resolveShimPath("next-shims-loader.mjs");
5381
+ }
5382
+ function getShimCjsPreloadPath() {
5383
+ return resolveShimPath("next-shims-cjs.cjs");
5384
+ }
5385
+ async function registerNextShimsInProcess() {
5386
+ let anyRegistered = false;
5387
+ const cjsPath = getShimCjsPreloadPath();
5388
+ if (cjsPath) {
5389
+ const { createRequire: createRequire3 } = await import("module");
5390
+ const req = createRequire3(pathToFileURL(getThisDir() + path6.sep).href);
5391
+ req(cjsPath);
5392
+ anyRegistered = true;
5393
+ }
5394
+ const loaderPath = getShimLoaderPath();
5395
+ if (loaderPath) {
5396
+ const { register } = await import("module");
5397
+ const loaderUrl = pathToFileURL(loaderPath).href;
5398
+ register(loaderUrl, pathToFileURL(getThisDir() + path6.sep).href);
5399
+ anyRegistered = true;
5400
+ }
5401
+ return anyRegistered;
5402
+ }
5403
+ function withNextShimsEnv(baseEnv) {
5404
+ const additions = [];
5405
+ const cjsPath = getShimCjsPreloadPath();
5406
+ if (cjsPath) additions.push(`-r ${quoteNodeOption(cjsPath)}`);
5407
+ const registerPath = getShimRegisterPath();
5408
+ if (registerPath)
5409
+ additions.push(`--import=${pathToFileURL(registerPath).href}`);
5410
+ if (additions.length === 0) return baseEnv;
5411
+ const existing = baseEnv.NODE_OPTIONS ?? "";
5412
+ const prepended = additions.join(" ");
5413
+ return {
5414
+ ...baseEnv,
5415
+ NODE_OPTIONS: existing ? `${prepended} ${existing}` : prepended
5416
+ };
5417
+ }
5418
+ function quoteNodeOption(value) {
5419
+ return /\s/.test(value) ? `"${value}"` : value;
5420
+ }
5421
+
5298
5422
  // src/utils/update-check.ts
5299
5423
  import { readFileSync } from "fs";
5300
5424
  import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
5301
5425
  import { createRequire } from "module";
5302
5426
  import os4 from "os";
5303
- import path6 from "path";
5304
- var CACHE_DIR = path6.join(os4.homedir(), ".mcp-use");
5305
- var CACHE_FILE = path6.join(CACHE_DIR, "update-check.json");
5427
+ import path7 from "path";
5428
+ var CACHE_DIR = path7.join(os4.homedir(), ".mcp-use");
5429
+ var CACHE_FILE = path7.join(CACHE_DIR, "update-check.json");
5306
5430
  var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
5307
5431
  var FETCH_TIMEOUT_MS = 3e3;
5308
5432
  var PACKAGE_NAME = "mcp-use";
@@ -5385,12 +5509,12 @@ function resolveInstalledVersion(projectPath) {
5385
5509
  if (projectPath) {
5386
5510
  attempts.push(() => {
5387
5511
  const projectRequire = createRequire(
5388
- path6.join(projectPath, "package.json")
5512
+ path7.join(projectPath, "package.json")
5389
5513
  );
5390
5514
  return projectRequire.resolve(`${PACKAGE_NAME}/package.json`);
5391
5515
  });
5392
5516
  }
5393
- attempts.push(() => path6.join(__dirname, "../../mcp-use/package.json"));
5517
+ attempts.push(() => path7.join(__dirname, "../../mcp-use/package.json"));
5394
5518
  for (const attempt of attempts) {
5395
5519
  try {
5396
5520
  const pkgPath = attempt();
@@ -5428,7 +5552,7 @@ A new release of ${source_default.bold(PACKAGE_NAME)} is available: ${source_def
5428
5552
  // src/index.ts
5429
5553
  var program = new Command6();
5430
5554
  var packageContent = readFileSync2(
5431
- path7.join(__dirname, "../package.json"),
5555
+ path8.join(__dirname, "../package.json"),
5432
5556
  "utf-8"
5433
5557
  );
5434
5558
  var packageJson = JSON.parse(packageContent);
@@ -5459,14 +5583,14 @@ function displayPackageVersions(projectPath) {
5459
5583
  if (projectPath) {
5460
5584
  try {
5461
5585
  const projectRequire = createRequire2(
5462
- path7.join(projectPath, "package.json")
5586
+ path8.join(projectPath, "package.json")
5463
5587
  );
5464
5588
  pkgPath = projectRequire.resolve(`${pkg.name}/package.json`);
5465
5589
  } catch (resolveError) {
5466
- pkgPath = path7.join(__dirname, pkg.relativePath);
5590
+ pkgPath = path8.join(__dirname, pkg.relativePath);
5467
5591
  }
5468
5592
  } else {
5469
- pkgPath = path7.join(__dirname, pkg.relativePath);
5593
+ pkgPath = path8.join(__dirname, pkg.relativePath);
5470
5594
  }
5471
5595
  const pkgContent = readFileSync2(pkgPath, "utf-8");
5472
5596
  const pkgJson = JSON.parse(pkgContent);
@@ -5618,20 +5742,92 @@ async function startTunnel(port, subdomain) {
5618
5742
  }, 3e4);
5619
5743
  });
5620
5744
  }
5621
- async function findServerFile(projectPath) {
5745
+ async function resolveEntryFile(projectPath, cliEntry, mcpDir) {
5746
+ if (cliEntry) {
5747
+ await access(path8.join(projectPath, cliEntry)).catch(() => {
5748
+ throw new Error(`File not found: ${cliEntry}`);
5749
+ });
5750
+ return cliEntry;
5751
+ }
5752
+ if (mcpDir) {
5753
+ const mcpCandidates = [
5754
+ path8.join(mcpDir, "index.ts"),
5755
+ path8.join(mcpDir, "index.tsx"),
5756
+ path8.join(mcpDir, "server.ts"),
5757
+ path8.join(mcpDir, "server.tsx")
5758
+ ];
5759
+ for (const candidate of mcpCandidates) {
5760
+ try {
5761
+ await access(path8.join(projectPath, candidate));
5762
+ return candidate;
5763
+ } catch {
5764
+ continue;
5765
+ }
5766
+ }
5767
+ throw new Error(
5768
+ `No entry file found inside ${mcpDir}.
5769
+
5770
+ Expected one of: ${mcpCandidates.map((c) => path8.relative(projectPath, path8.join(projectPath, c))).join(", ")}
5771
+
5772
+ Fix this by either:
5773
+ 1. Creating ${path8.join(mcpDir, "index.ts")}, or
5774
+ 2. Passing --entry <file> on the command line`
5775
+ );
5776
+ }
5622
5777
  const candidates = ["index.ts", "src/index.ts", "server.ts", "src/server.ts"];
5623
5778
  for (const candidate of candidates) {
5624
5779
  try {
5625
- await access(path7.join(projectPath, candidate));
5780
+ await access(path8.join(projectPath, candidate));
5626
5781
  return candidate;
5627
5782
  } catch {
5628
5783
  continue;
5629
5784
  }
5630
5785
  }
5631
- throw new Error("No server file found");
5786
+ throw new Error(
5787
+ `No entry file found.
5788
+
5789
+ Expected one of: ${candidates.join(", ")}
5790
+
5791
+ Fix this by either:
5792
+ 1. Creating one of the default entry files above, or
5793
+ 2. Passing --entry <file> or --mcp-dir <dir> on the command line`
5794
+ );
5795
+ }
5796
+ function resolveWidgetsDir(cliWidgetsDir, mcpDir) {
5797
+ if (cliWidgetsDir) return cliWidgetsDir;
5798
+ if (mcpDir) return path8.join(mcpDir, "resources");
5799
+ return "resources";
5800
+ }
5801
+ function makeWidgetServerOnlyGuard(widgetName) {
5802
+ const rejected = /* @__PURE__ */ new Set([
5803
+ "server-only",
5804
+ "client-only",
5805
+ "next/cache",
5806
+ "next/headers",
5807
+ "next/navigation",
5808
+ "next/server"
5809
+ ]);
5810
+ return {
5811
+ name: "mcp-use-widget-server-only-guard",
5812
+ enforce: "pre",
5813
+ resolveId(id, importer) {
5814
+ if (!rejected.has(id)) return null;
5815
+ const from = importer ? ` (imported from ${importer})` : "";
5816
+ throw new Error(
5817
+ `Widget "${widgetName}" imports "${id}"${from}, which is a Next.js server-only module. Widgets run in a browser iframe and cannot use server APIs.
5818
+
5819
+ To fix:
5820
+ \u2022 Remove the import from the widget (or from any module the widget transitively imports)
5821
+ \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`
5822
+ );
5823
+ }
5824
+ };
5825
+ }
5826
+ async function findServerFile(projectPath, cliEntry, cliMcpDir) {
5827
+ return resolveEntryFile(projectPath, cliEntry, cliMcpDir);
5632
5828
  }
5633
5829
  async function generateToolRegistryTypesForServer(projectPath, serverFileRelative) {
5634
- const serverFile = path7.join(projectPath, serverFileRelative);
5830
+ const serverFile = path8.join(projectPath, serverFileRelative);
5635
5831
  const serverFileExists = await access(serverFile).then(() => true).catch(() => false);
5636
5832
  if (!serverFileExists) {
5637
5833
  throw new Error(`Server file not found: ${serverFile}`);
@@ -5640,19 +5836,48 @@ async function generateToolRegistryTypesForServer(projectPath, serverFileRelativ
5640
5836
  try {
5641
5837
  globalThis.__mcpUseHmrMode = true;
5642
5838
  globalThis.__mcpUseLastServer = void 0;
5643
- const { tsImport } = await import("tsx/esm/api");
5644
- await tsImport(pathToFileURL(serverFile).href, {
5645
- parentURL: import.meta.url,
5646
- tsconfig: path7.join(projectPath, "tsconfig.json")
5647
- });
5839
+ if (await detectNextJsProject(projectPath)) {
5840
+ await loadNextJsEnvFiles(projectPath);
5841
+ await registerNextShimsInProcess();
5842
+ }
5843
+ const projectTsconfigPath = path8.join(projectPath, "tsconfig.json");
5844
+ const hasTsconfig = await access(projectTsconfigPath).then(() => true).catch(() => false);
5845
+ if (hasTsconfig) {
5846
+ process.env.TSX_TSCONFIG_PATH = projectTsconfigPath;
5847
+ }
5848
+ const previousCwd = process.cwd();
5849
+ if (previousCwd !== projectPath) process.chdir(projectPath);
5850
+ try {
5851
+ const projectRequire = createRequire2(
5852
+ path8.join(projectPath, "package.json")
5853
+ );
5854
+ const tsxEsmApiPath = projectRequire.resolve("tsx/esm/api");
5855
+ const tsxEsmApi = await import(pathToFileURL2(tsxEsmApiPath).href);
5856
+ if (typeof tsxEsmApi.register === "function") {
5857
+ tsxEsmApi.register({
5858
+ tsconfig: hasTsconfig ? projectTsconfigPath : void 0
5859
+ });
5860
+ }
5861
+ try {
5862
+ const tsxCjsApiPath = projectRequire.resolve("tsx/cjs/api");
5863
+ const tsxCjsApi = await import(pathToFileURL2(tsxCjsApiPath).href);
5864
+ if (typeof tsxCjsApi.register === "function") {
5865
+ tsxCjsApi.register();
5866
+ }
5867
+ } catch {
5868
+ }
5869
+ await import(`${pathToFileURL2(serverFile).href}?t=${Date.now()}`);
5870
+ } finally {
5871
+ if (process.cwd() !== previousCwd) process.chdir(previousCwd);
5872
+ }
5648
5873
  const server = globalThis.__mcpUseLastServer;
5649
5874
  if (!server) {
5650
5875
  throw new Error(
5651
5876
  "No MCPServer instance found. Make sure your server file creates an MCPServer instance."
5652
5877
  );
5653
5878
  }
5654
- const mcpUsePath = path7.join(projectPath, "node_modules", "mcp-use");
5655
- const { generateToolRegistryTypes } = await import(pathToFileURL(path7.join(mcpUsePath, "dist", "src", "server", "index.js")).href).then((mod) => mod);
5879
+ const mcpUsePath = path8.join(projectPath, "node_modules", "mcp-use");
5880
+ const { generateToolRegistryTypes } = await import(pathToFileURL2(path8.join(mcpUsePath, "dist", "src", "server", "index.js")).href).then((mod) => mod);
5656
5881
  if (!generateToolRegistryTypes) {
5657
5882
  throw new Error("generateToolRegistryTypes not found in mcp-use package");
5658
5883
  }
@@ -5667,21 +5892,24 @@ async function generateToolRegistryTypesForServer(projectPath, serverFileRelativ
5667
5892
  }
5668
5893
  async function buildWidgets(projectPath, options = {}) {
5669
5894
  const { inline = true } = options;
5670
- const { promises: fs10 } = await import("fs");
5895
+ const { promises: fs11 } = await import("fs");
5671
5896
  const { build } = await import("vite");
5672
- const resourcesDir = path7.join(projectPath, "resources");
5897
+ const widgetsDirRelative = options.widgetsDir ?? "resources";
5898
+ const resourcesDir = path8.resolve(projectPath, widgetsDirRelative);
5673
5899
  const mcpUrl = process.env.MCP_URL;
5674
5900
  try {
5675
5901
  await access(resourcesDir);
5676
5902
  } catch {
5677
5903
  console.log(
5678
- source_default.gray("No resources/ directory found - skipping widget build")
5904
+ source_default.gray(
5905
+ `No ${widgetsDirRelative}/ directory found - skipping widget build`
5906
+ )
5679
5907
  );
5680
5908
  return [];
5681
5909
  }
5682
5910
  const entries = [];
5683
5911
  try {
5684
- const files = await fs10.readdir(resourcesDir, { withFileTypes: true });
5912
+ const files = await fs11.readdir(resourcesDir, { withFileTypes: true });
5685
5913
  for (const dirent of files) {
5686
5914
  if (dirent.name.startsWith("._") || dirent.name.startsWith(".DS_Store")) {
5687
5915
  continue;
@@ -5689,12 +5917,12 @@ async function buildWidgets(projectPath, options = {}) {
5689
5917
  if (dirent.isFile() && (dirent.name.endsWith(".tsx") || dirent.name.endsWith(".ts"))) {
5690
5918
  entries.push({
5691
5919
  name: dirent.name.replace(/\.tsx?$/, ""),
5692
- path: path7.join(resourcesDir, dirent.name)
5920
+ path: path8.join(resourcesDir, dirent.name)
5693
5921
  });
5694
5922
  } else if (dirent.isDirectory()) {
5695
- const widgetPath = path7.join(resourcesDir, dirent.name, "widget.tsx");
5923
+ const widgetPath = path8.join(resourcesDir, dirent.name, "widget.tsx");
5696
5924
  try {
5697
- await fs10.access(widgetPath);
5925
+ await fs11.access(widgetPath);
5698
5926
  entries.push({
5699
5927
  name: dirent.name,
5700
5928
  path: widgetPath
@@ -5704,11 +5932,15 @@ async function buildWidgets(projectPath, options = {}) {
5704
5932
  }
5705
5933
  }
5706
5934
  } catch (error) {
5707
- console.log(source_default.gray("No widgets found in resources/ directory"));
5935
+ console.log(
5936
+ source_default.gray(`No widgets found in ${widgetsDirRelative}/ directory`)
5937
+ );
5708
5938
  return [];
5709
5939
  }
5710
5940
  if (entries.length === 0) {
5711
- console.log(source_default.gray("No widgets found in resources/ directory"));
5941
+ console.log(
5942
+ source_default.gray(`No widgets found in ${widgetsDirRelative}/ directory`)
5943
+ );
5712
5944
  return [];
5713
5945
  }
5714
5946
  console.log(
@@ -5718,10 +5950,17 @@ async function buildWidgets(projectPath, options = {}) {
5718
5950
  );
5719
5951
  const react = (await import("@vitejs/plugin-react")).default;
5720
5952
  const tailwindcss = (await import("@tailwindcss/vite")).default;
5721
- const packageJsonPath = path7.join(projectPath, "package.json");
5953
+ const projectTsconfigPath = path8.join(projectPath, "tsconfig.json");
5954
+ let hasProjectTsconfig = false;
5955
+ try {
5956
+ await access(projectTsconfigPath);
5957
+ hasProjectTsconfig = true;
5958
+ } catch {
5959
+ }
5960
+ const packageJsonPath = path8.join(projectPath, "package.json");
5722
5961
  let favicon = "";
5723
5962
  try {
5724
- const pkgContent = await fs10.readFile(packageJsonPath, "utf-8");
5963
+ const pkgContent = await fs11.readFile(packageJsonPath, "utf-8");
5725
5964
  const pkg = JSON.parse(pkgContent);
5726
5965
  favicon = pkg.mcpUse?.favicon || "";
5727
5966
  } catch {
@@ -5730,18 +5969,27 @@ async function buildWidgets(projectPath, options = {}) {
5730
5969
  const widgetName = entry.name;
5731
5970
  const entryPath = entry.path.replace(/\\/g, "/");
5732
5971
  console.log(source_default.gray(` - Building ${widgetName}...`));
5733
- const tempDir = path7.join(projectPath, ".mcp-use", widgetName);
5734
- await fs10.mkdir(tempDir, { recursive: true });
5735
- const relativeResourcesPath = path7.relative(tempDir, resourcesDir).replace(/\\/g, "/");
5736
- const mcpUsePath = path7.join(projectPath, "node_modules", "mcp-use");
5737
- const relativeMcpUsePath = path7.relative(tempDir, mcpUsePath).replace(/\\/g, "/");
5972
+ const tempDir = path8.join(projectPath, ".mcp-use", widgetName);
5973
+ await fs11.mkdir(tempDir, { recursive: true });
5974
+ const relativeResourcesPath = path8.relative(tempDir, resourcesDir).replace(/\\/g, "/");
5975
+ const mcpUsePath = path8.join(projectPath, "node_modules", "mcp-use");
5976
+ const relativeMcpUsePath = path8.relative(tempDir, mcpUsePath).replace(/\\/g, "/");
5977
+ const projectSrcDir = path8.join(projectPath, "src");
5978
+ let projectSrcSourceLine = "";
5979
+ try {
5980
+ await access(projectSrcDir);
5981
+ const relativeProjectSrcPath = path8.relative(tempDir, projectSrcDir).replace(/\\/g, "/");
5982
+ projectSrcSourceLine = `@source "${relativeProjectSrcPath}";
5983
+ `;
5984
+ } catch {
5985
+ }
5738
5986
  const cssContent = `@import "tailwindcss";
5739
5987
 
5740
5988
  /* Configure Tailwind to scan the resources directory and mcp-use package */
5741
5989
  @source "${relativeResourcesPath}";
5742
5990
  @source "${relativeMcpUsePath}/**/*.{ts,tsx,js,jsx}";
5743
- `;
5744
- await fs10.writeFile(path7.join(tempDir, "styles.css"), cssContent, "utf8");
5991
+ ${projectSrcSourceLine}`;
5992
+ await fs11.writeFile(path8.join(tempDir, "styles.css"), cssContent, "utf8");
5745
5993
  const entryContent = `import React from 'react'
5746
5994
  import { createRoot } from 'react-dom/client'
5747
5995
  import './styles.css'
@@ -5766,9 +6014,9 @@ if (container && Component) {
5766
6014
  <script type="module" src="/entry.tsx"></script>
5767
6015
  </body>
5768
6016
  </html>`;
5769
- await fs10.writeFile(path7.join(tempDir, "entry.tsx"), entryContent, "utf8");
5770
- await fs10.writeFile(path7.join(tempDir, "index.html"), htmlContent, "utf8");
5771
- const outDir = path7.join(
6017
+ await fs11.writeFile(path8.join(tempDir, "entry.tsx"), entryContent, "utf8");
6018
+ await fs11.writeFile(path8.join(tempDir, "index.html"), htmlContent, "utf8");
6019
+ const outDir = path8.join(
5772
6020
  projectPath,
5773
6021
  "dist",
5774
6022
  "resources",
@@ -5778,12 +6026,12 @@ if (container && Component) {
5778
6026
  const baseUrl = mcpUrl ? `${mcpUrl}/${widgetName}/` : `/mcp-use/widgets/${widgetName}/`;
5779
6027
  let widgetMetadata = {};
5780
6028
  try {
5781
- const metadataTempDir = path7.join(
6029
+ const metadataTempDir = path8.join(
5782
6030
  projectPath,
5783
6031
  ".mcp-use",
5784
6032
  `${widgetName}-metadata`
5785
6033
  );
5786
- await fs10.mkdir(metadataTempDir, { recursive: true });
6034
+ await fs11.mkdir(metadataTempDir, { recursive: true });
5787
6035
  const { createServer } = await import("vite");
5788
6036
  const nodeStubsPlugin = {
5789
6037
  name: "node-stubs",
@@ -5811,15 +6059,16 @@ export default PostHog;
5811
6059
  return null;
5812
6060
  }
5813
6061
  };
6062
+ const serverOnlyGuard = makeWidgetServerOnlyGuard(widgetName);
5814
6063
  const metadataServer = await createServer({
5815
6064
  root: metadataTempDir,
5816
- cacheDir: path7.join(metadataTempDir, ".vite-cache"),
5817
- plugins: [nodeStubsPlugin, tailwindcss(), react()],
5818
- resolve: {
5819
- alias: {
5820
- "@": resourcesDir
5821
- }
5822
- },
6065
+ cacheDir: path8.join(metadataTempDir, ".vite-cache"),
6066
+ plugins: [serverOnlyGuard, nodeStubsPlugin, tailwindcss(), react()],
6067
+ // When the project has a tsconfig, enable Vite's native tsconfig-paths
6068
+ // resolver so `@/*` (or any custom alias) resolves through the
6069
+ // project's own paths config. Without a tsconfig, fall back to the
6070
+ // legacy hardcoded alias.
6071
+ resolve: hasProjectTsconfig ? { tsconfigPaths: true } : { alias: { "@": resourcesDir } },
5823
6072
  server: {
5824
6073
  middlewareMode: true
5825
6074
  },
@@ -5898,7 +6147,7 @@ export default PostHog;
5898
6147
  } finally {
5899
6148
  await metadataServer.close();
5900
6149
  try {
5901
- await fs10.rm(metadataTempDir, { recursive: true, force: true });
6150
+ await fs11.rm(metadataTempDir, { recursive: true, force: true });
5902
6151
  } catch {
5903
6152
  }
5904
6153
  }
@@ -5993,12 +6242,14 @@ export default {
5993
6242
  return null;
5994
6243
  }
5995
6244
  };
6245
+ const buildServerOnlyGuard = makeWidgetServerOnlyGuard(widgetName);
5996
6246
  const buildPlugins = inline ? [
6247
+ buildServerOnlyGuard,
5997
6248
  buildNodeStubsPlugin,
5998
6249
  tailwindcss(),
5999
6250
  react(),
6000
6251
  viteSingleFile({ removeViteModuleLoader: true })
6001
- ] : [buildNodeStubsPlugin, tailwindcss(), react()];
6252
+ ] : [buildServerOnlyGuard, buildNodeStubsPlugin, tailwindcss(), react()];
6002
6253
  await build({
6003
6254
  root: tempDir,
6004
6255
  base: baseUrl,
@@ -6017,11 +6268,10 @@ export default {
6017
6268
  }
6018
6269
  }
6019
6270
  },
6020
- resolve: {
6021
- alias: {
6022
- "@": resourcesDir
6023
- }
6024
- },
6271
+ // When a tsconfig exists, enable Vite's native `resolve.tsconfigPaths`
6272
+ // so the project's path aliases resolve naturally. Otherwise fall
6273
+ // back to the legacy `@` → resourcesDir alias.
6274
+ resolve: hasProjectTsconfig ? { tsconfigPaths: true } : { alias: { "@": resourcesDir } },
6025
6275
  optimizeDeps: {
6026
6276
  // Exclude Node.js-only packages from browser bundling
6027
6277
  exclude: ["posthog-node"]
@@ -6043,7 +6293,7 @@ export default {
6043
6293
  // Inline all assets under 100MB (effectively all)
6044
6294
  } : {},
6045
6295
  rolldownOptions: {
6046
- input: path7.join(tempDir, "index.html"),
6296
+ input: path8.join(tempDir, "index.html"),
6047
6297
  external: (id) => {
6048
6298
  return false;
6049
6299
  }
@@ -6051,12 +6301,12 @@ export default {
6051
6301
  }
6052
6302
  });
6053
6303
  try {
6054
- const assetsDir = path7.join(outDir, "assets");
6055
- const assetFiles = await fs10.readdir(assetsDir);
6304
+ const assetsDir = path8.join(outDir, "assets");
6305
+ const assetFiles = await fs11.readdir(assetsDir);
6056
6306
  const jsFiles = assetFiles.filter((f) => f.endsWith(".js"));
6057
6307
  for (const jsFile of jsFiles) {
6058
- const jsPath = path7.join(assetsDir, jsFile);
6059
- let content = await fs10.readFile(jsPath, "utf8");
6308
+ const jsPath = path8.join(assetsDir, jsFile);
6309
+ let content = await fs11.readFile(jsPath, "utf8");
6060
6310
  const zodConfigPatterns = [
6061
6311
  // Non-minified: export const globalConfig = {}
6062
6312
  /export\s+const\s+globalConfig\s*=\s*\{\s*\}/g,
@@ -6075,7 +6325,7 @@ export default {
6075
6325
  }
6076
6326
  }
6077
6327
  if (patched) {
6078
- await fs10.writeFile(jsPath, content, "utf8");
6328
+ await fs11.writeFile(jsPath, content, "utf8");
6079
6329
  console.log(source_default.gray(` \u2192 Patched Zod JIT in ${jsFile}`));
6080
6330
  }
6081
6331
  }
@@ -6087,8 +6337,8 @@ export default {
6087
6337
  const mcpServerUrl = process.env.MCP_SERVER_URL;
6088
6338
  if (mcpServerUrl) {
6089
6339
  try {
6090
- const htmlPath = path7.join(outDir, "index.html");
6091
- let html = await fs10.readFile(htmlPath, "utf8");
6340
+ const htmlPath = path8.join(outDir, "index.html");
6341
+ let html = await fs11.readFile(htmlPath, "utf8");
6092
6342
  const injectionScript = `<script>window.__getFile = (filename) => { return "${mcpUrl}/${widgetName}/"+filename }; window.__mcpPublicUrl = "${mcpServerUrl}/mcp-use/public"; window.__mcpPublicAssetsUrl = "${mcpUrl}/public";</script>`;
6093
6343
  if (!html.includes("window.__mcpPublicUrl")) {
6094
6344
  html = html.replace(
@@ -6109,7 +6359,7 @@ export default {
6109
6359
  <base href="${mcpServerUrl}">`
6110
6360
  );
6111
6361
  }
6112
- await fs10.writeFile(htmlPath, html, "utf8");
6362
+ await fs11.writeFile(htmlPath, html, "utf8");
6113
6363
  console.log(
6114
6364
  source_default.gray(` \u2192 Injected MCP_SERVER_URL into ${widgetName}`)
6115
6365
  );
@@ -6123,22 +6373,32 @@ export default {
6123
6373
  }
6124
6374
  }
6125
6375
  console.log(source_default.green(` \u2713 Built ${widgetName}`));
6126
- return { name: widgetName, metadata: widgetMetadata };
6376
+ return {
6377
+ status: "built",
6378
+ name: widgetName,
6379
+ metadata: widgetMetadata
6380
+ };
6127
6381
  } catch (error) {
6128
6382
  console.error(source_default.red(` \u2717 Failed to build ${widgetName}:`), error);
6129
- return null;
6383
+ return { status: "failed", name: widgetName };
6130
6384
  }
6131
6385
  };
6132
6386
  const buildResults = await Promise.all(
6133
6387
  entries.map((entry) => buildSingleWidget(entry))
6134
6388
  );
6135
- const builtWidgets = buildResults.filter(
6136
- (result) => result !== null
6389
+ const failed = buildResults.filter((r) => r.status === "failed");
6390
+ if (failed.length > 0) {
6391
+ const names = failed.map((f) => f.name).join(", ");
6392
+ throw new Error(
6393
+ `${failed.length} widget(s) failed to build: ${names}. See errors above.`
6394
+ );
6395
+ }
6396
+ return buildResults.flatMap(
6397
+ (r) => r.status === "built" ? [{ name: r.name, metadata: r.metadata }] : []
6137
6398
  );
6138
- return builtWidgets;
6139
6399
  }
6140
6400
  async function collectTsFiles(projectPath, includePatterns, excludePatterns) {
6141
- const { promises: fs10 } = await import("fs");
6401
+ const { promises: fs11 } = await import("fs");
6142
6402
  const literalFiles = [];
6143
6403
  const dirPrefixes = [];
6144
6404
  for (const pattern of includePatterns) {
@@ -6153,7 +6413,7 @@ async function collectTsFiles(projectPath, includePatterns, excludePatterns) {
6153
6413
  for (const file of literalFiles) {
6154
6414
  if (/\.tsx?$/.test(file) && !file.endsWith(".d.ts")) {
6155
6415
  try {
6156
- await access(path7.join(projectPath, file));
6416
+ await access(path8.join(projectPath, file));
6157
6417
  files.push(file);
6158
6418
  } catch {
6159
6419
  }
@@ -6161,13 +6421,13 @@ async function collectTsFiles(projectPath, includePatterns, excludePatterns) {
6161
6421
  }
6162
6422
  const excludeSet = new Set(excludePatterns.map((e) => e.replace(/\*+/g, "")));
6163
6423
  for (const prefix of dirPrefixes) {
6164
- const dirPath = path7.join(projectPath, prefix);
6424
+ const dirPath = path8.join(projectPath, prefix);
6165
6425
  try {
6166
- const entries = await fs10.readdir(dirPath, { recursive: true });
6426
+ const entries = await fs11.readdir(dirPath, { recursive: true });
6167
6427
  for (const entry of entries) {
6168
6428
  const entryStr = String(entry);
6169
- const rel = path7.join(prefix, entryStr);
6170
- if (/\.tsx?$/.test(entryStr) && !entryStr.endsWith(".d.ts") && !excludeSet.has(rel.split(path7.sep)[0])) {
6429
+ const rel = path8.join(prefix, entryStr);
6430
+ if (/\.tsx?$/.test(entryStr) && !entryStr.endsWith(".d.ts") && !excludeSet.has(rel.split(path8.sep)[0])) {
6171
6431
  files.push(rel);
6172
6432
  }
6173
6433
  }
@@ -6178,11 +6438,11 @@ async function collectTsFiles(projectPath, includePatterns, excludePatterns) {
6178
6438
  }
6179
6439
  async function transpileWithEsbuild(projectPath) {
6180
6440
  const esbuild = await import("esbuild");
6181
- const { promises: fs10 } = await import("fs");
6182
- const tsconfigPath = path7.join(projectPath, "tsconfig.json");
6441
+ const { promises: fs11 } = await import("fs");
6442
+ const tsconfigPath = path8.join(projectPath, "tsconfig.json");
6183
6443
  let tsconfig = {};
6184
6444
  try {
6185
- const raw = await fs10.readFile(tsconfigPath, "utf-8");
6445
+ const raw = await fs11.readFile(tsconfigPath, "utf-8");
6186
6446
  tsconfig = JSON.parse(raw);
6187
6447
  } catch {
6188
6448
  }
@@ -6209,10 +6469,10 @@ async function transpileWithEsbuild(projectPath) {
6209
6469
  const target = (compilerOptions.target || "ES2022").toLowerCase();
6210
6470
  const moduleStr = (compilerOptions.module || "ESNext").toLowerCase();
6211
6471
  const format = moduleStr.includes("commonjs") ? "cjs" : "esm";
6212
- const outbase = compilerOptions.rootDir ? path7.resolve(projectPath, compilerOptions.rootDir) : projectPath;
6472
+ const outbase = compilerOptions.rootDir ? path8.resolve(projectPath, compilerOptions.rootDir) : projectPath;
6213
6473
  await esbuild.build({
6214
- entryPoints: files.map((f) => path7.join(projectPath, f)),
6215
- outdir: path7.join(projectPath, outDir),
6474
+ entryPoints: files.map((f) => path8.join(projectPath, f)),
6475
+ outdir: path8.join(projectPath, outDir),
6216
6476
  outbase,
6217
6477
  bundle: false,
6218
6478
  format,
@@ -6224,21 +6484,42 @@ async function transpileWithEsbuild(projectPath) {
6224
6484
  logLevel: "warning"
6225
6485
  });
6226
6486
  }
6227
- 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(
6487
+ program.command("build").description("Build TypeScript and MCP UI widgets").option("-p, --path <path>", "Path to project directory", process.cwd()).option(
6488
+ "--entry <file>",
6489
+ "Path to MCP server entry file (relative to project)"
6490
+ ).option(
6491
+ "--widgets-dir <dir>",
6492
+ "Path to widgets directory (relative to project)"
6493
+ ).option(
6494
+ "--mcp-dir <dir>",
6495
+ "Folder holding the MCP entry + resources (e.g. 'src/mcp' for Next.js apps)"
6496
+ ).option("--with-inspector", "Include inspector in production build").option(
6228
6497
  "--inline",
6229
6498
  "Inline all JS/CSS into HTML (required for VS Code MCP Apps)"
6230
6499
  ).option("--no-inline", "Keep JS/CSS as separate files (default)").option("--no-typecheck", "Skip TypeScript type checking (faster builds)").action(async (options) => {
6231
6500
  try {
6232
- const projectPath = path7.resolve(options.path);
6233
- const { promises: fs10 } = await import("fs");
6501
+ const projectPath = path8.resolve(options.path);
6502
+ const { promises: fs11 } = await import("fs");
6234
6503
  displayPackageVersions(projectPath);
6504
+ const mcpDir = options.mcpDir;
6505
+ const widgetsDir = resolveWidgetsDir(options.widgetsDir, mcpDir);
6235
6506
  const builtWidgets = await buildWidgets(projectPath, {
6236
- inline: options.inline ?? false
6507
+ inline: options.inline ?? false,
6508
+ widgetsDir
6237
6509
  });
6238
6510
  let sourceServerFile;
6239
6511
  try {
6240
- sourceServerFile = await findServerFile(projectPath);
6241
- } catch {
6512
+ sourceServerFile = await findServerFile(
6513
+ projectPath,
6514
+ options.entry,
6515
+ options.mcpDir
6516
+ );
6517
+ } catch (err) {
6518
+ console.log(
6519
+ source_default.yellow(
6520
+ `\u26A0 Could not locate a server entry file: ${err instanceof Error ? err.message : String(err)}`
6521
+ )
6522
+ );
6242
6523
  }
6243
6524
  if (sourceServerFile) {
6244
6525
  console.log(source_default.gray("Generating tool registry types..."));
@@ -6256,17 +6537,25 @@ program.command("build").description("Build TypeScript and MCP UI widgets").opti
6256
6537
  );
6257
6538
  }
6258
6539
  }
6259
- console.log(source_default.gray("Building TypeScript..."));
6260
- await transpileWithEsbuild(projectPath);
6261
- console.log(source_default.green("\u2713 TypeScript build complete!"));
6262
- if (options.typecheck !== false) {
6540
+ if (!mcpDir) {
6541
+ console.log(source_default.gray("Building TypeScript..."));
6542
+ await transpileWithEsbuild(projectPath);
6543
+ console.log(source_default.green("\u2713 TypeScript build complete!"));
6544
+ } else {
6545
+ console.log(
6546
+ source_default.gray(
6547
+ "Skipping TypeScript transpile (--mcp-dir mode runs source via tsx at start time)"
6548
+ )
6549
+ );
6550
+ }
6551
+ if (options.typecheck !== false && !mcpDir) {
6263
6552
  console.log(source_default.gray("Type checking..."));
6264
6553
  try {
6265
6554
  await runCommand(
6266
6555
  "node",
6267
6556
  [
6268
6557
  "--max-old-space-size=4096",
6269
- path7.join(
6558
+ path8.join(
6270
6559
  projectPath,
6271
6560
  "node_modules",
6272
6561
  "typescript",
@@ -6287,39 +6576,43 @@ program.command("build").description("Build TypeScript and MCP UI widgets").opti
6287
6576
  }
6288
6577
  let entryPoint;
6289
6578
  if (sourceServerFile) {
6290
- const baseName = path7.basename(sourceServerFile, ".ts") + ".js";
6291
- const possibleOutputs = [
6292
- `dist/${baseName}`,
6293
- // rootDir set to project root or src
6294
- `dist/src/${baseName}`,
6295
- // no rootDir, source in src/
6296
- `dist/${sourceServerFile.replace(/\.ts$/, ".js")}`
6297
- // exact path preserved
6298
- ];
6299
- for (const candidate of possibleOutputs) {
6300
- try {
6301
- await access(path7.join(projectPath, candidate));
6302
- entryPoint = candidate;
6303
- break;
6304
- } catch {
6305
- continue;
6579
+ if (mcpDir) {
6580
+ entryPoint = sourceServerFile;
6581
+ } else {
6582
+ const baseName = path8.basename(sourceServerFile, ".ts") + ".js";
6583
+ const possibleOutputs = [
6584
+ `dist/${baseName}`,
6585
+ // rootDir set to project root or src
6586
+ `dist/src/${baseName}`,
6587
+ // no rootDir, source in src/
6588
+ `dist/${sourceServerFile.replace(/\.ts$/, ".js")}`
6589
+ // exact path preserved
6590
+ ];
6591
+ for (const candidate of possibleOutputs) {
6592
+ try {
6593
+ await access(path8.join(projectPath, candidate));
6594
+ entryPoint = candidate;
6595
+ break;
6596
+ } catch {
6597
+ continue;
6598
+ }
6306
6599
  }
6307
6600
  }
6308
6601
  }
6309
- const publicDir = path7.join(projectPath, "public");
6602
+ const publicDir = path8.join(projectPath, "public");
6310
6603
  try {
6311
- await fs10.access(publicDir);
6604
+ await fs11.access(publicDir);
6312
6605
  console.log(source_default.gray("Copying public assets..."));
6313
- await fs10.cp(publicDir, path7.join(projectPath, "dist", "public"), {
6606
+ await fs11.cp(publicDir, path8.join(projectPath, "dist", "public"), {
6314
6607
  recursive: true
6315
6608
  });
6316
6609
  console.log(source_default.green("\u2713 Public assets copied"));
6317
6610
  } catch {
6318
6611
  }
6319
- const manifestPath = path7.join(projectPath, "dist", "mcp-use.json");
6612
+ const manifestPath = path8.join(projectPath, "dist", "mcp-use.json");
6320
6613
  let existingManifest = {};
6321
6614
  try {
6322
- const existingContent = await fs10.readFile(manifestPath, "utf-8");
6615
+ const existingContent = await fs11.readFile(manifestPath, "utf-8");
6323
6616
  existingManifest = JSON.parse(existingContent);
6324
6617
  } catch {
6325
6618
  }
@@ -6341,8 +6634,8 @@ program.command("build").description("Build TypeScript and MCP UI widgets").opti
6341
6634
  // Server entry point for `mcp-use start`
6342
6635
  widgets: widgetsData
6343
6636
  };
6344
- await fs10.mkdir(path7.dirname(manifestPath), { recursive: true });
6345
- await fs10.writeFile(
6637
+ await fs11.mkdir(path8.dirname(manifestPath), { recursive: true });
6638
+ await fs11.writeFile(
6346
6639
  manifestPath,
6347
6640
  JSON.stringify(manifest, null, 2),
6348
6641
  "utf8"
@@ -6362,14 +6655,23 @@ program.command("build").description("Build TypeScript and MCP UI widgets").opti
6362
6655
  process.exit(1);
6363
6656
  }
6364
6657
  });
6365
- 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(
6658
+ program.command("dev").description("Run development server with auto-reload and inspector").option("-p, --path <path>", "Path to project directory", process.cwd()).option(
6659
+ "--entry <file>",
6660
+ "Path to MCP server entry file (relative to project)"
6661
+ ).option(
6662
+ "--widgets-dir <dir>",
6663
+ "Path to widgets directory (relative to project)"
6664
+ ).option(
6665
+ "--mcp-dir <dir>",
6666
+ "Folder holding the MCP entry + resources (e.g. 'src/mcp' for Next.js apps)"
6667
+ ).option("--port <port>", "Server port", "3000").option(
6366
6668
  "--host <host>",
6367
6669
  "Server host (use 0.0.0.0 to listen on all interfaces)",
6368
6670
  "0.0.0.0"
6369
6671
  ).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) => {
6370
6672
  try {
6371
6673
  process.env.MCP_USE_CLI_DEV = "1";
6372
- const projectPath = path7.resolve(options.path);
6674
+ const projectPath = path8.resolve(options.path);
6373
6675
  let port = parseInt(options.port, 10);
6374
6676
  const host = options.host;
6375
6677
  const useHmr = options.hmr !== false;
@@ -6380,13 +6682,24 @@ program.command("dev").description("Run development server with auto-reload and
6380
6682
  console.log(source_default.green.bold(`\u2713 Using port ${availablePort} instead`));
6381
6683
  port = availablePort;
6382
6684
  }
6383
- const serverFile = await findServerFile(projectPath);
6685
+ const serverFile = await findServerFile(
6686
+ projectPath,
6687
+ options.entry,
6688
+ options.mcpDir
6689
+ );
6690
+ {
6691
+ const devMcpDir = options.mcpDir;
6692
+ const devWidgetsDir = resolveWidgetsDir(options.widgetsDir, devMcpDir);
6693
+ if (devWidgetsDir !== "resources") {
6694
+ process.env.MCP_USE_WIDGETS_DIR = devWidgetsDir;
6695
+ }
6696
+ }
6384
6697
  let tunnelProcess = void 0;
6385
6698
  let tunnelSubdomain = void 0;
6386
6699
  let tunnelUrl = void 0;
6387
6700
  if (options.tunnel) {
6388
6701
  try {
6389
- const manifestPath = path7.join(projectPath, "dist", "mcp-use.json");
6702
+ const manifestPath = path8.join(projectPath, "dist", "mcp-use.json");
6390
6703
  let existingSubdomain;
6391
6704
  try {
6392
6705
  const manifestContent = await readFile3(manifestPath, "utf-8");
@@ -6435,7 +6748,7 @@ program.command("dev").description("Run development server with auto-reload and
6435
6748
  manifest.tunnel = {};
6436
6749
  }
6437
6750
  manifest.tunnel.subdomain = tunnelSubdomain;
6438
- await mkdir3(path7.dirname(manifestPath), { recursive: true });
6751
+ await mkdir3(path8.dirname(manifestPath), { recursive: true });
6439
6752
  await writeFile3(
6440
6753
  manifestPath,
6441
6754
  JSON.stringify(manifest, null, 2),
@@ -6462,22 +6775,35 @@ program.command("dev").description("Run development server with auto-reload and
6462
6775
  } else if (!process.env.MCP_URL) {
6463
6776
  process.env.MCP_URL = mcpUrl;
6464
6777
  }
6778
+ const isNextJsProject = await detectNextJsProject(projectPath);
6779
+ if (isNextJsProject) {
6780
+ console.log(
6781
+ source_default.gray(
6782
+ "Next.js detected \u2014 installing server-runtime shims (server-only, next/cache, next/headers, next/navigation, next/server)"
6783
+ )
6784
+ );
6785
+ await loadNextJsEnvFiles(projectPath);
6786
+ }
6465
6787
  if (!useHmr) {
6466
6788
  console.log(source_default.gray("HMR disabled, using tsx watch (full restart)"));
6467
6789
  const processes = [];
6468
- const env2 = {
6790
+ const baseEnv = {
6791
+ // Inherit parent env (PATH, HOME, etc.) — without it, tsx can't
6792
+ // resolve its own tooling in some setups.
6793
+ ...process.env,
6469
6794
  PORT: String(port),
6470
6795
  HOST: host,
6471
6796
  NODE_ENV: "development",
6472
6797
  // Preserve user-provided MCP_URL (e.g., for reverse proxy setups)
6473
6798
  MCP_URL: process.env.MCP_URL || mcpUrl
6474
6799
  };
6800
+ const env2 = isNextJsProject ? withNextShimsEnv(baseEnv) : baseEnv;
6475
6801
  const { createRequire: createRequire4 } = await import("module");
6476
6802
  let cmd;
6477
6803
  let args;
6478
6804
  try {
6479
6805
  const projectRequire = createRequire4(
6480
- path7.join(projectPath, "package.json")
6806
+ path8.join(projectPath, "package.json")
6481
6807
  );
6482
6808
  const tsxPkgPath = projectRequire.resolve("tsx/package.json");
6483
6809
  const tsxPkg = JSON.parse(await readFile3(tsxPkgPath, "utf-8"));
@@ -6489,7 +6815,7 @@ program.command("dev").description("Run development server with auto-reload and
6489
6815
  } else {
6490
6816
  throw new Error("No bin field found in tsx package.json");
6491
6817
  }
6492
- const tsxBin = path7.resolve(path7.dirname(tsxPkgPath), binPath);
6818
+ const tsxBin = path8.resolve(path8.dirname(tsxPkgPath), binPath);
6493
6819
  cmd = "node";
6494
6820
  args = [tsxBin, "watch", serverFile];
6495
6821
  } catch (error) {
@@ -6564,18 +6890,56 @@ program.command("dev").description("Run development server with auto-reload and
6564
6890
  "HMR enabled - changes will hot reload without dropping connections"
6565
6891
  )
6566
6892
  );
6893
+ if (isNextJsProject) {
6894
+ const registered = await registerNextShimsInProcess();
6895
+ if (!registered) {
6896
+ console.warn(
6897
+ source_default.yellow(
6898
+ "[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."
6899
+ )
6900
+ );
6901
+ }
6902
+ }
6567
6903
  const chokidarModule = await import("chokidar");
6568
6904
  const chokidar = chokidarModule.default || chokidarModule;
6569
- const { fileURLToPath: fileURLToPath3 } = await import("url");
6905
+ const { fileURLToPath: fileURLToPath4 } = await import("url");
6570
6906
  const { createRequire: createRequire3 } = await import("module");
6571
- let tsImport = null;
6907
+ const projectTsconfigPath = path8.join(projectPath, "tsconfig.json");
6908
+ let tsconfigAvailable = false;
6909
+ try {
6910
+ await access(projectTsconfigPath);
6911
+ tsconfigAvailable = true;
6912
+ process.env.TSX_TSCONFIG_PATH = projectTsconfigPath;
6913
+ if (process.cwd() !== projectPath) process.chdir(projectPath);
6914
+ } catch {
6915
+ }
6916
+ let tsxLoaderActive = false;
6572
6917
  try {
6573
6918
  const projectRequire = createRequire3(
6574
- path7.join(projectPath, "package.json")
6919
+ path8.join(projectPath, "package.json")
6575
6920
  );
6576
- const tsxApiPath = projectRequire.resolve("tsx/esm/api");
6577
- const tsxApi = await import(pathToFileURL(tsxApiPath).href);
6578
- tsImport = tsxApi.tsImport;
6921
+ const tsxEsmApiPath = projectRequire.resolve("tsx/esm/api");
6922
+ const tsxEsmApi = await import(pathToFileURL2(tsxEsmApiPath).href);
6923
+ if (typeof tsxEsmApi.register === "function") {
6924
+ tsxEsmApi.register({
6925
+ tsconfig: tsconfigAvailable ? projectTsconfigPath : void 0,
6926
+ onImport: (url) => {
6927
+ const filePath = url.startsWith("file://") ? fileURLToPath4(url) : url;
6928
+ if (!filePath.includes("node_modules") && filePath.startsWith(projectPath)) {
6929
+ console.debug(`[HMR] Loaded: ${url}`);
6930
+ }
6931
+ }
6932
+ });
6933
+ tsxLoaderActive = true;
6934
+ }
6935
+ try {
6936
+ const tsxCjsApiPath = projectRequire.resolve("tsx/cjs/api");
6937
+ const tsxCjsApi = await import(pathToFileURL2(tsxCjsApiPath).href);
6938
+ if (typeof tsxCjsApi.register === "function") {
6939
+ tsxCjsApi.register();
6940
+ }
6941
+ } catch {
6942
+ }
6579
6943
  } catch {
6580
6944
  console.log(
6581
6945
  source_default.yellow(
@@ -6583,25 +6947,15 @@ program.command("dev").description("Run development server with auto-reload and
6583
6947
  )
6584
6948
  );
6585
6949
  }
6586
- const serverFilePath = path7.join(projectPath, serverFile);
6587
- const serverFileUrl = pathToFileURL(serverFilePath).href;
6950
+ const serverFilePath = path8.join(projectPath, serverFile);
6951
+ const serverFileUrl = pathToFileURL2(serverFilePath).href;
6588
6952
  globalThis.__mcpUseHmrMode = true;
6589
6953
  const importServerModule = async () => {
6590
6954
  const previousServer = globalThis.__mcpUseLastServer;
6591
6955
  globalThis.__mcpUseLastServer = null;
6592
- if (tsImport) {
6593
- await tsImport(`${serverFileUrl}?t=${Date.now()}`, {
6594
- parentURL: import.meta.url,
6595
- onImport: (file) => {
6596
- const filePath = file.startsWith("file://") ? fileURLToPath3(file) : file;
6597
- if (!filePath.includes("node_modules") && filePath.startsWith(projectPath)) {
6598
- console.debug(`[HMR] Loaded: ${file}`);
6599
- }
6600
- }
6601
- });
6602
- } else {
6603
- await import(`${serverFileUrl}?t=${Date.now()}`);
6956
+ if (!tsxLoaderActive) {
6604
6957
  }
6958
+ await import(`${serverFileUrl}?t=${Date.now()}`);
6605
6959
  const instance = globalThis.__mcpUseLastServer;
6606
6960
  if (!instance) {
6607
6961
  globalThis.__mcpUseLastServer = previousServer;
@@ -6615,7 +6969,7 @@ program.command("dev").description("Run development server with auto-reload and
6615
6969
  if (instance === previousServer) {
6616
6970
  console.warn(
6617
6971
  source_default.yellow(
6618
- "[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.")
6972
+ "[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.")
6619
6973
  )
6620
6974
  );
6621
6975
  return null;
@@ -6690,8 +7044,8 @@ program.command("dev").description("Run development server with auto-reload and
6690
7044
  }
6691
7045
  let watcher = chokidar.watch(".", {
6692
7046
  cwd: projectPath,
6693
- ignored: (path8, stats) => {
6694
- const normalizedPath = path8.replace(/\\/g, "/");
7047
+ ignored: (path9, stats) => {
7048
+ const normalizedPath = path9.replace(/\\/g, "/");
6695
7049
  if (/(^|\/)\.[^/]/.test(normalizedPath)) {
6696
7050
  return true;
6697
7051
  }
@@ -6865,7 +7219,7 @@ program.command("dev").description("Run development server with auto-reload and
6865
7219
  }
6866
7220
  tunnelUrl = void 0;
6867
7221
  if (withTunnel) {
6868
- const manifestPath = path7.join(projectPath, "dist", "mcp-use.json");
7222
+ const manifestPath = path8.join(projectPath, "dist", "mcp-use.json");
6869
7223
  let existingSubdomain;
6870
7224
  try {
6871
7225
  const manifestContent = await readFile3(manifestPath, "utf-8");
@@ -6902,7 +7256,7 @@ program.command("dev").description("Run development server with auto-reload and
6902
7256
  tunnelSubdomain = tunnelInfo.subdomain;
6903
7257
  process.env.MCP_URL = tunnelUrl;
6904
7258
  try {
6905
- const mPath = path7.join(projectPath, "dist", "mcp-use.json");
7259
+ const mPath = path8.join(projectPath, "dist", "mcp-use.json");
6906
7260
  let manifest = {};
6907
7261
  try {
6908
7262
  manifest = JSON.parse(await readFile3(mPath, "utf-8"));
@@ -6910,7 +7264,7 @@ program.command("dev").description("Run development server with auto-reload and
6910
7264
  }
6911
7265
  if (!manifest.tunnel) manifest.tunnel = {};
6912
7266
  manifest.tunnel.subdomain = tunnelSubdomain;
6913
- await mkdir3(path7.dirname(mPath), { recursive: true });
7267
+ await mkdir3(path8.dirname(mPath), { recursive: true });
6914
7268
  await writeFile3(mPath, JSON.stringify(manifest, null, 2), "utf-8");
6915
7269
  } catch {
6916
7270
  }
@@ -7006,9 +7360,15 @@ program.command("dev").description("Run development server with auto-reload and
7006
7360
  process.exit(1);
7007
7361
  }
7008
7362
  });
7009
- 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) => {
7363
+ program.command("start").description("Start production server").option("-p, --path <path>", "Path to project directory", process.cwd()).option(
7364
+ "--entry <file>",
7365
+ "Path to MCP server entry file (relative to project)"
7366
+ ).option(
7367
+ "--mcp-dir <dir>",
7368
+ "Folder holding the MCP entry + resources (e.g. 'src/mcp' for Next.js apps)"
7369
+ ).option("--port <port>", "Server port", "3000").option("--tunnel", "Expose server through a tunnel").action(async (options) => {
7010
7370
  try {
7011
- const projectPath = path7.resolve(options.path);
7371
+ const projectPath = path8.resolve(options.path);
7012
7372
  const portFlagProvided = process.argv.includes("--port") || process.argv.includes("-p") || process.argv.some((arg) => arg.startsWith("--port=")) || process.argv.some((arg) => arg.startsWith("-p="));
7013
7373
  let port = portFlagProvided ? parseInt(options.port, 10) : parseInt(process.env.PORT || options.port || "3000", 10);
7014
7374
  if (!await isPortAvailable(port)) {
@@ -7026,7 +7386,7 @@ program.command("start").description("Start production server").option("-p, --pa
7026
7386
  let tunnelSubdomain = void 0;
7027
7387
  if (options.tunnel) {
7028
7388
  try {
7029
- const manifestPath2 = path7.join(projectPath, "dist", "mcp-use.json");
7389
+ const manifestPath2 = path8.join(projectPath, "dist", "mcp-use.json");
7030
7390
  let existingSubdomain;
7031
7391
  try {
7032
7392
  const manifestContent = await readFile3(manifestPath2, "utf-8");
@@ -7081,7 +7441,7 @@ program.command("start").description("Start production server").option("-p, --pa
7081
7441
  manifest.tunnel = {};
7082
7442
  }
7083
7443
  manifest.tunnel.subdomain = subdomain;
7084
- await mkdir3(path7.dirname(manifestPath2), { recursive: true });
7444
+ await mkdir3(path8.dirname(manifestPath2), { recursive: true });
7085
7445
  await writeFile3(
7086
7446
  manifestPath2,
7087
7447
  JSON.stringify(manifest, null, 2),
@@ -7100,18 +7460,25 @@ program.command("start").description("Start production server").option("-p, --pa
7100
7460
  }
7101
7461
  }
7102
7462
  let serverFile;
7103
- const manifestPath = path7.join(projectPath, "dist", "mcp-use.json");
7463
+ const manifestPath = path8.join(projectPath, "dist", "mcp-use.json");
7104
7464
  try {
7105
7465
  const manifestContent = await readFile3(manifestPath, "utf-8");
7106
7466
  const manifest = JSON.parse(manifestContent);
7107
7467
  if (manifest.entryPoint) {
7108
- await access(path7.join(projectPath, manifest.entryPoint));
7468
+ await access(path8.join(projectPath, manifest.entryPoint));
7109
7469
  serverFile = manifest.entryPoint;
7110
7470
  }
7111
7471
  } catch {
7112
7472
  }
7113
7473
  if (!serverFile) {
7474
+ const startMcpDir = options.mcpDir;
7114
7475
  const serverCandidates = [
7476
+ ...startMcpDir ? [
7477
+ `${startMcpDir}/index.ts`,
7478
+ `${startMcpDir}/index.tsx`,
7479
+ `dist/${startMcpDir}/index.js`,
7480
+ `dist/${startMcpDir}/server.js`
7481
+ ] : [],
7115
7482
  "dist/index.js",
7116
7483
  "dist/server.js",
7117
7484
  "dist/src/index.js",
@@ -7119,7 +7486,7 @@ program.command("start").description("Start production server").option("-p, --pa
7119
7486
  ];
7120
7487
  for (const candidate of serverCandidates) {
7121
7488
  try {
7122
- await access(path7.join(projectPath, candidate));
7489
+ await access(path8.join(projectPath, candidate));
7123
7490
  serverFile = candidate;
7124
7491
  break;
7125
7492
  } catch {
@@ -7143,18 +7510,53 @@ Looked for:
7143
7510
  process.exit(1);
7144
7511
  }
7145
7512
  console.log("Starting production server...");
7146
- const env2 = {
7513
+ const isNextJsProject = await detectNextJsProject(projectPath);
7514
+ if (isNextJsProject) {
7515
+ console.log(
7516
+ source_default.gray(
7517
+ "Next.js detected \u2014 installing server-runtime shims for the production server"
7518
+ )
7519
+ );
7520
+ await loadNextJsEnvFiles(projectPath);
7521
+ }
7522
+ const baseEnv = {
7147
7523
  ...process.env,
7148
7524
  PORT: String(port),
7149
7525
  NODE_ENV: "production"
7150
7526
  };
7151
7527
  if (mcpUrl) {
7152
- env2.MCP_URL = mcpUrl;
7528
+ baseEnv.MCP_URL = mcpUrl;
7153
7529
  console.log(source_default.whiteBright(`Tunnel: ${mcpUrl}/mcp`));
7154
- } else if (!env2.MCP_URL) {
7155
- env2.MCP_URL = `http://localhost:${port}`;
7530
+ } else if (!baseEnv.MCP_URL) {
7531
+ baseEnv.MCP_URL = `http://localhost:${port}`;
7532
+ }
7533
+ const env2 = isNextJsProject ? withNextShimsEnv(baseEnv) : baseEnv;
7534
+ const isTsEntry = /\.(ts|tsx|mts|cts)$/.test(serverFile);
7535
+ let spawnCmd = "node";
7536
+ let spawnArgs = [serverFile];
7537
+ if (isTsEntry) {
7538
+ try {
7539
+ const projectRequire = createRequire2(
7540
+ path8.join(projectPath, "package.json")
7541
+ );
7542
+ const tsxPkgPath = projectRequire.resolve("tsx/package.json");
7543
+ const tsxPkg = JSON.parse(await readFile3(tsxPkgPath, "utf-8"));
7544
+ const binField = typeof tsxPkg.bin === "string" ? tsxPkg.bin : tsxPkg.bin?.tsx ?? Object.values(tsxPkg.bin ?? {})[0];
7545
+ if (!binField) throw new Error("tsx bin entry not found");
7546
+ const tsxBin = path8.resolve(path8.dirname(tsxPkgPath), binField);
7547
+ spawnCmd = "node";
7548
+ spawnArgs = [tsxBin, serverFile];
7549
+ } catch (error) {
7550
+ console.log(
7551
+ source_default.yellow(
7552
+ `Could not resolve local tsx (${error instanceof Error ? error.message : String(error)}); falling back to npx`
7553
+ )
7554
+ );
7555
+ spawnCmd = "npx";
7556
+ spawnArgs = ["tsx", serverFile];
7557
+ }
7156
7558
  }
7157
- const serverProc = spawn("node", [serverFile], {
7559
+ const serverProc = spawn(spawnCmd, spawnArgs, {
7158
7560
  cwd: projectPath,
7159
7561
  stdio: "inherit",
7160
7562
  env: env2
@@ -7290,7 +7692,7 @@ program.addCommand(createSkillsCommand());
7290
7692
  program.command("generate-types").description(
7291
7693
  "Generate TypeScript type definitions for tools (writes .mcp-use/tool-registry.d.ts)"
7292
7694
  ).option("-p, --path <path>", "Path to project directory", process.cwd()).option("--server <file>", "Server entry file", "index.ts").action(async (options) => {
7293
- const projectPath = path7.resolve(options.path);
7695
+ const projectPath = path8.resolve(options.path);
7294
7696
  try {
7295
7697
  console.log(source_default.blue("Generating tool registry types..."));
7296
7698
  const success = await generateToolRegistryTypesForServer(