@leanmcp/cli 0.1.0 → 0.1.1
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 +50 -56
- package/dist/index.mjs +50 -56
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -44,7 +44,7 @@ program.command("create <projectName>").description("Create a new LeanMCP projec
|
|
|
44
44
|
process.exit(1);
|
|
45
45
|
}
|
|
46
46
|
await import_fs_extra.default.mkdirp(targetDir);
|
|
47
|
-
await import_fs_extra.default.mkdirp(import_path.default.join(targetDir, "mcp"));
|
|
47
|
+
await import_fs_extra.default.mkdirp(import_path.default.join(targetDir, "mcp", "example"));
|
|
48
48
|
const pkg = {
|
|
49
49
|
name: projectName,
|
|
50
50
|
version: "1.0.0",
|
|
@@ -66,15 +66,10 @@ program.command("create <projectName>").description("Create a new LeanMCP projec
|
|
|
66
66
|
author: "",
|
|
67
67
|
license: "MIT",
|
|
68
68
|
dependencies: {
|
|
69
|
-
"@leanmcp/core": "^0.
|
|
70
|
-
"
|
|
71
|
-
"cors": "^2.8.5",
|
|
72
|
-
"dotenv": "^16.5.0",
|
|
73
|
-
"express": "^5.1.0"
|
|
69
|
+
"@leanmcp/core": "^0.2.0",
|
|
70
|
+
"dotenv": "^16.5.0"
|
|
74
71
|
},
|
|
75
72
|
devDependencies: {
|
|
76
|
-
"@types/cors": "^2.8.19",
|
|
77
|
-
"@types/express": "^5.0.3",
|
|
78
73
|
"@types/node": "^20.0.0",
|
|
79
74
|
"tsx": "^4.20.3",
|
|
80
75
|
"typescript": "^5.6.3"
|
|
@@ -108,7 +103,6 @@ program.command("create <projectName>").description("Create a new LeanMCP projec
|
|
|
108
103
|
});
|
|
109
104
|
const mainTs = `import dotenv from "dotenv";
|
|
110
105
|
import { createHTTPServer, MCPServer } from "@leanmcp/core";
|
|
111
|
-
import { ExampleService } from "./mcp/example.js";
|
|
112
106
|
|
|
113
107
|
// Load environment variables
|
|
114
108
|
dotenv.config();
|
|
@@ -117,25 +111,29 @@ const PORT = Number(process.env.PORT) || 3001;
|
|
|
117
111
|
|
|
118
112
|
/**
|
|
119
113
|
* Create and configure the MCP server
|
|
114
|
+
* Services are automatically discovered from ./mcp directory
|
|
120
115
|
*/
|
|
121
|
-
|
|
116
|
+
const serverFactory = async () => {
|
|
122
117
|
const server = new MCPServer({
|
|
123
118
|
name: "${projectName}",
|
|
124
|
-
version: "1.0.0"
|
|
119
|
+
version: "1.0.0",
|
|
120
|
+
logging: true
|
|
125
121
|
});
|
|
126
122
|
|
|
127
|
-
//
|
|
128
|
-
server.registerService(new ExampleService());
|
|
129
|
-
|
|
123
|
+
// Services are automatically discovered and registered from ./mcp
|
|
130
124
|
return server.getServer();
|
|
131
|
-
}
|
|
125
|
+
};
|
|
132
126
|
|
|
133
127
|
// Start the HTTP server
|
|
134
|
-
await createHTTPServer(
|
|
128
|
+
await createHTTPServer(serverFactory, {
|
|
135
129
|
port: PORT,
|
|
136
130
|
cors: true,
|
|
137
|
-
logging: true
|
|
131
|
+
logging: true // Log HTTP requests
|
|
138
132
|
});
|
|
133
|
+
|
|
134
|
+
console.log(\`\\n${projectName} MCP Server\`);
|
|
135
|
+
console.log(\`HTTP endpoint: http://localhost:\${PORT}/mcp\`);
|
|
136
|
+
console.log(\`Health check: http://localhost:\${PORT}/health\`);
|
|
139
137
|
`;
|
|
140
138
|
await import_fs_extra.default.writeFile(import_path.default.join(targetDir, "main.ts"), mainTs);
|
|
141
139
|
const exampleServiceTs = `import { Tool, Resource, Prompt, SchemaConstraint, Optional } from "@leanmcp/core";
|
|
@@ -243,7 +241,7 @@ export class ExampleService {
|
|
|
243
241
|
}
|
|
244
242
|
}
|
|
245
243
|
`;
|
|
246
|
-
await import_fs_extra.default.writeFile(import_path.default.join(targetDir, "mcp", "example.ts"), exampleServiceTs);
|
|
244
|
+
await import_fs_extra.default.writeFile(import_path.default.join(targetDir, "mcp", "example", "index.ts"), exampleServiceTs);
|
|
247
245
|
const gitignore = `node_modules
|
|
248
246
|
dist
|
|
249
247
|
.env
|
|
@@ -283,22 +281,36 @@ npm start
|
|
|
283
281
|
\`\`\`
|
|
284
282
|
${projectName}/
|
|
285
283
|
\u251C\u2500\u2500 main.ts # Server entry point
|
|
286
|
-
\u251C\u2500\u2500 mcp/
|
|
287
|
-
\u2502 \u2514\u2500\u2500 example
|
|
284
|
+
\u251C\u2500\u2500 mcp/ # Services directory (auto-discovered)
|
|
285
|
+
\u2502 \u2514\u2500\u2500 example/
|
|
286
|
+
\u2502 \u2514\u2500\u2500 index.ts # Example service
|
|
288
287
|
\u251C\u2500\u2500 .env # Environment variables
|
|
289
288
|
\u2514\u2500\u2500 package.json
|
|
290
289
|
\`\`\`
|
|
291
290
|
|
|
292
291
|
## Adding New Services
|
|
293
292
|
|
|
294
|
-
Create a new service
|
|
293
|
+
Create a new service directory in \`mcp/\`:
|
|
295
294
|
|
|
296
295
|
\`\`\`typescript
|
|
297
|
-
|
|
296
|
+
// mcp/myservice/index.ts
|
|
297
|
+
import { Tool, SchemaConstraint } from "@leanmcp/core";
|
|
298
|
+
|
|
299
|
+
// Define input schema
|
|
300
|
+
class MyToolInput {
|
|
301
|
+
@SchemaConstraint({
|
|
302
|
+
description: "Message to process",
|
|
303
|
+
minLength: 1
|
|
304
|
+
})
|
|
305
|
+
message!: string;
|
|
306
|
+
}
|
|
298
307
|
|
|
299
308
|
export class MyService {
|
|
300
|
-
@Tool({
|
|
301
|
-
|
|
309
|
+
@Tool({
|
|
310
|
+
description: "My awesome tool",
|
|
311
|
+
inputClass: MyToolInput
|
|
312
|
+
})
|
|
313
|
+
async myTool(input: MyToolInput) {
|
|
302
314
|
return {
|
|
303
315
|
content: [{
|
|
304
316
|
type: "text",
|
|
@@ -309,12 +321,15 @@ export class MyService {
|
|
|
309
321
|
}
|
|
310
322
|
\`\`\`
|
|
311
323
|
|
|
312
|
-
|
|
324
|
+
Services are automatically discovered and registered - no need to modify \`main.ts\`!
|
|
313
325
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
326
|
+
## Features
|
|
327
|
+
|
|
328
|
+
- **Zero-config auto-discovery** - Services automatically registered from \`./mcp\` directory
|
|
329
|
+
- **Type-safe decorators** - \`@Tool\`, \`@Prompt\`, \`@Resource\` with full TypeScript support
|
|
330
|
+
- **Schema validation** - Automatic input validation with \`@SchemaConstraint\`
|
|
331
|
+
- **HTTP transport** - Production-ready HTTP server with session management
|
|
332
|
+
- **Hot reload** - Development mode with automatic restart on file changes
|
|
318
333
|
|
|
319
334
|
## Testing with MCP Inspector
|
|
320
335
|
|
|
@@ -342,12 +357,13 @@ program.command("add <serviceName>").description("Add a new MCP service to your
|
|
|
342
357
|
console.error(import_chalk.default.red("ERROR: Not a LeanMCP project (main.ts missing)."));
|
|
343
358
|
process.exit(1);
|
|
344
359
|
}
|
|
345
|
-
|
|
346
|
-
const serviceFile = import_path.default.join(
|
|
347
|
-
if (import_fs_extra.default.existsSync(
|
|
360
|
+
const serviceDir = import_path.default.join(mcpDir, serviceName);
|
|
361
|
+
const serviceFile = import_path.default.join(serviceDir, "index.ts");
|
|
362
|
+
if (import_fs_extra.default.existsSync(serviceDir)) {
|
|
348
363
|
console.error(import_chalk.default.red(`ERROR: Service ${serviceName} already exists.`));
|
|
349
364
|
process.exit(1);
|
|
350
365
|
}
|
|
366
|
+
await import_fs_extra.default.mkdirp(serviceDir);
|
|
351
367
|
const indexTs = `import { Tool, Resource, Prompt, Optional, SchemaConstraint } from "@leanmcp/core";
|
|
352
368
|
|
|
353
369
|
// Input schema for greeting
|
|
@@ -408,33 +424,11 @@ export class ${capitalize(serviceName)}Service {
|
|
|
408
424
|
}
|
|
409
425
|
`;
|
|
410
426
|
await import_fs_extra.default.writeFile(serviceFile, indexTs);
|
|
411
|
-
const mainTsPath = import_path.default.join(cwd, "main.ts");
|
|
412
|
-
let mainTsContent = await import_fs_extra.default.readFile(mainTsPath, "utf-8");
|
|
413
|
-
const serviceClassName = `${capitalize(serviceName)}Service`;
|
|
414
|
-
const importStatement = `import { ${serviceClassName} } from "./mcp/${serviceName}.js";`;
|
|
415
|
-
const registerStatement = ` server.registerService(new ${serviceClassName}());`;
|
|
416
|
-
const lastImportMatch = mainTsContent.match(/import .* from .*;\n/g);
|
|
417
|
-
if (lastImportMatch) {
|
|
418
|
-
const lastImport = lastImportMatch[lastImportMatch.length - 1];
|
|
419
|
-
const lastImportIndex = mainTsContent.lastIndexOf(lastImport);
|
|
420
|
-
const afterLastImport = lastImportIndex + lastImport.length;
|
|
421
|
-
mainTsContent = mainTsContent.slice(0, afterLastImport) + importStatement + "\n" + mainTsContent.slice(afterLastImport);
|
|
422
|
-
}
|
|
423
|
-
const registerPattern = /server\.registerService\(new \w+\(\)\);/g;
|
|
424
|
-
const matches = [
|
|
425
|
-
...mainTsContent.matchAll(registerPattern)
|
|
426
|
-
];
|
|
427
|
-
if (matches.length > 0) {
|
|
428
|
-
const lastMatch = matches[matches.length - 1];
|
|
429
|
-
const insertPosition = lastMatch.index + lastMatch[0].length;
|
|
430
|
-
mainTsContent = mainTsContent.slice(0, insertPosition) + "\n" + registerStatement + mainTsContent.slice(insertPosition);
|
|
431
|
-
}
|
|
432
|
-
await import_fs_extra.default.writeFile(mainTsPath, mainTsContent);
|
|
433
427
|
console.log(import_chalk.default.green(`\\nCreated new service: ${import_chalk.default.bold(serviceName)}`));
|
|
434
|
-
console.log(import_chalk.default.gray(` File: mcp/${serviceName}.ts`));
|
|
428
|
+
console.log(import_chalk.default.gray(` File: mcp/${serviceName}/index.ts`));
|
|
435
429
|
console.log(import_chalk.default.gray(` Tool: greet`));
|
|
436
430
|
console.log(import_chalk.default.gray(` Prompt: welcomePrompt`));
|
|
437
431
|
console.log(import_chalk.default.gray(` Resource: getStatus`));
|
|
438
|
-
console.log(import_chalk.default.green(`\\nService automatically
|
|
432
|
+
console.log(import_chalk.default.green(`\\nService will be automatically discovered on next server start!`));
|
|
439
433
|
});
|
|
440
434
|
program.parse();
|
package/dist/index.mjs
CHANGED
|
@@ -22,7 +22,7 @@ program.command("create <projectName>").description("Create a new LeanMCP projec
|
|
|
22
22
|
process.exit(1);
|
|
23
23
|
}
|
|
24
24
|
await fs.mkdirp(targetDir);
|
|
25
|
-
await fs.mkdirp(path.join(targetDir, "mcp"));
|
|
25
|
+
await fs.mkdirp(path.join(targetDir, "mcp", "example"));
|
|
26
26
|
const pkg = {
|
|
27
27
|
name: projectName,
|
|
28
28
|
version: "1.0.0",
|
|
@@ -44,15 +44,10 @@ program.command("create <projectName>").description("Create a new LeanMCP projec
|
|
|
44
44
|
author: "",
|
|
45
45
|
license: "MIT",
|
|
46
46
|
dependencies: {
|
|
47
|
-
"@leanmcp/core": "^0.
|
|
48
|
-
"
|
|
49
|
-
"cors": "^2.8.5",
|
|
50
|
-
"dotenv": "^16.5.0",
|
|
51
|
-
"express": "^5.1.0"
|
|
47
|
+
"@leanmcp/core": "^0.2.0",
|
|
48
|
+
"dotenv": "^16.5.0"
|
|
52
49
|
},
|
|
53
50
|
devDependencies: {
|
|
54
|
-
"@types/cors": "^2.8.19",
|
|
55
|
-
"@types/express": "^5.0.3",
|
|
56
51
|
"@types/node": "^20.0.0",
|
|
57
52
|
"tsx": "^4.20.3",
|
|
58
53
|
"typescript": "^5.6.3"
|
|
@@ -86,7 +81,6 @@ program.command("create <projectName>").description("Create a new LeanMCP projec
|
|
|
86
81
|
});
|
|
87
82
|
const mainTs = `import dotenv from "dotenv";
|
|
88
83
|
import { createHTTPServer, MCPServer } from "@leanmcp/core";
|
|
89
|
-
import { ExampleService } from "./mcp/example.js";
|
|
90
84
|
|
|
91
85
|
// Load environment variables
|
|
92
86
|
dotenv.config();
|
|
@@ -95,25 +89,29 @@ const PORT = Number(process.env.PORT) || 3001;
|
|
|
95
89
|
|
|
96
90
|
/**
|
|
97
91
|
* Create and configure the MCP server
|
|
92
|
+
* Services are automatically discovered from ./mcp directory
|
|
98
93
|
*/
|
|
99
|
-
|
|
94
|
+
const serverFactory = async () => {
|
|
100
95
|
const server = new MCPServer({
|
|
101
96
|
name: "${projectName}",
|
|
102
|
-
version: "1.0.0"
|
|
97
|
+
version: "1.0.0",
|
|
98
|
+
logging: true
|
|
103
99
|
});
|
|
104
100
|
|
|
105
|
-
//
|
|
106
|
-
server.registerService(new ExampleService());
|
|
107
|
-
|
|
101
|
+
// Services are automatically discovered and registered from ./mcp
|
|
108
102
|
return server.getServer();
|
|
109
|
-
}
|
|
103
|
+
};
|
|
110
104
|
|
|
111
105
|
// Start the HTTP server
|
|
112
|
-
await createHTTPServer(
|
|
106
|
+
await createHTTPServer(serverFactory, {
|
|
113
107
|
port: PORT,
|
|
114
108
|
cors: true,
|
|
115
|
-
logging: true
|
|
109
|
+
logging: true // Log HTTP requests
|
|
116
110
|
});
|
|
111
|
+
|
|
112
|
+
console.log(\`\\n${projectName} MCP Server\`);
|
|
113
|
+
console.log(\`HTTP endpoint: http://localhost:\${PORT}/mcp\`);
|
|
114
|
+
console.log(\`Health check: http://localhost:\${PORT}/health\`);
|
|
117
115
|
`;
|
|
118
116
|
await fs.writeFile(path.join(targetDir, "main.ts"), mainTs);
|
|
119
117
|
const exampleServiceTs = `import { Tool, Resource, Prompt, SchemaConstraint, Optional } from "@leanmcp/core";
|
|
@@ -221,7 +219,7 @@ export class ExampleService {
|
|
|
221
219
|
}
|
|
222
220
|
}
|
|
223
221
|
`;
|
|
224
|
-
await fs.writeFile(path.join(targetDir, "mcp", "example.ts"), exampleServiceTs);
|
|
222
|
+
await fs.writeFile(path.join(targetDir, "mcp", "example", "index.ts"), exampleServiceTs);
|
|
225
223
|
const gitignore = `node_modules
|
|
226
224
|
dist
|
|
227
225
|
.env
|
|
@@ -261,22 +259,36 @@ npm start
|
|
|
261
259
|
\`\`\`
|
|
262
260
|
${projectName}/
|
|
263
261
|
\u251C\u2500\u2500 main.ts # Server entry point
|
|
264
|
-
\u251C\u2500\u2500 mcp/
|
|
265
|
-
\u2502 \u2514\u2500\u2500 example
|
|
262
|
+
\u251C\u2500\u2500 mcp/ # Services directory (auto-discovered)
|
|
263
|
+
\u2502 \u2514\u2500\u2500 example/
|
|
264
|
+
\u2502 \u2514\u2500\u2500 index.ts # Example service
|
|
266
265
|
\u251C\u2500\u2500 .env # Environment variables
|
|
267
266
|
\u2514\u2500\u2500 package.json
|
|
268
267
|
\`\`\`
|
|
269
268
|
|
|
270
269
|
## Adding New Services
|
|
271
270
|
|
|
272
|
-
Create a new service
|
|
271
|
+
Create a new service directory in \`mcp/\`:
|
|
273
272
|
|
|
274
273
|
\`\`\`typescript
|
|
275
|
-
|
|
274
|
+
// mcp/myservice/index.ts
|
|
275
|
+
import { Tool, SchemaConstraint } from "@leanmcp/core";
|
|
276
|
+
|
|
277
|
+
// Define input schema
|
|
278
|
+
class MyToolInput {
|
|
279
|
+
@SchemaConstraint({
|
|
280
|
+
description: "Message to process",
|
|
281
|
+
minLength: 1
|
|
282
|
+
})
|
|
283
|
+
message!: string;
|
|
284
|
+
}
|
|
276
285
|
|
|
277
286
|
export class MyService {
|
|
278
|
-
@Tool({
|
|
279
|
-
|
|
287
|
+
@Tool({
|
|
288
|
+
description: "My awesome tool",
|
|
289
|
+
inputClass: MyToolInput
|
|
290
|
+
})
|
|
291
|
+
async myTool(input: MyToolInput) {
|
|
280
292
|
return {
|
|
281
293
|
content: [{
|
|
282
294
|
type: "text",
|
|
@@ -287,12 +299,15 @@ export class MyService {
|
|
|
287
299
|
}
|
|
288
300
|
\`\`\`
|
|
289
301
|
|
|
290
|
-
|
|
302
|
+
Services are automatically discovered and registered - no need to modify \`main.ts\`!
|
|
291
303
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
304
|
+
## Features
|
|
305
|
+
|
|
306
|
+
- **Zero-config auto-discovery** - Services automatically registered from \`./mcp\` directory
|
|
307
|
+
- **Type-safe decorators** - \`@Tool\`, \`@Prompt\`, \`@Resource\` with full TypeScript support
|
|
308
|
+
- **Schema validation** - Automatic input validation with \`@SchemaConstraint\`
|
|
309
|
+
- **HTTP transport** - Production-ready HTTP server with session management
|
|
310
|
+
- **Hot reload** - Development mode with automatic restart on file changes
|
|
296
311
|
|
|
297
312
|
## Testing with MCP Inspector
|
|
298
313
|
|
|
@@ -320,12 +335,13 @@ program.command("add <serviceName>").description("Add a new MCP service to your
|
|
|
320
335
|
console.error(chalk.red("ERROR: Not a LeanMCP project (main.ts missing)."));
|
|
321
336
|
process.exit(1);
|
|
322
337
|
}
|
|
323
|
-
|
|
324
|
-
const serviceFile = path.join(
|
|
325
|
-
if (fs.existsSync(
|
|
338
|
+
const serviceDir = path.join(mcpDir, serviceName);
|
|
339
|
+
const serviceFile = path.join(serviceDir, "index.ts");
|
|
340
|
+
if (fs.existsSync(serviceDir)) {
|
|
326
341
|
console.error(chalk.red(`ERROR: Service ${serviceName} already exists.`));
|
|
327
342
|
process.exit(1);
|
|
328
343
|
}
|
|
344
|
+
await fs.mkdirp(serviceDir);
|
|
329
345
|
const indexTs = `import { Tool, Resource, Prompt, Optional, SchemaConstraint } from "@leanmcp/core";
|
|
330
346
|
|
|
331
347
|
// Input schema for greeting
|
|
@@ -386,33 +402,11 @@ export class ${capitalize(serviceName)}Service {
|
|
|
386
402
|
}
|
|
387
403
|
`;
|
|
388
404
|
await fs.writeFile(serviceFile, indexTs);
|
|
389
|
-
const mainTsPath = path.join(cwd, "main.ts");
|
|
390
|
-
let mainTsContent = await fs.readFile(mainTsPath, "utf-8");
|
|
391
|
-
const serviceClassName = `${capitalize(serviceName)}Service`;
|
|
392
|
-
const importStatement = `import { ${serviceClassName} } from "./mcp/${serviceName}.js";`;
|
|
393
|
-
const registerStatement = ` server.registerService(new ${serviceClassName}());`;
|
|
394
|
-
const lastImportMatch = mainTsContent.match(/import .* from .*;\n/g);
|
|
395
|
-
if (lastImportMatch) {
|
|
396
|
-
const lastImport = lastImportMatch[lastImportMatch.length - 1];
|
|
397
|
-
const lastImportIndex = mainTsContent.lastIndexOf(lastImport);
|
|
398
|
-
const afterLastImport = lastImportIndex + lastImport.length;
|
|
399
|
-
mainTsContent = mainTsContent.slice(0, afterLastImport) + importStatement + "\n" + mainTsContent.slice(afterLastImport);
|
|
400
|
-
}
|
|
401
|
-
const registerPattern = /server\.registerService\(new \w+\(\)\);/g;
|
|
402
|
-
const matches = [
|
|
403
|
-
...mainTsContent.matchAll(registerPattern)
|
|
404
|
-
];
|
|
405
|
-
if (matches.length > 0) {
|
|
406
|
-
const lastMatch = matches[matches.length - 1];
|
|
407
|
-
const insertPosition = lastMatch.index + lastMatch[0].length;
|
|
408
|
-
mainTsContent = mainTsContent.slice(0, insertPosition) + "\n" + registerStatement + mainTsContent.slice(insertPosition);
|
|
409
|
-
}
|
|
410
|
-
await fs.writeFile(mainTsPath, mainTsContent);
|
|
411
405
|
console.log(chalk.green(`\\nCreated new service: ${chalk.bold(serviceName)}`));
|
|
412
|
-
console.log(chalk.gray(` File: mcp/${serviceName}.ts`));
|
|
406
|
+
console.log(chalk.gray(` File: mcp/${serviceName}/index.ts`));
|
|
413
407
|
console.log(chalk.gray(` Tool: greet`));
|
|
414
408
|
console.log(chalk.gray(` Prompt: welcomePrompt`));
|
|
415
409
|
console.log(chalk.gray(` Resource: getStatus`));
|
|
416
|
-
console.log(chalk.green(`\\nService automatically
|
|
410
|
+
console.log(chalk.green(`\\nService will be automatically discovered on next server start!`));
|
|
417
411
|
});
|
|
418
412
|
program.parse();
|