mcp-new 1.2.2 → 1.6.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 (50) hide show
  1. package/README.md +33 -1
  2. package/dist/{chunk-3JG4FVS2.js → chunk-LJNMSDBU.js} +1157 -212
  3. package/dist/cli.js +1287 -18
  4. package/dist/index.d.ts +43 -10
  5. package/dist/index.js +22 -35
  6. package/package.json +4 -2
  7. package/templates/ci/circleci/config.yml.ejs +219 -0
  8. package/templates/ci/github/ci.yml.ejs +184 -0
  9. package/templates/ci/gitlab/.gitlab-ci.yml.ejs +233 -0
  10. package/templates/csharp/.env.example +6 -0
  11. package/templates/csharp/.gitignore.ejs +53 -0
  12. package/templates/csharp/McpServer.csproj.ejs +19 -0
  13. package/templates/csharp/README.md.ejs +136 -0
  14. package/templates/csharp/src/Program.cs.ejs +117 -0
  15. package/templates/elixir/.env.example +6 -0
  16. package/templates/elixir/.gitignore.ejs +33 -0
  17. package/templates/elixir/README.md.ejs +154 -0
  18. package/templates/elixir/config/config.exs.ejs +9 -0
  19. package/templates/elixir/config/dev.exs.ejs +3 -0
  20. package/templates/elixir/config/prod.exs.ejs +3 -0
  21. package/templates/elixir/lib/application.ex.ejs +19 -0
  22. package/templates/elixir/lib/cli.ex.ejs +17 -0
  23. package/templates/elixir/lib/server.ex.ejs +112 -0
  24. package/templates/elixir/mix.exs.ejs +32 -0
  25. package/templates/java/gradle/.env.example +6 -0
  26. package/templates/java/gradle/.gitignore.ejs +48 -0
  27. package/templates/java/gradle/README.md.ejs +132 -0
  28. package/templates/java/gradle/build.gradle.ejs +46 -0
  29. package/templates/java/gradle/settings.gradle.ejs +1 -0
  30. package/templates/java/gradle/src/main/java/com/example/mcp/McpServer.java.ejs +149 -0
  31. package/templates/java/gradle/src/main/resources/logback.xml +13 -0
  32. package/templates/java/maven/.env.example +6 -0
  33. package/templates/java/maven/.gitignore.ejs +53 -0
  34. package/templates/java/maven/README.md.ejs +131 -0
  35. package/templates/java/maven/pom.xml.ejs +86 -0
  36. package/templates/java/maven/src/main/java/com/example/mcp/McpServer.java.ejs +149 -0
  37. package/templates/java/maven/src/main/resources/logback.xml +13 -0
  38. package/templates/kotlin/gradle/.env.example +6 -0
  39. package/templates/kotlin/gradle/.gitignore.ejs +45 -0
  40. package/templates/kotlin/gradle/README.md.ejs +138 -0
  41. package/templates/kotlin/gradle/build.gradle.kts.ejs +48 -0
  42. package/templates/kotlin/gradle/settings.gradle.kts.ejs +1 -0
  43. package/templates/kotlin/gradle/src/main/kotlin/com/example/mcp/McpServer.kt.ejs +141 -0
  44. package/templates/kotlin/gradle/src/main/resources/logback.xml +13 -0
  45. package/templates/kotlin/maven/.env.example +6 -0
  46. package/templates/kotlin/maven/.gitignore.ejs +50 -0
  47. package/templates/kotlin/maven/README.md.ejs +96 -0
  48. package/templates/kotlin/maven/pom.xml.ejs +105 -0
  49. package/templates/kotlin/maven/src/main/kotlin/com/example/mcp/McpServer.kt.ejs +141 -0
  50. package/templates/kotlin/maven/src/main/resources/logback.xml +13 -0
package/dist/cli.js CHANGED
@@ -2,11 +2,32 @@
2
2
  import {
3
3
  PRESETS,
4
4
  addToolCommand,
5
+ clearPresetCache,
5
6
  createCommand,
7
+ createInitialCommit,
6
8
  createSpinner,
9
+ detectJavaBuildTool,
10
+ detectLanguageFromProject,
11
+ ensureDir,
12
+ exists,
13
+ generateCI,
14
+ generateFromWizard,
15
+ getCacheDir,
7
16
  initCommand,
8
- logger
9
- } from "./chunk-3JG4FVS2.js";
17
+ initGitRepository,
18
+ isGitInstalled,
19
+ isValidCIProvider,
20
+ listCachedPresets,
21
+ logger,
22
+ pluginRegistry,
23
+ promptCIProvider,
24
+ promptJavaBuildTool,
25
+ promptLanguage,
26
+ readDir,
27
+ readFile,
28
+ withSpinner,
29
+ writeFile
30
+ } from "./chunk-LJNMSDBU.js";
10
31
 
11
32
  // src/cli.ts
12
33
  import { Command } from "commander";
@@ -347,7 +368,7 @@ async function upgradeNodePackage(projectDir, info) {
347
368
  await fs2.writeJson(packageJsonPath, packageJson, { spaces: 2 });
348
369
  await execa("npm", ["install"], { cwd: projectDir });
349
370
  }
