@leanmcp/cli 0.5.0 → 0.5.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.
Files changed (2) hide show
  1. package/dist/index.js +335 -79
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1742,9 +1742,14 @@ async function deployCommand(folderPath, options = {}) {
1742
1742
  }
1743
1743
  const hasMainTs = await fs8.pathExists(path8.join(absolutePath, "main.ts"));
1744
1744
  const hasPackageJson = await fs8.pathExists(path8.join(absolutePath, "package.json"));
1745
- if (!hasMainTs && !hasPackageJson) {
1745
+ const hasMainPy = await fs8.pathExists(path8.join(absolutePath, "main.py"));
1746
+ const hasRequirementsTxt = await fs8.pathExists(path8.join(absolutePath, "requirements.txt"));
1747
+ const hasPyprojectToml = await fs8.pathExists(path8.join(absolutePath, "pyproject.toml"));
1748
+ const isNodeProject = hasMainTs || hasPackageJson;
1749
+ const isPythonProject = hasMainPy || hasRequirementsTxt || hasPyprojectToml;
1750
+ if (!isNodeProject && !isPythonProject) {
1746
1751
  logger.error("Not a valid project folder.");
1747
- logger.gray("Expected main.ts or package.json in the folder.\n");
1752
+ logger.gray("Expected one of: main.ts, package.json, main.py, requirements.txt, or pyproject.toml\n");
1748
1753
  process.exit(1);
1749
1754
  }
1750
1755
  const existingConfig = await readLeanMCPConfig(absolutePath);
@@ -3160,6 +3165,219 @@ export class ${capitalizedName}Service {
3160
3165
  }
3161
3166
  `, "getServiceIndexTemplate");
3162
3167
 
3168
+ // src/templates/python/main_py_v1.ts
3169
+ var getPythonMainTemplate = /* @__PURE__ */ __name((projectName) => `#!/usr/bin/env python3
3170
+ """
3171
+ ${projectName} - MCP Server with Streamable HTTP Transport
3172
+ """
3173
+ import os
3174
+ import uvicorn
3175
+ from dotenv import load_dotenv
3176
+ from mcp.server.fastmcp import FastMCP
3177
+
3178
+ # Load environment variables
3179
+ load_dotenv()
3180
+
3181
+ # Create the MCP server
3182
+ mcp = FastMCP("${projectName}")
3183
+
3184
+
3185
+ # === Define your tools, resources, and prompts below ===
3186
+
3187
+ @mcp.tool()
3188
+ def calculate(a: float, b: float, operation: str = "add") -> dict:
3189
+ """
3190
+ Perform arithmetic operations.
3191
+
3192
+ Args:
3193
+ a: First number
3194
+ b: Second number
3195
+ operation: Operation to perform (add, subtract, multiply, divide)
3196
+ """
3197
+ if operation == "add":
3198
+ result = a + b
3199
+ elif operation == "subtract":
3200
+ result = a - b
3201
+ elif operation == "multiply":
3202
+ result = a * b
3203
+ elif operation == "divide":
3204
+ if b == 0:
3205
+ raise ValueError("Cannot divide by zero")
3206
+ result = a / b
3207
+ else:
3208
+ raise ValueError(f"Invalid operation: {operation}")
3209
+
3210
+ return {"operation": operation, "a": a, "b": b, "result": result}
3211
+
3212
+
3213
+ @mcp.tool()
3214
+ def echo(message: str) -> dict:
3215
+ """Echo a message back with a timestamp."""
3216
+ from datetime import datetime
3217
+ return {"echoed": message, "timestamp": datetime.now().isoformat()}
3218
+
3219
+
3220
+ @mcp.resource("server://info")
3221
+ def server_info() -> str:
3222
+ """Get server information."""
3223
+ import json
3224
+ import time
3225
+ return json.dumps({
3226
+ "name": "${projectName}",
3227
+ "version": "1.0.0",
3228
+ "uptime": time.process_time()
3229
+ }, indent=2)
3230
+
3231
+
3232
+ @mcp.prompt()
3233
+ def greeting(name: str = "there") -> str:
3234
+ """Generate a greeting prompt."""
3235
+ return f"Hello {name}! Welcome to ${projectName}."
3236
+
3237
+
3238
+ if __name__ == "__main__":
3239
+ port = int(os.getenv("PORT", "3001"))
3240
+ print(f"\\n${projectName} MCP Server starting on port {port}...")
3241
+
3242
+ # Run with streamable HTTP transport
3243
+ uvicorn.run(
3244
+ mcp.streamable_http_app(),
3245
+ host="127.0.0.1",
3246
+ port=port,
3247
+ log_level="info"
3248
+ )
3249
+ `, "getPythonMainTemplate");
3250
+
3251
+ // src/templates/python/requirements_v1.ts
3252
+ var getPythonRequirementsTemplate = /* @__PURE__ */ __name(() => `# MCP Server Dependencies
3253
+ mcp>=1.0.0
3254
+ fastmcp>=0.1.0
3255
+ uvicorn>=0.30.0
3256
+ python-dotenv>=1.0.0
3257
+ pydantic>=2.0.0
3258
+
3259
+ # Optional: Add your dependencies below
3260
+ # requests>=2.31.0
3261
+ # httpx>=0.27.0
3262
+ `, "getPythonRequirementsTemplate");
3263
+
3264
+ // src/templates/python/gitignore_py_v1.ts
3265
+ var pythonGitignoreTemplate = `# Byte-compiled / optimized / DLL files
3266
+ __pycache__/
3267
+ *.py[cod]
3268
+ *$py.class
3269
+
3270
+ # Virtual environments
3271
+ venv/
3272
+ .venv/
3273
+ ENV/
3274
+ env/
3275
+
3276
+ # Distribution / packaging
3277
+ build/
3278
+ dist/
3279
+ *.egg-info/
3280
+ *.egg
3281
+
3282
+ # IDE
3283
+ .idea/
3284
+ .vscode/
3285
+ *.swp
3286
+ *.swo
3287
+
3288
+ # Environment variables
3289
+ .env
3290
+ .env.local
3291
+ .env.*.local
3292
+
3293
+ # Testing
3294
+ .pytest_cache/
3295
+ .coverage
3296
+ htmlcov/
3297
+
3298
+ # Logs
3299
+ *.log
3300
+
3301
+ # OS
3302
+ .DS_Store
3303
+ Thumbs.db
3304
+ `;
3305
+
3306
+ // src/templates/python/readme_py_v1.ts
3307
+ var getPythonReadmeTemplate = /* @__PURE__ */ __name((projectName) => `# ${projectName}
3308
+
3309
+ A Python MCP (Model Context Protocol) server with Streamable HTTP transport.
3310
+
3311
+ ## Quick Start
3312
+
3313
+ ### Prerequisites
3314
+
3315
+ - Python 3.10+
3316
+ - pip or uv package manager
3317
+
3318
+ ### Installation
3319
+
3320
+ \`\`\`bash
3321
+ # Create virtual environment
3322
+ python -m venv venv
3323
+ source venv/bin/activate # On Windows: venv\\Scripts\\activate
3324
+
3325
+ # Install dependencies
3326
+ pip install -r requirements.txt
3327
+ \`\`\`
3328
+
3329
+ ### Development
3330
+
3331
+ \`\`\`bash
3332
+ # Start development server
3333
+ python main.py
3334
+ \`\`\`
3335
+
3336
+ Server runs at http://localhost:3001
3337
+
3338
+ ### Test with MCP Inspector
3339
+
3340
+ \`\`\`bash
3341
+ npx @modelcontextprotocol/inspector http://localhost:3001/mcp
3342
+ \`\`\`
3343
+
3344
+ ## Project Structure
3345
+
3346
+ \`\`\`
3347
+ ${projectName}/
3348
+ main.py # Server entry point with tools/resources/prompts
3349
+ requirements.txt # Python dependencies
3350
+ .env # Environment variables
3351
+ \`\`\`
3352
+
3353
+ ## Adding New Tools
3354
+
3355
+ Add tools directly in \`main.py\` using the \`@mcp.tool()\` decorator:
3356
+
3357
+ \`\`\`python
3358
+ @mcp.tool()
3359
+ def my_tool(param: str) -> dict:
3360
+ """Tool description shown to AI.
3361
+
3362
+ Args:
3363
+ param: Parameter description
3364
+ """
3365
+ return {"result": param}
3366
+ \`\`\`
3367
+
3368
+ ## Deploy to LeanMCP Cloud
3369
+
3370
+ \`\`\`bash
3371
+ leanmcp deploy .
3372
+ \`\`\`
3373
+
3374
+ ## Resources
3375
+
3376
+ - [MCP Documentation](https://modelcontextprotocol.io)
3377
+ - [FastMCP Documentation](https://github.com/jlowin/fastmcp)
3378
+ - [LeanMCP Documentation](https://docs.leanmcp.com)
3379
+ `, "getPythonReadmeTemplate");
3380
+
3163
3381
  // src/index.ts
