mcp-new 1.2.1 → 1.5.0

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 (47) hide show
  1. package/README.md +48 -1
  2. package/dist/{chunk-BHGUGEHE.js → chunk-YISSMIHU.js} +223 -46
  3. package/dist/cli.js +817 -51
  4. package/dist/index.d.ts +25 -7
  5. package/dist/index.js +17 -3
  6. package/package.json +1 -1
  7. package/templates/csharp/.env.example +6 -0
  8. package/templates/csharp/.gitignore.ejs +53 -0
  9. package/templates/csharp/McpServer.csproj.ejs +19 -0
  10. package/templates/csharp/README.md.ejs +136 -0
  11. package/templates/csharp/src/Program.cs.ejs +117 -0
  12. package/templates/elixir/.env.example +6 -0
  13. package/templates/elixir/.gitignore.ejs +33 -0
  14. package/templates/elixir/README.md.ejs +154 -0
  15. package/templates/elixir/config/config.exs.ejs +9 -0
  16. package/templates/elixir/config/dev.exs.ejs +3 -0
  17. package/templates/elixir/config/prod.exs.ejs +3 -0
  18. package/templates/elixir/lib/application.ex.ejs +19 -0
  19. package/templates/elixir/lib/cli.ex.ejs +17 -0
  20. package/templates/elixir/lib/server.ex.ejs +112 -0
  21. package/templates/elixir/mix.exs.ejs +32 -0
  22. package/templates/java/gradle/.env.example +6 -0
  23. package/templates/java/gradle/.gitignore.ejs +48 -0
  24. package/templates/java/gradle/README.md.ejs +132 -0
  25. package/templates/java/gradle/build.gradle.ejs +46 -0
  26. package/templates/java/gradle/settings.gradle.ejs +1 -0
  27. package/templates/java/gradle/src/main/java/com/example/mcp/McpServer.java.ejs +149 -0
  28. package/templates/java/gradle/src/main/resources/logback.xml +13 -0
  29. package/templates/java/maven/.env.example +6 -0
  30. package/templates/java/maven/.gitignore.ejs +53 -0
  31. package/templates/java/maven/README.md.ejs +131 -0
  32. package/templates/java/maven/pom.xml.ejs +86 -0
  33. package/templates/java/maven/src/main/java/com/example/mcp/McpServer.java.ejs +149 -0
  34. package/templates/java/maven/src/main/resources/logback.xml +13 -0
  35. package/templates/kotlin/gradle/.env.example +6 -0
  36. package/templates/kotlin/gradle/.gitignore.ejs +45 -0
  37. package/templates/kotlin/gradle/README.md.ejs +138 -0
  38. package/templates/kotlin/gradle/build.gradle.kts.ejs +48 -0
  39. package/templates/kotlin/gradle/settings.gradle.kts.ejs +1 -0
  40. package/templates/kotlin/gradle/src/main/kotlin/com/example/mcp/McpServer.kt.ejs +141 -0
  41. package/templates/kotlin/gradle/src/main/resources/logback.xml +13 -0
  42. package/templates/kotlin/maven/.env.example +6 -0
  43. package/templates/kotlin/maven/.gitignore.ejs +50 -0
  44. package/templates/kotlin/maven/README.md.ejs +96 -0
  45. package/templates/kotlin/maven/pom.xml.ejs +105 -0
  46. package/templates/kotlin/maven/src/main/kotlin/com/example/mcp/McpServer.kt.ejs +141 -0
  47. package/templates/kotlin/maven/src/main/resources/logback.xml +13 -0
package/dist/cli.js CHANGED
@@ -1,84 +1,850 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ PRESETS,
3
4
  addToolCommand,
4
5
  createCommand,
5
- initCommand
6
- } from "./chunk-BHGUGEHE.js";
6
+ createInitialCommit,
7
+ createSpinner,
8
+ ensureDir,
9
+ exists,
10
+ generateFromWizard,
11
+ initCommand,
12
+ initGitRepository,
13
+ isGitInstalled,
14
+ logger,
15
+ promptJavaBuildTool,
16
+ promptLanguage,
17
+ readDir,
18
+ readFile,
19
+ withSpinner,
20
+ writeFile
21
+ } from "./chunk-YISSMIHU.js";
7
22
 
8
23
  // src/cli.ts
9
24
  import { Command } from "commander";
25
+ import chalk4 from "chalk";
26
+
27
+ // src/commands/list-presets.ts
10
28
  import chalk from "chalk";
