@leanmcp/cli 0.5.4-alpha.9.641ef8f → 0.5.5

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/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2025 LeanMCP Contributors
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2025 LeanMCP Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/bin/leanmcp.js CHANGED
File without changes
package/dist/index.js CHANGED
@@ -3,8 +3,8 @@ var __name = (target, value) => __defProp(target, "name", { value, configurable:
3
3
 
4
4
  // src/index.ts
5
5
  import { Command } from "commander";
6
- import fs10 from "fs-extra";
7
- import path10 from "path";
6
+ import fs11 from "fs-extra";
7
+ import path11 from "path";
8
8
  import ora8 from "ora";
9
9
  import { createRequire } from "module";
10
10
  import { confirm as confirm4 } from "@inquirer/prompts";
@@ -841,11 +841,194 @@ __name(devCommand, "devCommand");
841
841
  // src/commands/build.ts
842
842
  import { spawn as spawn2 } from "child_process";
843
843
  import ora2 from "ora";
844
+ import path5 from "path";
845
+ import fs5 from "fs-extra";
846
+
847
+ // src/schema-extractor.ts
844
848
  import path4 from "path";
845
849
  import fs4 from "fs-extra";
850
+ import { Project, Node } from "ts-morph";
851
+ async function generateSchemaMetadata(projectDir) {
852
+ const metadata = {};
853
+ const classesByName = /* @__PURE__ */ new Map();
854
+ const classesToProcess = /* @__PURE__ */ new Set();
855
+ const processedClasses = /* @__PURE__ */ new Set();
856
+ const project = new Project({
857
+ skipAddingFilesFromTsConfig: true,
858
+ skipFileDependencyResolution: true
859
+ });
860
+ const tsFiles = await findTsFiles(projectDir);
861
+ for (const filePath of tsFiles) {
862
+ try {
863
+ const sourceFile = project.addSourceFileAtPath(filePath);
864
+ for (const classDecl of sourceFile.getClasses()) {
865
+ const className = classDecl.getName();
866
+ if (className) {
867
+ classesByName.set(className, classDecl);
868
+ }
869
+ }
870
+ for (const classDecl of sourceFile.getClasses()) {
871
+ for (const method of classDecl.getInstanceMethods()) {
872
+ const decorators = method.getDecorators();
873
+ const isTool = decorators.some((d) => {
874
+ const name = d.getName();
875
+ return [
876
+ "Tool",
877
+ "Prompt",
878
+ "Resource"
879
+ ].includes(name);
880
+ });
881
+ if (isTool) {
882
+ const params = method.getParameters();
883
+ if (params.length > 0) {
884
+ const paramToken = params[0];
885
+ const typeNode = paramToken.getTypeNode();
886
+ if (typeNode) {
887
+ const typeName = typeNode.getText().trim();
888
+ if (!isPrimitive(typeName)) {
889
+ classesToProcess.add(extractBaseClassName(typeName));
890
+ }
891
+ }
892
+ }
893
+ }
894
+ }
895
+ }
896
+ } catch (error) {
897
+ }
898
+ }
899
+ for (const className of classesToProcess) {
900
+ if (processedClasses.has(className)) continue;
901
+ const classDecl = classesByName.get(className);
902
+ if (!classDecl) continue;
903
+ const propertyTypes = {};
904
+ for (const prop of classDecl.getInstanceProperties()) {
905
+ if (Node.isPropertyDeclaration(prop)) {
906
+ const propName = prop.getName();
907
+ const typeNode = prop.getTypeNode();
908
+ if (typeNode) {
909
+ const typeText = typeNode.getText();
910
+ const schemaType = typeTextToSchemaType(typeText);
911
+ propertyTypes[propName] = schemaType;
912
+ const baseType = extractBaseClassName(typeText);
913
+ if (!isPrimitive(baseType) && !processedClasses.has(baseType)) {
914
+ classesToProcess.add(baseType);
915
+ }
916
+ }
917
+ }
918
+ }
919
+ if (Object.keys(propertyTypes).length > 0) {
920
+ metadata[className] = propertyTypes;
921
+ }
922
+ processedClasses.add(className);
923
+ }
924
+ const outputPath = path4.join(projectDir, "dist", "schema-metadata.json");
925
+ await fs4.ensureDir(path4.dirname(outputPath));
926
+ await fs4.writeJSON(outputPath, metadata, {
927
+ spaces: 2
928
+ });
929
+ }
930
+ __name(generateSchemaMetadata, "generateSchemaMetadata");
931
+ function isPrimitive(typeName) {
932
+ const t = typeName.toLowerCase();
933
+ return [
934
+ "string",
935
+ "number",
936
+ "boolean",
937
+ "any",
938
+ "void",
939
+ "null",
940
+ "undefined",
941
+ "object",
942
+ "date"
943
+ ].includes(t);
944
+ }
945
+ __name(isPrimitive, "isPrimitive");
946
+ function extractBaseClassName(typeText) {
947
+ let name = typeText.trim();
948
+ if (name.endsWith("[]")) {
949
+ name = name.slice(0, -2);
950
+ }
951
+ const arrayMatch = name.match(/^Array<(.+)>$/i);
952
+ if (arrayMatch) {
953
+ name = arrayMatch[1];
954
+ }
955
+ return name.trim();
956
+ }
957
+ __name(extractBaseClassName, "extractBaseClassName");
958
+ async function findTsFiles(dir) {
959
+ const files = [];
960
+ async function scan(currentDir) {
961
+ const entries = await fs4.readdir(currentDir, {
962
+ withFileTypes: true
963
+ });
964
+ for (const entry of entries) {
965
+ const fullPath = path4.join(currentDir, entry.name);
966
+ if (entry.isDirectory()) {
967
+ if ([
968
+ "node_modules",
969
+ "dist",
970
+ ".git",
971
+ ".leanmcp"
972
+ ].includes(entry.name)) {
973
+ continue;
974
+ }
975
+ await scan(fullPath);
976
+ } else if (entry.isFile() && entry.name.endsWith(".ts") && !entry.name.endsWith(".d.ts")) {
977
+ files.push(fullPath);
978
+ }
979
+ }
980
+ }
981
+ __name(scan, "scan");
982
+ await scan(dir);
983
+ return files;
984
+ }
985
+ __name(findTsFiles, "findTsFiles");
986
+ function typeTextToSchemaType(typeText) {
987
+ const type = typeText.trim().replace(/\?$/, "");
988
+ if (type.endsWith("[]")) {
989
+ const elementType = type.slice(0, -2).toLowerCase();
990
+ return {
991
+ type: "array",
992
+ items: {
993
+ type: mapPrimitiveType(elementType)
994
+ }
995
+ };
996
+ }
997
+ const arrayMatch = type.match(/^Array<(.+)>$/i);
998
+ if (arrayMatch) {
999
+ const elementType = arrayMatch[1].trim().toLowerCase();
1000
+ return {
1001
+ type: "array",
1002
+ items: {
1003
+ type: mapPrimitiveType(elementType)
1004
+ }
1005
+ };
1006
+ }
1007
+ return {
1008
+ type: mapPrimitiveType(type.toLowerCase())
1009
+ };
1010
+ }
1011
+ __name(typeTextToSchemaType, "typeTextToSchemaType");
1012
+ function mapPrimitiveType(tsType) {
1013
+ switch (tsType) {
1014
+ case "string":
1015
+ return "string";
1016
+ case "number":
1017
+ return "number";
1018
+ case "boolean":
1019
+ return "boolean";
1020
+ case "integer":
1021
+ return "integer";
1022
+ default:
1023
+ return "string";
1024
+ }
1025
+ }
1026
+ __name(mapPrimitiveType, "mapPrimitiveType");
1027
+
1028
+ // src/commands/build.ts
846
1029
  async function buildCommand() {
847
1030
  const cwd = process.cwd();
848
- if (!await fs4.pathExists(path4.join(cwd, "main.ts"))) {
1031
+ if (!await fs5.pathExists(path5.join(cwd, "main.ts"))) {
849
1032
  logger.error("ERROR: Not a LeanMCP project (main.ts not found).");
850
1033
  logger.gray("Run this command from your project root.");
851
1034
  process.exit(1);
@@ -910,6 +1093,14 @@ async function buildCommand() {
910
1093
  logger.error(error instanceof Error ? error.message : String(error));
911
1094
  process.exit(1);
912
1095
  }
1096
+ const schemaSpinner = ora2("Generating schema metadata...").start();
1097
+ try {
1098
+ await generateSchemaMetadata(cwd);
1099
+ schemaSpinner.succeed("Schema metadata generated");
1100
+ } catch (error) {
1101
+ schemaSpinner.warn("Schema metadata generation failed (runtime fallback will be used)");
1102
+ logger.gray(` ${error instanceof Error ? error.message : String(error)}`);
1103
+ }
913
1104
  logger.success("\nBuild complete!");
914
1105
  logger.gray("\nTo start the server:");
915
1106
  logger.info(" npm run start:node\n");
@@ -919,11 +1110,11 @@ __name(buildCommand, "buildCommand");
919
1110
  // src/commands/start.ts
920
1111
  import { spawn as spawn3 } from "child_process";
921
1112
  import ora3 from "ora";
922
- import path5 from "path";
923
- import fs5 from "fs-extra";
1113
+ import path6 from "path";
1114
+ import fs6 from "fs-extra";
924
1115
  async function startCommand() {
925
1116
  const cwd = process.cwd();
926
- if (!await fs5.pathExists(path5.join(cwd, "main.ts"))) {
1117
+ if (!await fs6.pathExists(path6.join(cwd, "main.ts"))) {
927
1118
  logger.error("ERROR: Not a LeanMCP project (main.ts not found).");
928
1119
  logger.gray("Run this command from your project root.");
929
1120
  process.exit(1);
@@ -1025,8 +1216,8 @@ __name(startCommand, "startCommand");
1025
1216
 
1026
1217
  // src/commands/login.ts
1027
1218
  import ora4 from "ora";
1028
- import path6 from "path";
1029
- import fs6 from "fs-extra";
1219
+ import path7 from "path";
1220
+ import fs7 from "fs-extra";
1030
1221
  import os2 from "os";
1031
1222
  import { input, confirm } from "@inquirer/prompts";
1032
1223
  var DEBUG_MODE2 = false;
@@ -1040,12 +1231,12 @@ function debug2(message, ...args) {
1040
1231
  }
1041
1232
  }
1042
1233
  __name(debug2, "debug");
1043
- var CONFIG_DIR = path6.join(os2.homedir(), ".leanmcp");
1044
- var CONFIG_FILE = path6.join(CONFIG_DIR, "config.json");
1234
+ var CONFIG_DIR = path7.join(os2.homedir(), ".leanmcp");
1235
+ var CONFIG_FILE = path7.join(CONFIG_DIR, "config.json");
1045
1236
  async function loadConfig() {
1046
1237
  try {
1047
- if (await fs6.pathExists(CONFIG_FILE)) {
1048
- return await fs6.readJSON(CONFIG_FILE);
1238
+ if (await fs7.pathExists(CONFIG_FILE)) {
1239
+ return await fs7.readJSON(CONFIG_FILE);
1049
1240
  }
1050
1241
  } catch (error) {
1051
1242
  }
@@ -1053,8 +1244,8 @@ async function loadConfig() {
1053
1244
  }
1054
1245
  __name(loadConfig, "loadConfig");
1055
1246
  async function saveConfig(config) {
1056
- await fs6.ensureDir(CONFIG_DIR);
1057
- await fs6.writeJSON(CONFIG_FILE, config, {
1247
+ await fs7.ensureDir(CONFIG_DIR);
1248
+ await fs7.writeJSON(CONFIG_FILE, config, {
1058
1249
  spaces: 2
1059
1250
  });
1060
1251
  }
@@ -1261,8 +1452,8 @@ __name(whoamiCommand, "whoamiCommand");
1261
1452
 
1262
1453
  // src/commands/deploy.ts
1263
1454
  import ora5 from "ora";
1264
- import path8 from "path";
1265
- import fs8 from "fs-extra";
1455
+ import path9 from "path";
1456
+ import fs9 from "fs-extra";
1266
1457
  import os3 from "os";
1267
1458
  import archiver from "archiver";
1268
1459
  import { input as input2, confirm as confirm2, select } from "@inquirer/prompts";
@@ -1445,8 +1636,8 @@ function generateProjectName() {
1445
1636
  __name(generateProjectName, "generateProjectName");
1446
1637
 
1447
1638
  // src/utils/env-parser.ts
1448
- import fs7 from "fs-extra";
1449
- import path7 from "path";
1639
+ import fs8 from "fs-extra";
1640
+ import path8 from "path";
1450
1641
  var RESERVED_ENV_KEYS = [
1451
1642
  "AWS_REGION",
1452
1643
  "AWS_ACCESS_KEY_ID",
@@ -1509,16 +1700,16 @@ function parseEnvFile(content) {
1509
1700
  }
1510
1701
  __name(parseEnvFile, "parseEnvFile");
1511
1702
  async function loadEnvFile(filePath) {
1512
- const absolutePath = path7.resolve(filePath);
1513
- if (!await fs7.pathExists(absolutePath)) {
1703
+ const absolutePath = path8.resolve(filePath);
1704
+ if (!await fs8.pathExists(absolutePath)) {
1514
1705
  throw new Error(`Env file not found: ${absolutePath}`);
1515
1706
  }
1516
- const content = await fs7.readFile(absolutePath, "utf-8");
1707
+ const content = await fs8.readFile(absolutePath, "utf-8");
1517
1708
  return parseEnvFile(content);
1518
1709
  }
1519
1710
  __name(loadEnvFile, "loadEnvFile");
1520
1711
  async function writeEnvFile(filePath, vars) {
1521
- const absolutePath = path7.resolve(filePath);
1712
+ const absolutePath = path8.resolve(filePath);
1522
1713
  const lines = [
1523
1714
  "# Environment variables",
1524
1715
  `# Generated by leanmcp CLI at ${(/* @__PURE__ */ new Date()).toISOString()}`,
@@ -1534,7 +1725,7 @@ async function writeEnvFile(filePath, vars) {
1534
1725
  }
1535
1726
  }
1536
1727
  lines.push("");
1537
- await fs7.writeFile(absolutePath, lines.join("\n"));
1728
+ await fs8.writeFile(absolutePath, lines.join("\n"));
1538
1729
  }
1539
1730
  __name(writeEnvFile, "writeEnvFile");
1540
1731
  function isValidEnvKey(key) {
@@ -1612,10 +1803,10 @@ var API_ENDPOINTS = {
1612
1803
  var LEANMCP_CONFIG_DIR = ".leanmcp";
1613
1804
  var LEANMCP_CONFIG_FILE = "config.json";
1614
1805
  async function readLeanMCPConfig(projectPath) {
1615
- const configPath = path8.join(projectPath, LEANMCP_CONFIG_DIR, LEANMCP_CONFIG_FILE);
1806
+ const configPath = path9.join(projectPath, LEANMCP_CONFIG_DIR, LEANMCP_CONFIG_FILE);
1616
1807
  try {
1617
- if (await fs8.pathExists(configPath)) {
1618
- const config = await fs8.readJSON(configPath);
1808
+ if (await fs9.pathExists(configPath)) {
1809
+ const config = await fs9.readJSON(configPath);
1619
1810
  debug3("Found existing .leanmcp config:", config);
1620
1811
  return config;
1621
1812
  }
@@ -1626,10 +1817,10 @@ async function readLeanMCPConfig(projectPath) {
1626
1817
  }
1627
1818
  __name(readLeanMCPConfig, "readLeanMCPConfig");
1628
1819
  async function writeLeanMCPConfig(projectPath, config) {
1629
- const configDir = path8.join(projectPath, LEANMCP_CONFIG_DIR);
1630
- const configPath = path8.join(configDir, LEANMCP_CONFIG_FILE);
1631
- await fs8.ensureDir(configDir);
1632
- await fs8.writeJSON(configPath, config, {
1820
+ const configDir = path9.join(projectPath, LEANMCP_CONFIG_DIR);
1821
+ const configPath = path9.join(configDir, LEANMCP_CONFIG_FILE);
1822
+ await fs9.ensureDir(configDir);
1823
+ await fs9.writeJSON(configPath, config, {
1633
1824
  spaces: 2
1634
1825
  });
1635
1826
  debug3("Saved .leanmcp config:", config);
@@ -1637,7 +1828,7 @@ async function writeLeanMCPConfig(projectPath, config) {
1637
1828
  __name(writeLeanMCPConfig, "writeLeanMCPConfig");
1638
1829
  async function createZipArchive(folderPath, outputPath) {
1639
1830
  return new Promise((resolve, reject) => {
1640
- const output = fs8.createWriteStream(outputPath);
1831
+ const output = fs9.createWriteStream(outputPath);
1641
1832
  const archive = archiver("zip", {
1642
1833
  zlib: {
1643
1834
  level: 9
@@ -1747,16 +1938,16 @@ async function deployCommand(folderPath, options = {}) {
1747
1938
  }
1748
1939
  const apiUrl = await getApiUrl();
1749
1940
  debug3("API URL:", apiUrl);
1750
- const absolutePath = path8.resolve(process.cwd(), folderPath);
1751
- if (!await fs8.pathExists(absolutePath)) {
1941
+ const absolutePath = path9.resolve(process.cwd(), folderPath);
1942
+ if (!await fs9.pathExists(absolutePath)) {
1752
1943
  logger.error(`Folder not found: ${absolutePath}`);
1753
1944
  process.exit(1);
1754
1945
  }
1755
- const hasMainTs = await fs8.pathExists(path8.join(absolutePath, "main.ts"));
1756
- const hasPackageJson = await fs8.pathExists(path8.join(absolutePath, "package.json"));
1757
- const hasMainPy = await fs8.pathExists(path8.join(absolutePath, "main.py"));
1758
- const hasRequirementsTxt = await fs8.pathExists(path8.join(absolutePath, "requirements.txt"));
1759
- const hasPyprojectToml = await fs8.pathExists(path8.join(absolutePath, "pyproject.toml"));
1946
+ const hasMainTs = await fs9.pathExists(path9.join(absolutePath, "main.ts"));
1947
+ const hasPackageJson = await fs9.pathExists(path9.join(absolutePath, "package.json"));
1948
+ const hasMainPy = await fs9.pathExists(path9.join(absolutePath, "main.py"));
1949
+ const hasRequirementsTxt = await fs9.pathExists(path9.join(absolutePath, "requirements.txt"));
1950
+ const hasPyprojectToml = await fs9.pathExists(path9.join(absolutePath, "pyproject.toml"));
1760
1951
  const isNodeProject = hasMainTs || hasPackageJson;
1761
1952
  const isPythonProject = hasMainPy || hasRequirementsTxt || hasPyprojectToml;
1762
1953
  if (!isNodeProject && !isPythonProject) {
@@ -1828,10 +2019,10 @@ Generated project name: ${chalk.bold(projectName)}
1828
2019
  } catch (e) {
1829
2020
  debug3("Could not fetch existing projects");
1830
2021
  }
1831
- let folderName = path8.basename(absolutePath);
2022
+ let folderName = path9.basename(absolutePath);
1832
2023
  if (hasPackageJson) {
1833
2024
  try {
1834
- const pkg2 = await fs8.readJSON(path8.join(absolutePath, "package.json"));
2025
+ const pkg2 = await fs9.readJSON(path9.join(absolutePath, "package.json"));
1835
2026
  folderName = pkg2.name || folderName;
1836
2027
  } catch (e) {
1837
2028
  }
@@ -1982,7 +2173,7 @@ ${error instanceof Error ? error.message : String(error)}`);
1982
2173
  }
1983
2174
  const uploadSpinner = ora5("Packaging and uploading...").start();
1984
2175
  try {
1985
- const tempZip = path8.join(os3.tmpdir(), `leanmcp-${Date.now()}.zip`);
2176
+ const tempZip = path9.join(os3.tmpdir(), `leanmcp-${Date.now()}.zip`);
1986
2177
  const zipSize = await createZipArchive(absolutePath, tempZip);
1987
2178
  uploadSpinner.text = `Packaging... (${Math.round(zipSize / 1024)}KB)`;
1988
2179
  debug3("Step 2a: Getting upload URL for project:", projectId);
@@ -2009,7 +2200,7 @@ ${error instanceof Error ? error.message : String(error)}`);
2009
2200
  throw new Error("Backend did not return upload URL");
2010
2201
  }
2011
2202
  debug3("Step 2b: Uploading to S3...");
2012
- const zipBuffer = await fs8.readFile(tempZip);
2203
+ const zipBuffer = await fs9.readFile(tempZip);
2013
2204
  const s3Response = await fetch(uploadUrl, {
2014
2205
  method: "PUT",
2015
2206
  body: zipBuffer,
@@ -2032,7 +2223,7 @@ ${error instanceof Error ? error.message : String(error)}`);
2032
2223
  s3Location
2033
2224
  })
2034
2225
  });
2035
- await fs8.remove(tempZip);
2226
+ await fs9.remove(tempZip);
2036
2227
  uploadSpinner.succeed("Project uploaded");
2037
2228
  } catch (error) {
2038
2229
  uploadSpinner.fail("Failed to upload");
@@ -2307,8 +2498,8 @@ __name(projectsDeleteCommand, "projectsDeleteCommand");
2307
2498
 
2308
2499
  // src/commands/env.ts
2309
2500
  import ora7 from "ora";
2310
- import path9 from "path";
2311
- import fs9 from "fs-extra";
2501
+ import path10 from "path";
2502
+ import fs10 from "fs-extra";
2312
2503
  import { confirm as confirm3 } from "@inquirer/prompts";
2313
2504
  var DEBUG_MODE4 = false;
2314
2505
  function setEnvDebugMode(enabled) {
@@ -2341,10 +2532,10 @@ __name(debugFetch2, "debugFetch");
2341
2532
  var LEANMCP_CONFIG_DIR2 = ".leanmcp";
2342
2533
  var LEANMCP_CONFIG_FILE2 = "config.json";
2343
2534
  async function readLeanMCPConfig2(projectPath) {
2344
- const configPath = path9.join(projectPath, LEANMCP_CONFIG_DIR2, LEANMCP_CONFIG_FILE2);
2535
+ const configPath = path10.join(projectPath, LEANMCP_CONFIG_DIR2, LEANMCP_CONFIG_FILE2);
2345
2536
  try {
2346
- if (await fs9.pathExists(configPath)) {
2347
- const config = await fs9.readJSON(configPath);
2537
+ if (await fs10.pathExists(configPath)) {
2538
+ const config = await fs10.readJSON(configPath);
2348
2539
  debug4("Found existing .leanmcp config:", config);
2349
2540
  return config;
2350
2541
  }
@@ -2362,7 +2553,7 @@ async function getDeploymentContext(folderPath) {
2362
2553
  return null;
2363
2554
  }
2364
2555
  const apiUrl = await getApiUrl();
2365
- const absolutePath = path9.resolve(process.cwd(), folderPath);
2556
+ const absolutePath = path10.resolve(process.cwd(), folderPath);
2366
2557
  const config = await readLeanMCPConfig2(absolutePath);
2367
2558
  if (!config) {
2368
2559
  logger.error("No deployment found.");
@@ -3445,29 +3636,29 @@ program.command("create <projectName>").description("Create a new LeanMCP projec
3445
3636
  ...options
3446
3637
  });
3447
3638
  const spinner = ora8(`Creating project ${projectName}...`).start();
3448
- const targetDir = path10.join(process.cwd(), projectName);
3449
- if (fs10.existsSync(targetDir)) {
3639
+ const targetDir = path11.join(process.cwd(), projectName);
3640
+ if (fs11.existsSync(targetDir)) {
3450
3641
  spinner.fail(`Folder ${projectName} already exists.`);
3451
3642
  process.exit(1);
3452
3643
  }
3453
- await fs10.mkdirp(targetDir);
3644
+ await fs11.mkdirp(targetDir);
3454
3645
  const isPython = options.python === true;
3455
3646
  if (isPython) {
3456
3647
  const requirements = getPythonRequirementsTemplate();
3457
- await fs10.writeFile(path10.join(targetDir, "requirements.txt"), requirements);
3648
+ await fs11.writeFile(path11.join(targetDir, "requirements.txt"), requirements);
3458
3649
  const mainPy = getPythonMainTemplate(projectName);
3459
- await fs10.writeFile(path10.join(targetDir, "main.py"), mainPy);
3460
- await fs10.writeFile(path10.join(targetDir, ".gitignore"), pythonGitignoreTemplate);
3650
+ await fs11.writeFile(path11.join(targetDir, "main.py"), mainPy);
3651
+ await fs11.writeFile(path11.join(targetDir, ".gitignore"), pythonGitignoreTemplate);
3461
3652
  const env = `# Server Configuration
3462
3653
  PORT=3001
3463
3654
 
3464
3655
  # Add your environment variables here
3465
3656
  `;
3466
- await fs10.writeFile(path10.join(targetDir, ".env"), env);
3657
+ await fs11.writeFile(path11.join(targetDir, ".env"), env);
3467
3658
  const readme = getPythonReadmeTemplate(projectName);
3468
- await fs10.writeFile(path10.join(targetDir, "README.md"), readme);
3659
+ await fs11.writeFile(path11.join(targetDir, "README.md"), readme);
3469
3660
  } else {
3470
- await fs10.mkdirp(path10.join(targetDir, "mcp", "example"));
3661
+ await fs11.mkdirp(path11.join(targetDir, "mcp", "example"));
3471
3662
  const pkg2 = {
3472
3663
  name: projectName,
3473
3664
  version: "1.0.0",
@@ -3503,7 +3694,7 @@ PORT=3001
3503
3694
  typescript: "^5.6.3"
3504
3695
  }
3505
3696
  };
3506
- await fs10.writeJSON(path10.join(targetDir, "package.json"), pkg2, {
3697
+ await fs11.writeJSON(path11.join(targetDir, "package.json"), pkg2, {
3507
3698
  spaces: 2
3508
3699
  });
3509
3700
  const tsconfig = {
@@ -3526,15 +3717,15 @@ PORT=3001
3526
3717
  "dist"
3527
3718
  ]
3528
3719
  };
3529
- await fs10.writeJSON(path10.join(targetDir, "tsconfig.json"), tsconfig, {
3720
+ await fs11.writeJSON(path11.join(targetDir, "tsconfig.json"), tsconfig, {
3530
3721
  spaces: 2
3531
3722
  });
3532
3723
  const dashboardLine = options.dashboard === false ? `
3533
3724
  dashboard: false, // Dashboard disabled via --no-dashboard` : "";
3534
3725
  const mainTs = getMainTsTemplate(projectName, dashboardLine);
3535
- await fs10.writeFile(path10.join(targetDir, "main.ts"), mainTs);
3726
+ await fs11.writeFile(path11.join(targetDir, "main.ts"), mainTs);
3536
3727
  const exampleServiceTs = getExampleServiceTemplate(projectName);
3537
- await fs10.writeFile(path10.join(targetDir, "mcp", "example", "index.ts"), exampleServiceTs);
3728
+ await fs11.writeFile(path11.join(targetDir, "mcp", "example", "index.ts"), exampleServiceTs);
3538
3729
  const gitignore = gitignoreTemplate;
3539
3730
  const env = `# Server Configuration
3540
3731
  PORT=3001
@@ -3542,10 +3733,10 @@ NODE_ENV=development
3542
3733
 
3543
3734
  # Add your environment variables here
3544
3735
  `;
3545
- await fs10.writeFile(path10.join(targetDir, ".gitignore"), gitignore);
3546
- await fs10.writeFile(path10.join(targetDir, ".env"), env);
3736
+ await fs11.writeFile(path11.join(targetDir, ".gitignore"), gitignore);
3737
+ await fs11.writeFile(path11.join(targetDir, ".env"), env);
3547
3738
  const readme = getReadmeTemplate(projectName);
3548
- await fs10.writeFile(path10.join(targetDir, "README.md"), readme);
3739
+ await fs11.writeFile(path11.join(targetDir, "README.md"), readme);
3549
3740
  }
3550
3741
  spinner.succeed(`Project ${projectName} created!`);
3551
3742
  logger.log("\nSuccess! Your MCP server is ready.\n", chalk.green);
@@ -3669,20 +3860,20 @@ NODE_ENV=development
3669
3860
  });
3670
3861
  program.command("add <serviceName>").description("Add a new MCP service to your project").action(async (serviceName) => {
3671
3862
  const cwd = process.cwd();
3672
- const mcpDir = path10.join(cwd, "mcp");
3673
- if (!fs10.existsSync(path10.join(cwd, "main.ts"))) {
3863
+ const mcpDir = path11.join(cwd, "mcp");
3864
+ if (!fs11.existsSync(path11.join(cwd, "main.ts"))) {
3674
3865
  logger.log("ERROR: Not a LeanMCP project (main.ts missing).", chalk.red);
3675
3866
  process.exit(1);
3676
3867
  }
3677
- const serviceDir = path10.join(mcpDir, serviceName);
3678
- const serviceFile = path10.join(serviceDir, "index.ts");
3679
- if (fs10.existsSync(serviceDir)) {
3868
+ const serviceDir = path11.join(mcpDir, serviceName);
3869
+ const serviceFile = path11.join(serviceDir, "index.ts");
3870
+ if (fs11.existsSync(serviceDir)) {
3680
3871
  logger.log(`ERROR: Service ${serviceName} already exists.`, chalk.red);
3681
3872
  process.exit(1);
3682
3873
  }
3683
- await fs10.mkdirp(serviceDir);
3874
+ await fs11.mkdirp(serviceDir);
3684
3875
  const indexTs = getServiceIndexTemplate(serviceName, capitalize(serviceName));
3685
- await fs10.writeFile(serviceFile, indexTs);
3876
+ await fs11.writeFile(serviceFile, indexTs);
3686
3877
  logger.log(`\\nCreated new service: ${chalk.bold(serviceName)}`, chalk.green);
3687
3878
  logger.log(` File: mcp/${serviceName}/index.ts`, chalk.gray);
3688
3879
  logger.log(` Tool: greet`, chalk.gray);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leanmcp/cli",
3
- "version": "0.5.4-alpha.9.641ef8f",
3
+ "version": "0.5.5",
4
4
  "description": "Command-line interface for scaffolding LeanMCP projects",
5
5
  "bin": {
6
6
  "leanmcp": "bin/leanmcp.js"
@@ -40,7 +40,8 @@
40
40
  "postcss": "^8.4.32",
41
41
  "tailwindcss": "^3.4.0",
42
42
  "vite": "^5.4.0",
43
- "vite-plugin-singlefile": "^2.3.0"
43
+ "vite-plugin-singlefile": "^2.3.0",
44
+ "ts-morph": "^24.0.0"
44
45
  },
45
46
  "devDependencies": {
46
47
  "@types/archiver": "^6.0.3",