3164
3382
  var require2 = createRequire(import.meta.url);
3165
3383
  var pkg = require2("../package.json");
@@ -3182,7 +3400,8 @@ __name(enableDebugIfNeeded, "enableDebugIfNeeded");
3182
3400
  enableDebugIfNeeded();
3183
3401
  program.name("leanmcp").description("LeanMCP CLI \u2014 create production-ready MCP servers with Streamable HTTP").version(pkg.version, "-v, --version", "Output the current version").helpOption("-h, --help", "Display help for command").option("-d, --debug", "Enable debug logging for all commands").addHelpText("after", `
3184
3402
  Examples:
3185
- $ leanmcp create my-app # Create new project (interactive)
3403
+ $ leanmcp create my-app # Create new TypeScript project (interactive)
3404
+ $ leanmcp create my-app --python # Create new Python project
3186
3405
  $ leanmcp create my-app -i # Create and install deps (non-interactive)
3187
3406
  $ leanmcp create my-app --no-install # Create without installing deps
3188
3407
  $ leanmcp dev # Start development server
@@ -3200,7 +3419,7 @@ Global Options:
3200
3419
  -h, --help Display help for command
3201
3420
  -d, --debug Enable debug logging for all commands
3202
3421
  `);
3203
- program.command("create <projectName>").description("Create a new LeanMCP project with Streamable HTTP transport").option("--allow-all", "Skip interactive confirmations and assume Yes").option("--no-dashboard", "Disable dashboard UI at / and /mcp GET endpoints").option("-i, --install", "Install dependencies automatically (non-interactive, no dev server)").option("--no-install", "Skip dependency installation (non-interactive)").action(async (projectName, options) => {
3422
+ program.command("create <projectName>").description("Create a new LeanMCP project with Streamable HTTP transport").option("--allow-all", "Skip interactive confirmations and assume Yes").option("--no-dashboard", "Disable dashboard UI at / and /mcp GET endpoints").option("-i, --install", "Install dependencies automatically (non-interactive, no dev server)").option("--no-install", "Skip dependency installation (non-interactive)").option("--python", "Create a Python MCP project instead of TypeScript").action(async (projectName, options) => {
3204
3423
  trackCommand("create", {
3205
3424
  projectName,
3206
3425
  ...options
@@ -3212,85 +3431,102 @@ program.command("create <projectName>").description("Create a new LeanMCP projec
3212
3431
  process.exit(1);
3213
3432
  }
3214
3433
  await fs10.mkdirp(targetDir);
3215
- await fs10.mkdirp(path10.join(targetDir, "mcp", "example"));
3216
- const pkg2 = {
3217
- name: projectName,
3218
- version: "1.0.0",
3219
- description: "MCP Server with Streamable HTTP Transport and LeanMCP SDK",
3220
- main: "dist/main.js",
3221
- type: "module",
3222
- scripts: {
3223
- dev: "leanmcp dev",
3224
- build: "leanmcp build",
3225
- start: "leanmcp start",
3226
- inspect: "npx @modelcontextprotocol/inspector node dist/main.js",
3227
- "start:node": "node dist/main.js",
3228
- clean: "rm -rf dist"
3229
- },
3230
- keywords: [
3231
- "mcp",
3232
- "model-context-protocol",
3233
- "streamable-http",
3234
- "leanmcp"
3235
- ],
3236
- author: "",
3237
- license: "MIT",
3238
- dependencies: {
3239
- "@leanmcp/core": "^0.3.14",
3240
- "@leanmcp/ui": "^0.2.1",
3241
- "@leanmcp/auth": "^0.4.0",
3242
- "dotenv": "^16.5.0"
3243
- },
3244
- devDependencies: {
3245
- "@leanmcp/cli": "^0.4.0",
3246
- "@types/node": "^20.0.0",
3247
- "tsx": "^4.20.3",
3248
- "typescript": "^5.6.3"
3249
- }
3250
- };
3251
- await fs10.writeJSON(path10.join(targetDir, "package.json"), pkg2, {
3252
- spaces: 2
3253
- });
3254
- const tsconfig = {
3255
- compilerOptions: {
3256
- module: "ESNext",
3257
- target: "ES2022",
3258
- moduleResolution: "Node",
3259
- esModuleInterop: true,
3260
- strict: true,
3261
- skipLibCheck: true,
3262
- outDir: "dist",
3263
- experimentalDecorators: true,
3264
- emitDecoratorMetadata: true
3265
- },
3266
- include: [
3267
- "**/*.ts"
3268
- ],
3269
- exclude: [
3270
- "node_modules",
3271
- "dist"
3272
- ]
3273
- };
3274
- await fs10.writeJSON(path10.join(targetDir, "tsconfig.json"), tsconfig, {
3275
- spaces: 2
3276
- });
3277
- const dashboardLine = options.dashboard === false ? `
3434
+ const isPython = options.python === true;
3435
+ if (isPython) {
3436
+ const requirements = getPythonRequirementsTemplate();
3437
+ await fs10.writeFile(path10.join(targetDir, "requirements.txt"), requirements);
3438
+ const mainPy = getPythonMainTemplate(projectName);
3439
+ await fs10.writeFile(path10.join(targetDir, "main.py"), mainPy);
3440
+ await fs10.writeFile(path10.join(targetDir, ".gitignore"), pythonGitignoreTemplate);
3441
+ const env = `# Server Configuration
3442
+ PORT=3001
3443
+
3444
+ # Add your environment variables here
3445
+ `;
3446
+ await fs10.writeFile(path10.join(targetDir, ".env"), env);
3447
+ const readme = getPythonReadmeTemplate(projectName);
3448
+ await fs10.writeFile(path10.join(targetDir, "README.md"), readme);
3449
+ } else {
3450
+ await fs10.mkdirp(path10.join(targetDir, "mcp", "example"));
3451
+ const pkg2 = {
3452
+ name: projectName,
3453
+ version: "1.0.0",
3454
+ description: "MCP Server with Streamable HTTP Transport and LeanMCP SDK",
3455
+ main: "dist/main.js",
3456
+ type: "module",
3457
+ scripts: {
3458
+ dev: "leanmcp dev",
3459
+ build: "leanmcp build",
3460
+ start: "leanmcp start",
3461
+ inspect: "npx @modelcontextprotocol/inspector node dist/main.js",
3462
+ "start:node": "node dist/main.js",
3463
+ clean: "rm -rf dist"
3464
+ },
3465
+ keywords: [
3466
+ "mcp",
3467
+ "model-context-protocol",
3468
+ "streamable-http",
3469
+ "leanmcp"
3470
+ ],
3471
+ author: "",
3472
+ license: "MIT",
3473
+ dependencies: {
3474
+ "@leanmcp/core": "^0.3.14",
3475
+ "@leanmcp/ui": "^0.2.1",
3476
+ "@leanmcp/auth": "^0.4.0",
3477
+ "dotenv": "^16.5.0"
3478
+ },
3479
+ devDependencies: {
3480
+ "@leanmcp/cli": "^0.4.0",
3481
+ "@types/node": "^20.0.0",
3482
+ "tsx": "^4.20.3",
3483
+ "typescript": "^5.6.3"
3484
+ }
3485
+ };
3486
+ await fs10.writeJSON(path10.join(targetDir, "package.json"), pkg2, {
3487
+ spaces: 2
3488
+ });
3489
+ const tsconfig = {
3490
+ compilerOptions: {
3491
+ module: "ESNext",
3492
+ target: "ES2022",
3493
+ moduleResolution: "Node",
3494
+ esModuleInterop: true,
3495
+ strict: true,
3496
+ skipLibCheck: true,
3497
+ outDir: "dist",
3498
+ experimentalDecorators: true,
3499
+ emitDecoratorMetadata: true
3500
+ },
3501
+ include: [
3502
+ "**/*.ts"
3503
+ ],
3504
+ exclude: [
3505
+ "node_modules",
3506
+ "dist"
3507
+ ]
3508
+ };
3509
+ await fs10.writeJSON(path10.join(targetDir, "tsconfig.json"), tsconfig, {
3510
+ spaces: 2
3511
+ });
3512
+ const dashboardLine = options.dashboard === false ? `
3278
3513
  dashboard: false, // Dashboard disabled via --no-dashboard` : "";
3279
- const mainTs = getMainTsTemplate(projectName, dashboardLine);
3280
- await fs10.writeFile(path10.join(targetDir, "main.ts"), mainTs);
3281
- const exampleServiceTs = getExampleServiceTemplate(projectName);
3282
- await fs10.writeFile(path10.join(targetDir, "mcp", "example", "index.ts"), exampleServiceTs);
3283
- const gitignore = gitignoreTemplate;
3284
- const env = `# Server Configuration
3514
+ const mainTs = getMainTsTemplate(projectName, dashboardLine);
3515
+ await fs10.writeFile(path10.join(targetDir, "main.ts"), mainTs);
3516
+ const exampleServiceTs = getExampleServiceTemplate(projectName);
3517
+ await fs10.writeFile(path10.join(targetDir, "mcp", "example", "index.ts"), exampleServiceTs);
3518
+ const gitignore = gitignoreTemplate;
3519
+ const env = `# Server Configuration
3285
3520
  PORT=3001
3286
3521
  NODE_ENV=development
3287
3522
 
3288
3523
  # Add your environment variables here
3289
3524
  `;
3290
- await fs10.writeFile(path10.join(targetDir, ".gitignore"), gitignore);
3291
- await fs10.writeFile(path10.join(targetDir, ".env"), env);
3292
- const readme = getReadmeTemplate(projectName);
3293
- await fs10.writeFile(path10.join(targetDir, "README.md"), readme);
3525
+ await fs10.writeFile(path10.join(targetDir, ".gitignore"), gitignore);
3526
+ await fs10.writeFile(path10.join(targetDir, ".env"), env);
3527
+ const readme = getReadmeTemplate(projectName);
3528
+ await fs10.writeFile(path10.join(targetDir, "README.md"), readme);
3529
+ }
3294
3530
  spinner.succeed(`Project ${projectName} created!`);
3295
3531
  logger.log("\nSuccess! Your MCP server is ready.\n", chalk.green);
3296
3532
  logger.log("To deploy to LeanMCP cloud:", chalk.cyan);
@@ -3303,8 +3539,28 @@ NODE_ENV=development
3303
3539
  if (options.install === false) {
3304
3540
  logger.log("To get started:", chalk.cyan);
3305
3541
  logger.log(` cd ${projectName}`, chalk.gray);
3306
- logger.log(` npm install`, chalk.gray);
3307
- logger.log(` npm run dev`, chalk.gray);
3542
+ if (isPython) {
3543
+ logger.log(` python -m venv venv`, chalk.gray);
3544
+ logger.log(` source venv/bin/activate # On Windows: venv\\Scripts\\activate`, chalk.gray);
3545
+ logger.log(` pip install -r requirements.txt`, chalk.gray);
3546
+ logger.log(` python main.py`, chalk.gray);
3547
+ } else {
3548
+ logger.log(` npm install`, chalk.gray);
3549
+ logger.log(` npm run dev`, chalk.gray);
3550
+ }
3551
+ logger.log("");
3552
+ logger.log("To deploy to LeanMCP cloud:", chalk.cyan);
3553
+ logger.log(` cd ${projectName}`, chalk.gray);
3554
+ logger.log(` leanmcp deploy .`, chalk.gray);
3555
+ return;
3556
+ }
3557
+ if (isPython) {
3558
+ logger.log("\nTo get started:", chalk.cyan);
3559
+ logger.log(` cd ${projectName}`, chalk.gray);
3560
+ logger.log(` python -m venv venv`, chalk.gray);
3561
+ logger.log(` source venv/bin/activate # On Windows: venv\\Scripts\\activate`, chalk.gray);
3562
+ logger.log(` pip install -r requirements.txt`, chalk.gray);
3563
+ logger.log(` python main.py`, chalk.gray);
3308
3564
  logger.log("");
3309
3565
  logger.log("To deploy to LeanMCP cloud:", chalk.cyan);
3310
3566
  logger.log(` cd ${projectName}`, chalk.gray);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leanmcp/cli",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "Command-line interface for scaffolding LeanMCP projects",
5
5
  "bin": {
6
6
  "leanmcp": "bin/leanmcp.js"