350
- async function checkPythonPackage(projectDir) {
371
+ async function checkPythonPackage(_projectDir) {
351
372
  const packageName = "mcp";
352
373
  let current = null;
353
374
  let latest = null;
@@ -421,6 +442,1130 @@ async function upgradeRustPackage(projectDir, info) {
421
442
  await execa("cargo", ["update", "-p", info.packageName], { cwd: projectDir });
422
443
  }
423
444
 
445
+ // src/commands/monorepo.ts
446
+ import path3 from "path";
447
+ import inquirer from "inquirer";
448
+ async function monorepoInitCommand(workspaceName, options) {
449
+ try {
450
+ let name = workspaceName;
451
+ if (!name) {
452
+ const { inputName } = await inquirer.prompt([
453
+ {
454
+ type: "input",
455
+ name: "inputName",
456
+ message: "Workspace name:",
457
+ default: "mcp-workspace"
458
+ }
459
+ ]);
460
+ name = inputName;
461
+ }
462
+ const outputDir = path3.resolve(process.cwd(), name);
463
+ if (await exists(outputDir)) {
464
+ const files = await readDir(outputDir);
465
+ if (files.length > 0 && !options.force) {
466
+ logger.error(
467
+ `Directory "${name}" already exists and is not empty. Use --force to override.`
468
+ );
469
+ process.exit(1);
470
+ }
471
+ }
472
+ logger.title(`Creating MCP Monorepo: ${name}`);
473
+ await withSpinner(
474
+ "Creating workspace structure...",
475
+ async () => {
476
+ await ensureDir(outputDir);
477
+ await ensureDir(path3.join(outputDir, "packages"));
478
+ await ensureDir(path3.join(outputDir, "shared"));
479
+ },
480
+ "Workspace structure created"
481
+ );
482
+ const workspaceConfig = {
483
+ name,
484
+ packages: []
485
+ };
486
+ await withSpinner(
487
+ "Creating configuration files...",
488
+ async () => {
489
+ await writeFile(
490
+ path3.join(outputDir, "mcp.workspace.json"),
491
+ JSON.stringify(workspaceConfig, null, 2)
492
+ );
493
+ const packageJson = {
494
+ name,
495
+ version: "1.0.0",
496
+ private: true,
497
+ workspaces: ["packages/*", "shared/*"],
498
+ scripts: {
499
+ build: "npm run build --workspaces",
500
+ dev: "npm run dev --workspaces --if-present",
501
+ test: "npm run test --workspaces --if-present"
502
+ }
503
+ };
504
+ await writeFile(path3.join(outputDir, "package.json"), JSON.stringify(packageJson, null, 2));
505
+ const readme = `# ${name}
506
+
507
+ MCP Monorepo Workspace
508
+
509
+ ## Structure
510
+
511
+ \`\`\`
512
+ ${name}/
513
+ \u251C\u2500\u2500 packages/ # MCP servers
514
+ \u251C\u2500\u2500 shared/ # Shared utilities and types
515
+ \u251C\u2500\u2500 mcp.workspace.json # Workspace configuration
516
+ \u2514\u2500\u2500 package.json # Root package.json
517
+ \`\`\`
518
+
519
+ ## Commands
520
+
521
+ \`\`\`bash
522
+ # Add a new MCP server
523
+ mcp-new monorepo add <server-name>
524
+
525
+ # Build all packages
526
+ npm run build
527
+
528
+ # Run all dev servers
529
+ npm run dev
530
+ \`\`\`
531
+
532
+ ## Packages
533
+
534
+ ${workspaceConfig.packages.length === 0 ? "_No packages yet. Run `mcp-new monorepo add <name>` to create one._" : workspaceConfig.packages.map((p) => `- ${p}`).join("\n")}
535
+ `;
536
+ await writeFile(path3.join(outputDir, "README.md"), readme);
537
+ const gitignore = `# Dependencies
538
+ node_modules/
539
+
540
+ # Build outputs
541
+ dist/
542
+ build/
543
+ *.tsbuildinfo
544
+
545
+ # Environment
546
+ .env
547
+ .env.local
548
+ .env.*.local
549
+
550
+ # IDE
551
+ .idea/
552
+ .vscode/
553
+ *.swp
554
+ *.swo
555
+
556
+ # OS
557
+ .DS_Store
558
+ Thumbs.db
559
+
560
+ # Logs
561
+ *.log
562
+ npm-debug.log*
563
+ `;
564
+ await writeFile(path3.join(outputDir, ".gitignore"), gitignore);
565
+ },
566
+ "Configuration files created"
567
+ );
568
+ const gitInstalled = await isGitInstalled();
569
+ if (gitInstalled) {
570
+ await withSpinner(
571
+ "Initializing git repository...",
572
+ async () => {
573
+ await initGitRepository(outputDir);
574
+ await createInitialCommit(outputDir);
575
+ },
576
+ "Git repository initialized"
577
+ );
578
+ }
579
+ logger.success(`Monorepo workspace "${name}" created successfully!`);
580
+ logger.box("Next steps:", ["", ` cd ${name}`, ` mcp-new monorepo add my-first-server`, ""]);
581
+ } catch (error) {
582
+ if (error instanceof Error) {
583
+ logger.error(error.message);
584
+ } else {
585
+ logger.error("An unexpected error occurred");
586
+ }
587
+ process.exit(1);
588
+ }
589
+ }
590
+ async function monorepoAddCommand(serverName, options) {
591
+ try {
592
+ const workspaceConfigPath = path3.resolve(process.cwd(), "mcp.workspace.json");
593
+ if (!await exists(workspaceConfigPath)) {
594
+ logger.error(
595
+ "Not in a monorepo workspace. Run `mcp-new monorepo init` first, or cd into an existing workspace."
596
+ );
597
+ process.exit(1);
598
+ }
599
+ let name = serverName || options.name;
600
+ if (!name) {
601
+ const { inputName } = await inquirer.prompt([
602
+ {
603
+ type: "input",
604
+ name: "inputName",
605
+ message: "Server name:",
606
+ default: "my-mcp-server"
607
+ }
608
+ ]);
609
+ name = inputName;
610
+ }
611
+ let language;
612
+ const opts = options;
613
+ if (opts.typescript === true || opts.t === true) language = "typescript";
614
+ else if (opts.python === true || opts.p === true) language = "python";
615
+ else if (opts.go === true || opts.g === true) language = "go";
616
+ else if (opts.rust === true || opts.r === true) language = "rust";
617
+ else if (opts.java === true || opts.j === true) language = "java";
618
+ else if (opts.kotlin === true || opts.k === true) language = "kotlin";
619
+ else if (opts.csharp === true || opts.c === true) language = "csharp";
620
+ else if (opts.elixir === true || opts.e === true) language = "elixir";
621
+ if (!language) {
622
+ language = await promptLanguage();
623
+ }
624
+ let javaBuildTool;
625
+ if (language === "java" || language === "kotlin") {
626
+ javaBuildTool = options.maven ? "maven" : options.gradle ? "gradle" : await promptJavaBuildTool();
627
+ }
628
+ const outputDir = path3.join(process.cwd(), "packages", name);
629
+ if (await exists(outputDir)) {
630
+ logger.error(`Server "${name}" already exists in packages/`);
631
+ process.exit(1);
632
+ }
633
+ logger.title(`Adding MCP Server: ${name}`);
634
+ await generateFromWizard(
635
+ {
636
+ name,
637
+ description: "",
638
+ language,
639
+ transport: "stdio",
640
+ tools: [],
641
+ resources: [],
642
+ includeExampleTool: true,
643
+ skipInstall: options.skipInstall || false,
644
+ initGit: false,
645
+ // Don't init git for individual packages
646
+ javaBuildTool
647
+ },
648
+ outputDir
649
+ );
650
+ const workspaceConfigContent = await readFile(workspaceConfigPath);
651
+ const workspaceConfig = JSON.parse(workspaceConfigContent);
652
+ workspaceConfig.packages.push(name);
653
+ await writeFile(workspaceConfigPath, JSON.stringify(workspaceConfig, null, 2));
654
+ logger.success(`Server "${name}" added to packages/`);
655
+ logger.info(`Language: ${language}${javaBuildTool ? ` (${javaBuildTool})` : ""}`);
656
+ logger.box("Next steps:", [
657
+ "",
658
+ ` cd packages/${name}`,
659
+ " # Start developing your MCP server",
660
+ ""
661
+ ]);
662
+ } catch (error) {
663
+ if (error instanceof Error) {
664
+ logger.error(error.message);
665
+ } else {
666
+ logger.error("An unexpected error occurred");
667
+ }
668
+ process.exit(1);
669
+ }
670
+ }
671
+ async function monorepoListCommand() {
672
+ try {
673
+ const workspaceConfigPath = path3.resolve(process.cwd(), "mcp.workspace.json");
674
+ if (!await exists(workspaceConfigPath)) {
675
+ logger.error("Not in a monorepo workspace.");
676
+ process.exit(1);
677
+ }
678
+ const workspaceConfigContent = await readFile(workspaceConfigPath);
679
+ const workspaceConfig = JSON.parse(workspaceConfigContent);
680
+ logger.title(`Workspace: ${workspaceConfig.name}`);
681
+ if (workspaceConfig.packages.length === 0) {
682
+ logger.info("No packages yet. Run `mcp-new monorepo add <name>` to create one.");
683
+ } else {
684
+ logger.info("Packages:");
685
+ workspaceConfig.packages.forEach((pkg, index) => {
686
+ console.log(` ${index + 1}. ${pkg}`);
687
+ });
688
+ }
689
+ } catch (error) {
690
+ if (error instanceof Error) {
691
+ logger.error(error.message);
692
+ } else {
693
+ logger.error("An unexpected error occurred");
694
+ }
695
+ process.exit(1);
696
+ }
697
+ }
698
+
699
+ // src/commands/add-ci.ts
700
+ import path4 from "path";
701
+ async function addCICommand(provider, options = {}) {
702
+ const projectDir = process.cwd();
703
+ try {
704
+ const hasPackageJson = await exists(path4.join(projectDir, "package.json"));
705
+ const hasPyproject = await exists(path4.join(projectDir, "pyproject.toml"));
706
+ const hasGoMod = await exists(path4.join(projectDir, "go.mod"));
707
+ const hasCargoToml = await exists(path4.join(projectDir, "Cargo.toml"));
708
+ const hasPomXml = await exists(path4.join(projectDir, "pom.xml"));
709
+ const hasBuildGradle = await exists(path4.join(projectDir, "build.gradle"));
710
+ const hasBuildGradleKts = await exists(path4.join(projectDir, "build.gradle.kts"));
711
+ const hasMixExs = await exists(path4.join(projectDir, "mix.exs"));
712
+ const hasProjectFile = hasPackageJson || hasPyproject || hasGoMod || hasCargoToml || hasPomXml || hasBuildGradle || hasBuildGradleKts || hasMixExs;
713
+ if (!hasProjectFile) {
714
+ logger.error(
715
+ "No project configuration file found. Please run this command from a project root directory."
716
+ );
717
+ process.exit(1);
718
+ }
719
+ let ciProvider;
720
+ const providerArg = provider || options.provider;
721
+ if (providerArg) {
722
+ if (!isValidCIProvider(providerArg)) {
723
+ logger.error(`Invalid CI provider: ${providerArg}. Valid options: github, gitlab, circleci`);
724
+ process.exit(1);
725
+ }
726
+ ciProvider = providerArg;
727
+ } else {
728
+ ciProvider = await promptCIProvider();
729
+ }
730
+ let language = await detectLanguageFromProject(projectDir);
731
+ if (!language) {
732
+ logger.warning("Could not detect project language. Please select manually:");
733
+ language = await promptLanguage();
734
+ }
735
+ let javaBuildTool = await detectJavaBuildTool(projectDir);
736
+ if ((language === "java" || language === "kotlin") && !javaBuildTool) {
737
+ javaBuildTool = await promptJavaBuildTool();
738
+ }
739
+ await generateCI({
740
+ projectDir,
741
+ provider: ciProvider,
742
+ language,
743
+ javaBuildTool,
744
+ projectName: path4.basename(projectDir)
745
+ });
746
+ logger.success(`CI/CD configuration added successfully!`);
747
+ const providerInstructions = {
748
+ github: [
749
+ "Your GitHub Actions workflow has been created at .github/workflows/ci.yml",
750
+ "It will automatically run on push and pull requests to main branch",
751
+ "Commit and push to see it in action"
752
+ ],
753
+ gitlab: [
754
+ "Your GitLab CI configuration has been created at .gitlab-ci.yml",
755
+ "It will automatically run on push to any branch",
756
+ "Push to your GitLab repository to see it in action"
757
+ ],
758
+ circleci: [
759
+ "Your CircleCI configuration has been created at .circleci/config.yml",
760
+ "Connect your repository to CircleCI to enable the pipeline",
761
+ "Visit https://circleci.com to set up your project"
762
+ ]
763
+ };
764
+ logger.nextSteps(providerInstructions[ciProvider]);
765
+ } catch (error) {
766
+ if (error instanceof Error) {
767
+ logger.error(error.message);
768
+ } else {
769
+ logger.error("An unexpected error occurred");
770
+ }
771
+ process.exit(1);
772
+ }
773
+ }
774
+
775
+ // src/commands/docs.ts
776
+ import path8 from "path";
777
+
778
+ // src/docs-server/server.ts
779
+ import http from "http";
780
+ import path7 from "path";
781
+
782
+ // src/docs-server/markdown.ts
783
+ import { marked } from "marked";
784
+ marked.setOptions({
785
+ gfm: true,
786
+ breaks: true
787
+ });
788
+ function parseMarkdown(content) {
789
+ const html = marked.parse(content);
790
+ const titleMatch = content.match(/^#\s+(.+)$/m);
791
+ const title = titleMatch ? titleMatch[1] : "Untitled";
792
+ const headings = [];
793
+ const headingRegex = /^#{1,3}\s+(.+)$/gm;
794
+ let match;
795
+ while ((match = headingRegex.exec(content)) !== null) {
796
+ headings.push(match[1]);
797
+ }
798
+ return {
799
+ title,
800
+ content: html,
801
+ headings,
802
+ rawContent: content
803
+ };
804
+ }
805
+ function renderMarkdownToHTML(content, title) {
806
+ const parsed = parseMarkdown(content);
807
+ return `<!DOCTYPE html>
808
+ <html lang="en">
809
+ <head>
810
+ <meta charset="UTF-8">
811
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
812
+ <title>${title} - mcp-new Documentation</title>
813
+ <style>
814
+ * {
815
+ margin: 0;
816
+ padding: 0;
817
+ box-sizing: border-box;
818
+ }
819
+
820
+ body {
821
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
822
+ line-height: 1.6;
823
+ color: #333;
824
+ background: #f5f5f5;
825
+ }
826
+
827
+ .container {
828
+ display: flex;
829
+ min-height: 100vh;
830
+ }
831
+
832
+ .sidebar {
833
+ width: 280px;
834
+ background: #1a1a2e;
835
+ color: #fff;
836
+ padding: 20px;
837
+ position: fixed;
838
+ height: 100vh;
839
+ overflow-y: auto;
840
+ }
841
+
842
+ .sidebar h1 {
843
+ font-size: 1.5rem;
844
+ margin-bottom: 20px;
845
+ color: #4fc3f7;
846
+ }
847
+
848
+ .sidebar nav ul {
849
+ list-style: none;
850
+ }
851
+
852
+ .sidebar nav li {
853
+ margin: 8px 0;
854
+ }
855
+
856
+ .sidebar nav a {
857
+ color: #b0bec5;
858
+ text-decoration: none;
859
+ display: block;
860
+ padding: 8px 12px;
861
+ border-radius: 4px;
862
+ transition: background 0.2s, color 0.2s;
863
+ }
864
+
865
+ .sidebar nav a:hover,
866
+ .sidebar nav a.active {
867
+ background: rgba(79, 195, 247, 0.1);
868
+ color: #4fc3f7;
869
+ }
870
+
871
+ .search-box {
872
+ margin-bottom: 20px;
873
+ }
874
+
875
+ .search-box input {
876
+ width: 100%;
877
+ padding: 10px 12px;
878
+ border: none;
879
+ border-radius: 4px;
880
+ background: rgba(255,255,255,0.1);
881
+ color: #fff;
882
+ font-size: 14px;
883
+ }
884
+
885
+ .search-box input::placeholder {
886
+ color: #78909c;
887
+ }
888
+
889
+ .main {
890
+ margin-left: 280px;
891
+ flex: 1;
892
+ padding: 40px;
893
+ max-width: 900px;
894
+ }
895
+
896
+ .content {
897
+ background: #fff;
898
+ padding: 40px;
899
+ border-radius: 8px;
900
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
901
+ }
902
+
903
+ .content h1 {
904
+ font-size: 2.5rem;
905
+ margin-bottom: 20px;
906
+ color: #1a1a2e;
907
+ border-bottom: 2px solid #4fc3f7;
908
+ padding-bottom: 10px;
909
+ }
910
+
911
+ .content h2 {
912
+ font-size: 1.8rem;
913
+ margin-top: 40px;
914
+ margin-bottom: 16px;
915
+ color: #1a1a2e;
916
+ }
917
+
918
+ .content h3 {
919
+ font-size: 1.4rem;
920
+ margin-top: 30px;
921
+ margin-bottom: 12px;
922
+ color: #333;
923
+ }
924
+
925
+ .content p {
926
+ margin-bottom: 16px;
927
+ }
928
+
929
+ .content code {
930
+ background: #f0f0f0;
931
+ padding: 2px 6px;
932
+ border-radius: 3px;
933
+ font-family: 'Fira Code', 'Monaco', monospace;
934
+ font-size: 0.9em;
935
+ }
936
+
937
+ .content pre {
938
+ background: #1a1a2e;
939
+ color: #f0f0f0;
940
+ padding: 20px;
941
+ border-radius: 6px;
942
+ overflow-x: auto;
943
+ margin: 20px 0;
944
+ }
945
+
946
+ .content pre code {
947
+ background: none;
948
+ padding: 0;
949
+ color: inherit;
950
+ }
951
+
952
+ .content ul, .content ol {
953
+ margin: 16px 0;
954
+ padding-left: 30px;
955
+ }
956
+
957
+ .content li {
958
+ margin: 8px 0;
959
+ }
960
+
961
+ .content a {
962
+ color: #4fc3f7;
963
+ text-decoration: none;
964
+ }
965
+
966
+ .content a:hover {
967
+ text-decoration: underline;
968
+ }
969
+
970
+ .content blockquote {
971
+ border-left: 4px solid #4fc3f7;
972
+ padding-left: 20px;
973
+ margin: 20px 0;
974
+ color: #666;
975
+ font-style: italic;
976
+ }
977
+
978
+ .content table {
979
+ width: 100%;
980
+ border-collapse: collapse;
981
+ margin: 20px 0;
982
+ }
983
+
984
+ .content th, .content td {
985
+ border: 1px solid #ddd;
986
+ padding: 12px;
987
+ text-align: left;
988
+ }
989
+
990
+ .content th {
991
+ background: #f5f5f5;
992
+ }
993
+
994
+ .search-results {
995
+ display: none;
996
+ position: absolute;
997
+ top: 100%;
998
+ left: 0;
999
+ right: 0;
1000
+ background: #2a2a3e;
1001
+ border-radius: 4px;
1002
+ max-height: 300px;
1003
+ overflow-y: auto;
1004
+ margin-top: 4px;
1005
+ }
1006
+
1007
+ .search-results.active {
1008
+ display: block;
1009
+ }
1010
+
1011
+ .search-result-item {
1012
+ padding: 10px 12px;
1013
+ border-bottom: 1px solid rgba(255,255,255,0.1);
1014
+ cursor: pointer;
1015
+ }
1016
+
1017
+ .search-result-item:hover {
1018
+ background: rgba(79, 195, 247, 0.1);
1019
+ }
1020
+
1021
+ .search-result-item h4 {
1022
+ color: #fff;
1023
+ font-size: 14px;
1024
+ margin-bottom: 4px;
1025
+ }
1026
+
1027
+ .search-result-item p {
1028
+ color: #78909c;
1029
+ font-size: 12px;
1030
+ }
1031
+
1032
+ @media (max-width: 768px) {
1033
+ .sidebar {
1034
+ display: none;
1035
+ }
1036
+ .main {
1037
+ margin-left: 0;
1038
+ padding: 20px;
1039
+ }
1040
+ }
1041
+ </style>
1042
+ </head>
1043
+ <body>
1044
+ <div class="container">
1045
+ <aside class="sidebar">
1046
+ <h1>mcp-new</h1>
1047
+ <div class="search-box" style="position: relative;">
1048
+ <input type="text" id="search" placeholder="Search docs..." autocomplete="off">
1049
+ <div id="search-results" class="search-results"></div>
1050
+ </div>
1051
+ <nav id="nav">
1052
+ <ul>
1053
+ ${parsed.headings.map((h) => `<li><a href="#${slugify(h)}">${h}</a></li>`).join("\n ")}
1054
+ </ul>
1055
+ </nav>
1056
+ </aside>
1057
+ <main class="main">
1058
+ <article class="content">
1059
+ ${parsed.content}
1060
+ </article>
1061
+ </main>
1062
+ </div>
1063
+
1064
+ <script>
1065
+ // SSE for hot reload
1066
+ const evtSource = new EventSource('/sse');
1067
+ evtSource.onmessage = function(event) {
1068
+ if (event.data === 'reload') {
1069
+ location.reload();
1070
+ }
1071
+ };
1072
+
1073
+ // Search functionality
1074
+ const searchInput = document.getElementById('search');
1075
+ const searchResults = document.getElementById('search-results');
1076
+
1077
+ searchInput.addEventListener('input', async (e) => {
1078
+ const query = e.target.value.trim();
1079
+ if (query.length < 2) {
1080
+ searchResults.classList.remove('active');
1081
+ return;
1082
+ }
1083
+
1084
+ try {
1085
+ const res = await fetch('/api/search?q=' + encodeURIComponent(query));
1086
+ const results = await res.json();
1087
+
1088
+ if (results.length > 0) {
1089
+ searchResults.innerHTML = results.map(r =>
1090
+ '<div class="search-result-item" onclick="location.href=\\'' + r.path + '\\'"><h4>' + r.title + '</h4><p>' + r.snippet + '</p></div>'
1091
+ ).join('');
1092
+ searchResults.classList.add('active');
1093
+ } else {
1094
+ searchResults.classList.remove('active');
1095
+ }
1096
+ } catch (err) {
1097
+ console.error('Search error:', err);
1098
+ }
1099
+ });
1100
+
1101
+ document.addEventListener('click', (e) => {
1102
+ if (!e.target.closest('.search-box')) {
1103
+ searchResults.classList.remove('active');
1104
+ }
1105
+ });
1106
+ </script>
1107
+ </body>
1108
+ </html>`;
1109
+ }
1110
+ function slugify(text) {
1111
+ return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
1112
+ }
1113
+
1114
+ // src/docs-server/search.ts
1115
+ import path5 from "path";
1116
+ var DocsSearchEngine = class {
1117
+ index = { documents: /* @__PURE__ */ new Map() };
1118
+ docsDir;
1119
+ constructor(docsDir) {
1120
+ this.docsDir = docsDir;
1121
+ }
1122
+ async buildIndex() {
1123
+ this.index.documents.clear();
1124
+ await this.indexDirectory(this.docsDir);
1125
+ }
1126
+ async indexDirectory(dir) {
1127
+ if (!await exists(dir)) {
1128
+ return;
1129
+ }
1130
+ const entries = await readDir(dir);
1131
+ for (const entry of entries) {
1132
+ const fullPath = path5.join(dir, entry);
1133
+ try {
1134
+ const subEntries = await readDir(fullPath);
1135
+ await this.indexDirectory(fullPath);
1136
+ } catch {
1137
+ if (entry.endsWith(".md")) {
1138
+ await this.indexFile(fullPath);
1139
+ }
1140
+ }
1141
+ }
1142
+ }
1143
+ async indexFile(filePath) {
1144
+ try {
1145
+ const content = await readFile(filePath);
1146
+ const parsed = parseMarkdown(content);
1147
+ const relativePath = "/" + path5.relative(this.docsDir, filePath).replace(/\.md$/, "");
1148
+ const words = /* @__PURE__ */ new Set();
1149
+ const textContent = parsed.rawContent.toLowerCase();
1150
+ const wordMatches = textContent.match(/\b[a-z0-9]+\b/g);
1151
+ if (wordMatches) {
1152
+ wordMatches.forEach((w) => words.add(w));
1153
+ }
1154
+ this.index.documents.set(relativePath, {
1155
+ path: relativePath,
1156
+ title: parsed.title,
1157
+ content: parsed.rawContent,
1158
+ words
1159
+ });
1160
+ } catch (error) {
1161
+ }
1162
+ }
1163
+ search(query, limit = 10) {
1164
+ const queryWords = query.toLowerCase().split(/\s+/).filter((w) => w.length > 1);
1165
+ if (queryWords.length === 0) {
1166
+ return [];
1167
+ }
1168
+ const results = [];
1169
+ for (const [docPath, doc] of this.index.documents) {
1170
+ let score = 0;
1171
+ const titleLower = doc.title.toLowerCase();
1172
+ for (const word of queryWords) {
1173
+ if (titleLower.includes(word)) {
1174
+ score += 10;
1175
+ }
1176
+ }
1177
+ const contentLower = doc.content.toLowerCase();
1178
+ for (const word of queryWords) {
1179
+ if (doc.words.has(word)) {
1180
+ score += 1;
1181
+ }
1182
+ if (contentLower.includes(word)) {
1183
+ score += 2;
1184
+ }
1185
+ }
1186
+ if (score > 0) {
1187
+ const snippet = this.extractSnippet(doc.content, queryWords[0]);
1188
+ results.push({
1189
+ path: docPath,
1190
+ title: doc.title,
1191
+ snippet,
1192
+ score
1193
+ });
1194
+ }
1195
+ }
1196
+ results.sort((a, b) => b.score - a.score);
1197
+ return results.slice(0, limit);
1198
+ }
1199
+ extractSnippet(content, query) {
1200
+ const index = content.toLowerCase().indexOf(query.toLowerCase());
1201
+ if (index === -1) {
1202
+ return content.slice(0, 150) + "...";
1203
+ }
1204
+ const start = Math.max(0, index - 50);
1205
+ const end = Math.min(content.length, index + query.length + 100);
1206
+ let snippet = content.slice(start, end);
1207
+ if (start > 0) snippet = "..." + snippet;
1208
+ if (end < content.length) snippet = snippet + "...";
1209
+ return snippet.replace(/\n/g, " ").trim();
1210
+ }
1211
+ getDocuments() {
1212
+ return this.index.documents;
1213
+ }
1214
+ };
1215
+
1216
+ // src/docs-server/watcher.ts
1217
+ import fs3 from "fs";
1218
+ import path6 from "path";
1219
+ import { EventEmitter } from "events";
1220
+ var DocsWatcher = class extends EventEmitter {
1221
+ watchers = /* @__PURE__ */ new Map();
1222
+ debounceTimer = null;
1223
+ docsDir;
1224
+ constructor(docsDir) {
1225
+ super();
1226
+ this.docsDir = docsDir;
1227
+ }
1228
+ async start() {
1229
+ await this.watchDirectory(this.docsDir);
1230
+ }
1231
+ async watchDirectory(dir) {
1232
+ if (!await exists(dir)) {
1233
+ return;
1234
+ }
1235
+ const watcher = fs3.watch(dir, { recursive: false }, (eventType, filename) => {
1236
+ if (filename && filename.endsWith(".md")) {
1237
+ this.debouncedEmit();
1238
+ }
1239
+ });
1240
+ this.watchers.set(dir, watcher);
1241
+ try {
1242
+ const entries = await readDir(dir);
1243
+ for (const entry of entries) {
1244
+ const fullPath = path6.join(dir, entry);
1245
+ try {
1246
+ const subEntries = await readDir(fullPath);
1247
+ await this.watchDirectory(fullPath);
1248
+ } catch {
1249
+ }
1250
+ }
1251
+ } catch {
1252
+ }
1253
+ }
1254
+ debouncedEmit() {
1255
+ if (this.debounceTimer) {
1256
+ clearTimeout(this.debounceTimer);
1257
+ }
1258
+ this.debounceTimer = setTimeout(() => {
1259
+ this.emit("change");
1260
+ }, 100);
1261
+ }
1262
+ stop() {
1263
+ for (const watcher of this.watchers.values()) {
1264
+ watcher.close();
1265
+ }
1266
+ this.watchers.clear();
1267
+ if (this.debounceTimer) {
1268
+ clearTimeout(this.debounceTimer);
1269
+ }
1270
+ }
1271
+ };
1272
+
1273
+ // src/docs-server/server.ts
1274
+ var DocsServer = class {
1275
+ server = null;
1276
+ searchEngine;
1277
+ watcher;
1278
+ sseClients = /* @__PURE__ */ new Set();
1279
+ options;
1280
+ constructor(options) {
1281
+ this.options = options;
1282
+ this.searchEngine = new DocsSearchEngine(options.docsDir);
1283
+ this.watcher = new DocsWatcher(options.docsDir);
1284
+ }
1285
+ async start() {
1286
+ await this.searchEngine.buildIndex();
1287
+ await this.watcher.start();
1288
+ this.watcher.on("change", async () => {
1289
+ await this.searchEngine.buildIndex();
1290
+ this.notifyClients();
1291
+ });
1292
+ this.server = http.createServer((req, res) => {
1293
+ this.handleRequest(req, res);
1294
+ });
1295
+ return new Promise((resolve) => {
1296
+ this.server.listen(this.options.port, () => {
1297
+ resolve();
1298
+ });
1299
+ });
1300
+ }
1301
+ stop() {
1302
+ if (this.server) {
1303
+ this.server.close();
1304
+ }
1305
+ this.watcher.stop();
1306
+ for (const client of this.sseClients) {
1307
+ client.end();
1308
+ }
1309
+ this.sseClients.clear();
1310
+ }
1311
+ async handleRequest(req, res) {
1312
+ const url = new URL(req.url || "/", `http://localhost:${this.options.port}`);
1313
+ const pathname = url.pathname;
1314
+ try {
1315
+ if (pathname === "/sse") {
1316
+ this.handleSSE(res);
1317
+ return;
1318
+ }
1319
+ if (pathname === "/api/search") {
1320
+ const query = url.searchParams.get("q") || "";
1321
+ const results = this.searchEngine.search(query);
1322
+ res.writeHead(200, { "Content-Type": "application/json" });
1323
+ res.end(JSON.stringify(results));
1324
+ return;
1325
+ }
1326
+ if (pathname.startsWith("/static/")) {
1327
+ await this.serveStatic(pathname, res);
1328
+ return;
1329
+ }
1330
+ await this.serveMarkdown(pathname, res);
1331
+ } catch (error) {
1332
+ this.serveError(res, 500, "Internal Server Error");
1333
+ }
1334
+ }
1335
+ handleSSE(res) {
1336
+ res.writeHead(200, {
1337
+ "Content-Type": "text/event-stream",
1338
+ "Cache-Control": "no-cache",
1339
+ Connection: "keep-alive"
1340
+ });
1341
+ this.sseClients.add(res);
1342
+ res.on("close", () => {
1343
+ this.sseClients.delete(res);
1344
+ });
1345
+ }
1346
+ notifyClients() {
1347
+ for (const client of this.sseClients) {
1348
+ client.write("data: reload\n\n");
1349
+ }
1350
+ }
1351
+ async serveMarkdown(pathname, res) {
1352
+ let filePath = pathname === "/" ? "/index" : pathname;
1353
+ if (!filePath.endsWith(".md")) {
1354
+ filePath += ".md";
1355
+ }
1356
+ const fullPath = path7.join(this.options.docsDir, filePath);
1357
+ if (await exists(fullPath)) {
1358
+ const content = await readFile(fullPath);
1359
+ const title = path7.basename(filePath, ".md");
1360
+ const html = renderMarkdownToHTML(content, title);
1361
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
1362
+ res.end(html);
1363
+ return;
1364
+ }
1365
+ const indexPath = path7.join(this.options.docsDir, pathname, "index.md");
1366
+ if (await exists(indexPath)) {
1367
+ const content = await readFile(indexPath);
1368
+ const title = path7.basename(pathname) || "Home";
1369
+ const html = renderMarkdownToHTML(content, title);
1370
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
1371
+ res.end(html);
1372
+ return;
1373
+ }
1374
+ const dirPath = path7.join(this.options.docsDir, pathname);
1375
+ if (await exists(dirPath)) {
1376
+ const indexHtml = await this.generateDirectoryIndex(pathname);
1377
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
1378
+ res.end(indexHtml);
1379
+ return;
1380
+ }
1381
+ this.serveError(res, 404, "Page Not Found");
1382
+ }
1383
+ async generateDirectoryIndex(pathname) {
1384
+ const docs = this.searchEngine.getDocuments();
1385
+ const prefix = pathname === "/" ? "" : pathname;
1386
+ const links = [];
1387
+ for (const [docPath, doc] of docs) {
1388
+ if (docPath.startsWith(prefix) || prefix === "") {
1389
+ links.push(`- [${doc.title}](${docPath})`);
1390
+ }
1391
+ }
1392
+ const content = `# Documentation
1393
+
1394
+ ${links.join("\n")}`;
1395
+ return renderMarkdownToHTML(content, "Documentation");
1396
+ }
1397
+ async serveStatic(pathname, res) {
1398
+ const ext = path7.extname(pathname);
1399
+ const contentTypes = {
1400
+ ".css": "text/css",
1401
+ ".js": "application/javascript",
1402
+ ".png": "image/png",
1403
+ ".svg": "image/svg+xml"
1404
+ };
1405
+ const contentType = contentTypes[ext] || "application/octet-stream";
1406
+ const fullPath = path7.join(this.options.docsDir, pathname);
1407
+ if (await exists(fullPath)) {
1408
+ const content = await readFile(fullPath);
1409
+ res.writeHead(200, { "Content-Type": contentType });
1410
+ res.end(content);
1411
+ } else {
1412
+ this.serveError(res, 404, "Not Found");
1413
+ }
1414
+ }
1415
+ serveError(res, status, message) {
1416
+ const html = renderMarkdownToHTML(`# ${status}
1417
+
1418
+ ${message}`, `${status} Error`);
1419
+ res.writeHead(status, { "Content-Type": "text/html; charset=utf-8" });
1420
+ res.end(html);
1421
+ }
1422
+ };
1423
+
1424
+ // src/commands/docs.ts
1425
+ async function docsCommand(options = {}) {
1426
+ const port = parseInt(options.port || "3000", 10);
1427
+ if (isNaN(port) || port < 1 || port > 65535) {
1428
+ logger.error("Invalid port number. Please provide a port between 1 and 65535.");
1429
+ process.exit(1);
1430
+ }
1431
+ const possibleDocsDirs = [
1432
+ path8.join(process.cwd(), "docs"),
1433
+ path8.join(process.cwd(), "documentation"),
1434
+ path8.join(process.cwd(), "doc"),
1435
+ process.cwd()
1436
+ // Use current directory if it contains markdown files
1437
+ ];
1438
+ let docsDir = null;
1439
+ for (const dir of possibleDocsDirs) {
1440
+ if (await exists(dir)) {
1441
+ const hasMarkdown = await hasMarkdownFiles(dir);
1442
+ if (hasMarkdown) {
1443
+ docsDir = dir;
1444
+ break;
1445
+ }
1446
+ }
1447
+ }
1448
+ if (!docsDir) {
1449
+ logger.error("No documentation directory found.");
1450
+ logger.info('Create a "docs" directory with .md files to use this command.');
1451
+ process.exit(1);
1452
+ }
1453
+ logger.title("mcp-new Documentation Server");
1454
+ logger.info(`Serving docs from: ${docsDir}`);
1455
+ const server = new DocsServer({
1456
+ port,
1457
+ docsDir
1458
+ });
1459
+ try {
1460
+ await server.start();
1461
+ logger.success(`Documentation server running at http://localhost:${port}`);
1462
+ logger.info("Hot reload enabled - changes will refresh automatically");
1463
+ logger.info("Press Ctrl+C to stop");
1464
+ process.on("SIGINT", () => {
1465
+ logger.info("\nShutting down...");
1466
+ server.stop();
1467
+ process.exit(0);
1468
+ });
1469
+ process.on("SIGTERM", () => {
1470
+ server.stop();
1471
+ process.exit(0);
1472
+ });
1473
+ } catch (error) {
1474
+ if (error instanceof Error) {
1475
+ if (error.message.includes("EADDRINUSE")) {
1476
+ logger.error(`Port ${port} is already in use. Try a different port with --port.`);
1477
+ } else {
1478
+ logger.error(error.message);
1479
+ }
1480
+ } else {
1481
+ logger.error("Failed to start documentation server");
1482
+ }
1483
+ process.exit(1);
1484
+ }
1485
+ }
1486
+ async function hasMarkdownFiles(dir) {
1487
+ try {
1488
+ const { readdir } = await import("fs/promises");
1489
+ const entries = await readdir(dir, { withFileTypes: true });
1490
+ for (const entry of entries) {
1491
+ if (entry.isFile() && entry.name.endsWith(".md")) {
1492
+ return true;
1493
+ }
1494
+ if (entry.isDirectory() && !entry.name.startsWith(".")) {
1495
+ const subDir = path8.join(dir, entry.name);
1496
+ if (await hasMarkdownFiles(subDir)) {
1497
+ return true;
1498
+ }
1499
+ }
1500
+ }
1501
+ } catch {
1502
+ return false;
1503
+ }
1504
+ return false;
1505
+ }
1506
+
1507
+ // src/commands/preset-cache.ts
1508
+ async function presetCacheCommand(action) {
1509
+ try {
1510
+ if (!action) {
1511
+ action = "list";
1512
+ }
1513
+ switch (action) {
1514
+ case "clear":
1515
+ await handleClear();
1516
+ break;
1517
+ case "list":
1518
+ await handleList();
1519
+ break;
1520
+ case "path":
1521
+ handlePath();
1522
+ break;
1523
+ default:
1524
+ logger.error(`Unknown action: ${action}. Valid actions: clear, list, path`);
1525
+ process.exit(1);
1526
+ }
1527
+ } catch (error) {
1528
+ if (error instanceof Error) {
1529
+ logger.error(error.message);
1530
+ } else {
1531
+ logger.error("An unexpected error occurred");
1532
+ }
1533
+ process.exit(1);
1534
+ }
1535
+ }
1536
+ async function handleClear() {
1537
+ const count = await clearPresetCache();
1538
+ if (count > 0) {
1539
+ logger.success(`Cleared ${count} cached preset(s)`);
1540
+ } else {
1541
+ logger.info("No cached presets to clear");
1542
+ }
1543
+ }
1544
+ async function handleList() {
1545
+ const presets = await listCachedPresets();
1546
+ if (presets.length === 0) {
1547
+ logger.info("No presets cached");
1548
+ logger.info(`Cache directory: ${getCacheDir()}`);
1549
+ return;
1550
+ }
1551
+ logger.title("Cached Presets");
1552
+ for (const preset of presets) {
1553
+ const age = Date.now() - preset.cachedAt;
1554
+ const hoursAgo = Math.round(age / (1e3 * 60 * 60));
1555
+ const ageStr = hoursAgo < 1 ? "less than an hour ago" : `${hoursAgo} hour(s) ago`;
1556
+ console.log(`
1557
+ ${preset.manifest.name} (${preset.manifest.version})`);
1558
+ console.log(` Source: ${preset.source.type}:${preset.source.identifier}`);
1559
+ console.log(` Cached: ${ageStr}`);
1560
+ console.log(` Tools: ${preset.manifest.tools.map((t) => t.name).join(", ")}`);
1561
+ }
1562
+ console.log(`
1563
+ Cache directory: ${getCacheDir()}`);
1564
+ }
1565
+ function handlePath() {
1566
+ console.log(getCacheDir());
1567
+ }
1568
+
424
1569
  // src/cli.ts
425
1570
  var program = new Command();
426
1571
  var logo = `
@@ -464,15 +1609,21 @@ ${chalk4.bold("Supported Languages:")}
464
1609
  ${chalk4.green("-p, --python")} Python with pip
465
1610
  ${chalk4.green("-g, --go")} Go with go modules
466
1611
  ${chalk4.green("-r, --rust")} Rust with cargo
1612
+ ${chalk4.green("-j, --java")} Java with Maven/Gradle
1613
+ ${chalk4.green("-k, --kotlin")} Kotlin with Maven/Gradle
1614
+ ${chalk4.green("-c, --csharp")} C# with .NET
1615
+ ${chalk4.green("-e, --elixir")} Elixir with Mix
467
1616
 
468
1617
  ${chalk4.bold("Learn More:")}
469
1618
 
470
1619
  Documentation: ${chalk4.underline("https://github.com/d1maash/mcp-new")}
471
1620
  MCP Spec: ${chalk4.underline("https://spec.modelcontextprotocol.io")}
472
1621
  `;
473
- program.name("mcp-new").description("CLI tool for generating MCP (Model Context Protocol) servers").version("1.2.2").addHelpText("beforeAll", logo).addHelpText("after", examples);
474
- 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);
475
- 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", `
1622
+ program.name("mcp-new").description("CLI tool for generating MCP (Model Context Protocol) servers").version("1.5.0").addHelpText("beforeAll", logo).addHelpText("after", examples);
1623
+ 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, or @org/package, github:user/repo)").option("--ci <provider>", "Add CI/CD configuration (github, gitlab, circleci)").option("-y, --yes", "Skip prompts and use defaults").action(createCommand);
1624
+ 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(
1625
+ "after",
1626
+ `
476
1627
  ${chalk4.bold("Examples:")}
477
1628
 
478
1629
  ${chalk4.gray("# Initialize in current directory")}
@@ -483,8 +1634,11 @@ ${chalk4.bold("Examples:")}
483
1634
 
484
1635
  ${chalk4.gray("# Force initialize (overwrite existing files)")}
485
1636
  ${chalk4.cyan("$")} mcp-new init -f
486
- `).action(initCommand);
487
- program.command("add-tool").description("Add a new tool to an existing MCP server").option("-n, --name <name>", "Tool name (snake_case)").addHelpText("after", `
1637
+ `
1638
+ ).action(initCommand);
1639
+ program.command("add-tool").description("Add a new tool to an existing MCP server").option("-n, --name <name>", "Tool name (snake_case)").addHelpText(
1640
+ "after",
1641
+ `
488
1642
  ${chalk4.bold("Examples:")}
489
1643
 
490
1644
  ${chalk4.gray("# Add tool interactively")}
@@ -492,14 +1646,20 @@ ${chalk4.bold("Examples:")}
492
1646
 
493
1647
  ${chalk4.gray("# Add tool with name")}
494
1648
  ${chalk4.cyan("$")} mcp-new add-tool -n my_new_tool
495
- `).action(addToolCommand);
496
- program.command("list-presets").description("List all available preset templates").addHelpText("after", `
1649
+ `
1650
+ ).action(addToolCommand);
1651
+ program.command("list-presets").description("List all available preset templates").addHelpText(
1652
+ "after",
1653
+ `
497
1654
  ${chalk4.bold("Examples:")}
498
1655
 
499
1656
  ${chalk4.gray("# Show all presets with their tools")}
500
1657
  ${chalk4.cyan("$")} mcp-new list-presets
501
- `).action(listPresetsCommand);
502
- program.command("validate").description("Validate the current MCP server project").addHelpText("after", `
1658
+ `
1659
+ ).action(listPresetsCommand);
1660
+ program.command("validate").description("Validate the current MCP server project").addHelpText(
1661
+ "after",
1662
+ `
503
1663
  ${chalk4.bold("Examples:")}
504
1664
 
505
1665
  ${chalk4.gray("# Validate current project")}
@@ -510,8 +1670,11 @@ ${chalk4.bold("Checks:")}
510
1670
  \u2022 MCP SDK dependency presence and version
511
1671
  \u2022 Entry point file existence
512
1672
  \u2022 Basic project structure
513
- `).action(validateCommand);
514
- program.command("upgrade").description("Upgrade MCP SDK to the latest version").option("-c, --check", "Check for updates without installing").addHelpText("after", `
1673
+ `
1674
+ ).action(validateCommand);
1675
+ program.command("upgrade").description("Upgrade MCP SDK to the latest version").option("-c, --check", "Check for updates without installing").addHelpText(
1676
+ "after",
1677
+ `
515
1678
  ${chalk4.bold("Examples:")}
516
1679
 
517
1680
  ${chalk4.gray("# Upgrade MCP SDK to latest version")}
@@ -525,8 +1688,114 @@ ${chalk4.bold("Supported languages:")}
525
1688
  \u2022 Python (pip)
526
1689
  \u2022 Go (go modules)
527
1690
  \u2022 Rust (cargo)
528
- `).action(upgradeCommand);
529
- program.parse();
530
- if (process.argv.length === 2) {
531
- program.help();
1691
+ `
1692
+ ).action(upgradeCommand);
1693
+ program.command("preset-cache [action]").description("Manage external preset cache").addHelpText(
1694
+ "after",
1695
+ `
1696
+ ${chalk4.bold("Actions:")}
1697
+
1698
+ ${chalk4.green("list")} List cached presets (default)
1699
+ ${chalk4.green("clear")} Clear all cached presets
1700
+ ${chalk4.green("path")} Show cache directory path
1701
+
1702
+ ${chalk4.bold("Examples:")}
1703
+
1704
+ ${chalk4.gray("# List cached presets")}
1705
+ ${chalk4.cyan("$")} mcp-new preset-cache list
1706
+
1707
+ ${chalk4.gray("# Clear preset cache")}
1708
+ ${chalk4.cyan("$")} mcp-new preset-cache clear
1709
+
1710
+ ${chalk4.bold("External Presets:")}
1711
+
1712
+ Use external presets with the --preset flag:
1713
+ ${chalk4.cyan("$")} mcp-new my-project --preset @company/custom-preset
1714
+ ${chalk4.cyan("$")} mcp-new my-project --preset github:user/repo
1715
+ `
1716
+ ).action(presetCacheCommand);
1717
+ program.command("docs").description("Start an interactive documentation server").option("-p, --port <port>", "Port to run the server on", "3000").addHelpText(
1718
+ "after",
1719
+ `
1720
+ ${chalk4.bold("Examples:")}
1721
+
1722
+ ${chalk4.gray("# Start docs server on default port (3000)")}
1723
+ ${chalk4.cyan("$")} mcp-new docs
1724
+
1725
+ ${chalk4.gray("# Start docs server on custom port")}
1726
+ ${chalk4.cyan("$")} mcp-new docs --port 4000
1727
+
1728
+ ${chalk4.bold("Features:")}
1729
+ ${chalk4.green("Hot reload")} - Changes refresh automatically
1730
+ ${chalk4.green("Search")} - Full-text search across all docs
1731
+ ${chalk4.green("Markdown")} - Renders GitHub-flavored markdown
1732
+ `
1733
+ ).action(docsCommand);
1734
+ program.command("add-ci [provider]").description("Add CI/CD configuration to an existing project").addHelpText(
1735
+ "after",
1736
+ `
1737
+ ${chalk4.bold("Examples:")}
1738
+
1739
+ ${chalk4.gray("# Add CI interactively")}
1740
+ ${chalk4.cyan("$")} mcp-new add-ci
1741
+
1742
+ ${chalk4.gray("# Add GitHub Actions")}
1743
+ ${chalk4.cyan("$")} mcp-new add-ci github
1744
+
1745
+ ${chalk4.gray("# Add GitLab CI")}
1746
+ ${chalk4.cyan("$")} mcp-new add-ci gitlab
1747
+
1748
+ ${chalk4.gray("# Add CircleCI")}
1749
+ ${chalk4.cyan("$")} mcp-new add-ci circleci
1750
+
1751
+ ${chalk4.bold("Supported providers:")}
1752
+ ${chalk4.green("github")} GitHub Actions
1753
+ ${chalk4.green("gitlab")} GitLab CI
1754
+ ${chalk4.green("circleci")} CircleCI
1755
+ `
1756
+ ).action(addCICommand);
1757
+ var monorepo = program.command("monorepo").description("Manage MCP monorepo workspaces");
1758
+ monorepo.command("init [workspace-name]").description("Initialize a new MCP monorepo workspace").option("-f, --force", "Initialize even if directory contains files").addHelpText(
1759
+ "after",
1760
+ `
1761
+ ${chalk4.bold("Examples:")}
1762
+
1763
+ ${chalk4.gray("# Create a new monorepo workspace")}
1764
+ ${chalk4.cyan("$")} mcp-new monorepo init my-workspace
1765
+
1766
+ ${chalk4.gray("# Create in current directory name")}
1767
+ ${chalk4.cyan("$")} mcp-new monorepo init
1768
+ `
1769
+ ).action((_workspaceName, _options, command) => {
1770
+ const opts = command.optsWithGlobals();
1771
+ const workspaceName = command.args[0];
1772
+ monorepoInitCommand(workspaceName, opts);
1773
+ });
1774
+ 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(
1775
+ "after",
1776
+ `
1777
+ ${chalk4.bold("Examples:")}
1778
+
1779
+ ${chalk4.gray("# Add a TypeScript server")}
1780
+ ${chalk4.cyan("$")} mcp-new monorepo add my-server -t
1781
+
1782
+ ${chalk4.gray("# Add a Java server with Gradle")}
1783
+ ${chalk4.cyan("$")} mcp-new monorepo add api-server -j --gradle
1784
+ `
1785
+ ).action((_serverName, _options, command) => {
1786
+ const opts = command.optsWithGlobals();
1787
+ const serverName = command.args[0];
1788
+ monorepoAddCommand(serverName, opts);
1789
+ });
1790
+ monorepo.command("list").description("List all packages in the workspace").action(monorepoListCommand);
1791
+ async function main() {
1792
+ await pluginRegistry.initialize();
1793
+ program.parse();
1794
+ if (process.argv.length === 2) {
1795
+ program.help();
1796
+ }
532
1797
  }
1798
+ main().catch((error) => {
1799
+ console.error("Error:", error.message);
1800
+ process.exit(1);
1801
+ });