@mcp-use/cli 3.1.0-canary.2 → 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(
@@ -5230,7 +5230,7 @@ async function addSkillsToProject(projectPath) {
5230
5230
  Readable.fromWeb(response.body),
5231
5231
  extract({
5232
5232
  cwd: tempDir,
5233
- filter: (path8) => path8.includes("/skills/"),
5233
+ filter: (path9) => path9.includes("/skills/"),
5234
5234
  strip: 1
5235
5235
  })
5236
5236
  );
@@ -5295,14 +5295,114 @@ function createSkillsCommand() {
5295
5295
  return skills;
5296
5296
  }
5297
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
+
5298
5398
  // src/utils/update-check.ts
5299
5399
  import { readFileSync } from "fs";
5300
5400
  import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
5301
5401
  import { createRequire } from "module";
5302
5402
  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");
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");
5306
5406
  var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
5307
5407
  var FETCH_TIMEOUT_MS = 3e3;
5308
5408
  var PACKAGE_NAME = "mcp-use";
@@ -5385,12 +5485,12 @@ function resolveInstalledVersion(projectPath) {
5385
5485
  if (projectPath) {
5386
5486
  attempts.push(() => {
5387
5487
  const projectRequire = createRequire(
5388
- path6.join(projectPath, "package.json")
5488
+ path7.join(projectPath, "package.json")
5389
5489
  );
5390
5490
  return projectRequire.resolve(`${PACKAGE_NAME}/package.json`);
5391
5491
  });
5392
5492
  }
5393
- attempts.push(() => path6.join(__dirname, "../../mcp-use/package.json"));
5493
+ attempts.push(() => path7.join(__dirname, "../../mcp-use/package.json"));
5394
5494
  for (const attempt of attempts) {
5395
5495
  try {
5396
5496
  const pkgPath = attempt();
@@ -5428,7 +5528,7 @@ A new release of ${source_default.bold(PACKAGE_NAME)} is available: ${source_def
5428
5528
  // src/index.ts
5429
5529
  var program = new Command6();
5430
5530
  var packageContent = readFileSync2(
5431
- path7.join(__dirname, "../package.json"),
5531
+ path8.join(__dirname, "../package.json"),
5432
5532
  "utf-8"
5433
5533
  );
5434
5534
  var packageJson = JSON.parse(packageContent);
@@ -5459,14 +5559,14 @@ function displayPackageVersions(projectPath) {
5459
5559
  if (projectPath) {
5460
5560
  try {
5461
5561
  const projectRequire = createRequire2(
5462
- path7.join(projectPath, "package.json")
5562
+ path8.join(projectPath, "package.json")
5463
5563
  );
5464
5564
  pkgPath = projectRequire.resolve(`${pkg.name}/package.json`);
5465
5565
  } catch (resolveError) {
5466
- pkgPath = path7.join(__dirname, pkg.relativePath);
5566
+ pkgPath = path8.join(__dirname, pkg.relativePath);
5467
5567
  }
5468
5568
  } else {
5469
- pkgPath = path7.join(__dirname, pkg.relativePath);
5569
+ pkgPath = path8.join(__dirname, pkg.relativePath);
5470
5570
  }
5471
5571
  const pkgContent = readFileSync2(pkgPath, "utf-8");
5472
5572
  const pkgJson = JSON.parse(pkgContent);
@@ -5618,20 +5718,92 @@ async function startTunnel(port, subdomain) {
5618
5718
  }, 3e4);
5619
5719
  });
5620
5720
  }
5621
- 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
+ }
5622
5753
  const candidates = ["index.ts", "src/index.ts", "server.ts", "src/server.ts"];
5623
5754
  for (const candidate of candidates) {
5624
5755
  try {
5625
- await access(path7.join(projectPath, candidate));
5756
+ await access(path8.join(projectPath, candidate));
5626
5757
  return candidate;
5627
5758
  } catch {
5628
5759
  continue;
5629
5760
  }
5630
5761
  }
5631
- 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);
5632
5804
  }
5633
5805
  async function generateToolRegistryTypesForServer(projectPath, serverFileRelative) {
5634
- const serverFile = path7.join(projectPath, serverFileRelative);
5806
+ const serverFile = path8.join(projectPath, serverFileRelative);
5635
5807
  const serverFileExists = await access(serverFile).then(() => true).catch(() => false);
5636
5808
  if (!serverFileExists) {
5637
5809
  throw new Error(`Server file not found: ${serverFile}`);
@@ -5640,19 +5812,48 @@ async function generateToolRegistryTypesForServer(projectPath, serverFileRelativ
5640
5812
  try {
5641
5813
  globalThis.__mcpUseHmrMode = true;
5642
5814
  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
- });
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
+ }
5648
5849
  const server = globalThis.__mcpUseLastServer;
5649
5850
  if (!server) {
5650
5851
  throw new Error(
5651
5852
  "No MCPServer instance found. Make sure your server file creates an MCPServer instance."
5652
5853
  );
5653
5854
  }
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);
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);
5656
5857
  if (!generateToolRegistryTypes) {
5657
5858
  throw new Error("generateToolRegistryTypes not found in mcp-use package");
5658
5859
  }