29
+ async function listPresetsCommand() {
30
+ console.log();
31
+ console.log(chalk.bold.white("Available Presets"));
32
+ console.log(chalk.gray("\u2500".repeat(60)));
33
+ console.log();
34
+ for (const [id, preset] of Object.entries(PRESETS)) {
35
+ console.log(chalk.yellow.bold(` ${id}`));
36
+ console.log(chalk.gray(` ${preset.description}`));
37
+ console.log();
38
+ console.log(chalk.white(" Tools:"));
39
+ for (const tool of preset.tools) {
40
+ const paramCount = tool.parameters.length;
41
+ const paramText = paramCount === 0 ? chalk.gray("no params") : chalk.gray(`${paramCount} param${paramCount > 1 ? "s" : ""}`);
42
+ console.log(` ${chalk.cyan("\u2022")} ${chalk.white(tool.name)} ${paramText}`);
43
+ console.log(` ${chalk.gray(tool.description)}`);
44
+ }
45
+ console.log();
46
+ console.log(chalk.gray(" " + "\u2500".repeat(56)));
47
+ console.log();
48
+ }
49
+ console.log(chalk.bold.white("Usage:"));
50
+ console.log();
51
+ console.log(chalk.gray(" $"), chalk.cyan("mcp-new my-project --preset database"));
52
+ console.log(chalk.gray(" $"), chalk.cyan("mcp-new my-api --preset rest-api -t"));
53
+ console.log(chalk.gray(" $"), chalk.cyan("mcp-new my-fs --preset filesystem -p"));
54
+ console.log();
55
+ }
56
+
57
+ // src/commands/validate.ts
58
+ import path from "path";
59
+ import fs from "fs-extra";
60
+ import chalk2 from "chalk";
61
+ async function validateCommand() {
62
+ const currentDir = process.cwd();
63
+ logger.title("Validating MCP Server");
64
+ const result = await validateProject(currentDir);
65
+ console.log();
66
+ if (result.language) {
67
+ console.log(chalk2.blue("i"), `Detected language: ${chalk2.cyan(result.language)}`);
68
+ }
69
+ for (const info of result.info) {
70
+ console.log(chalk2.blue("i"), info);
71
+ }
72
+ for (const warning of result.warnings) {
73
+ console.log(chalk2.yellow("\u26A0"), warning);
74
+ }
75
+ for (const error of result.errors) {
76
+ console.log(chalk2.red("\u2717"), error);
77
+ }
78
+ console.log();
79
+ if (result.valid) {
80
+ logger.success("MCP server is valid!");
81
+ } else {
82
+ logger.error("Validation failed. Please fix the issues above.");
83
+ process.exit(1);
84
+ }
85
+ }
86
+ async function validateProject(projectDir) {
87
+ const result = {
88
+ valid: true,
89
+ language: null,
90
+ errors: [],
91
+ warnings: [],
92
+ info: []
93
+ };
94
+ const hasPackageJson = await fs.pathExists(path.join(projectDir, "package.json"));
95
+ const hasPyproject = await fs.pathExists(path.join(projectDir, "pyproject.toml"));
96
+ const hasGoMod = await fs.pathExists(path.join(projectDir, "go.mod"));
97
+ const hasCargoToml = await fs.pathExists(path.join(projectDir, "Cargo.toml"));
98
+ if (!hasPackageJson && !hasPyproject && !hasGoMod && !hasCargoToml) {
99
+ result.errors.push("No project configuration found (package.json, pyproject.toml, go.mod, or Cargo.toml)");
100
+ result.valid = false;
101
+ return result;
102
+ }
103
+ if (hasPackageJson) {
104
+ result.language = "typescript";
105
+ await validateTypeScriptProject(projectDir, result);
106
+ }
107
+ if (hasPyproject) {
108
+ result.language = "python";
109
+ await validatePythonProject(projectDir, result);
110
+ }
111
+ if (hasGoMod) {
112
+ result.language = "go";
113
+ await validateGoProject(projectDir, result);
114
+ }
115
+ if (hasCargoToml) {
116
+ result.language = "rust";
117
+ await validateRustProject(projectDir, result);
118
+ }
119
+ return result;
120
+ }
121
+ async function validateTypeScriptProject(projectDir, result) {
122
+ try {
123
+ const packageJsonPath = path.join(projectDir, "package.json");
124
+ const packageJson = await fs.readJson(packageJsonPath);
125
+ const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
126
+ if (deps["@modelcontextprotocol/sdk"]) {
127
+ result.info.push(`MCP SDK version: ${chalk2.green(deps["@modelcontextprotocol/sdk"])}`);
128
+ } else {
129
+ result.errors.push("Missing @modelcontextprotocol/sdk dependency");
130
+ result.valid = false;
131
+ }
132
+ const srcIndex = path.join(projectDir, "src", "index.ts");
133
+ const srcIndexJs = path.join(projectDir, "src", "index.js");
134
+ const distIndex = path.join(projectDir, "dist", "index.js");
135
+ if (await fs.pathExists(srcIndex)) {
136
+ result.info.push("Entry point: src/index.ts");
137
+ await validateTypeScriptEntryPoint(srcIndex, result);
138
+ } else if (await fs.pathExists(srcIndexJs)) {
139
+ result.info.push("Entry point: src/index.js");
140
+ } else if (await fs.pathExists(distIndex)) {
141
+ result.info.push("Entry point: dist/index.js (built)");
142
+ } else {
143
+ result.warnings.push("No entry point found (expected src/index.ts or src/index.js)");
144
+ }
145
+ if (await fs.pathExists(path.join(projectDir, "tsconfig.json"))) {
146
+ result.info.push("TypeScript config: tsconfig.json");
147
+ } else {
148
+ result.warnings.push("No tsconfig.json found");
149
+ }
150
+ if (packageJson.scripts?.build) {
151
+ result.info.push(`Build script: ${chalk2.gray(packageJson.scripts.build)}`);
152
+ } else {
153
+ result.warnings.push("No build script found in package.json");
154
+ }
155
+ } catch (error) {
156
+ result.errors.push("Failed to parse package.json");
157
+ result.valid = false;
158
+ }
159
+ }
160
+ async function validateTypeScriptEntryPoint(filePath, result) {
161
+ try {
162
+ const content = await fs.readFile(filePath, "utf-8");
163
+ if (content.includes("@modelcontextprotocol/sdk")) {
164
+ result.info.push("MCP SDK import found");
165
+ } else {
166
+ result.warnings.push("No MCP SDK import found in entry point");
167
+ }
168
+ if (content.includes("McpServer") || content.includes("Server")) {
169
+ result.info.push("MCP Server class usage found");
170
+ } else {
171
+ result.warnings.push("No MCP Server class found");
172
+ }
173
+ const toolMatches = content.match(/\.tool\s*\(/g) || content.match(/setRequestHandler.*CallToolRequest/g);
174
+ if (toolMatches) {
175
+ result.info.push(`Tools defined: ${chalk2.green(toolMatches.length)}`);
176
+ }
177
+ } catch {
178
+ result.warnings.push("Could not analyze entry point file");
179
+ }
180
+ }
181
+ async function validatePythonProject(projectDir, result) {
182
+ try {
183
+ const pyprojectPath = path.join(projectDir, "pyproject.toml");
184
+ const content = await fs.readFile(pyprojectPath, "utf-8");
185
+ if (content.includes("mcp") || content.includes("modelcontextprotocol")) {
186
+ result.info.push("MCP dependency found in pyproject.toml");
187
+ } else {
188
+ const requirementsPath = path.join(projectDir, "requirements.txt");
189
+ if (await fs.pathExists(requirementsPath)) {
190
+ const requirements = await fs.readFile(requirementsPath, "utf-8");
191
+ if (requirements.includes("mcp")) {
192
+ result.info.push("MCP dependency found in requirements.txt");
193
+ } else {
194
+ result.errors.push("Missing MCP dependency");
195
+ result.valid = false;
196
+ }
197
+ } else {
198
+ result.warnings.push("No requirements.txt found");
199
+ }
200
+ }
201
+ const serverPath = path.join(projectDir, "src", "server.py");
202
+ if (await fs.pathExists(serverPath)) {
203
+ result.info.push("Server file: src/server.py");
204
+ } else {
205
+ result.warnings.push("No server.py found in src/");
206
+ }
207
+ } catch (error) {
208
+ result.errors.push("Failed to parse pyproject.toml");
209
+ result.valid = false;
210
+ }
211
+ }
212
+ async function validateGoProject(projectDir, result) {
213
+ try {
214
+ const goModPath = path.join(projectDir, "go.mod");
215
+ const content = await fs.readFile(goModPath, "utf-8");
216
+ if (content.includes("mcp-go") || content.includes("modelcontextprotocol")) {
217
+ result.info.push("MCP Go SDK found in go.mod");
218
+ } else {
219
+ result.errors.push("Missing MCP Go SDK dependency");
220
+ result.valid = false;
221
+ }
222
+ const mainPath = path.join(projectDir, "cmd", "server", "main.go");
223
+ const altMainPath = path.join(projectDir, "main.go");
224
+ if (await fs.pathExists(mainPath)) {
225
+ result.info.push("Entry point: cmd/server/main.go");
226
+ } else if (await fs.pathExists(altMainPath)) {
227
+ result.info.push("Entry point: main.go");
228
+ } else {
229
+ result.warnings.push("No main.go found");
230
+ }
231
+ } catch (error) {
232
+ result.errors.push("Failed to parse go.mod");
233
+ result.valid = false;
234
+ }
235
+ }
236
+ async function validateRustProject(projectDir, result) {
237
+ try {
238
+ const cargoPath = path.join(projectDir, "Cargo.toml");
239
+ const content = await fs.readFile(cargoPath, "utf-8");
240
+ if (content.includes("rmcp") || content.includes("mcp")) {
241
+ result.info.push("MCP Rust SDK found in Cargo.toml");
242
+ } else {
243
+ result.errors.push("Missing MCP Rust SDK dependency");
244
+ result.valid = false;
245
+ }
246
+ const mainPath = path.join(projectDir, "src", "main.rs");
247
+ if (await fs.pathExists(mainPath)) {
248
+ result.info.push("Entry point: src/main.rs");
249
+ } else {
250
+ result.warnings.push("No main.rs found");
251
+ }
252
+ } catch (error) {
253
+ result.errors.push("Failed to parse Cargo.toml");
254
+ result.valid = false;
255
+ }
256
+ }
257
+
258
+ // src/commands/upgrade.ts
259
+ import path2 from "path";
260
+ import fs2 from "fs-extra";
261
+ import chalk3 from "chalk";
262
+ import { execa } from "execa";
263
+ async function upgradeCommand(options) {
264
+ const currentDir = process.cwd();
265
+ logger.title("MCP SDK Upgrade");
266
+ const hasPackageJson = await fs2.pathExists(path2.join(currentDir, "package.json"));
267
+ const hasPyproject = await fs2.pathExists(path2.join(currentDir, "pyproject.toml"));
268
+ const hasGoMod = await fs2.pathExists(path2.join(currentDir, "go.mod"));
269
+ const hasCargoToml = await fs2.pathExists(path2.join(currentDir, "Cargo.toml"));
270
+ if (!hasPackageJson && !hasPyproject && !hasGoMod && !hasCargoToml) {
271
+ logger.error("No project configuration found.");
272
+ logger.info("Run this command from the root of your MCP server project.");
273
+ process.exit(1);
274
+ }
275
+ let info = null;
276
+ if (hasPackageJson) {
277
+ info = await checkNodePackage(currentDir);
278
+ } else if (hasPyproject) {
279
+ info = await checkPythonPackage(currentDir);
280
+ } else if (hasGoMod) {
281
+ info = await checkGoPackage(currentDir);
282
+ } else if (hasCargoToml) {
283
+ info = await checkRustPackage(currentDir);
284
+ }
285
+ if (!info) {
286
+ logger.error("Could not detect MCP SDK version.");
287
+ process.exit(1);
288
+ }
289
+ console.log();
290
+ console.log(chalk3.blue("i"), `Package: ${chalk3.cyan(info.packageName)}`);
291
+ console.log(chalk3.blue("i"), `Current version: ${info.current ? chalk3.yellow(info.current) : chalk3.gray("not installed")}`);
292
+ console.log(chalk3.blue("i"), `Latest version: ${info.latest ? chalk3.green(info.latest) : chalk3.gray("unknown")}`);
293
+ console.log();
294
+ if (options.check) {
295
+ if (info.current === info.latest) {
296
+ logger.success("Already up to date!");
297
+ } else if (info.latest) {
298
+ logger.info(`Update available: ${info.current} \u2192 ${info.latest}`);
299
+ logger.code(`mcp-new upgrade`);
300
+ }
301
+ return;
302
+ }
303
+ if (info.current === info.latest) {
304
+ logger.success("Already up to date!");
305
+ return;
306
+ }
307
+ if (!info.latest) {
308
+ logger.error("Could not fetch latest version.");
309
+ process.exit(1);
310
+ }
311
+ const spinner = createSpinner(`Upgrading ${info.packageName}...`);
312
+ spinner.start();
313
+ try {
314
+ if (hasPackageJson) {
315
+ await upgradeNodePackage(currentDir, info);
316
+ } else if (hasPyproject) {
317
+ await upgradePythonPackage(currentDir, info);
318
+ } else if (hasGoMod) {
319
+ await upgradeGoPackage(currentDir, info);
320
+ } else if (hasCargoToml) {
321
+ await upgradeRustPackage(currentDir, info);
322
+ }
323
+ spinner.succeed(`Upgraded to ${info.packageName}@${info.latest}`);
324
+ logger.blank();
325
+ logger.success("MCP SDK upgraded successfully!");
326
+ } catch (error) {
327
+ spinner.fail("Upgrade failed");
328
+ if (error instanceof Error) {
329
+ logger.error(error.message);
330
+ }
331
+ process.exit(1);
332
+ }
333
+ }
334
+ async function checkNodePackage(projectDir) {
335
+ const packageName = "@modelcontextprotocol/sdk";
336
+ let current = null;
337
+ let latest = null;
338
+ try {
339
+ const packageJson = await fs2.readJson(path2.join(projectDir, "package.json"));
340
+ const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
341
+ current = deps[packageName]?.replace(/[\^~]/, "") || null;
342
+ } catch {
343
+ }
344
+ try {
345
+ const { stdout } = await execa("npm", ["view", packageName, "version"]);
346
+ latest = stdout.trim();
347
+ } catch {
348
+ }
349
+ return { current, latest, packageManager: "npm", packageName };
350
+ }
351
+ async function upgradeNodePackage(projectDir, info) {
352
+ const packageJsonPath = path2.join(projectDir, "package.json");
353
+ const packageJson = await fs2.readJson(packageJsonPath);
354
+ if (packageJson.dependencies?.[info.packageName]) {
355
+ packageJson.dependencies[info.packageName] = `^${info.latest}`;
356
+ } else if (packageJson.devDependencies?.[info.packageName]) {
357
+ packageJson.devDependencies[info.packageName] = `^${info.latest}`;
358
+ }
359
+ await fs2.writeJson(packageJsonPath, packageJson, { spaces: 2 });
360
+ await execa("npm", ["install"], { cwd: projectDir });
361
+ }
362
+ async function checkPythonPackage(_projectDir) {
363
+ const packageName = "mcp";
364
+ let current = null;
365
+ let latest = null;
366
+ try {
367
+ const { stdout } = await execa("pip", ["show", packageName]);
368
+ const versionMatch = stdout.match(/Version:\s*(\S+)/);
369
+ current = versionMatch?.[1] || null;
370
+ } catch {
371
+ }
372
+ try {
373
+ const { stdout } = await execa("pip", ["index", "versions", packageName]);
374
+ const match = stdout.match(/Available versions:\s*([^\s,]+)/);
375
+ latest = match?.[1] || null;
376
+ } catch {
377
+ try {
378
+ const { stdout } = await execa("pip", ["install", `${packageName}==`, "--dry-run"], { reject: false });
379
+ const match = stdout.match(/from versions:\s*([^)]+)\)/);
380
+ if (match) {
381
+ const versions = match[1].split(",").map((v) => v.trim());
382
+ latest = versions[versions.length - 1] || null;
383
+ }
384
+ } catch {
385
+ }
386
+ }
387
+ return { current, latest, packageManager: "pip", packageName };
388
+ }
389
+ async function upgradePythonPackage(_projectDir, info) {
390
+ await execa("pip", ["install", "--upgrade", info.packageName]);
391
+ }
392
+ async function checkGoPackage(projectDir) {
393
+ const packageName = "github.com/mark3labs/mcp-go";
394
+ let current = null;
395
+ let latest = null;
396
+ try {
397
+ const goModContent = await fs2.readFile(path2.join(projectDir, "go.mod"), "utf-8");
398
+ const match = goModContent.match(new RegExp(`${packageName.replace(/\//g, "\\/")}\\s+v([\\d.]+)`));
399
+ current = match?.[1] || null;
400
+ } catch {
401
+ }
402
+ try {
403
+ const { stdout } = await execa("go", ["list", "-m", "-versions", packageName]);
404
+ const versions = stdout.split(" ").slice(1);
405
+ latest = versions[versions.length - 1]?.replace("v", "") || null;
406
+ } catch {
407
+ }
408
+ return { current, latest, packageManager: "go", packageName };
409
+ }
410
+ async function upgradeGoPackage(projectDir, info) {
411
+ await execa("go", ["get", "-u", `${info.packageName}@latest`], { cwd: projectDir });
412
+ await execa("go", ["mod", "tidy"], { cwd: projectDir });
413
+ }
414
+ async function checkRustPackage(projectDir) {
415
+ const packageName = "rmcp";
416
+ let current = null;
417
+ let latest = null;
418
+ try {
419
+ const cargoContent = await fs2.readFile(path2.join(projectDir, "Cargo.toml"), "utf-8");
420
+ const match = cargoContent.match(/rmcp\s*=\s*["']([^"']+)["']/);
421
+ current = match?.[1] || null;
422
+ } catch {
423
+ }
424
+ try {
425
+ const { stdout } = await execa("cargo", ["search", packageName, "--limit", "1"]);
426
+ const match = stdout.match(/rmcp\s*=\s*"([^"]+)"/);
427
+ latest = match?.[1] || null;
428
+ } catch {
429
+ }
430
+ return { current, latest, packageManager: "cargo", packageName };
431
+ }
432
+ async function upgradeRustPackage(projectDir, info) {
433
+ await execa("cargo", ["update", "-p", info.packageName], { cwd: projectDir });
434
+ }
435
+
436
+ // src/commands/monorepo.ts
437
+ import path3 from "path";
438
+ import inquirer from "inquirer";
439
+ async function monorepoInitCommand(workspaceName, options) {
440
+ try {
441
+ let name = workspaceName;
442
+ if (!name) {
443
+ const { inputName } = await inquirer.prompt([
444
+ {
445
+ type: "input",
446
+ name: "inputName",
447
+ message: "Workspace name:",
448
+ default: "mcp-workspace"
449
+ }
450
+ ]);
451
+ name = inputName;
452
+ }
453
+ const outputDir = path3.resolve(process.cwd(), name);
454
+ if (await exists(outputDir)) {
455
+ const files = await readDir(outputDir);
456
+ if (files.length > 0 && !options.force) {
457
+ logger.error(
458
+ `Directory "${name}" already exists and is not empty. Use --force to override.`
459
+ );
460
+ process.exit(1);
461
+ }
462
+ }
463
+ logger.title(`Creating MCP Monorepo: ${name}`);
464
+ await withSpinner(
465
+ "Creating workspace structure...",
466
+ async () => {
467
+ await ensureDir(outputDir);
468
+ await ensureDir(path3.join(outputDir, "packages"));
469
+ await ensureDir(path3.join(outputDir, "shared"));
470
+ },
471
+ "Workspace structure created"
472
+ );
473
+ const workspaceConfig = {
474
+ name,
475
+ packages: []
476
+ };
477
+ await withSpinner(
478
+ "Creating configuration files...",
479
+ async () => {
480
+ await writeFile(
481
+ path3.join(outputDir, "mcp.workspace.json"),
482
+ JSON.stringify(workspaceConfig, null, 2)
483
+ );
484
+ const packageJson = {
485
+ name,
486
+ version: "1.0.0",
487
+ private: true,
488
+ workspaces: ["packages/*", "shared/*"],
489
+ scripts: {
490
+ build: "npm run build --workspaces",
491
+ dev: "npm run dev --workspaces --if-present",
492
+ test: "npm run test --workspaces --if-present"
493
+ }
494
+ };
495
+ await writeFile(path3.join(outputDir, "package.json"), JSON.stringify(packageJson, null, 2));
496
+ const readme = `# ${name}
497
+
498
+ MCP Monorepo Workspace
499
+
500
+ ## Structure
501
+
502
+ \`\`\`
503
+ ${name}/
504
+ \u251C\u2500\u2500 packages/ # MCP servers
505
+ \u251C\u2500\u2500 shared/ # Shared utilities and types
506
+ \u251C\u2500\u2500 mcp.workspace.json # Workspace configuration
507
+ \u2514\u2500\u2500 package.json # Root package.json
508
+ \`\`\`
509
+
510
+ ## Commands
511
+
512
+ \`\`\`bash
513
+ # Add a new MCP server
514
+ mcp-new monorepo add <server-name>
515
+
516
+ # Build all packages
517
+ npm run build
518
+
519
+ # Run all dev servers
520
+ npm run dev
521
+ \`\`\`
522
+
523
+ ## Packages
524
+
525
+ ${workspaceConfig.packages.length === 0 ? "_No packages yet. Run `mcp-new monorepo add <name>` to create one._" : workspaceConfig.packages.map((p) => `- ${p}`).join("\n")}
526
+ `;
527
+ await writeFile(path3.join(outputDir, "README.md"), readme);
528
+ const gitignore = `# Dependencies
529
+ node_modules/
530
+
531
+ # Build outputs
532
+ dist/
533
+ build/
534
+ *.tsbuildinfo
535
+
536
+ # Environment
537
+ .env
538
+ .env.local
539
+ .env.*.local
540
+
541
+ # IDE
542
+ .idea/
543
+ .vscode/
544
+ *.swp
545
+ *.swo
546
+
547
+ # OS
548
+ .DS_Store
549
+ Thumbs.db
550
+
551
+ # Logs
552
+ *.log
553
+ npm-debug.log*
554
+ `;
555
+ await writeFile(path3.join(outputDir, ".gitignore"), gitignore);
556
+ },
557
+ "Configuration files created"
558
+ );
559
+ const gitInstalled = await isGitInstalled();
560
+ if (gitInstalled) {
561
+ await withSpinner(
562
+ "Initializing git repository...",
563
+ async () => {
564
+ await initGitRepository(outputDir);
565
+ await createInitialCommit(outputDir);
566
+ },
567
+ "Git repository initialized"
568
+ );
569
+ }
570
+ logger.success(`Monorepo workspace "${name}" created successfully!`);
571
+ logger.box("Next steps:", ["", ` cd ${name}`, ` mcp-new monorepo add my-first-server`, ""]);
572
+ } catch (error) {
573
+ if (error instanceof Error) {
574
+ logger.error(error.message);
575
+ } else {
576
+ logger.error("An unexpected error occurred");
577
+ }
578
+ process.exit(1);
579
+ }
580
+ }
581
+ async function monorepoAddCommand(serverName, options) {
582
+ try {
583
+ const workspaceConfigPath = path3.resolve(process.cwd(), "mcp.workspace.json");
584
+ if (!await exists(workspaceConfigPath)) {
585
+ logger.error(
586
+ "Not in a monorepo workspace. Run `mcp-new monorepo init` first, or cd into an existing workspace."
587
+ );
588
+ process.exit(1);
589
+ }
590
+ let name = serverName || options.name;
591
+ if (!name) {
592
+ const { inputName } = await inquirer.prompt([
593
+ {
594
+ type: "input",
595
+ name: "inputName",
596
+ message: "Server name:",
597
+ default: "my-mcp-server"
598
+ }
599
+ ]);
600
+ name = inputName;
601
+ }
602
+ let language;
603
+ const opts = options;
604
+ if (opts.typescript === true || opts.t === true) language = "typescript";
605
+ else if (opts.python === true || opts.p === true) language = "python";
606
+ else if (opts.go === true || opts.g === true) language = "go";
607
+ else if (opts.rust === true || opts.r === true) language = "rust";
608
+ else if (opts.java === true || opts.j === true) language = "java";
609
+ else if (opts.kotlin === true || opts.k === true) language = "kotlin";
610
+ else if (opts.csharp === true || opts.c === true) language = "csharp";
611
+ else if (opts.elixir === true || opts.e === true) language = "elixir";
612
+ if (!language) {
613
+ language = await promptLanguage();
614
+ }
615
+ let javaBuildTool;
616
+ if (language === "java" || language === "kotlin") {
617
+ javaBuildTool = options.maven ? "maven" : options.gradle ? "gradle" : await promptJavaBuildTool();
618
+ }
619
+ const outputDir = path3.join(process.cwd(), "packages", name);
620
+ if (await exists(outputDir)) {
621
+ logger.error(`Server "${name}" already exists in packages/`);
622
+ process.exit(1);
623
+ }
624
+ logger.title(`Adding MCP Server: ${name}`);
625
+ await generateFromWizard(
626
+ {
627
+ name,
628
+ description: "",
629
+ language,
630
+ transport: "stdio",
631
+ tools: [],
632
+ resources: [],
633
+ includeExampleTool: true,
634
+ skipInstall: options.skipInstall || false,
635
+ initGit: false,
636
+ // Don't init git for individual packages
637
+ javaBuildTool
638
+ },
639
+ outputDir
640
+ );
641
+ const workspaceConfigContent = await readFile(workspaceConfigPath);
642
+ const workspaceConfig = JSON.parse(workspaceConfigContent);
643
+ workspaceConfig.packages.push(name);
644
+ await writeFile(workspaceConfigPath, JSON.stringify(workspaceConfig, null, 2));
645
+ logger.success(`Server "${name}" added to packages/`);
646
+ logger.info(`Language: ${language}${javaBuildTool ? ` (${javaBuildTool})` : ""}`);
647
+ logger.box("Next steps:", [
648
+ "",
649
+ ` cd packages/${name}`,
650
+ " # Start developing your MCP server",
651
+ ""
652
+ ]);
653
+ } catch (error) {
654
+ if (error instanceof Error) {
655
+ logger.error(error.message);
656
+ } else {
657
+ logger.error("An unexpected error occurred");
658
+ }
659
+ process.exit(1);
660
+ }
661
+ }
662
+ async function monorepoListCommand() {
663
+ try {
664
+ const workspaceConfigPath = path3.resolve(process.cwd(), "mcp.workspace.json");
665
+ if (!await exists(workspaceConfigPath)) {
666
+ logger.error("Not in a monorepo workspace.");
667
+ process.exit(1);
668
+ }
669
+ const workspaceConfigContent = await readFile(workspaceConfigPath);
670
+ const workspaceConfig = JSON.parse(workspaceConfigContent);
671
+ logger.title(`Workspace: ${workspaceConfig.name}`);
672
+ if (workspaceConfig.packages.length === 0) {
673
+ logger.info("No packages yet. Run `mcp-new monorepo add <name>` to create one.");
674
+ } else {
675
+ logger.info("Packages:");
676
+ workspaceConfig.packages.forEach((pkg, index) => {
677
+ console.log(` ${index + 1}. ${pkg}`);
678
+ });
679
+ }
680
+ } catch (error) {
681
+ if (error instanceof Error) {
682
+ logger.error(error.message);
683
+ } else {
684
+ logger.error("An unexpected error occurred");
685
+ }
686
+ process.exit(1);
687
+ }
688
+ }
689
+
690
+ // src/cli.ts
11
691
  var program = new Command();
