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.
- package/dist/{chunk-YISSMIHU.js → chunk-LJNMSDBU.js} +975 -206
- package/dist/cli.js +955 -5
- package/dist/index.d.ts +23 -8
- package/dist/index.js +19 -46
- package/package.json +4 -2
- package/templates/ci/circleci/config.yml.ejs +219 -0
- package/templates/ci/github/ci.yml.ejs +184 -0
- package/templates/ci/gitlab/.gitlab-ci.yml.ejs +233 -0
|
@@ -40,12 +40,12 @@ function validateUrl(url) {
|
|
|
40
40
|
return { valid: false, error: "Invalid URL format" };
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
|
-
function validateFilePath(
|
|
44
|
-
if (!
|
|
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(
|
|
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
|
|
1346
|
+
import { execa as execa2 } from "execa";
|
|
1347
|
+
import path5 from "path";
|
|
849
1348
|
async function isGitInstalled() {
|
|
850
1349
|
try {
|
|
851
|
-
await
|
|
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 =
|
|
1357
|
+
const gitDir = path5.join(projectPath, ".git");
|
|
859
1358
|
if (await exists(gitDir)) {
|
|
860
1359
|
return;
|
|
861
1360
|
}
|
|
862
|
-
await
|
|
1361
|
+
await execa2("git", ["init"], { cwd: projectPath });
|
|
863
1362
|
}
|
|
864
1363
|
async function createInitialCommit(projectPath) {
|
|
865
1364
|
try {
|
|
866
|
-
await
|
|
867
|
-
await
|
|
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
|
-
|
|
877
|
-
|
|
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
|
|
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
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
installCmd = "
|
|
963
|
-
runCmd = "
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
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
|
|
1049
|
-
import { execa as
|
|
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(
|
|
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 =
|
|
1068
|
-
const outputPath =
|
|
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
|
|
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
|
|
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
|
|
1690
|
+
await execa3("go", ["mod", "download"], {
|
|
1165
1691
|
cwd: this.outputDir
|
|
1166
1692
|
});
|
|
1167
|
-
await
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
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
|
|
1769
|
+
await execa3("dotnet", ["restore"], {
|
|
1244
1770
|
cwd: this.outputDir
|
|
1245
1771
|
});
|
|
1246
|
-
await
|
|
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
|
|
1790
|
+
await execa3("mix", ["deps.get"], {
|
|
1265
1791
|
cwd: this.outputDir
|
|
1266
1792
|
});
|
|
1267
|
-
await
|
|
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
|
|
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 ||
|
|
1840
|
+
const outputDir = outputPath || path6.resolve(process.cwd(), config.name);
|
|
1315
1841
|
let templateDir;
|
|
1316
|
-
|
|
1317
|
-
|
|
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 =
|
|
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
|
|
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 [
|
|
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(
|
|
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(
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
1783
|
-
if (
|
|
1784
|
-
|
|
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
|
|
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 =
|
|
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 ||
|
|
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
|
-
|
|
1907
|
-
|
|
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
|
|
1919
|
-
import
|
|
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 =
|
|
1924
|
-
const hasPackageJson = await exists(
|
|
1925
|
-
const hasPyproject = await exists(
|
|
1926
|
-
const hasGoMod = await exists(
|
|
1927
|
-
const hasCargoToml = await exists(
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
2006
|
-
const isPython = await exists(
|
|
2007
|
-
const isGo = await exists(
|
|
2008
|
-
const isRust = await exists(
|
|
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 =
|
|
2794
|
+
const toolsDir = path10.join(projectDir, "src", "tools");
|
|
2047
2795
|
const toolFileName = `${tool.name.replace(/_/g, "-")}.ts`;
|
|
2048
|
-
const toolFilePath =
|
|
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 =
|
|
2812
|
+
const toolsDir = path10.join(projectDir, "src", "tools");
|
|
2065
2813
|
const toolFileName = `${tool.name}.py`;
|
|
2066
|
-
const toolFilePath =
|
|
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 =
|
|
2958
|
+
const toolsDir = path10.join(projectDir, "internal", "tools");
|
|
2211
2959
|
const toolFileName = `${tool.name}.go`;
|
|
2212
|
-
const toolFilePath =
|
|
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 =
|
|
2976
|
+
const srcDir = path10.join(projectDir, "src");
|
|
2229
2977
|
const toolFileName = `${tool.name}.rs`;
|
|
2230
|
-
const toolFilePath =
|
|
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
|