@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 +21 -21
- package/bin/leanmcp.js +0 -0
- package/dist/index.js +263 -72
- package/package.json +3 -2
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
|
|
7
|
-
import
|
|
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
|
|
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
|
|
923
|
-
import
|
|
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
|
|
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
|
|
1029
|
-
import
|
|
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 =
|
|
1044
|
-
var CONFIG_FILE =
|
|
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
|
|
1048
|
-
return await
|
|
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
|
|
1057
|
-
await
|
|
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
|
|
1265
|
-
import
|
|
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
|
|
1449
|
-
import
|
|
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 =
|
|
1513
|
-
if (!await
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
1806
|
+
const configPath = path9.join(projectPath, LEANMCP_CONFIG_DIR, LEANMCP_CONFIG_FILE);
|
|
1616
1807
|
try {
|
|
1617
|
-
if (await
|
|
1618
|
-
const config = await
|
|
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 =
|
|
1630
|
-
const configPath =
|
|
1631
|
-
await
|
|
1632
|
-
await
|
|
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 =
|
|
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 =
|
|
1751
|
-
if (!await
|
|
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
|
|
1756
|
-
const hasPackageJson = await
|
|
1757
|
-
const hasMainPy = await
|
|
1758
|
-
const hasRequirementsTxt = await
|
|
1759
|
-
const hasPyprojectToml = await
|
|
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 =
|
|
2022
|
+
let folderName = path9.basename(absolutePath);
|
|
1832
2023
|
if (hasPackageJson) {
|
|
1833
2024
|
try {
|
|
1834
|
-
const pkg2 = await
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
2311
|
-
import
|
|
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 =
|
|
2535
|
+
const configPath = path10.join(projectPath, LEANMCP_CONFIG_DIR2, LEANMCP_CONFIG_FILE2);
|
|
2345
2536
|
try {
|
|
2346
|
-
if (await
|
|
2347
|
-
const config = await
|
|
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 =
|
|
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 =
|
|
3449
|
-
if (
|
|
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
|
|
3644
|
+
await fs11.mkdirp(targetDir);
|
|
3454
3645
|
const isPython = options.python === true;
|
|
3455
3646
|
if (isPython) {
|
|
3456
3647
|
const requirements = getPythonRequirementsTemplate();
|
|
3457
|
-
await
|
|
3648
|
+
await fs11.writeFile(path11.join(targetDir, "requirements.txt"), requirements);
|
|
3458
3649
|
const mainPy = getPythonMainTemplate(projectName);
|
|
3459
|
-
await
|
|
3460
|
-
await
|
|
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
|
|
3657
|
+
await fs11.writeFile(path11.join(targetDir, ".env"), env);
|
|
3467
3658
|
const readme = getPythonReadmeTemplate(projectName);
|
|
3468
|
-
await
|
|
3659
|
+
await fs11.writeFile(path11.join(targetDir, "README.md"), readme);
|
|
3469
3660
|
} else {
|
|
3470
|
-
await
|
|
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
|
|
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
|
|
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
|
|
3726
|
+
await fs11.writeFile(path11.join(targetDir, "main.ts"), mainTs);
|
|
3536
3727
|
const exampleServiceTs = getExampleServiceTemplate(projectName);
|
|
3537
|
-
await
|
|
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
|
|
3546
|
-
await
|
|
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
|
|
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 =
|
|
3673
|
-
if (!
|
|
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 =
|
|
3678
|
-
const serviceFile =
|
|
3679
|
-
if (
|
|
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
|
|
3874
|
+
await fs11.mkdirp(serviceDir);
|
|
3684
3875
|
const indexTs = getServiceIndexTemplate(serviceName, capitalize(serviceName));
|
|
3685
|
-
await
|
|
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.
|
|
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",
|