12
692
  var logo = `
13
- ${chalk.cyan("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557")}
14
- ${chalk.cyan("\u2551")} ${chalk.bold.white("mcp-new")} ${chalk.cyan("\u2551")}
15
- ${chalk.cyan("\u2551")} ${chalk.gray("Generate MCP servers in seconds")} ${chalk.cyan("\u2551")}
16
- ${chalk.cyan("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D")}
693
+ ${chalk4.cyan("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557")}
694
+ ${chalk4.cyan("\u2551")} ${chalk4.bold.white("mcp-new")} ${chalk4.cyan("\u2551")}
695
+ ${chalk4.cyan("\u2551")} ${chalk4.gray("Generate MCP servers in seconds")} ${chalk4.cyan("\u2551")}
696
+ ${chalk4.cyan("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D")}
17
697
  `;
18
698
  var examples = `
19
- ${chalk.bold("Examples:")}
699
+ ${chalk4.bold("Examples:")}
20
700
 
21
- ${chalk.gray("# Create a new MCP server with interactive wizard")}
22
- ${chalk.cyan("$")} mcp-new my-server
701
+ ${chalk4.gray("# Create a new MCP server with interactive wizard")}
702
+ ${chalk4.cyan("$")} mcp-new my-server
23
703
 
24
- ${chalk.gray("# Create TypeScript server with defaults")}
25
- ${chalk.cyan("$")} mcp-new my-server -t -y
704
+ ${chalk4.gray("# Create TypeScript server with defaults")}
705
+ ${chalk4.cyan("$")} mcp-new my-server -t -y
26
706
 
27
- ${chalk.gray("# Create Python server")}
28
- ${chalk.cyan("$")} mcp-new my-server -p
707
+ ${chalk4.gray("# Create Python server")}
708
+ ${chalk4.cyan("$")} mcp-new my-server -p
29
709
 
30
- ${chalk.gray("# Create from preset template")}
31
- ${chalk.cyan("$")} mcp-new my-db --preset database
32
- ${chalk.cyan("$")} mcp-new my-api --preset rest-api
33
- ${chalk.cyan("$")} mcp-new my-fs --preset filesystem
710
+ ${chalk4.gray("# Create from preset template")}
711
+ ${chalk4.cyan("$")} mcp-new my-db --preset database
712
+ ${chalk4.cyan("$")} mcp-new my-api --preset rest-api
713
+ ${chalk4.cyan("$")} mcp-new my-fs --preset filesystem
34
714
 
35
- ${chalk.gray("# Create from OpenAPI specification")}
36
- ${chalk.cyan("$")} mcp-new my-api --from-openapi ./openapi.yaml
715
+ ${chalk4.gray("# Create from OpenAPI specification")}
716
+ ${chalk4.cyan("$")} mcp-new my-api --from-openapi ./openapi.yaml
37
717
 
38
- ${chalk.gray("# Create using AI (requires ANTHROPIC_API_KEY)")}
39
- ${chalk.cyan("$")} mcp-new my-server --from-prompt
718
+ ${chalk4.gray("# Create using AI (requires ANTHROPIC_API_KEY)")}
719
+ ${chalk4.cyan("$")} mcp-new my-server --from-prompt
40
720
 
41
- ${chalk.bold("Available Presets:")}
721
+ ${chalk4.bold("Available Presets:")}
42
722
 
43
- ${chalk.yellow("database")} Database CRUD tools (query, insert, update, delete, list_tables)
44
- ${chalk.yellow("rest-api")} REST API tools (http_get, http_post, http_put, http_delete, set_base_url)
45
- ${chalk.yellow("filesystem")} File system tools (read_file, write_file, list_directory, search_files, file_info)
723
+ ${chalk4.yellow("database")} Database CRUD tools (query, insert, update, delete, list_tables)
724
+ ${chalk4.yellow("rest-api")} REST API tools (http_get, http_post, http_put, http_delete, set_base_url)
725
+ ${chalk4.yellow("filesystem")} File system tools (read_file, write_file, list_directory, search_files, file_info)
46
726
 
47
- ${chalk.bold("Supported Languages:")}
727
+ ${chalk4.bold("Supported Languages:")}
48
728
 
49
- ${chalk.green("-t, --typescript")} TypeScript with npm
50
- ${chalk.green("-p, --python")} Python with pip
51
- ${chalk.green("-g, --go")} Go with go modules
52
- ${chalk.green("-r, --rust")} Rust with cargo
729
+ ${chalk4.green("-t, --typescript")} TypeScript with npm
730
+ ${chalk4.green("-p, --python")} Python with pip
731
+ ${chalk4.green("-g, --go")} Go with go modules
732
+ ${chalk4.green("-r, --rust")} Rust with cargo
733
+ ${chalk4.green("-j, --java")} Java with Maven/Gradle
734
+ ${chalk4.green("-k, --kotlin")} Kotlin with Maven/Gradle
735
+ ${chalk4.green("-c, --csharp")} C# with .NET
736
+ ${chalk4.green("-e, --elixir")} Elixir with Mix
53
737
 
54
- ${chalk.bold("Learn More:")}
738
+ ${chalk4.bold("Learn More:")}
55
739
 
56
- Documentation: ${chalk.underline("https://github.com/d1maash/mcp-new")}
57
- MCP Spec: ${chalk.underline("https://spec.modelcontextprotocol.io")}
740
+ Documentation: ${chalk4.underline("https://github.com/d1maash/mcp-new")}
741
+ MCP Spec: ${chalk4.underline("https://spec.modelcontextprotocol.io")}
58
742
  `;
