mcp-new 1.5.0 → 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.
@@ -40,12 +40,12 @@ function validateUrl(url) {
40
40
  return { valid: false, error: "Invalid URL format" };
41
41
  }
42
42
  }
43
- function validateFilePath(path7) {
44
- if (!path7 || path7.trim().length === 0) {
43
+ function validateFilePath(path11) {
44
+ if (!path11 || path11.trim().length === 0) {
45
45
  return { valid: false, error: "File path cannot be empty" };
46
46
  }
47
47
  const invalidChars = /[<>:"|?*]/;
48
- if (invalidChars.test(path7)) {
48
+ if (invalidChars.test(path11)) {
49
49
  return { valid: false, error: "File path contains invalid characters" };
50
50
  }
51
51
  return { valid: true };
@@ -90,24 +90,270 @@ async function promptProjectDescription() {
90
90
  return description;
91
91
  }
92
92
 
93
+ // src/utils/file-system.ts
94
+ import fs from "fs-extra";
95
+ import path from "path";
96
+ import ejs from "ejs";
97
+ async function ensureDir(dirPath) {
98
+ await fs.ensureDir(dirPath);
99
+ }
100
+ async function writeFile(filePath, content) {
101
+ await fs.ensureDir(path.dirname(filePath));
102
+ await fs.writeFile(filePath, content, "utf-8");
103
+ }
104
+ async function readFile(filePath) {
105
+ return fs.readFile(filePath, "utf-8");
106
+ }
107
+ async function copyFile(src, dest) {
108
+ await fs.ensureDir(path.dirname(dest));
109
+ await fs.copyFile(src, dest);
110
+ }
111
+ async function copyDir(src, dest) {
112
+ await fs.copy(src, dest);
113
+ }
114
+ async function exists(filePath) {
115
+ return fs.pathExists(filePath);
116
+ }
117
+ async function remove(filePath) {
118
+ await fs.remove(filePath);
119
+ }
120
+ async function readDir(dirPath) {
121
+ return fs.readdir(dirPath);
122
+ }
123
+ async function isDirectory(filePath) {
124
+ try {
125
+ const stat = await fs.stat(filePath);
126
+ return stat.isDirectory();
127
+ } catch {
128
+ return false;
129
+ }
130
+ }
131
+ async function renderTemplate(templatePath, data) {
132
+ const template = await readFile(templatePath);
133
+ return ejs.render(template, data, { async: false });
134
+ }
135
+ async function renderTemplateToFile(templatePath, outputPath, data) {
136
+ const content = await renderTemplate(templatePath, data);
137
+ const finalPath = outputPath.replace(/\.ejs$/, "");
138
+ await writeFile(finalPath, content);
139
+ }
140
+ async function walkDir(dirPath, callback) {
141
+ const entries = await fs.readdir(dirPath, { withFileTypes: true });
142
+ for (const entry of entries) {
143
+ const fullPath = path.join(dirPath, entry.name);
144
+ const isDir = entry.isDirectory();
145
+ await callback(fullPath, isDir);
146
+ if (isDir) {
147
+ await walkDir(fullPath, callback);
148
+ }
149
+ }
150
+ }
151
+ function getTemplateDir() {
152
+ const devPath = path.join(import.meta.dirname, "..", "templates");
153
+ const prodPath = path.join(import.meta.dirname, "..", "..", "templates");
154
+ if (fs.existsSync(devPath)) {
155
+ return devPath;
156
+ }
157
+ return prodPath;
158
+ }
159
+ function getTemplateDirForLanguage(language, pluginTemplateDir) {
160
+ if (pluginTemplateDir) {
161
+ return pluginTemplateDir;
162
+ }
163
+ return path.join(getTemplateDir(), language);
164
+ }
165
+ function resolveOutputPath(basePath, ...segments) {
166
+ return path.resolve(basePath, ...segments);
167
+ }
168
+
169
+ // src/plugins/discovery.ts
170
+ import path2 from "path";
171
+
172
+ // src/types/plugin.ts
173
+ import { z } from "zod";
174
+ var LanguagePluginManifestSchema = z.object({
175
+ languageId: z.string(),
176
+ languageDisplayName: z.string(),
177
+ templateDir: z.string().default("templates"),
178
+ installCommand: z.string().optional(),
179
+ runCommand: z.string().optional(),
180
+ buildCommand: z.string().optional(),
181
+ fileExtension: z.string().optional()
182
+ });
183
+ var PLUGIN_MANIFEST_FILE = "mcp-plugin.json";
184
+
185
+ // src/plugins/discovery.ts
186
+ async function discoverPlugins() {
187
+ const plugins = [];
188
+ const searchPaths = [
189
+ // Global npm modules
190
+ getGlobalNodeModulesPath(),
191
+ // Local node_modules
192
+ path2.join(process.cwd(), "node_modules"),
193
+ // Relative to this package
194
+ path2.join(import.meta.dirname, "..", "..", "node_modules")
195
+ ];
196
+ for (const searchPath of searchPaths) {
197
+ const scopedPath = path2.join(searchPath, "@mcp-new");
198
+ if (!await exists(scopedPath)) {
199
+ continue;
200
+ }
201
+ try {
202
+ const { readdir } = await import("fs/promises");
203
+ const entries = await readdir(scopedPath, { withFileTypes: true });
204
+ for (const entry of entries) {
205
+ if (!entry.isDirectory()) continue;
206
+ if (!entry.name.startsWith("template-")) continue;
207
+ const pluginPath = path2.join(scopedPath, entry.name);
208
+ const plugin = await loadPlugin(pluginPath, `@mcp-new/${entry.name}`);
209
+ if (plugin) {
210
+ if (!plugins.some((p) => p.manifest.languageId === plugin.manifest.languageId)) {
211
+ plugins.push(plugin);
212
+ }
213
+ }
214
+ }
215
+ } catch {
216
+ }
217
+ }
218
+ return plugins;
219
+ }
220
+ async function loadPlugin(packagePath, packageName) {
221
+ const manifestPath = path2.join(packagePath, PLUGIN_MANIFEST_FILE);
222
+ if (!await exists(manifestPath)) {
223
+ return null;
224
+ }
225
+ try {
226
+ const content = await readFile(manifestPath);
227
+ const rawManifest = JSON.parse(content);
228
+ const manifest = LanguagePluginManifestSchema.parse(rawManifest);
229
+ const templatePath = path2.join(packagePath, manifest.templateDir);
230
+ if (!await exists(templatePath)) {
231
+ console.warn(`Plugin ${packageName}: templates directory not found at ${templatePath}`);
232
+ return null;
233
+ }
234
+ return {
235
+ manifest,
236
+ packageName,
237
+ packagePath,
238
+ templatePath
239
+ };
240
+ } catch (error) {
241
+ console.warn(`Failed to load plugin ${packageName}:`, error);
242
+ return null;
243
+ }
244
+ }
245
+ function getGlobalNodeModulesPath() {
246
+ if (process.platform === "win32") {
247
+ return path2.join(process.env.APPDATA || "", "npm", "node_modules");
248
+ }
249
+ return "/usr/local/lib/node_modules";
250
+ }
251
+ function isPluginLanguage(languageId, plugins) {
252
+ return plugins.some((p) => p.manifest.languageId === languageId);
253
+ }
254
+ function getPluginForLanguage(languageId, plugins) {
255
+ return plugins.find((p) => p.manifest.languageId === languageId);
256
+ }
257
+
258
+ // src/plugins/registry.ts
259
+ var PluginRegistry = class {
260
+ plugins = [];
261
+ initialized = false;
262
+ /**
263
+ * Initialize the registry by discovering installed plugins.
264
+ */
265
+ async initialize() {
266
+ if (this.initialized) {
267
+ return;
268
+ }
269
+ this.plugins = await discoverPlugins();
270
+ this.initialized = true;
271
+ }
272
+ /**
273
+ * Get all registered plugins.
274
+ */
275
+ getPlugins() {
276
+ return [...this.plugins];
277
+ }
278
+ /**
279
+ * Get a plugin by language ID.
280
+ */
281
+ getPlugin(languageId) {
282
+ return getPluginForLanguage(languageId, this.plugins);
283
+ }
284
+ /**
285
+ * Check if a language is provided by a plugin.
286
+ */
287
+ isPluginLanguage(languageId) {
288
+ return isPluginLanguage(languageId, this.plugins);
289
+ }
290
+ /**
291
+ * Get all available languages from plugins.
292
+ */
293
+ getPluginLanguages() {
294
+ return this.plugins.map((p) => ({
295
+ id: p.manifest.languageId,
296
+ name: p.manifest.languageDisplayName
297
+ }));
298
+ }
299
+ /**
300
+ * Get the template directory for a plugin language.
301
+ */
302
+ getPluginTemplateDir(languageId) {
303
+ const plugin = this.getPlugin(languageId);
304
+ return plugin?.templatePath;
305
+ }
306
+ /**
307
+ * Get the install command for a plugin language.
308
+ */
309
+ getPluginInstallCommand(languageId) {
310
+ const plugin = this.getPlugin(languageId);
311
+ return plugin?.manifest.installCommand;
312
+ }
313
+ /**
314
+ * Get the run command for a plugin language.
315
+ */
316
+ getPluginRunCommand(languageId) {
317
+ const plugin = this.getPlugin(languageId);
318
+ return plugin?.manifest.runCommand;
319
+ }
320
+ /**
321
+ * Reset the registry (for testing).
322
+ */
323
+ reset() {
324
+ this.plugins = [];
325
+ this.initialized = false;
326
+ }
327
+ };
328
+ var pluginRegistry = new PluginRegistry();
329
+
93
330
  // src/prompts/language.ts
94
331
  import inquirer2 from "inquirer";
332
+ var BUILTIN_CHOICES = [
333
+ { name: "TypeScript", value: "typescript" },
334
+ { name: "Python", value: "python" },
335
+ { name: "Go", value: "go" },
336
+ { name: "Rust", value: "rust" },
337
+ { name: "Java", value: "java" },
338
+ { name: "Kotlin", value: "kotlin" },
339
+ { name: "C# (.NET)", value: "csharp" },
340
+ { name: "Elixir", value: "elixir" }
341
+ ];
95
342
  async function promptLanguage() {
343
+ const pluginLanguages = pluginRegistry.getPluginLanguages();
344
+ const choices = [...BUILTIN_CHOICES];
345
+ if (pluginLanguages.length > 0) {
346
+ choices.push(new inquirer2.Separator("\u2500\u2500 Plugins \u2500\u2500"));
347
+ for (const lang of pluginLanguages) {
348
+ choices.push({ name: `${lang.name} (plugin)`, value: lang.id });
349
+ }
350
+ }
96
351
  const { language } = await inquirer2.prompt([
97
352
  {
98
353
  type: "list",
99
354
  name: "language",
100
355
  message: "Select language:",
101
- choices: [
102
- { name: "TypeScript", value: "typescript" },
103
- { name: "Python", value: "python" },
104
- { name: "Go", value: "go" },
105
- { name: "Rust", value: "rust" },
106
- { name: "Java", value: "java" },
107
- { name: "Kotlin", value: "kotlin" },
108
- { name: "C# (.NET)", value: "csharp" },
109
- { name: "Elixir", value: "elixir" }
110
- ],
356
+ choices,
111
357
  default: "typescript"
112
358
  }
113
359
  ]);
@@ -368,6 +614,65 @@ async function promptGenerationMethod() {
368
614
  return method;
369
615
  }
370
616
 
617
+ // src/types/config.ts
618
+ import { z as z2 } from "zod";
619
+ var BuiltinLanguageSchema = z2.enum([
620
+ "typescript",
621
+ "python",
622
+ "go",
623
+ "rust",
624
+ "java",
625
+ "kotlin",
626
+ "csharp",
627
+ "elixir"
628
+ ]);
629
+ var LanguageSchema = z2.string();
630
+ var BUILTIN_LANGUAGES = [
631
+ "typescript",
632
+ "python",
633
+ "go",
634
+ "rust",
635
+ "java",
636
+ "kotlin",
637
+ "csharp",
638
+ "elixir"
639
+ ];
640
+ function isBuiltinLanguage(lang) {
641
+ return BUILTIN_LANGUAGES.includes(lang);
642
+ }
643
+ var JavaBuildToolSchema = z2.enum(["maven", "gradle"]);
644
+ var TransportSchema = z2.enum(["stdio", "sse"]);
645
+ var ToolParameterSchema = z2.object({
646
+ name: z2.string(),
647
+ type: z2.enum(["string", "number", "boolean", "object", "array"]),
648
+ description: z2.string(),
649
+ required: z2.boolean().default(true)
650
+ });
651
+ var ToolConfigSchema = z2.object({
652
+ name: z2.string(),
653
+ description: z2.string(),
654
+ parameters: z2.array(ToolParameterSchema).default([])
655
+ });
656
+ var ResourceConfigSchema = z2.object({
657
+ name: z2.string(),
658
+ uri: z2.string(),
659
+ description: z2.string(),
660
+ mimeType: z2.string().optional()
661
+ });
662
+ var ProjectConfigSchema = z2.object({
663
+ name: z2.string().min(1, "Project name is required"),
664
+ description: z2.string().default(""),
665
+ language: LanguageSchema,
666
+ transport: TransportSchema,
667
+ tools: z2.array(ToolConfigSchema).default([]),
668
+ resources: z2.array(ResourceConfigSchema).default([]),
669
+ includeExampleTool: z2.boolean().default(true),
670
+ skipInstall: z2.boolean().default(false),
671
+ initGit: z2.boolean().default(true),
672
+ javaBuildTool: JavaBuildToolSchema.optional()
673
+ });
674
+ var PresetIdSchema = z2.enum(["database", "rest-api", "filesystem"]);
675
+
371
676
  // src/prompts/preset.ts
372
677
  import inquirer8 from "inquirer";
373
678
 
@@ -679,6 +984,230 @@ var FILESYSTEM_PRESET = {
679
984
  ]
680
985
  };
681
986
 
987
+ // src/types/marketplace.ts
988
+ import { z as z3 } from "zod";
989
+ var ExternalPresetManifestSchema = z3.object({
990
+ id: z3.string(),
991
+ name: z3.string(),
992
+ description: z3.string(),
993
+ version: z3.string(),
994
+ tools: z3.array(ToolConfigSchema),
995
+ author: z3.string().optional(),
996
+ repository: z3.string().optional()
997
+ });
998
+ var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
999
+ function parsePresetIdentifier(identifier) {
1000
+ if (identifier.startsWith("github:")) {
1001
+ return {
1002
+ type: "github",
1003
+ identifier: identifier.replace("github:", "")
1004
+ };
1005
+ }
1006
+ if (identifier.startsWith("@")) {
1007
+ return {
1008
+ type: "npm",
1009
+ identifier
1010
+ };
1011
+ }
1012
+ return {
1013
+ type: "local",
1014
+ identifier
1015
+ };
1016
+ }
1017
+ function isExternalPreset(identifier) {
1018
+ return identifier.startsWith("@") || identifier.startsWith("github:");
1019
+ }
1020
+
1021
+ // src/marketplace/preset-resolver.ts
1022
+ import path4 from "path";
1023
+ import os2 from "os";
1024
+ import { execa } from "execa";
1025
+ import * as tar from "tar";
1026
+
1027
+ // src/marketplace/cache.ts
1028
+ import path3 from "path";
1029
+ import os from "os";
1030
+ var CACHE_DIR = path3.join(os.homedir(), ".mcp-new", "preset-cache");
1031
+ function getCacheFilePath(source) {
1032
+ const safeId = source.identifier.replace(/[^a-zA-Z0-9]/g, "_");
1033
+ return path3.join(CACHE_DIR, `${source.type}_${safeId}.json`);
1034
+ }
1035
+ async function getCachedPreset(source) {
1036
+ const cacheFile = getCacheFilePath(source);
1037
+ if (!await exists(cacheFile)) {
1038
+ return null;
1039
+ }
1040
+ try {
1041
+ const content = await readFile(cacheFile);
1042
+ const cached = JSON.parse(content);
1043
+ const now = Date.now();
1044
+ if (now - cached.cachedAt > CACHE_TTL_MS) {
1045
+ return null;
1046
+ }
1047
+ return cached;
1048
+ } catch {
1049
+ return null;
1050
+ }
1051
+ }
1052
+ async function setCachedPreset(source, manifest) {
1053
+ await ensureDir(CACHE_DIR);
1054
+ const cached = {
1055
+ manifest,
1056
+ cachedAt: Date.now(),
1057
+ source
1058
+ };
1059
+ const cacheFile = getCacheFilePath(source);
1060
+ await writeFile(cacheFile, JSON.stringify(cached, null, 2));
1061
+ }
1062
+ async function clearPresetCache() {
1063
+ if (!await exists(CACHE_DIR)) {
1064
+ return 0;
1065
+ }
1066
+ try {
1067
+ const { readdir, stat, unlink } = await import("fs/promises");
1068
+ const files = await readdir(CACHE_DIR);
1069
+ let count = 0;
1070
+ for (const file of files) {
1071
+ if (file.endsWith(".json")) {
1072
+ await unlink(path3.join(CACHE_DIR, file));
1073
+ count++;
1074
+ }
1075
+ }
1076
+ return count;
1077
+ } catch {
1078
+ return 0;
1079
+ }
1080
+ }
1081
+ async function listCachedPresets() {
1082
+ if (!await exists(CACHE_DIR)) {
1083
+ return [];
1084
+ }
1085
+ try {
1086
+ const { readdir } = await import("fs/promises");
1087
+ const files = await readdir(CACHE_DIR);
1088
+ const presets = [];
1089
+ for (const file of files) {
1090
+ if (file.endsWith(".json")) {
1091
+ try {
1092
+ const content = await readFile(path3.join(CACHE_DIR, file));
1093
+ const cached = JSON.parse(content);
1094
+ presets.push(cached);
1095
+ } catch {
1096
+ }
1097
+ }
1098
+ }
1099
+ return presets;
1100
+ } catch {
1101
+ return [];
1102
+ }
1103
+ }
1104
+ function getCacheDir() {
1105
+ return CACHE_DIR;
1106
+ }
1107
+
1108
+ // src/marketplace/preset-resolver.ts
1109
+ var TEMP_DIR = path4.join(os2.tmpdir(), "mcp-new-presets");
1110
+ async function resolveExternalPreset(source) {
1111
+ const cached = await getCachedPreset(source);
1112
+ if (cached) {
1113
+ return cached.manifest;
1114
+ }
1115
+ let manifest;
1116
+ switch (source.type) {
1117
+ case "npm":
1118
+ manifest = await resolveNpmPreset(source.identifier);
1119
+ break;
1120
+ case "github":
1121
+ manifest = await resolveGitHubPreset(source.identifier);
1122
+ break;
1123
+ default:
1124
+ throw new Error(`Unknown preset source type: ${source.type}`);
1125
+ }
1126
+ await setCachedPreset(source, manifest);
1127
+ return manifest;
1128
+ }
1129
+ async function resolveNpmPreset(packageName) {
1130
+ await ensureDir(TEMP_DIR);
1131
+ const extractDir = path4.join(TEMP_DIR, `npm_${packageName.replace(/[^a-zA-Z0-9]/g, "_")}`);
1132
+ try {
1133
+ if (await exists(extractDir)) {
1134
+ await remove(extractDir);
1135
+ }
1136
+ await ensureDir(extractDir);
1137
+ const { stdout } = await execa("npm", ["pack", packageName, "--pack-destination", extractDir], {
1138
+ cwd: extractDir
1139
+ });
1140
+ const tarballName = stdout.trim();
1141
+ const tarballPath = path4.join(extractDir, tarballName);
1142
+ await tar.x({
1143
+ file: tarballPath,
1144
+ cwd: extractDir
1145
+ });
1146
+ const manifestPath = path4.join(extractDir, "package", "mcp-preset.json");
1147
+ if (!await exists(manifestPath)) {
1148
+ throw new Error(
1149
+ `Invalid preset package: ${packageName} - missing mcp-preset.json manifest file`
1150
+ );
1151
+ }
1152
+ const manifestContent = await readFile(manifestPath);
1153
+ const rawManifest = JSON.parse(manifestContent);
1154
+ const manifest = ExternalPresetManifestSchema.parse(rawManifest);
1155
+ return manifest;
1156
+ } finally {
1157
+ try {
1158
+ if (await exists(extractDir)) {
1159
+ await remove(extractDir);
1160
+ }
1161
+ } catch {
1162
+ }
1163
+ }
1164
+ }
1165
+ async function resolveGitHubPreset(repoPath) {
1166
+ const [repo, branch = "main"] = repoPath.split("@");
1167
+ const [owner, repoName] = repo.split("/");
1168
+ if (!owner || !repoName) {
1169
+ throw new Error(`Invalid GitHub preset format: ${repoPath}. Expected: user/repo[@branch]`);
1170
+ }
1171
+ await ensureDir(TEMP_DIR);
1172
+ const extractDir = path4.join(TEMP_DIR, `github_${owner}_${repoName}`);
1173
+ try {
1174
+ if (await exists(extractDir)) {
1175
+ await remove(extractDir);
1176
+ }
1177
+ await ensureDir(extractDir);
1178
+ const tarballUrl = `https://github.com/${owner}/${repoName}/archive/refs/heads/${branch}.tar.gz`;
1179
+ const tarballPath = path4.join(extractDir, "repo.tar.gz");
1180
+ await execa("curl", ["-L", "-o", tarballPath, tarballUrl]);
1181
+ await tar.x({
1182
+ file: tarballPath,
1183
+ cwd: extractDir
1184
+ });
1185
+ const { readdir } = await import("fs/promises");
1186
+ const entries = await readdir(extractDir);
1187
+ const repoDir = entries.find((e) => e.startsWith(`${repoName}-`));
1188
+ if (!repoDir) {
1189
+ throw new Error(`Failed to extract GitHub repository: ${repoPath}`);
1190
+ }
1191
+ const manifestPath = path4.join(extractDir, repoDir, "mcp-preset.json");
1192
+ if (!await exists(manifestPath)) {
1193
+ throw new Error(
1194
+ `Invalid preset repository: ${repoPath} - missing mcp-preset.json manifest file`
1195
+ );
1196
+ }
1197
+ const manifestContent = await readFile(manifestPath);
1198
+ const rawManifest = JSON.parse(manifestContent);
1199
+ const manifest = ExternalPresetManifestSchema.parse(rawManifest);
1200
+ return manifest;
1201
+ } finally {
1202
+ try {
1203
+ if (await exists(extractDir)) {
1204
+ await remove(extractDir);
1205
+ }
1206
+ } catch {
1207
+ }
1208
+ }
1209
+ }
1210
+
682
1211
  // src/presets/index.ts
683
1212
  var PRESETS = {
684
1213
  database: DATABASE_PRESET,
@@ -692,6 +1221,28 @@ function getPreset(id) {
692
1221
  function isValidPresetId(id) {
693
1222
  return id in PRESETS;
694
1223
  }
1224
+ async function getPresetByIdentifier(identifier) {
1225
+ if (!isExternalPreset(identifier)) {
1226
+ const preset = getPreset(identifier);
1227
+ if (!preset) {
1228
+ throw new Error(
1229
+ `Unknown preset: ${identifier}. Valid local presets: ${PRESET_IDS.join(", ")}`
1230
+ );
1231
+ }
1232
+ return preset;
1233
+ }
1234
+ const source = parsePresetIdentifier(identifier);
1235
+ const manifest = await resolveExternalPreset(source);
1236
+ return externalManifestToPreset(manifest);
1237
+ }
1238
+ function externalManifestToPreset(manifest) {
1239
+ return {
1240
+ id: manifest.id,
1241
+ name: manifest.name,
1242
+ description: manifest.description,
1243
+ tools: manifest.tools
1244
+ };
1245
+ }
695
1246
 
696
1247
  // src/prompts/preset.ts
697
1248
  async function promptPreset() {
@@ -711,6 +1262,24 @@ async function promptPreset() {
711
1262
  return preset;
712
1263
  }
713
1264
 
1265
+ // src/prompts/ci-provider.ts
1266
+ import inquirer9 from "inquirer";
1267
+ async function promptCIProvider() {
1268
+ const { provider } = await inquirer9.prompt([
1269
+ {
1270
+ type: "list",
1271
+ name: "provider",
1272
+ message: "Which CI/CD provider would you like to use?",
1273
+ choices: [
1274
+ { name: "GitHub Actions", value: "github" },
1275
+ { name: "GitLab CI", value: "gitlab" },
1276
+ { name: "CircleCI", value: "circleci" }
1277
+ ]
1278
+ }
1279
+ ]);
1280
+ return provider;
1281
+ }
1282
+
714
1283
  // src/prompts/index.ts
715
1284
  async function runWizard(options = {}) {
716
1285
  const name = await promptProjectName(options.defaultName);
@@ -773,98 +1342,28 @@ async function runQuickWizard(defaultName, presetLanguage, presetJavaBuildTool)
773
1342
  };
774
1343
  }
775
1344
 
776
- // src/utils/file-system.ts
777
- import fs from "fs-extra";
778
- import path from "path";
779
- import ejs from "ejs";
780
- async function ensureDir(dirPath) {
781
- await fs.ensureDir(dirPath);
782
- }
783
- async function writeFile(filePath, content) {
784
- await fs.ensureDir(path.dirname(filePath));
785
- await fs.writeFile(filePath, content, "utf-8");
786
- }
787
- async function readFile(filePath) {
788
- return fs.readFile(filePath, "utf-8");
789
- }
790
- async function copyFile(src, dest) {
791
- await fs.ensureDir(path.dirname(dest));
792
- await fs.copyFile(src, dest);
793
- }
794
- async function copyDir(src, dest) {
795
- await fs.copy(src, dest);
796
- }
797
- async function exists(filePath) {
798
- return fs.pathExists(filePath);
799
- }
800
- async function remove(filePath) {
801
- await fs.remove(filePath);
802
- }
803
- async function readDir(dirPath) {
804
- return fs.readdir(dirPath);
805
- }
806
- async function isDirectory(filePath) {
807
- try {
808
- const stat = await fs.stat(filePath);
809
- return stat.isDirectory();
810
- } catch {
811
- return false;
812
- }
813
- }
814
- async function renderTemplate(templatePath, data) {
815
- const template = await readFile(templatePath);
816
- return ejs.render(template, data, { async: false });
817
- }
818
- async function renderTemplateToFile(templatePath, outputPath, data) {
819
- const content = await renderTemplate(templatePath, data);
820
- const finalPath = outputPath.replace(/\.ejs$/, "");
821
- await writeFile(finalPath, content);
822
- }
823
- async function walkDir(dirPath, callback) {
824
- const entries = await fs.readdir(dirPath, { withFileTypes: true });
825
- for (const entry of entries) {
826
- const fullPath = path.join(dirPath, entry.name);
827
- const isDir = entry.isDirectory();
828
- await callback(fullPath, isDir);
829
- if (isDir) {
830
- await walkDir(fullPath, callback);
831
- }
832
- }
833
- }
834
- function getTemplateDir() {
835
- const devPath = path.join(import.meta.dirname, "..", "templates");
836
- const prodPath = path.join(import.meta.dirname, "..", "..", "templates");
837
- if (fs.existsSync(devPath)) {
838
- return devPath;
839
- }
840
- return prodPath;
841
- }
842
- function resolveOutputPath(basePath, ...segments) {
843
- return path.resolve(basePath, ...segments);
844
- }
845
-
846
1345
  // src/utils/git.ts
847
- import { execa } from "execa";
848
- import path2 from "path";
1346
+ import { execa as execa2 } from "execa";
1347
+ import path5 from "path";
849
1348
  async function isGitInstalled() {
850
1349
  try {
851
- await execa("git", ["--version"]);
1350
+ await execa2("git", ["--version"]);
852
1351
  return true;
853
1352
  } catch {
854
1353
  return false;
855
1354
  }
856
1355
  }
857
1356
  async function initGitRepository(projectPath) {
858
- const gitDir = path2.join(projectPath, ".git");
1357
+ const gitDir = path5.join(projectPath, ".git");
859
1358
  if (await exists(gitDir)) {
860
1359
  return;
861
1360
  }
862
- await execa("git", ["init"], { cwd: projectPath });
1361
+ await execa2("git", ["init"], { cwd: projectPath });
863
1362
  }
864
1363
  async function createInitialCommit(projectPath) {
865
1364
  try {
866
- await execa("git", ["add", "."], { cwd: projectPath });
867
- await execa("git", ["commit", "-m", "Initial commit from create-mcp-server"], {
1365
+ await execa2("git", ["add", "."], { cwd: projectPath });
1366
+ await execa2("git", ["commit", "-m", "Initial commit from create-mcp-server"], {
868
1367
  cwd: projectPath
869
1368
  });
870
1369
  } catch {
@@ -873,8 +1372,8 @@ async function createInitialCommit(projectPath) {
873
1372
  async function getGitUser() {
874
1373
  try {
875
1374
  const [nameResult, emailResult] = await Promise.all([
876
- execa("git", ["config", "--get", "user.name"]).catch(() => ({ stdout: "" })),
877
- execa("git", ["config", "--get", "user.email"]).catch(() => ({ stdout: "" }))
1375
+ execa2("git", ["config", "--get", "user.name"]).catch(() => ({ stdout: "" })),
1376
+ execa2("git", ["config", "--get", "user.email"]).catch(() => ({ stdout: "" }))
878
1377
  ]);
879
1378
  return {
880
1379
  name: nameResult.stdout.trim() || void 0,
@@ -886,7 +1385,7 @@ async function getGitUser() {
886
1385
  }
887
1386
  async function isInsideGitRepository(dirPath) {
888
1387
  try {
889
- await execa("git", ["rev-parse", "--is-inside-work-tree"], { cwd: dirPath });
1388
+ await execa2("git", ["rev-parse", "--is-inside-work-tree"], { cwd: dirPath });
890
1389
  return true;
891
1390
  } catch {
892
1391
  return false;
@@ -939,44 +1438,51 @@ var logger = {
939
1438
  logger.blank();
940
1439
  let installCmd;
941
1440
  let runCmd;
942
- switch (language) {
943
- case "typescript":
944
- installCmd = "npm install";
945
- runCmd = "npm run dev";
946
- break;
947
- case "python":
948
- installCmd = "pip install -e .";
949
- runCmd = "python -m src.server";
950
- break;
951
- case "go":
952
- installCmd = "go mod download";
953
- runCmd = "go run ./cmd/server";
954
- break;
955
- case "rust":
956
- installCmd = "cargo build";
957
- runCmd = "cargo run";
958
- break;
959
- case "java":
960
- case "kotlin":
961
- if (javaBuildTool === "gradle") {
962
- installCmd = "./gradlew build";
963
- runCmd = "./gradlew run";
964
- } else {
965
- installCmd = "mvn install";
966
- runCmd = 'mvn exec:java -Dexec.mainClass="com.example.mcp.McpServer"';
967
- }
968
- break;
969
- case "csharp":
970
- installCmd = "dotnet restore";
971
- runCmd = "dotnet run";
972
- break;
973
- case "elixir":
974
- installCmd = "mix deps.get";
975
- runCmd = "mix run --no-halt";
976
- break;
977
- default:
978
- installCmd = "npm install";
979
- runCmd = "npm run dev";
1441
+ if (!isBuiltinLanguage(language)) {
1442
+ const pluginInstallCmd = pluginRegistry.getPluginInstallCommand(language);
1443
+ const pluginRunCmd = pluginRegistry.getPluginRunCommand(language);
1444
+ installCmd = pluginInstallCmd || "# See plugin documentation for install command";
1445
+ runCmd = pluginRunCmd || "# See plugin documentation for run command";
1446
+ } else {
1447
+ switch (language) {
1448
+ case "typescript":
1449
+ installCmd = "npm install";
1450
+ runCmd = "npm run dev";
1451
+ break;
1452
+ case "python":
1453
+ installCmd = "pip install -e .";
1454
+ runCmd = "python -m src.server";
1455
+ break;
1456
+ case "go":
1457
+ installCmd = "go mod download";
1458
+ runCmd = "go run ./cmd/server";
1459
+ break;
1460
+ case "rust":
1461
+ installCmd = "cargo build";
1462
+ runCmd = "cargo run";
1463
+ break;
1464
+ case "java":
1465
+ case "kotlin":
1466
+ if (javaBuildTool === "gradle") {
1467
+ installCmd = "./gradlew build";
1468
+ runCmd = "./gradlew run";
1469
+ } else {
1470
+ installCmd = "mvn install";
1471
+ runCmd = 'mvn exec:java -Dexec.mainClass="com.example.mcp.McpServer"';
1472
+ }
1473
+ break;
1474
+ case "csharp":
1475
+ installCmd = "dotnet restore";
1476
+ runCmd = "dotnet run";
1477
+ break;
1478
+ case "elixir":
1479
+ installCmd = "mix deps.get";
1480
+ runCmd = "mix run --no-halt";
1481
+ break;
1482
+ default:
1483
+ installCmd = "npm install";
1484
+ runCmd = "npm run dev";
1485
+ }
980
1486
  }
981
1487
  logger.box("Next steps:", [
982
1488
  "",
@@ -1045,8 +1551,8 @@ async function withSpinner(text, fn, successText, failText) {
1045
1551
  }
1046
1552
 
1047
1553
  // src/generators/base.ts
1048
- import path3 from "path";
1049
- import { execa as execa2 } from "execa";
1554
+ import path6 from "path";
1555
+ import { execa as execa3 } from "execa";
1050
1556
  var BaseGenerator = class {
1051
1557
  config;
1052
1558
  outputDir;
@@ -1058,14 +1564,14 @@ var BaseGenerator = class {
1058
1564
  }
1059
1565
  async createProjectStructure() {
1060
1566
  await ensureDir(this.outputDir);
1061
- await ensureDir(path3.join(this.outputDir, "src"));
1567
+ await ensureDir(path6.join(this.outputDir, "src"));
1062
1568
  }
1063
1569
  async renderTemplates() {
1064
1570
  const templateData = this.getTemplateData();
1065
1571
  await walkDir(this.templateDir, async (filePath, isDir) => {
1066
1572
  if (isDir) return;
1067
- const relativePath = path3.relative(this.templateDir, filePath);
1068
- const outputPath = path3.join(this.outputDir, relativePath);
1573
+ const relativePath = path6.relative(this.templateDir, filePath);
1574
+ const outputPath = path6.join(this.outputDir, relativePath);
1069
1575
  if (filePath.endsWith(".ejs")) {
1070
1576
  await renderTemplateToFile(filePath, outputPath, templateData);
1071
1577
  } else {
@@ -1096,6 +1602,10 @@ var BaseGenerator = class {
1096
1602
  logger.info("Skipping dependency installation (--skip-install)");
1097
1603
  return;
1098
1604
  }
1605
+ if (!isBuiltinLanguage(this.config.language)) {
1606
+ await this.installPluginDependencies();
1607
+ return;
1608
+ }
1099
1609
  switch (this.config.language) {
1100
1610
  case "typescript":
1101
1611
  await this.installNodeDependencies();
@@ -1121,11 +1631,27 @@ var BaseGenerator = class {
1121
1631
  break;
1122
1632
  }
1123
1633
  }
1634
+ async installPluginDependencies() {
1635
+ const installCommand = pluginRegistry.getPluginInstallCommand(this.config.language);
1636
+ if (!installCommand) {
1637
+ logger.info(`No install command defined for plugin language: ${this.config.language}`);
1638
+ return;
1639
+ }
1640
+ await withSpinner(
1641
+ "Installing dependencies...",
1642
+ async () => {
1643
+ const [cmd, ...args] = installCommand.split(" ");
1644
+ await execa3(cmd, args, { cwd: this.outputDir });
1645
+ },
1646
+ "Dependencies installed",
1647
+ "Failed to install dependencies"
1648
+ );
1649
+ }
1124
1650
  async installNodeDependencies() {
1125
1651
  await withSpinner(
1126
1652
  "Installing dependencies...",
1127
1653
  async () => {
1128
- await execa2("npm", ["install"], { cwd: this.outputDir });
1654
+ await execa3("npm", ["install"], { cwd: this.outputDir });
1129
1655
  },
1130
1656
  "Dependencies installed",
1131
1657
  "Failed to install dependencies"
@@ -1143,7 +1669,7 @@ var BaseGenerator = class {
1143
1669
  await withSpinner(
1144
1670
  "Installing dependencies...",
1145
1671
  async () => {
1146
- await execa2(pipCommand, ["install", "-r", "requirements.txt"], {
1672
+ await execa3(pipCommand, ["install", "-r", "requirements.txt"], {
1147
1673
  cwd: this.outputDir
1148
1674
  });
1149
1675
  },
@@ -1161,10 +1687,10 @@ var BaseGenerator = class {
1161
1687
  await withSpinner(
1162
1688
  "Installing Go dependencies...",
1163
1689
  async () => {
1164
- await execa2("go", ["mod", "download"], {
1690
+ await execa3("go", ["mod", "download"], {
1165
1691
  cwd: this.outputDir
1166
1692
  });
1167
- await execa2("go", ["mod", "tidy"], {
1693
+ await execa3("go", ["mod", "tidy"], {
1168
1694
  cwd: this.outputDir
1169
1695
  });
1170
1696
  },
@@ -1182,7 +1708,7 @@ var BaseGenerator = class {
1182
1708
  await withSpinner(
1183
1709
  "Building Rust project...",
1184
1710
  async () => {
1185
- await execa2("cargo", ["build"], {
1711
+ await execa3("cargo", ["build"], {
1186
1712
  cwd: this.outputDir
1187
1713
  });
1188
1714
  },
@@ -1202,7 +1728,7 @@ var BaseGenerator = class {
1202
1728
  await withSpinner(
1203
1729
  "Installing Maven dependencies...",
1204
1730
  async () => {
1205
- await execa2("mvn", ["install", "-DskipTests"], {
1731
+ await execa3("mvn", ["install", "-DskipTests"], {
1206
1732
  cwd: this.outputDir
1207
1733
  });
1208
1734
  },
@@ -1211,7 +1737,7 @@ var BaseGenerator = class {
1211
1737
  );
1212
1738
  } else {
1213
1739
  const hasGradle = await this.checkCommand("gradle");
1214
- const hasGradlew = await exists(path3.join(this.outputDir, "gradlew"));
1740
+ const hasGradlew = await exists(path6.join(this.outputDir, "gradlew"));
1215
1741
  if (!hasGradle && !hasGradlew) {
1216
1742
  logger.warning("Gradle not found. Please install dependencies manually:");
1217
1743
  logger.code("gradle build");
@@ -1221,7 +1747,7 @@ var BaseGenerator = class {
1221
1747
  await withSpinner(
1222
1748
  "Installing Gradle dependencies...",
1223
1749
  async () => {
1224
- await execa2(gradleCmd, ["build", "-x", "test"], {
1750
+ await execa3(gradleCmd, ["build", "-x", "test"], {
1225
1751
  cwd: this.outputDir
1226
1752
  });
1227
1753
  },
@@ -1240,10 +1766,10 @@ var BaseGenerator = class {
1240
1766
  await withSpinner(
1241
1767
  "Restoring .NET dependencies...",
1242
1768
  async () => {
1243
- await execa2("dotnet", ["restore"], {
1769
+ await execa3("dotnet", ["restore"], {
1244
1770
  cwd: this.outputDir
1245
1771
  });
1246
- await execa2("dotnet", ["build"], {
1772
+ await execa3("dotnet", ["build"], {
1247
1773
  cwd: this.outputDir
1248
1774
  });
1249
1775
  },
@@ -1261,10 +1787,10 @@ var BaseGenerator = class {
1261
1787
  await withSpinner(
1262
1788
  "Installing Elixir dependencies...",
1263
1789
  async () => {
1264
- await execa2("mix", ["deps.get"], {
1790
+ await execa3("mix", ["deps.get"], {
1265
1791
  cwd: this.outputDir
1266
1792
  });
1267
- await execa2("mix", ["compile"], {
1793
+ await execa3("mix", ["compile"], {
1268
1794
  cwd: this.outputDir
1269
1795
  });
1270
1796
  },
@@ -1275,7 +1801,7 @@ var BaseGenerator = class {
1275
1801
  async checkCommand(command) {
1276
1802
  try {
1277
1803
  const checkCmd = process.platform === "win32" ? "where" : "which";
1278
- await execa2(checkCmd, [command]);
1804
+ await execa3(checkCmd, [command]);
1279
1805
  return true;
1280
1806
  } catch {
1281
1807
  return false;
@@ -1311,12 +1837,15 @@ var BaseGenerator = class {
1311
1837
  }
1312
1838
  };
1313
1839
  function createGeneratorContext(config, outputPath) {
1314
- const outputDir = outputPath || path3.resolve(process.cwd(), config.name);
1840
+ const outputDir = outputPath || path6.resolve(process.cwd(), config.name);
1315
1841
  let templateDir;
1316
- if ((config.language === "java" || config.language === "kotlin") && config.javaBuildTool) {
1317
- templateDir = path3.join(getTemplateDir(), config.language, config.javaBuildTool);
1842
+ const pluginTemplateDir = pluginRegistry.getPluginTemplateDir(config.language);
1843
+ if (pluginTemplateDir) {
1844
+ templateDir = pluginTemplateDir;
1845
+ } else if ((config.language === "java" || config.language === "kotlin") && config.javaBuildTool) {
1846
+ templateDir = path6.join(getTemplateDir(), config.language, config.javaBuildTool);
1318
1847
  } else {
1319
- templateDir = path3.join(getTemplateDir(), config.language);
1848
+ templateDir = path6.join(getTemplateDir(), config.language);
1320
1849
  }
1321
1850
  return {
1322
1851
  config,
@@ -1366,7 +1895,7 @@ async function generateFromWizard(config, outputPath) {
1366
1895
 
1367
1896
  // src/parsers/openapi.ts
1368
1897
  import YAML from "yaml";
1369
- import inquirer9 from "inquirer";
1898
+ import inquirer10 from "inquirer";
1370
1899
  async function parseOpenAPISpec(content) {
1371
1900
  let spec;
1372
1901
  try {
@@ -1378,18 +1907,18 @@ async function parseOpenAPISpec(content) {
1378
1907
  throw new Error('Invalid OpenAPI specification. Missing "openapi" or "swagger" field.');
1379
1908
  }
1380
1909
  const endpoints = [];
1381
- for (const [path7, pathItem] of Object.entries(spec.paths)) {
1910
+ for (const [path11, pathItem] of Object.entries(spec.paths)) {
1382
1911
  const methods = ["get", "post", "put", "delete", "patch", "options", "head"];
1383
1912
  for (const method of methods) {
1384
1913
  const operation = pathItem[method];
1385
1914
  if (!operation) continue;
1386
- const endpoint = parseOperation(path7, method, operation, spec);
1915
+ const endpoint = parseOperation(path11, method, operation, spec);
1387
1916
  endpoints.push(endpoint);
1388
1917
  }
1389
1918
  }
1390
1919
  return endpoints;
1391
1920
  }
1392
- function parseOperation(path7, method, operation, spec) {
1921
+ function parseOperation(path11, method, operation, spec) {
1393
1922
  const parameters = [];
1394
1923
  if (operation.parameters) {
1395
1924
  for (const param of operation.parameters) {
@@ -1413,7 +1942,7 @@ function parseOperation(path7, method, operation, spec) {
1413
1942
  }
1414
1943
  }
1415
1944
  return {
1416
- path: path7,
1945
+ path: path11,
1417
1946
  method: method.toUpperCase(),
1418
1947
  operationId: operation.operationId || "",
1419
1948
  summary: operation.summary || "",
@@ -1466,7 +1995,7 @@ async function selectEndpoints(endpoints) {
1466
1995
  value: ep,
1467
1996
  checked: true
1468
1997
  }));
1469
- const { selected } = await inquirer9.prompt([
1998
+ const { selected } = await inquirer10.prompt([
1470
1999
  {
1471
2000
  type: "checkbox",
1472
2001
  name: "selected",
@@ -1601,7 +2130,7 @@ function mapOpenAPIType(type) {
1601
2130
 
1602
2131
  // src/generators/from-prompt.ts
1603
2132
  import Anthropic from "@anthropic-ai/sdk";
1604
- import inquirer10 from "inquirer";
2133
+ import inquirer11 from "inquirer";
1605
2134
  var SYSTEM_PROMPT = `You are an expert at designing MCP (Model Context Protocol) servers.
1606
2135
  Given a description of an API or functionality, you generate a list of tools that would be useful for that API.
1607
2136
 
@@ -1657,7 +2186,7 @@ var PromptGenerator = class extends BaseGenerator {
1657
2186
  }
1658
2187
  };
1659
2188
  async function generateFromPrompt(baseConfig) {
1660
- const { description } = await inquirer10.prompt([
2189
+ const { description } = await inquirer11.prompt([
1661
2190
  {
1662
2191
  type: "editor",
1663
2192
  name: "description",
@@ -1713,7 +2242,7 @@ async function generateFromPrompt(baseConfig) {
1713
2242
  logger.list([`${index + 1}. ${tool.name} - ${tool.description}`]);
1714
2243
  });
1715
2244
  logger.blank();
1716
- const { confirm } = await inquirer10.prompt([
2245
+ const { confirm } = await inquirer11.prompt([
1717
2246
  {
1718
2247
  type: "confirm",
1719
2248
  name: "confirm",
@@ -1779,9 +2308,20 @@ var PresetGenerator = class extends BaseGenerator {
1779
2308
  }
1780
2309
  };
1781
2310
  async function generateFromPreset(options) {
1782
- const preset = getPreset(options.presetId);
1783
- if (!preset) {
1784
- throw new Error(`Invalid preset: ${options.presetId}`);
2311
+ let preset;
2312
+ if (isExternalPreset(options.presetId)) {
2313
+ logger.info(`Fetching external preset: ${options.presetId}...`);
2314
+ preset = await withSpinner(
2315
+ "Resolving external preset...",
2316
+ async () => await getPresetByIdentifier(options.presetId),
2317
+ "Preset resolved",
2318
+ "Failed to resolve preset"
2319
+ );
2320
+ } else {
2321
+ preset = getPreset(options.presetId);
2322
+ if (!preset) {
2323
+ throw new Error(`Invalid preset: ${options.presetId}`);
2324
+ }
1785
2325
  }
1786
2326
  const name = options.projectName || await promptProjectName();
1787
2327
  const description = options.useDefaults ? "" : await promptProjectDescription();
@@ -1806,6 +2346,7 @@ async function generateFromPreset(options) {
1806
2346
  const context = createGeneratorContext(config);
1807
2347
  const generator = new PresetGenerator(context, preset.name);
1808
2348
  await generator.generate();
2349
+ return config;
1809
2350
  }
1810
2351
  function validatePresetId(presetId) {
1811
2352
  if (!isValidPresetId(presetId)) {
@@ -1816,7 +2357,188 @@ function validatePresetId(presetId) {
1816
2357
  }
1817
2358
 
1818
2359
  // src/commands/create.ts
1819
- import path4 from "path";
2360
+ import path8 from "path";
2361
+
2362
+ // src/types/ci.ts
2363
+ import { z as z4 } from "zod";
2364
+ var CIProviderSchema = z4.enum(["github", "gitlab", "circleci"]);
2365
+ var CI_PROVIDERS = ["github", "gitlab", "circleci"];
2366
+ function isValidCIProvider(provider) {
2367
+ return CI_PROVIDERS.includes(provider);
2368
+ }
2369
+
2370
+ // src/generators/ci.ts
2371
+ import path7 from "path";
2372
+
2373
+ // src/ci/language-config.ts
2374
+ var LANGUAGE_CI_CONFIG = {
2375
+ typescript: {
2376
+ installCommand: "npm ci",
2377
+ buildCommand: "npm run build",
2378
+ testCommand: "npm test",
2379
+ nodeVersion: "20"
2380
+ },
2381
+ python: {
2382
+ installCommand: "pip install -r requirements.txt",
2383
+ testCommand: "pytest",
2384
+ pythonVersion: "3.11"
2385
+ },
2386
+ go: {
2387
+ installCommand: "go mod download",
2388
+ buildCommand: "go build ./...",
2389
+ testCommand: "go test ./...",
2390
+ goVersion: "1.21"
2391
+ },
2392
+ rust: {
2393
+ installCommand: "cargo fetch",
2394
+ buildCommand: "cargo build --release",
2395
+ testCommand: "cargo test",
2396
+ rustVersion: "stable"
2397
+ },
2398
+ java: {
2399
+ installCommand: "mvn install -DskipTests",
2400
+ buildCommand: "mvn package -DskipTests",
2401
+ testCommand: "mvn test",
2402
+ javaVersion: "21"
2403
+ },
2404
+ kotlin: {
2405
+ installCommand: "mvn install -DskipTests",
2406
+ buildCommand: "mvn package -DskipTests",
2407
+ testCommand: "mvn test",
2408
+ javaVersion: "21"
2409
+ },
2410
+ csharp: {
2411
+ installCommand: "dotnet restore",
2412
+ buildCommand: "dotnet build --configuration Release",
2413
+ testCommand: "dotnet test",
2414
+ dotnetVersion: "8.0"
2415
+ },
2416
+ elixir: {
2417
+ installCommand: "mix deps.get",
2418
+ buildCommand: "mix compile",
2419
+ testCommand: "mix test",
2420
+ elixirVersion: "1.15"
2421
+ }
2422
+ };
2423
+ function getJavaCIConfig(buildTool) {
2424
+ if (buildTool === "gradle") {
2425
+ return {
2426
+ installCommand: "./gradlew dependencies",
2427
+ buildCommand: "./gradlew build -x test",
2428
+ testCommand: "./gradlew test",
2429
+ javaVersion: "21"
2430
+ };
2431
+ }
2432
+ return LANGUAGE_CI_CONFIG.java;
2433
+ }
2434
+ function getCITemplateContext(projectName, language, javaBuildTool) {
2435
+ const config = (language === "java" || language === "kotlin") && javaBuildTool === "gradle" ? getJavaCIConfig("gradle") : LANGUAGE_CI_CONFIG[language];
2436
+ return {
2437
+ projectName,
2438
+ language,
2439
+ ...config
2440
+ };
2441
+ }
2442
+
2443
+ // src/generators/ci.ts
2444
+ var CIGenerator = class {
2445
+ options;
2446
+ templateDir;
2447
+ context;
2448
+ constructor(options) {
2449
+ this.options = options;
2450
+ this.templateDir = path7.join(getTemplateDir(), "ci", options.provider);
2451
+ this.context = getCITemplateContext(
2452
+ options.projectName || path7.basename(options.projectDir),
2453
+ options.language,
2454
+ options.javaBuildTool
2455
+ );
2456
+ }
2457
+ async generate() {
2458
+ await withSpinner(
2459
+ `Generating ${this.getProviderName()} CI configuration...`,
2460
+ async () => {
2461
+ switch (this.options.provider) {
2462
+ case "github":
2463
+ await this.generateGitHubActions();
2464
+ break;
2465
+ case "gitlab":
2466
+ await this.generateGitLabCI();
2467
+ break;
2468
+ case "circleci":
2469
+ await this.generateCircleCI();
2470
+ break;
2471
+ }
2472
+ },
2473
+ `${this.getProviderName()} CI configuration generated`,
2474
+ `Failed to generate ${this.getProviderName()} CI configuration`
2475
+ );
2476
+ }
2477
+ async generateGitHubActions() {
2478
+ const workflowDir = path7.join(this.options.projectDir, ".github", "workflows");
2479
+ await ensureDir(workflowDir);
2480
+ const templatePath = path7.join(this.templateDir, "ci.yml.ejs");
2481
+ const outputPath = path7.join(workflowDir, "ci.yml");
2482
+ await renderTemplateToFile(templatePath, outputPath, this.context);
2483
+ }
2484
+ async generateGitLabCI() {
2485
+ const templatePath = path7.join(this.templateDir, ".gitlab-ci.yml.ejs");
2486
+ const outputPath = path7.join(this.options.projectDir, ".gitlab-ci.yml");
2487
+ await renderTemplateToFile(templatePath, outputPath, this.context);
2488
+ }
2489
+ async generateCircleCI() {
2490
+ const circleDir = path7.join(this.options.projectDir, ".circleci");
2491
+ await ensureDir(circleDir);
2492
+ const templatePath = path7.join(this.templateDir, "config.yml.ejs");
2493
+ const outputPath = path7.join(circleDir, "config.yml");
2494
+ await renderTemplateToFile(templatePath, outputPath, this.context);
2495
+ }
2496
+ getProviderName() {
2497
+ switch (this.options.provider) {
2498
+ case "github":
2499
+ return "GitHub Actions";
2500
+ case "gitlab":
2501
+ return "GitLab CI";
2502
+ case "circleci":
2503
+ return "CircleCI";
2504
+ }
2505
+ }
2506
+ };
2507
+ async function generateCI(options) {
2508
+ const generator = new CIGenerator(options);
2509
+ await generator.generate();
2510
+ }
2511
+ async function detectLanguageFromProject(projectDir) {
2512
+ const checks = [
2513
+ { file: "package.json", language: "typescript" },
2514
+ { file: "pyproject.toml", language: "python" },
2515
+ { file: "requirements.txt", language: "python" },
2516
+ { file: "go.mod", language: "go" },
2517
+ { file: "Cargo.toml", language: "rust" },
2518
+ { file: "pom.xml", language: "java" },
2519
+ { file: "build.gradle", language: "java" },
2520
+ { file: "build.gradle.kts", language: "kotlin" },
2521
+ { file: "*.csproj", language: "csharp" },
2522
+ { file: "mix.exs", language: "elixir" }
2523
+ ];
2524
+ for (const check of checks) {
2525
+ if (await exists(path7.join(projectDir, check.file))) {
2526
+ return check.language;
2527
+ }
2528
+ }
2529
+ return void 0;
2530
+ }
2531
+ async function detectJavaBuildTool(projectDir) {
2532
+ if (await exists(path7.join(projectDir, "build.gradle")) || await exists(path7.join(projectDir, "build.gradle.kts"))) {
2533
+ return "gradle";
2534
+ }
2535
+ if (await exists(path7.join(projectDir, "pom.xml"))) {
2536
+ return "maven";
2537
+ }
2538
+ return void 0;
2539
+ }
2540
+
2541
+ // src/commands/create.ts
1820
2542
  function getLanguageFromOptions(options) {
1821
2543
  if (options.typescript) return "typescript";
1822
2544
  if (options.python) return "python";
@@ -1869,7 +2591,7 @@ async function handleWizardGeneration(projectName, options) {
1869
2591
  if (options.skipInstall) {
1870
2592
  config.skipInstall = true;
1871
2593
  }
1872
- const outputPath = path4.resolve(process.cwd(), config.name);
2594
+ const outputPath = path8.resolve(process.cwd(), config.name);
1873
2595
  if (await exists(outputPath)) {
1874
2596
  const files = await import("fs-extra").then((m) => m.readdir(outputPath));
1875
2597
  if (files.length > 0) {
@@ -1878,6 +2600,9 @@ async function handleWizardGeneration(projectName, options) {
1878
2600
  }
1879
2601
  }
1880
2602
  await generateFromWizard(config);
2603
+ if (options.ci) {
2604
+ await handleCIGeneration(config.name, config.language, config.javaBuildTool, options.ci);
2605
+ }
1881
2606
  }
1882
2607
  async function handleOpenAPIGeneration(projectName, options) {
1883
2608
  const specPath = options.fromOpenapi;
@@ -1885,7 +2610,7 @@ async function handleOpenAPIGeneration(projectName, options) {
1885
2610
  logger.error(`OpenAPI specification file not found: ${specPath}`);
1886
2611
  process.exit(1);
1887
2612
  }
1888
- const name = projectName || path4.basename(specPath, path4.extname(specPath)) + "-mcp";
2613
+ const name = projectName || path8.basename(specPath, path8.extname(specPath)) + "-mcp";
1889
2614
  await generateFromOpenAPI(specPath, {
1890
2615
  name,
1891
2616
  language: getLanguageFromOptions(options),
@@ -1903,8 +2628,14 @@ async function handlePromptGeneration(projectName, options) {
1903
2628
  }
1904
2629
  async function handlePresetGeneration(projectName, options) {
1905
2630
  const presetId = options.preset;
1906
- validatePresetId(presetId);
1907
- await generateFromPreset({
2631
+ if (!isExternalPreset(presetId) && !isValidPresetId(presetId)) {
2632
+ logger.error(
2633
+ `Invalid preset "${presetId}". Valid local presets: ${PRESET_IDS.join(", ")}
2634
+ For external presets use: @org/package or github:user/repo`
2635
+ );
2636
+ process.exit(1);
2637
+ }
2638
+ const result = await generateFromPreset({
1908
2639
  projectName,
1909
2640
  presetId,
1910
2641
  language: getLanguageFromOptions(options),
@@ -1912,21 +2643,38 @@ async function handlePresetGeneration(projectName, options) {
1912
2643
  useDefaults: options.yes,
1913
2644
  javaBuildTool: getJavaBuildToolFromOptions(options)
1914
2645
  });
2646
+ if (options.ci && result) {
2647
+ await handleCIGeneration(result.name, result.language, result.javaBuildTool, options.ci);
2648
+ }
2649
+ }
2650
+ async function handleCIGeneration(projectName, language, javaBuildTool, ciProvider) {
2651
+ if (!isValidCIProvider(ciProvider)) {
2652
+ logger.error(`Invalid CI provider: ${ciProvider}. Valid options: github, gitlab, circleci`);
2653
+ return;
2654
+ }
2655
+ const projectDir = path8.resolve(process.cwd(), projectName);
2656
+ await generateCI({
2657
+ projectDir,
2658
+ provider: ciProvider,
2659
+ language,
2660
+ javaBuildTool,
2661
+ projectName
2662
+ });
1915
2663
  }
1916
2664
 
1917
2665
  // src/commands/init.ts
1918
- import path5 from "path";
1919
- import inquirer11 from "inquirer";
2666
+ import path9 from "path";
2667
+ import inquirer12 from "inquirer";
1920
2668
  async function initCommand(options) {
1921
2669
  try {
1922
2670
  const currentDir = process.cwd();
1923
- const dirName = path5.basename(currentDir);
1924
- const hasPackageJson = await exists(path5.join(currentDir, "package.json"));
1925
- const hasPyproject = await exists(path5.join(currentDir, "pyproject.toml"));
1926
- const hasGoMod = await exists(path5.join(currentDir, "go.mod"));
1927
- const hasCargoToml = await exists(path5.join(currentDir, "Cargo.toml"));
2671
+ const dirName = path9.basename(currentDir);
2672
+ const hasPackageJson = await exists(path9.join(currentDir, "package.json"));
2673
+ const hasPyproject = await exists(path9.join(currentDir, "pyproject.toml"));
2674
+ const hasGoMod = await exists(path9.join(currentDir, "go.mod"));
2675
+ const hasCargoToml = await exists(path9.join(currentDir, "Cargo.toml"));
1928
2676
  if ((hasPackageJson || hasPyproject || hasGoMod || hasCargoToml) && !options.force) {
1929
- const { proceed } = await inquirer11.prompt([
2677
+ const { proceed } = await inquirer12.prompt([
1930
2678
  {
1931
2679
  type: "confirm",
1932
2680
  name: "proceed",
@@ -1968,7 +2716,7 @@ async function initCommand(options) {
1968
2716
  let projectName = dirName;
1969
2717
  if (hasPackageJson) {
1970
2718
  try {
1971
- const pkgContent = await readFile(path5.join(currentDir, "package.json"));
2719
+ const pkgContent = await readFile(path9.join(currentDir, "package.json"));
1972
2720
  const pkg = JSON.parse(pkgContent);
1973
2721
  projectName = pkg.name || dirName;
1974
2722
  } catch {
@@ -1998,14 +2746,14 @@ async function initCommand(options) {
1998
2746
  }
1999
2747
 
2000
2748
  // src/commands/add-tool.ts
2001
- import path6 from "path";
2749
+ import path10 from "path";
2002
2750
  async function addToolCommand(options) {
2003
2751
  try {
2004
2752
  const currentDir = process.cwd();
2005
- const isTypeScript = await exists(path6.join(currentDir, "package.json"));
2006
- const isPython = await exists(path6.join(currentDir, "pyproject.toml"));
2007
- const isGo = await exists(path6.join(currentDir, "go.mod"));
2008
- const isRust = await exists(path6.join(currentDir, "Cargo.toml"));
2753
+ const isTypeScript = await exists(path10.join(currentDir, "package.json"));
2754
+ const isPython = await exists(path10.join(currentDir, "pyproject.toml"));
2755
+ const isGo = await exists(path10.join(currentDir, "go.mod"));
2756
+ const isRust = await exists(path10.join(currentDir, "Cargo.toml"));
2009
2757
  if (!isTypeScript && !isPython && !isGo && !isRust) {
2010
2758
  logger.error("No MCP server project found in current directory.");
2011
2759
  logger.info("Run this command from the root of your MCP server project.");
@@ -2043,9 +2791,9 @@ async function addToolCommand(options) {
2043
2791
  }
2044
2792
  }
2045
2793
  async function addToolToTypeScript(projectDir, tool) {
2046
- const toolsDir = path6.join(projectDir, "src", "tools");
2794
+ const toolsDir = path10.join(projectDir, "src", "tools");
2047
2795
  const toolFileName = `${tool.name.replace(/_/g, "-")}.ts`;
2048
- const toolFilePath = path6.join(toolsDir, toolFileName);
2796
+ const toolFilePath = path10.join(toolsDir, toolFileName);
2049
2797
  if (await exists(toolFilePath)) {
2050
2798
  logger.error(`Tool file already exists: ${toolFilePath}`);
2051
2799
  process.exit(1);
@@ -2061,9 +2809,9 @@ async function addToolToTypeScript(projectDir, tool) {
2061
2809
  ]);
2062
2810
  }
2063
2811
  async function addToolToPython(projectDir, tool) {
2064
- const toolsDir = path6.join(projectDir, "src", "tools");
2812
+ const toolsDir = path10.join(projectDir, "src", "tools");
2065
2813
  const toolFileName = `${tool.name}.py`;
2066
- const toolFilePath = path6.join(toolsDir, toolFileName);
2814
+ const toolFilePath = path10.join(toolsDir, toolFileName);
2067
2815
  if (await exists(toolFilePath)) {
2068
2816
  logger.error(`Tool file already exists: ${toolFilePath}`);
2069
2817
  process.exit(1);
@@ -2207,9 +2955,9 @@ function mapTypeToPython(type) {
2207
2955
  }
2208
2956
  }
2209
2957
  async function addToolToGo(projectDir, tool) {
2210
- const toolsDir = path6.join(projectDir, "internal", "tools");
2958
+ const toolsDir = path10.join(projectDir, "internal", "tools");
2211
2959
  const toolFileName = `${tool.name}.go`;
2212
- const toolFilePath = path6.join(toolsDir, toolFileName);
2960
+ const toolFilePath = path10.join(toolsDir, toolFileName);
2213
2961
  if (await exists(toolFilePath)) {
2214
2962
  logger.error(`Tool file already exists: ${toolFilePath}`);
2215
2963
  process.exit(1);
@@ -2225,9 +2973,9 @@ async function addToolToGo(projectDir, tool) {
2225
2973
  ]);
2226
2974
  }
2227
2975
  async function addToolToRust(projectDir, tool) {
2228
- const srcDir = path6.join(projectDir, "src");
2976
+ const srcDir = path10.join(projectDir, "src");
2229
2977
  const toolFileName = `${tool.name}.rs`;
2230
- const toolFilePath = path6.join(srcDir, toolFileName);
2978
+ const toolFilePath = path10.join(srcDir, toolFileName);
2231
2979
  if (await exists(toolFilePath)) {
2232
2980
  logger.error(`Tool file already exists: ${toolFilePath}`);
2233
2981
  process.exit(1);
@@ -2375,6 +3123,7 @@ function mapTypeToRust(type) {
2375
3123
  }
2376
3124
 
2377
3125
  export {
3126
+ isValidCIProvider,
2378
3127
  projectNameRegex,
2379
3128
  validateProjectName,
2380
3129
  validateToolName,
@@ -2384,6 +3133,22 @@ export {
2384
3133
  safeParseAndValidate,
2385
3134
  promptProjectName,
2386
3135
  promptProjectDescription,
3136
+ ensureDir,
3137
+ writeFile,
3138
+ readFile,
3139
+ copyFile,
3140
+ copyDir,
3141
+ exists,
3142
+ remove,
3143
+ readDir,
3144
+ isDirectory,
3145
+ renderTemplate,
3146
+ renderTemplateToFile,
3147
+ walkDir,
3148
+ getTemplateDir,
3149
+ getTemplateDirForLanguage,
3150
+ resolveOutputPath,
3151
+ pluginRegistry,
2387
3152
  promptLanguage,
2388
3153
  promptTransport,
2389
3154
  promptIncludeExampleTool,
@@ -2395,24 +3160,25 @@ export {
2395
3160
  promptMultipleResources,
2396
3161
  promptJavaBuildTool,
2397
3162
  promptGenerationMethod,
3163
+ BuiltinLanguageSchema,
3164
+ LanguageSchema,
3165
+ BUILTIN_LANGUAGES,
3166
+ isBuiltinLanguage,
3167
+ JavaBuildToolSchema,
3168
+ TransportSchema,
3169
+ ToolParameterSchema,
3170
+ ToolConfigSchema,
3171
+ ResourceConfigSchema,
3172
+ ProjectConfigSchema,
3173
+ PresetIdSchema,
3174
+ clearPresetCache,
3175
+ listCachedPresets,
3176
+ getCacheDir,
2398
3177
  PRESETS,
2399
3178
  promptPreset,
3179
+ promptCIProvider,
2400
3180
  runWizard,
2401
3181
  runQuickWizard,
2402
- ensureDir,
2403
- writeFile,
2404
- readFile,
2405
- copyFile,
2406
- copyDir,
2407
- exists,
2408
- remove,
2409
- readDir,
2410
- isDirectory,
2411
- renderTemplate,
2412
- renderTemplateToFile,
2413
- walkDir,
2414
- getTemplateDir,
2415
- resolveOutputPath,
2416
3182
  isGitInstalled,
2417
3183
  initGitRepository,
2418
3184
  createInitialCommit,
@@ -2435,6 +3201,9 @@ export {
2435
3201
  PresetGenerator,
2436
3202
  generateFromPreset,
2437
3203
  validatePresetId,
3204
+ generateCI,
3205
+ detectLanguageFromProject,
3206
+ detectJavaBuildTool,
2438
3207
  createCommand,
2439
3208
  initCommand,
2440
3209
  addToolCommand