@@ -5667,21 +5868,24 @@ async function generateToolRegistryTypesForServer(projectPath, serverFileRelativ
5667
5868
  }
5668
5869
  async function buildWidgets(projectPath, options = {}) {
5669
5870
  const { inline = true } = options;
5670
- const { promises: fs10 } = await import("fs");
5871
+ const { promises: fs11 } = await import("fs");
5671
5872
  const { build } = await import("vite");
5672
- const resourcesDir = path7.join(projectPath, "resources");
5873
+ const widgetsDirRelative = options.widgetsDir ?? "resources";
5874
+ const resourcesDir = path8.resolve(projectPath, widgetsDirRelative);
5673
5875
  const mcpUrl = process.env.MCP_URL;
5674
5876
  try {
5675
5877
  await access(resourcesDir);
5676
5878
  } catch {
5677
5879
  console.log(
5678
- source_default.gray("No resources/ directory found - skipping widget build")
5880
+ source_default.gray(
5881
+ `No ${widgetsDirRelative}/ directory found - skipping widget build`
5882
+ )
5679
5883
  );
5680
5884
  return [];
5681
5885
  }
5682
5886
  const entries = [];
5683
5887
  try {
5684
- const files = await fs10.readdir(resourcesDir, { withFileTypes: true });
5888
+ const files = await fs11.readdir(resourcesDir, { withFileTypes: true });
5685
5889
  for (const dirent of files) {
5686
5890
  if (dirent.name.startsWith("._") || dirent.name.startsWith(".DS_Store")) {
5687
5891
  continue;
@@ -5689,12 +5893,12 @@ async function buildWidgets(projectPath, options = {}) {
5689
5893
  if (dirent.isFile() && (dirent.name.endsWith(".tsx") || dirent.name.endsWith(".ts"))) {
5690
5894
  entries.push({
5691
5895
  name: dirent.name.replace(/\.tsx?$/, ""),
5692
- path: path7.join(resourcesDir, dirent.name)
5896
+ path: path8.join(resourcesDir, dirent.name)
5693
5897
  });
5694
5898
  } else if (dirent.isDirectory()) {
5695
- const widgetPath = path7.join(resourcesDir, dirent.name, "widget.tsx");
5899
+ const widgetPath = path8.join(resourcesDir, dirent.name, "widget.tsx");
5696
5900
  try {
5697
- await fs10.access(widgetPath);
5901
+ await fs11.access(widgetPath);
5698
5902
  entries.push({
5699
5903
  name: dirent.name,
5700
5904
  path: widgetPath
@@ -5704,11 +5908,15 @@ async function buildWidgets(projectPath, options = {}) {
5704
5908
  }
5705
5909
  }
5706
5910
  } catch (error) {
5707
- 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
+ );
5708
5914
  return [];
5709
5915
  }
5710
5916
  if (entries.length === 0) {
5711
- 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
+ );
5712
5920
  return [];
5713
5921
  }
5714
5922
  console.log(
@@ -5718,10 +5926,17 @@ async function buildWidgets(projectPath, options = {}) {
5718
5926
  );
5719
5927
  const react = (await import("@vitejs/plugin-react")).default;
5720
5928
  const tailwindcss = (await import("@tailwindcss/vite")).default;
5721
- 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");
5722
5937
  let favicon = "";
5723
5938
  try {
5724
- const pkgContent = await fs10.readFile(packageJsonPath, "utf-8");
5939
+ const pkgContent = await fs11.readFile(packageJsonPath, "utf-8");
5725
5940
  const pkg = JSON.parse(pkgContent);
5726
5941
  favicon = pkg.mcpUse?.favicon || "";
5727
5942
  } catch {
@@ -5730,18 +5945,27 @@ async function buildWidgets(projectPath, options = {}) {
5730
5945
  const widgetName = entry.name;
5731
5946
  const entryPath = entry.path.replace(/\\/g, "/");
5732
5947
  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, "/");
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
+ }
5738
5962
  const cssContent = `@import "tailwindcss";
5739
5963
 
5740
5964
  /* Configure Tailwind to scan the resources directory and mcp-use package */
5741
5965
  @source "${relativeResourcesPath}";
5742
5966
  @source "${relativeMcpUsePath}/**/*.{ts,tsx,js,jsx}";
5743
- `;
5744
- await fs10.writeFile(path7.join(tempDir, "styles.css"), cssContent, "utf8");
5967
+ ${projectSrcSourceLine}`;
5968
+ await fs11.writeFile(path8.join(tempDir, "styles.css"), cssContent, "utf8");
5745
5969
  const entryContent = `import React from 'react'
5746
5970
  import { createRoot } from 'react-dom/client'
5747
5971
  import './styles.css'
@@ -5766,9 +5990,9 @@ if (container && Component) {
5766
5990
  <script type="module" src="/entry.tsx"></script>
5767
5991
  </body>
5768
5992
  </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(
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(
5772
5996
  projectPath,
5773
5997
  "dist",
5774
5998
  "resources",
@@ -5778,12 +6002,12 @@ if (container && Component) {
5778
6002
  const baseUrl = mcpUrl ? `${mcpUrl}/${widgetName}/` : `/mcp-use/widgets/${widgetName}/`;
5779
6003
  let widgetMetadata = {};
5780
6004
  try {
5781
- const metadataTempDir = path7.join(
6005
+ const metadataTempDir = path8.join(
5782
6006
  projectPath,
5783
6007
  ".mcp-use",
5784
6008
  `${widgetName}-metadata`
5785
6009
  );
5786
- await fs10.mkdir(metadataTempDir, { recursive: true });
6010
+ await fs11.mkdir(metadataTempDir, { recursive: true });
5787
6011
  const { createServer } = await import("vite");
5788
6012
  const nodeStubsPlugin = {
5789
6013
  name: "node-stubs",
@@ -5811,15 +6035,16 @@ export default PostHog;
5811
6035
  return null;
5812
6036
  }
5813
6037
  };
6038
+ const serverOnlyGuard = makeWidgetServerOnlyGuard(widgetName);
5814
6039
  const metadataServer = await createServer({
5815
6040
  root: metadataTempDir,
5816
- cacheDir: path7.join(metadataTempDir, ".vite-cache"),
5817
- plugins: [nodeStubsPlugin, tailwindcss(), react()],
5818
- resolve: {
5819
- alias: {
5820
- "@": resourcesDir
5821
- }
5822
- },
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 } },
5823
6048
  server: {
5824
6049
  middlewareMode: true
5825
6050
  },
@@ -5898,7 +6123,7 @@ export default PostHog;
5898
6123
  } finally {
5899
6124
  await metadataServer.close();
5900
6125
  try {
5901
- await fs10.rm(metadataTempDir, { recursive: true, force: true });
6126
+ await fs11.rm(metadataTempDir, { recursive: true, force: true });
5902
6127
  } catch {
5903
6128
  }
5904
6129
  }
@@ -5993,12 +6218,14 @@ export default {
5993
6218
  return null;
5994
6219
  }
5995
6220
  };
6221
+ const buildServerOnlyGuard = makeWidgetServerOnlyGuard(widgetName);
5996
6222
  const buildPlugins = inline ? [
6223
+ buildServerOnlyGuard,
5997
6224
  buildNodeStubsPlugin,
5998
6225
  tailwindcss(),
5999
6226
  react(),
6000
6227
  viteSingleFile({ removeViteModuleLoader: true })
6001
- ] : [buildNodeStubsPlugin, tailwindcss(), react()];
6228
+ ] : [buildServerOnlyGuard, buildNodeStubsPlugin, tailwindcss(), react()];
6002
6229
  await build({
6003
6230
  root: tempDir,
6004
6231
  base: baseUrl,
@@ -6017,11 +6244,10 @@ export default {
6017
6244
  }
6018
6245
  }
6019
6246
  },
6020
- resolve: {
6021
- alias: {
6022
- "@": resourcesDir
6023
- }
6024
- },
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 } },
6025
6251
  optimizeDeps: {
6026
6252
  // Exclude Node.js-only packages from browser bundling
6027
6253
  exclude: ["posthog-node"]
@@ -6043,7 +6269,7 @@ export default {
6043
6269
  // Inline all assets under 100MB (effectively all)
6044
6270
  } : {},
6045
6271
  rolldownOptions: {
6046
- input: path7.join(tempDir, "index.html"),
6272
+ input: path8.join(tempDir, "index.html"),
6047
6273
  external: (id) => {
6048
6274
  return false;
6049
6275
  }
@@ -6051,12 +6277,12 @@ export default {
6051
6277
  }
6052
6278
  });
6053
6279
  try {
6054
- const assetsDir = path7.join(outDir, "assets");
6055
- const assetFiles = await fs10.readdir(assetsDir);
6280
+ const assetsDir = path8.join(outDir, "assets");
6281
+ const assetFiles = await fs11.readdir(assetsDir);
6056
6282
  const jsFiles = assetFiles.filter((f) => f.endsWith(".js"));
6057
6283
  for (const jsFile of jsFiles) {
6058
- const jsPath = path7.join(assetsDir, jsFile);
6059
- let content = await fs10.readFile(jsPath, "utf8");
6284
+ const jsPath = path8.join(assetsDir, jsFile);
6285
+ let content = await fs11.readFile(jsPath, "utf8");
6060
6286
  const zodConfigPatterns = [
6061
6287
  // Non-minified: export const globalConfig = {}
6062
6288
  /export\s+const\s+globalConfig\s*=\s*\{\s*\}/g,
@@ -6075,7 +6301,7 @@ export default {
6075
6301
  }
6076
6302
  }
6077
6303
  if (patched) {
6078
- await fs10.writeFile(jsPath, content, "utf8");
6304
+ await fs11.writeFile(jsPath, content, "utf8");
6079
6305
  console.log(source_default.gray(` \u2192 Patched Zod JIT in ${jsFile}`));
6080
6306
  }
6081
6307
  }
@@ -6087,8 +6313,8 @@ export default {
6087
6313
  const mcpServerUrl = process.env.MCP_SERVER_URL;
6088
6314
  if (mcpServerUrl) {
6089
6315
  try {
6090
- const htmlPath = path7.join(outDir, "index.html");
6091
- let html = await fs10.readFile(htmlPath, "utf8");
6316
+ const htmlPath = path8.join(outDir, "index.html");
6317
+ let html = await fs11.readFile(htmlPath, "utf8");
6092
6318
  const injectionScript = `<script>window.__getFile = (filename) => { return "${mcpUrl}/${widgetName}/"+filename }; window.__mcpPublicUrl = "${mcpServerUrl}/mcp-use/public"; window.__mcpPublicAssetsUrl = "${mcpUrl}/public";</script>`;
6093
6319
  if (!html.includes("window.__mcpPublicUrl")) {
6094
6320
  html = html.replace(
@@ -6109,7 +6335,7 @@ export default {
6109
6335
  <base href="${mcpServerUrl}">`
6110
6336
  );
6111
6337
  }
6112
- await fs10.writeFile(htmlPath, html, "utf8");
6338
+ await fs11.writeFile(htmlPath, html, "utf8");
6113
6339
  console.log(
6114
6340
  source_default.gray(` \u2192 Injected MCP_SERVER_URL into ${widgetName}`)
6115
6341
  );
@@ -6123,22 +6349,32 @@ export default {
6123
6349
  }
6124
6350
  }
6125
6351
  console.log(source_default.green(` \u2713 Built ${widgetName}`));
6126
- return { name: widgetName, metadata: widgetMetadata };
6352
+ return {
6353
+ status: "built",
6354
+ name: widgetName,
6355
+ metadata: widgetMetadata
6356
+ };
6127
6357
  } catch (error) {
6128
6358
  console.error(source_default.red(` \u2717 Failed to build ${widgetName}:`), error);
6129
- return null;
6359
+ return { status: "failed", name: widgetName };
6130
6360
  }
6131
6361
  };
6132
6362
  const buildResults = await Promise.all(
6133
6363
  entries.map((entry) => buildSingleWidget(entry))
6134
6364
  );
6135
- const builtWidgets = buildResults.filter(
6136
- (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 }] : []
6137
6374
  );
6138
- return builtWidgets;
6139
6375
  }
6140
6376
  async function collectTsFiles(projectPath, includePatterns, excludePatterns) {
6141
- const { promises: fs10 } = await import("fs");
6377
+ const { promises: fs11 } = await import("fs");
6142
6378
  const literalFiles = [];
6143
6379
  const dirPrefixes = [];
6144
6380
  for (const pattern of includePatterns) {
@@ -6153,7 +6389,7 @@ async function collectTsFiles(projectPath, includePatterns, excludePatterns) {
6153
6389
  for (const file of literalFiles) {
6154
6390
  if (/\.tsx?$/.test(file) && !file.endsWith(".d.ts")) {
6155
6391
  try {
6156
- await access(path7.join(projectPath, file));
6392
+ await access(path8.join(projectPath, file));
6157
6393
  files.push(file);
6158
6394
  } catch {
6159
6395
  }
@@ -6161,13 +6397,13 @@ async function collectTsFiles(projectPath, includePatterns, excludePatterns) {
6161
6397
  }
6162
6398
  const excludeSet = new Set(excludePatterns.map((e) => e.replace(/\*+/g, "")));
6163
6399
  for (const prefix of dirPrefixes) {
6164
- const dirPath = path7.join(projectPath, prefix);
6400
+ const dirPath = path8.join(projectPath, prefix);
6165
6401
  try {
6166
- const entries = await fs10.readdir(dirPath, { recursive: true });
6402
+ const entries = await fs11.readdir(dirPath, { recursive: true });
6167
6403
  for (const entry of entries) {
6168
6404
  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])) {
6405
+ const rel = path8.join(prefix, entryStr);
6406
+ if (/\.tsx?$/.test(entryStr) && !entryStr.endsWith(".d.ts") && !excludeSet.has(rel.split(path8.sep)[0])) {
6171
6407
  files.push(rel);
6172
6408
  }
6173
6409
  }
@@ -6178,11 +6414,11 @@ async function collectTsFiles(projectPath, includePatterns, excludePatterns) {
6178
6414
  }
6179
6415
  async function transpileWithEsbuild(projectPath) {
6180
6416
  const esbuild = await import("esbuild");
6181
- const { promises: fs10 } = await import("fs");
6182
- const tsconfigPath = path7.join(projectPath, "tsconfig.json");
6417
+ const { promises: fs11 } = await import("fs");
6418
+ const tsconfigPath = path8.join(projectPath, "tsconfig.json");
6183
6419
  let tsconfig = {};
6184
6420
  try {
6185
- const raw = await fs10.readFile(tsconfigPath, "utf-8");
6421
+ const raw = await fs11.readFile(tsconfigPath, "utf-8");
6186
6422
  tsconfig = JSON.parse(raw);
6187
6423
  } catch {
6188
6424
  }
@@ -6209,10 +6445,10 @@ async function transpileWithEsbuild(projectPath) {
6209
6445
  const target = (compilerOptions.target || "ES2022").toLowerCase();
6210
6446
  const moduleStr = (compilerOptions.module || "ESNext").toLowerCase();
6211
6447
  const format = moduleStr.includes("commonjs") ? "cjs" : "esm";
6212
- const outbase = compilerOptions.rootDir ? path7.resolve(projectPath, compilerOptions.rootDir) : projectPath;
6448
+ const outbase = compilerOptions.rootDir ? path8.resolve(projectPath, compilerOptions.rootDir) : projectPath;
6213
6449
  await esbuild.build({
6214
- entryPoints: files.map((f) => path7.join(projectPath, f)),
6215
- outdir: path7.join(projectPath, outDir),
6450
+ entryPoints: files.map((f) => path8.join(projectPath, f)),
6451
+ outdir: path8.join(projectPath, outDir),
6216
6452
  outbase,
6217
6453
  bundle: false,
6218
6454
  format,
@@ -6224,21 +6460,42 @@ async function transpileWithEsbuild(projectPath) {
6224
6460
  logLevel: "warning"
6225
6461
  });
6226
6462
  }
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(
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(
6228
6473
  "--inline",
6229
6474
  "Inline all JS/CSS into HTML (required for VS Code MCP Apps)"
6230
6475
  ).option("--no-inline", "Keep JS/CSS as separate files (default)").option("--no-typecheck", "Skip TypeScript type checking (faster builds)").action(async (options) => {
6231
6476
  try {
6232
- const projectPath = path7.resolve(options.path);
6233
- const { promises: fs10 } = await import("fs");
6477
+ const projectPath = path8.resolve(options.path);
6478
+ const { promises: fs11 } = await import("fs");
6234
6479
  displayPackageVersions(projectPath);
6480
+ const mcpDir = options.mcpDir;
6481
+ const widgetsDir = resolveWidgetsDir(options.widgetsDir, mcpDir);
6235
6482
  const builtWidgets = await buildWidgets(projectPath, {
6236
- inline: options.inline ?? false
6483
+ inline: options.inline ?? false,
6484
+ widgetsDir
6237
6485
  });
6238
6486
  let sourceServerFile;
6239
6487
  try {
6240
- sourceServerFile = await findServerFile(projectPath);
6241
- } 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
+ );
6242
6499
  }
6243
6500
  if (sourceServerFile) {
6244
6501
  console.log(source_default.gray("Generating tool registry types..."));
@@ -6256,17 +6513,25 @@ program.command("build").description("Build TypeScript and MCP UI widgets").opti
6256
6513
  );
6257
6514
  }
6258
6515
  }
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) {
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) {
6263
6528
  console.log(source_default.gray("Type checking..."));
6264
6529
  try {
6265
6530
  await runCommand(
6266
6531
  "node",
6267
6532
  [
6268
6533
  "--max-old-space-size=4096",
6269
- path7.join(
6534
+ path8.join(
6270
6535
  projectPath,
6271
6536
  "node_modules",
6272
6537
  "typescript",
@@ -6287,39 +6552,43 @@ program.command("build").description("Build TypeScript and MCP UI widgets").opti
6287
6552
  }
6288
6553
  let entryPoint;
6289
6554
  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;
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
+ }
6306
6575
  }
6307
6576
  }
6308
6577
  }
6309
- const publicDir = path7.join(projectPath, "public");
6578
+ const publicDir = path8.join(projectPath, "public");
6310
6579
  try {
6311
- await fs10.access(publicDir);
6580
+ await fs11.access(publicDir);
6312
6581
  console.log(source_default.gray("Copying public assets..."));
6313
- await fs10.cp(publicDir, path7.join(projectPath, "dist", "public"), {
6582
+ await fs11.cp(publicDir, path8.join(projectPath, "dist", "public"), {
6314
6583
  recursive: true
6315
6584
  });
6316
6585
  console.log(source_default.green("\u2713 Public assets copied"));
6317
6586
  } catch {
6318
6587
  }
6319
- const manifestPath = path7.join(projectPath, "dist", "mcp-use.json");
6588
+ const manifestPath = path8.join(projectPath, "dist", "mcp-use.json");
6320
6589
  let existingManifest = {};
6321
6590
  try {
6322
- const existingContent = await fs10.readFile(manifestPath, "utf-8");
6591
+ const existingContent = await fs11.readFile(manifestPath, "utf-8");
6323
6592
  existingManifest = JSON.parse(existingContent);
6324
6593
  } catch {
6325
6594
  }
@@ -6341,8 +6610,8 @@ program.command("build").description("Build TypeScript and MCP UI widgets").opti
6341
6610
  // Server entry point for `mcp-use start`
6342
6611
  widgets: widgetsData
6343
6612
  };
6344
- await fs10.mkdir(path7.dirname(manifestPath), { recursive: true });
6345
- await fs10.writeFile(
6613
+ await fs11.mkdir(path8.dirname(manifestPath), { recursive: true });
6614
+ await fs11.writeFile(
6346
6615
  manifestPath,
6347
6616
  JSON.stringify(manifest, null, 2),
6348
6617
  "utf8"
@@ -6362,14 +6631,23 @@ program.command("build").description("Build TypeScript and MCP UI widgets").opti
6362
6631
  process.exit(1);
6363
6632
  }
6364
6633
  });
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(
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(
6366
6644
  "--host <host>",
6367
6645
  "Server host (use 0.0.0.0 to listen on all interfaces)",
6368
6646
  "0.0.0.0"
6369
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) => {
6370
6648
  try {
6371
6649
  process.env.MCP_USE_CLI_DEV = "1";
6372
- const projectPath = path7.resolve(options.path);
6650
+ const projectPath = path8.resolve(options.path);
6373
6651
  let port = parseInt(options.port, 10);
6374
6652
  const host = options.host;
6375
6653
  const useHmr = options.hmr !== false;
@@ -6380,13 +6658,24 @@ program.command("dev").description("Run development server with auto-reload and
6380
6658
  console.log(source_default.green.bold(`\u2713 Using port ${availablePort} instead`));
6381
6659
  port = availablePort;
6382
6660
  }
6383
- 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
+ }
6384
6673
  let tunnelProcess = void 0;
6385
6674
  let tunnelSubdomain = void 0;
6386
6675
  let tunnelUrl = void 0;
6387
6676
  if (options.tunnel) {
6388
6677
  try {
6389
- const manifestPath = path7.join(projectPath, "dist", "mcp-use.json");
6678
+ const manifestPath = path8.join(projectPath, "dist", "mcp-use.json");
6390
6679
  let existingSubdomain;
6391
6680
  try {
6392
6681
  const manifestContent = await readFile3(manifestPath, "utf-8");
@@ -6435,7 +6724,7 @@ program.command("dev").description("Run development server with auto-reload and
6435
6724
  manifest.tunnel = {};
6436
6725
  }
6437
6726
  manifest.tunnel.subdomain = tunnelSubdomain;
6438
- await mkdir3(path7.dirname(manifestPath), { recursive: true });
6727
+ await mkdir3(path8.dirname(manifestPath), { recursive: true });
6439
6728
  await writeFile3(
6440
6729
  manifestPath,
6441
6730
  JSON.stringify(manifest, null, 2),
@@ -6462,22 +6751,35 @@ program.command("dev").description("Run development server with auto-reload and
6462
6751
  } else if (!process.env.MCP_URL) {
6463
6752
  process.env.MCP_URL = mcpUrl;
6464
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
+ }
6465
6763
  if (!useHmr) {
6466
6764
  console.log(source_default.gray("HMR disabled, using tsx watch (full restart)"));
6467
6765
  const processes = [];
6468
- 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,
6469
6770
  PORT: String(port),
6470
6771
  HOST: host,
6471
6772
  NODE_ENV: "development",
6472
6773
  // Preserve user-provided MCP_URL (e.g., for reverse proxy setups)
6473
6774
  MCP_URL: process.env.MCP_URL || mcpUrl
6474
6775
  };
6776
+ const env2 = isNextJsProject ? withNextShimsEnv(baseEnv) : baseEnv;
6475
6777
  const { createRequire: createRequire4 } = await import("module");
6476
6778
  let cmd;
6477
6779
  let args;
6478
6780
  try {
6479
6781
  const projectRequire = createRequire4(
6480
- path7.join(projectPath, "package.json")
6782
+ path8.join(projectPath, "package.json")
6481
6783
  );
6482
6784
  const tsxPkgPath = projectRequire.resolve("tsx/package.json");
6483
6785
  const tsxPkg = JSON.parse(await readFile3(tsxPkgPath, "utf-8"));
@@ -6489,7 +6791,7 @@ program.command("dev").description("Run development server with auto-reload and
6489
6791
  } else {
6490
6792
  throw new Error("No bin field found in tsx package.json");
6491
6793
  }
6492
- const tsxBin = path7.resolve(path7.dirname(tsxPkgPath), binPath);
6794
+ const tsxBin = path8.resolve(path8.dirname(tsxPkgPath), binPath);
6493
6795
  cmd = "node";
6494
6796
  args = [tsxBin, "watch", serverFile];
6495
6797
  } catch (error) {
@@ -6564,18 +6866,56 @@ program.command("dev").description("Run development server with auto-reload and
6564
6866
  "HMR enabled - changes will hot reload without dropping connections"
6565
6867
  )
6566
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
+ }
6567
6879
  const chokidarModule = await import("chokidar");
6568
6880
  const chokidar = chokidarModule.default || chokidarModule;
6569
- const { fileURLToPath: fileURLToPath3 } = await import("url");
6881
+ const { fileURLToPath: fileURLToPath4 } = await import("url");
6570
6882
  const { createRequire: createRequire3 } = await import("module");
6571
- 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;
6572
6893
  try {
6573
6894
  const projectRequire = createRequire3(
6574
- path7.join(projectPath, "package.json")
6895
+ path8.join(projectPath, "package.json")
6575
6896
  );
6576
- const tsxApiPath = projectRequire.resolve("tsx/esm/api");
6577
- const tsxApi = await import(pathToFileURL(tsxApiPath).href);
6578
- 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
+ }
6579
6919
  } catch {
6580
6920
  console.log(
6581
6921
  source_default.yellow(
@@ -6583,25 +6923,15 @@ program.command("dev").description("Run development server with auto-reload and
6583
6923
  )
6584
6924
  );
6585
6925
  }
6586
- const serverFilePath = path7.join(projectPath, serverFile);
6587
- const serverFileUrl = pathToFileURL(serverFilePath).href;
6926
+ const serverFilePath = path8.join(projectPath, serverFile);
6927
+ const serverFileUrl = pathToFileURL2(serverFilePath).href;
6588
6928
  globalThis.__mcpUseHmrMode = true;
6589
6929
  const importServerModule = async () => {
6590
6930
  const previousServer = globalThis.__mcpUseLastServer;
6591
6931
  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()}`);
6932
+ if (!tsxLoaderActive) {
6604
6933
  }
6934
+ await import(`${serverFileUrl}?t=${Date.now()}`);
6605
6935
  const instance = globalThis.__mcpUseLastServer;
6606
6936
  if (!instance) {
6607
6937
  globalThis.__mcpUseLastServer = previousServer;
@@ -6615,7 +6945,7 @@ program.command("dev").description("Run development server with auto-reload and
6615
6945
  if (instance === previousServer) {
6616
6946
  console.warn(
6617
6947
  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.")
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.")
6619
6949
  )
6620
6950
  );
6621
6951
  return null;
@@ -6690,8 +7020,8 @@ program.command("dev").description("Run development server with auto-reload and
6690
7020
  }
6691
7021
  let watcher = chokidar.watch(".", {
6692
7022
  cwd: projectPath,
6693
- ignored: (path8, stats) => {
6694
- const normalizedPath = path8.replace(/\\/g, "/");
7023
+ ignored: (path9, stats) => {
7024
+ const normalizedPath = path9.replace(/\\/g, "/");
6695
7025
  if (/(^|\/)\.[^/]/.test(normalizedPath)) {
6696
7026
  return true;
6697
7027
  }
@@ -6865,7 +7195,7 @@ program.command("dev").description("Run development server with auto-reload and
6865
7195
  }
6866
7196
  tunnelUrl = void 0;
6867
7197
  if (withTunnel) {
6868
- const manifestPath = path7.join(projectPath, "dist", "mcp-use.json");
7198
+ const manifestPath = path8.join(projectPath, "dist", "mcp-use.json");
6869
7199
  let existingSubdomain;
6870
7200
  try {
6871
7201
  const manifestContent = await readFile3(manifestPath, "utf-8");
@@ -6902,7 +7232,7 @@ program.command("dev").description("Run development server with auto-reload and
6902
7232
  tunnelSubdomain = tunnelInfo.subdomain;
6903
7233
  process.env.MCP_URL = tunnelUrl;
6904
7234
  try {
6905
- const mPath = path7.join(projectPath, "dist", "mcp-use.json");
7235
+ const mPath = path8.join(projectPath, "dist", "mcp-use.json");
6906
7236
  let manifest = {};
6907
7237
  try {
6908
7238
  manifest = JSON.parse(await readFile3(mPath, "utf-8"));
@@ -6910,7 +7240,7 @@ program.command("dev").description("Run development server with auto-reload and
6910
7240
  }
6911
7241
  if (!manifest.tunnel) manifest.tunnel = {};
6912
7242
  manifest.tunnel.subdomain = tunnelSubdomain;
6913
- await mkdir3(path7.dirname(mPath), { recursive: true });
7243
+ await mkdir3(path8.dirname(mPath), { recursive: true });
6914
7244
  await writeFile3(mPath, JSON.stringify(manifest, null, 2), "utf-8");
6915
7245
  } catch {
6916
7246
  }
@@ -7006,9 +7336,15 @@ program.command("dev").description("Run development server with auto-reload and
7006
7336
  process.exit(1);
7007
7337
  }
7008
7338
  });
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) => {
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) => {
7010
7346
  try {
7011
- const projectPath = path7.resolve(options.path);
7347
+ const projectPath = path8.resolve(options.path);
7012
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="));
7013
7349
  let port = portFlagProvided ? parseInt(options.port, 10) : parseInt(process.env.PORT || options.port || "3000", 10);
7014
7350
  if (!await isPortAvailable(port)) {
@@ -7026,7 +7362,7 @@ program.command("start").description("Start production server").option("-p, --pa
7026
7362
  let tunnelSubdomain = void 0;
7027
7363
  if (options.tunnel) {
7028
7364
  try {
7029
- const manifestPath2 = path7.join(projectPath, "dist", "mcp-use.json");
7365
+ const manifestPath2 = path8.join(projectPath, "dist", "mcp-use.json");
7030
7366
  let existingSubdomain;
7031
7367
  try {
7032
7368
  const manifestContent = await readFile3(manifestPath2, "utf-8");
@@ -7081,7 +7417,7 @@ program.command("start").description("Start production server").option("-p, --pa
7081
7417
  manifest.tunnel = {};
7082
7418
  }
7083
7419
  manifest.tunnel.subdomain = subdomain;
7084
- await mkdir3(path7.dirname(manifestPath2), { recursive: true });
7420
+ await mkdir3(path8.dirname(manifestPath2), { recursive: true });
7085
7421
  await writeFile3(
7086
7422
  manifestPath2,
7087
7423
  JSON.stringify(manifest, null, 2),
@@ -7100,18 +7436,25 @@ program.command("start").description("Start production server").option("-p, --pa
7100
7436
  }
7101
7437
  }
7102
7438
  let serverFile;
7103
- const manifestPath = path7.join(projectPath, "dist", "mcp-use.json");
7439
+ const manifestPath = path8.join(projectPath, "dist", "mcp-use.json");
7104
7440
  try {
7105
7441
  const manifestContent = await readFile3(manifestPath, "utf-8");
7106
7442
  const manifest = JSON.parse(manifestContent);
7107
7443
  if (manifest.entryPoint) {
7108
- await access(path7.join(projectPath, manifest.entryPoint));
7444
+ await access(path8.join(projectPath, manifest.entryPoint));
7109
7445
  serverFile = manifest.entryPoint;
7110
7446
  }
7111
7447
  } catch {
7112
7448
  }
7113
7449
  if (!serverFile) {
7450
+ const startMcpDir = options.mcpDir;
7114
7451
  const serverCandidates = [
7452
+ ...startMcpDir ? [
7453
+ `${startMcpDir}/index.ts`,
7454
+ `${startMcpDir}/index.tsx`,
7455
+ `dist/${startMcpDir}/index.js`,
7456
+ `dist/${startMcpDir}/server.js`
7457
+ ] : [],
7115
7458
  "dist/index.js",
7116
7459
  "dist/server.js",
7117
7460
  "dist/src/index.js",
@@ -7119,7 +7462,7 @@ program.command("start").description("Start production server").option("-p, --pa
7119
7462
  ];
7120
7463
  for (const candidate of serverCandidates) {
7121
7464
  try {
7122
- await access(path7.join(projectPath, candidate));
7465
+ await access(path8.join(projectPath, candidate));
7123
7466
  serverFile = candidate;
7124
7467
  break;
7125
7468
  } catch {
@@ -7143,18 +7486,53 @@ Looked for:
7143
7486
  process.exit(1);
7144
7487
  }
7145
7488
  console.log("Starting production server...");
7146
- 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 = {
7147
7499
  ...process.env,
7148
7500
  PORT: String(port),
7149
7501
  NODE_ENV: "production"
7150
7502
  };
7151
7503
  if (mcpUrl) {
7152
- env2.MCP_URL = mcpUrl;
7504
+ baseEnv.MCP_URL = mcpUrl;
7153
7505
  console.log(source_default.whiteBright(`Tunnel: ${mcpUrl}/mcp`));
7154
- } else if (!env2.MCP_URL) {
7155
- 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
+ }
7156
7534
  }
7157
- const serverProc = spawn("node", [serverFile], {
7535
+ const serverProc = spawn(spawnCmd, spawnArgs, {
7158
7536
  cwd: projectPath,
7159
7537
  stdio: "inherit",
7160
7538
  env: env2
@@ -7290,7 +7668,7 @@ program.addCommand(createSkillsCommand());
7290
7668
  program.command("generate-types").description(
7291
7669
  "Generate TypeScript type definitions for tools (writes .mcp-use/tool-registry.d.ts)"
7292
7670
  ).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);
7671
+ const projectPath = path8.resolve(options.path);
7294
7672
  try {
7295
7673
  console.log(source_default.blue("Generating tool registry types..."));
7296
7674
  const success = await generateToolRegistryTypesForServer(