59
- program.name("mcp-new").description("CLI tool for generating MCP (Model Context Protocol) servers").version("1.2.1").addHelpText("beforeAll", logo).addHelpText("after", examples);
60
- program.argument("[project-name]", "Name of the project to create").option("-t, --typescript", "Use TypeScript template").option("-p, --python", "Use Python template").option("-g, --go", "Use Go template").option("-r, --rust", "Use Rust template").option("--skip-install", "Skip dependency installation").option("--from-openapi <path>", "Generate from OpenAPI/Swagger specification").option("--from-prompt", "Generate tools using AI from text description").option("--preset <name>", "Use a preset template (database, rest-api, filesystem)").option("-y, --yes", "Skip prompts and use defaults").action(createCommand);
61
- program.command("init").description("Initialize MCP server in the current directory").option("-t, --typescript", "Use TypeScript template").option("-p, --python", "Use Python template").option("-g, --go", "Use Go template").option("-r, --rust", "Use Rust template").option("--skip-install", "Skip dependency installation").option("-f, --force", "Initialize even if directory contains files").addHelpText("after", `
62
- ${chalk.bold("Examples:")}
743
+ program.name("mcp-new").description("CLI tool for generating MCP (Model Context Protocol) servers").version("1.5.0").addHelpText("beforeAll", logo).addHelpText("after", examples);
744
+ program.argument("[project-name]", "Name of the project to create").option("-t, --typescript", "Use TypeScript template").option("-p, --python", "Use Python template").option("-g, --go", "Use Go template").option("-r, --rust", "Use Rust template").option("-j, --java", "Use Java template").option("-k, --kotlin", "Use Kotlin template").option("-c, --csharp", "Use C# (.NET) template").option("-e, --elixir", "Use Elixir template").option("--maven", "Use Maven build tool (for Java/Kotlin)").option("--gradle", "Use Gradle build tool (for Java/Kotlin)").option("--skip-install", "Skip dependency installation").option("--from-openapi <path>", "Generate from OpenAPI/Swagger specification").option("--from-prompt", "Generate tools using AI from text description").option("--preset <name>", "Use a preset template (database, rest-api, filesystem)").option("-y, --yes", "Skip prompts and use defaults").action(createCommand);
745
+ program.command("init").description("Initialize MCP server in the current directory").option("-t, --typescript", "Use TypeScript template").option("-p, --python", "Use Python template").option("-g, --go", "Use Go template").option("-r, --rust", "Use Rust template").option("-j, --java", "Use Java template").option("-k, --kotlin", "Use Kotlin template").option("-c, --csharp", "Use C# (.NET) template").option("-e, --elixir", "Use Elixir template").option("--maven", "Use Maven build tool (for Java/Kotlin)").option("--gradle", "Use Gradle build tool (for Java/Kotlin)").option("--skip-install", "Skip dependency installation").option("-f, --force", "Initialize even if directory contains files").addHelpText(
746
+ "after",
747
+ `
748
+ ${chalk4.bold("Examples:")}
749
+
750
+ ${chalk4.gray("# Initialize in current directory")}
751
+ ${chalk4.cyan("$")} mcp-new init
752
+
753
+ ${chalk4.gray("# Initialize with TypeScript")}
754
+ ${chalk4.cyan("$")} mcp-new init -t
755
+
756
+ ${chalk4.gray("# Force initialize (overwrite existing files)")}
757
+ ${chalk4.cyan("$")} mcp-new init -f
758
+ `
759
+ ).action(initCommand);
760
+ program.command("add-tool").description("Add a new tool to an existing MCP server").option("-n, --name <name>", "Tool name (snake_case)").addHelpText(
761
+ "after",
762
+ `
763
+ ${chalk4.bold("Examples:")}
764
+
765
+ ${chalk4.gray("# Add tool interactively")}
766
+ ${chalk4.cyan("$")} mcp-new add-tool
767
+
768
+ ${chalk4.gray("# Add tool with name")}
769
+ ${chalk4.cyan("$")} mcp-new add-tool -n my_new_tool
770
+ `
771
+ ).action(addToolCommand);
772
+ program.command("list-presets").description("List all available preset templates").addHelpText(
773
+ "after",
774
+ `
775
+ ${chalk4.bold("Examples:")}
776
+
777
+ ${chalk4.gray("# Show all presets with their tools")}
778
+ ${chalk4.cyan("$")} mcp-new list-presets
779
+ `
780
+ ).action(listPresetsCommand);
781
+ program.command("validate").description("Validate the current MCP server project").addHelpText(
782
+ "after",
783
+ `
784
+ ${chalk4.bold("Examples:")}
785
+
786
+ ${chalk4.gray("# Validate current project")}
787
+ ${chalk4.cyan("$")} mcp-new validate
788
+
789
+ ${chalk4.bold("Checks:")}
790
+ \u2022 Project configuration (package.json, pyproject.toml, etc.)
791
+ \u2022 MCP SDK dependency presence and version
792
+ \u2022 Entry point file existence
793
+ \u2022 Basic project structure
794
+ `
795
+ ).action(validateCommand);
796
+ program.command("upgrade").description("Upgrade MCP SDK to the latest version").option("-c, --check", "Check for updates without installing").addHelpText(
797
+ "after",
798
+ `
799
+ ${chalk4.bold("Examples:")}
800
+
801
+ ${chalk4.gray("# Upgrade MCP SDK to latest version")}
802
+ ${chalk4.cyan("$")} mcp-new upgrade
803
+
804
+ ${chalk4.gray("# Check for updates without installing")}
805
+ ${chalk4.cyan("$")} mcp-new upgrade --check
63
806
 
64
- ${chalk.gray("# Initialize in current directory")}
65
- ${chalk.cyan("$")} mcp-new init
807
+ ${chalk4.bold("Supported languages:")}
808
+ \u2022 TypeScript/JavaScript (npm)
809
+ \u2022 Python (pip)
810
+ \u2022 Go (go modules)
811
+ \u2022 Rust (cargo)
812
+ `
813
+ ).action(upgradeCommand);
814
+ var monorepo = program.command("monorepo").description("Manage MCP monorepo workspaces");
815
+ monorepo.command("init [workspace-name]").description("Initialize a new MCP monorepo workspace").option("-f, --force", "Initialize even if directory contains files").addHelpText(
816
+ "after",
817
+ `
818
+ ${chalk4.bold("Examples:")}
66
819
 
67
- ${chalk.gray("# Initialize with TypeScript")}
68
- ${chalk.cyan("$")} mcp-new init -t
820
+ ${chalk4.gray("# Create a new monorepo workspace")}
821
+ ${chalk4.cyan("$")} mcp-new monorepo init my-workspace
69
822
 
70
- ${chalk.gray("# Force initialize (overwrite existing files)")}
71
- ${chalk.cyan("$")} mcp-new init -f
72
- `).action(initCommand);
73
- program.command("add-tool").description("Add a new tool to an existing MCP server").option("-n, --name <name>", "Tool name (snake_case)").addHelpText("after", `
74
- ${chalk.bold("Examples:")}
823
+ ${chalk4.gray("# Create in current directory name")}
824
+ ${chalk4.cyan("$")} mcp-new monorepo init
825
+ `
826
+ ).action((_workspaceName, _options, command) => {
827
+ const opts = command.optsWithGlobals();
828
+ const workspaceName = command.args[0];
829
+ monorepoInitCommand(workspaceName, opts);
830
+ });
831
+ monorepo.command("add [server-name]").description("Add a new MCP server to the workspace").option("-n, --name <name>", "Server name").option("-t, --typescript", "Use TypeScript template").option("-p, --python", "Use Python template").option("-g, --go", "Use Go template").option("-r, --rust", "Use Rust template").option("-j, --java", "Use Java template").option("-k, --kotlin", "Use Kotlin template").option("-c, --csharp", "Use C# (.NET) template").option("-e, --elixir", "Use Elixir template").option("--maven", "Use Maven build tool (for Java/Kotlin)").option("--gradle", "Use Gradle build tool (for Java/Kotlin)").option("--skip-install", "Skip dependency installation").addHelpText(
832
+ "after",
833
+ `
834
+ ${chalk4.bold("Examples:")}
75
835
 
76
- ${chalk.gray("# Add tool interactively")}
77
- ${chalk.cyan("$")} mcp-new add-tool
836
+ ${chalk4.gray("# Add a TypeScript server")}
837
+ ${chalk4.cyan("$")} mcp-new monorepo add my-server -t
78
838
 
79
- ${chalk.gray("# Add tool with name")}
80
- ${chalk.cyan("$")} mcp-new add-tool -n my_new_tool
81
- `).action(addToolCommand);
839
+ ${chalk4.gray("# Add a Java server with Gradle")}
840
+ ${chalk4.cyan("$")} mcp-new monorepo add api-server -j --gradle
841
+ `
842
+ ).action((_serverName, _options, command) => {
843
+ const opts = command.optsWithGlobals();
844
+ const serverName = command.args[0];
845
+ monorepoAddCommand(serverName, opts);
846
+ });
847
+ monorepo.command("list").description("List all packages in the workspace").action(monorepoListCommand);
82
848
  program.parse();
83
849
  if (process.argv.length === 2) {
84
850
  program.help();