mcp-new 0.1.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.
@@ -0,0 +1,1540 @@
1
+ // src/utils/validator.ts
2
+ var projectNameRegex = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/;
3
+ function validateProjectName(name) {
4
+ if (!name || name.trim().length === 0) {
5
+ return { valid: false, error: "Project name cannot be empty" };
6
+ }
7
+ if (name.length > 214) {
8
+ return { valid: false, error: "Project name must be less than 214 characters" };
9
+ }
10
+ if (!projectNameRegex.test(name)) {
11
+ return {
12
+ valid: false,
13
+ error: "Project name must start and end with lowercase letter or number, and contain only lowercase letters, numbers, and hyphens"
14
+ };
15
+ }
16
+ const reservedNames = ["node_modules", "favicon.ico", "npm", "npx"];
17
+ if (reservedNames.includes(name.toLowerCase())) {
18
+ return { valid: false, error: `"${name}" is a reserved name` };
19
+ }
20
+ return { valid: true };
21
+ }
22
+ function validateToolName(name) {
23
+ if (!name || name.trim().length === 0) {
24
+ return { valid: false, error: "Tool name cannot be empty" };
25
+ }
26
+ const toolNameRegex = /^[a-z][a-z0-9_]*$/;
27
+ if (!toolNameRegex.test(name)) {
28
+ return {
29
+ valid: false,
30
+ error: "Tool name must start with a letter and contain only lowercase letters, numbers, and underscores"
31
+ };
32
+ }
33
+ return { valid: true };
34
+ }
35
+ function validateUrl(url) {
36
+ try {
37
+ new URL(url);
38
+ return { valid: true };
39
+ } catch {
40
+ return { valid: false, error: "Invalid URL format" };
41
+ }
42
+ }
43
+ function validateFilePath(path7) {
44
+ if (!path7 || path7.trim().length === 0) {
45
+ return { valid: false, error: "File path cannot be empty" };
46
+ }
47
+ const invalidChars = /[<>:"|?*]/;
48
+ if (invalidChars.test(path7)) {
49
+ return { valid: false, error: "File path contains invalid characters" };
50
+ }
51
+ return { valid: true };
52
+ }
53
+ function parseAndValidate(schema, data) {
54
+ return schema.parse(data);
55
+ }
56
+ function safeParseAndValidate(schema, data) {
57
+ const result = schema.safeParse(data);
58
+ if (result.success) {
59
+ return { success: true, data: result.data };
60
+ }
61
+ return { success: false, error: result.error };
62
+ }
63
+
64
+ // src/prompts/project-name.ts
65
+ import inquirer from "inquirer";
66
+ async function promptProjectName(defaultName) {
67
+ const { projectName } = await inquirer.prompt([
68
+ {
69
+ type: "input",
70
+ name: "projectName",
71
+ message: "Project name:",
72
+ default: defaultName || "my-mcp-server",
73
+ validate: (input) => {
74
+ const result = validateProjectName(input);
75
+ return result.valid ? true : result.error || "Invalid project name";
76
+ }
77
+ }
78
+ ]);
79
+ return projectName;
80
+ }
81
+ async function promptProjectDescription() {
82
+ const { description } = await inquirer.prompt([
83
+ {
84
+ type: "input",
85
+ name: "description",
86
+ message: "Project description (optional):",
87
+ default: ""
88
+ }
89
+ ]);
90
+ return description;
91
+ }
92
+
93
+ // src/prompts/language.ts
94
+ import inquirer2 from "inquirer";
95
+ async function promptLanguage() {
96
+ const { language } = await inquirer2.prompt([
97
+ {
98
+ type: "list",
99
+ name: "language",
100
+ message: "Select language:",
101
+ choices: [
102
+ { name: "TypeScript", value: "typescript" },
103
+ { name: "Python", value: "python" }
104
+ ],
105
+ default: "typescript"
106
+ }
107
+ ]);
108
+ return language;
109
+ }
110
+
111
+ // src/prompts/transport.ts
112
+ import inquirer3 from "inquirer";
113
+ async function promptTransport() {
114
+ const { transport } = await inquirer3.prompt([
115
+ {
116
+ type: "list",
117
+ name: "transport",
118
+ message: "Select transport:",
119
+ choices: [
120
+ { name: "stdio (standard input/output)", value: "stdio" },
121
+ { name: "SSE (Server-Sent Events)", value: "sse" }
122
+ ],
123
+ default: "stdio"
124
+ }
125
+ ]);
126
+ return transport;
127
+ }
128
+
129
+ // src/prompts/tools.ts
130
+ import inquirer4 from "inquirer";
131
+ async function promptIncludeExampleTool() {
132
+ const { includeExample } = await inquirer4.prompt([
133
+ {
134
+ type: "confirm",
135
+ name: "includeExample",
136
+ message: "Add example tool?",
137
+ default: true
138
+ }
139
+ ]);
140
+ return includeExample;
141
+ }
142
+ async function promptAddTools() {
143
+ const { addTools } = await inquirer4.prompt([
144
+ {
145
+ type: "confirm",
146
+ name: "addTools",
147
+ message: "Do you want to add custom tools?",
148
+ default: false
149
+ }
150
+ ]);
151
+ return addTools;
152
+ }
153
+ async function promptToolConfig() {
154
+ const { name, description } = await inquirer4.prompt([
155
+ {
156
+ type: "input",
157
+ name: "name",
158
+ message: "Tool name (snake_case):",
159
+ validate: (input) => {
160
+ const result = validateToolName(input);
161
+ return result.valid ? true : result.error || "Invalid tool name";
162
+ }
163
+ },
164
+ {
165
+ type: "input",
166
+ name: "description",
167
+ message: "Tool description:",
168
+ validate: (input) => input.trim().length > 0 ? true : "Description is required"
169
+ }
170
+ ]);
171
+ const parameters = await promptToolParameters();
172
+ return { name, description, parameters };
173
+ }
174
+ async function promptToolParameters() {
175
+ const parameters = [];
176
+ let addMore = true;
177
+ while (addMore) {
178
+ const { shouldAdd } = await inquirer4.prompt([
179
+ {
180
+ type: "confirm",
181
+ name: "shouldAdd",
182
+ message: parameters.length === 0 ? "Add parameter?" : "Add another parameter?",
183
+ default: parameters.length === 0
184
+ }
185
+ ]);
186
+ if (!shouldAdd) {
187
+ addMore = false;
188
+ continue;
189
+ }
190
+ const param = await promptSingleParameter();
191
+ parameters.push(param);
192
+ }
193
+ return parameters;
194
+ }
195
+ async function promptSingleParameter() {
196
+ const { name, type, description, required } = await inquirer4.prompt([
197
+ {
198
+ type: "input",
199
+ name: "name",
200
+ message: "Parameter name:",
201
+ validate: (input) => input.trim().length > 0 ? true : "Parameter name is required"
202
+ },
203
+ {
204
+ type: "list",
205
+ name: "type",
206
+ message: "Parameter type:",
207
+ choices: [
208
+ { name: "string", value: "string" },
209
+ { name: "number", value: "number" },
210
+ { name: "boolean", value: "boolean" },
211
+ { name: "object", value: "object" },
212
+ { name: "array", value: "array" }
213
+ ]
214
+ },
215
+ {
216
+ type: "input",
217
+ name: "description",
218
+ message: "Parameter description:",
219
+ default: ""
220
+ },
221
+ {
222
+ type: "confirm",
223
+ name: "required",
224
+ message: "Required parameter?",
225
+ default: true
226
+ }
227
+ ]);
228
+ return { name, type, description, required };
229
+ }
230
+ async function promptMultipleTools() {
231
+ const tools = [];
232
+ let addMore = true;
233
+ while (addMore) {
234
+ const tool = await promptToolConfig();
235
+ tools.push(tool);
236
+ const { shouldAddMore } = await inquirer4.prompt([
237
+ {
238
+ type: "confirm",
239
+ name: "shouldAddMore",
240
+ message: "Add another tool?",
241
+ default: false
242
+ }
243
+ ]);
244
+ addMore = shouldAddMore;
245
+ }
246
+ return tools;
247
+ }
248
+
249
+ // src/prompts/resources.ts
250
+ import inquirer5 from "inquirer";
251
+ async function promptAddResources() {
252
+ const { addResources } = await inquirer5.prompt([
253
+ {
254
+ type: "confirm",
255
+ name: "addResources",
256
+ message: "Do you want to add resources?",
257
+ default: false
258
+ }
259
+ ]);
260
+ return addResources;
261
+ }
262
+ async function promptResourceConfig() {
263
+ const { name, uri, description, mimeType } = await inquirer5.prompt([
264
+ {
265
+ type: "input",
266
+ name: "name",
267
+ message: "Resource name:",
268
+ validate: (input) => input.trim().length > 0 ? true : "Name is required"
269
+ },
270
+ {
271
+ type: "input",
272
+ name: "uri",
273
+ message: "Resource URI:",
274
+ validate: (input) => input.trim().length > 0 ? true : "URI is required"
275
+ },
276
+ {
277
+ type: "input",
278
+ name: "description",
279
+ message: "Resource description:",
280
+ default: ""
281
+ },
282
+ {
283
+ type: "input",
284
+ name: "mimeType",
285
+ message: "MIME type (optional):",
286
+ default: ""
287
+ }
288
+ ]);
289
+ return {
290
+ name,
291
+ uri,
292
+ description,
293
+ mimeType: mimeType || void 0
294
+ };
295
+ }
296
+ async function promptMultipleResources() {
297
+ const resources = [];
298
+ let addMore = true;
299
+ while (addMore) {
300
+ const resource = await promptResourceConfig();
301
+ resources.push(resource);
302
+ const { shouldAddMore } = await inquirer5.prompt([
303
+ {
304
+ type: "confirm",
305
+ name: "shouldAddMore",
306
+ message: "Add another resource?",
307
+ default: false
308
+ }
309
+ ]);
310
+ addMore = shouldAddMore;
311
+ }
312
+ return resources;
313
+ }
314
+
315
+ // src/prompts/index.ts
316
+ async function runWizard(options = {}) {
317
+ const name = await promptProjectName(options.defaultName);
318
+ let description = "";
319
+ if (!options.skipDescription) {
320
+ description = await promptProjectDescription();
321
+ }
322
+ const language = options.presetLanguage || await promptLanguage();
323
+ const transport = await promptTransport();
324
+ const includeExampleTool = await promptIncludeExampleTool();
325
+ let tools = [];
326
+ let resources = [];
327
+ if (!options.skipAdvanced) {
328
+ const wantTools = await promptAddTools();
329
+ if (wantTools) {
330
+ tools = await promptMultipleTools();
331
+ }
332
+ const wantResources = await promptAddResources();
333
+ if (wantResources) {
334
+ resources = await promptMultipleResources();
335
+ }
336
+ }
337
+ return {
338
+ name,
339
+ description,
340
+ language,
341
+ transport,
342
+ tools,
343
+ resources,
344
+ includeExampleTool,
345
+ skipInstall: false,
346
+ initGit: true
347
+ };
348
+ }
349
+ async function runQuickWizard(defaultName, presetLanguage) {
350
+ const name = await promptProjectName(defaultName);
351
+ const language = presetLanguage || await promptLanguage();
352
+ const transport = await promptTransport();
353
+ const includeExampleTool = await promptIncludeExampleTool();
354
+ return {
355
+ name,
356
+ description: "",
357
+ language,
358
+ transport,
359
+ tools: [],
360
+ resources: [],
361
+ includeExampleTool,
362
+ skipInstall: false,
363
+ initGit: true
364
+ };
365
+ }
366
+
367
+ // src/utils/file-system.ts
368
+ import fs from "fs-extra";
369
+ import path from "path";
370
+ import ejs from "ejs";
371
+ async function ensureDir(dirPath) {
372
+ await fs.ensureDir(dirPath);
373
+ }
374
+ async function writeFile(filePath, content) {
375
+ await fs.ensureDir(path.dirname(filePath));
376
+ await fs.writeFile(filePath, content, "utf-8");
377
+ }
378
+ async function readFile(filePath) {
379
+ return fs.readFile(filePath, "utf-8");
380
+ }
381
+ async function copyFile(src, dest) {
382
+ await fs.ensureDir(path.dirname(dest));
383
+ await fs.copyFile(src, dest);
384
+ }
385
+ async function copyDir(src, dest) {
386
+ await fs.copy(src, dest);
387
+ }
388
+ async function exists(filePath) {
389
+ return fs.pathExists(filePath);
390
+ }
391
+ async function remove(filePath) {
392
+ await fs.remove(filePath);
393
+ }
394
+ async function readDir(dirPath) {
395
+ return fs.readdir(dirPath);
396
+ }
397
+ async function isDirectory(filePath) {
398
+ try {
399
+ const stat = await fs.stat(filePath);
400
+ return stat.isDirectory();
401
+ } catch {
402
+ return false;
403
+ }
404
+ }
405
+ async function renderTemplate(templatePath, data) {
406
+ const template = await readFile(templatePath);
407
+ return ejs.render(template, data, { async: false });
408
+ }
409
+ async function renderTemplateToFile(templatePath, outputPath, data) {
410
+ const content = await renderTemplate(templatePath, data);
411
+ const finalPath = outputPath.replace(/\.ejs$/, "");
412
+ await writeFile(finalPath, content);
413
+ }
414
+ async function walkDir(dirPath, callback) {
415
+ const entries = await fs.readdir(dirPath, { withFileTypes: true });
416
+ for (const entry of entries) {
417
+ const fullPath = path.join(dirPath, entry.name);
418
+ const isDir = entry.isDirectory();
419
+ await callback(fullPath, isDir);
420
+ if (isDir) {
421
+ await walkDir(fullPath, callback);
422
+ }
423
+ }
424
+ }
425
+ function getTemplateDir() {
426
+ const devPath = path.join(import.meta.dirname, "..", "templates");
427
+ const prodPath = path.join(import.meta.dirname, "..", "..", "templates");
428
+ if (fs.existsSync(devPath)) {
429
+ return devPath;
430
+ }
431
+ return prodPath;
432
+ }
433
+ function resolveOutputPath(basePath, ...segments) {
434
+ return path.resolve(basePath, ...segments);
435
+ }
436
+
437
+ // src/utils/git.ts
438
+ import { execa } from "execa";
439
+ import path2 from "path";
440
+ async function isGitInstalled() {
441
+ try {
442
+ await execa("git", ["--version"]);
443
+ return true;
444
+ } catch {
445
+ return false;
446
+ }
447
+ }
448
+ async function initGitRepository(projectPath) {
449
+ const gitDir = path2.join(projectPath, ".git");
450
+ if (await exists(gitDir)) {
451
+ return;
452
+ }
453
+ await execa("git", ["init"], { cwd: projectPath });
454
+ }
455
+ async function createInitialCommit(projectPath) {
456
+ try {
457
+ await execa("git", ["add", "."], { cwd: projectPath });
458
+ await execa("git", ["commit", "-m", "Initial commit from create-mcp-server"], {
459
+ cwd: projectPath
460
+ });
461
+ } catch {
462
+ }
463
+ }
464
+ async function getGitUser() {
465
+ try {
466
+ const [nameResult, emailResult] = await Promise.all([
467
+ execa("git", ["config", "--get", "user.name"]).catch(() => ({ stdout: "" })),
468
+ execa("git", ["config", "--get", "user.email"]).catch(() => ({ stdout: "" }))
469
+ ]);
470
+ return {
471
+ name: nameResult.stdout.trim() || void 0,
472
+ email: emailResult.stdout.trim() || void 0
473
+ };
474
+ } catch {
475
+ return {};
476
+ }
477
+ }
478
+ async function isInsideGitRepository(dirPath) {
479
+ try {
480
+ await execa("git", ["rev-parse", "--is-inside-work-tree"], { cwd: dirPath });
481
+ return true;
482
+ } catch {
483
+ return false;
484
+ }
485
+ }
486
+
487
+ // src/utils/logger.ts
488
+ import chalk from "chalk";
489
+ var logger = {
490
+ info: (message) => {
491
+ console.log(chalk.blue("i"), message);
492
+ },
493
+ success: (message) => {
494
+ console.log(chalk.green("\u2713"), message);
495
+ },
496
+ warning: (message) => {
497
+ console.log(chalk.yellow("\u26A0"), message);
498
+ },
499
+ error: (message) => {
500
+ console.log(chalk.red("\u2717"), message);
501
+ },
502
+ step: (step, total, message) => {
503
+ console.log(chalk.cyan(`[${step}/${total}]`), message);
504
+ },
505
+ title: (message) => {
506
+ console.log();
507
+ console.log(chalk.bold.white(message));
508
+ console.log(chalk.gray("\u2500".repeat(50)));
509
+ },
510
+ blank: () => {
511
+ console.log();
512
+ },
513
+ list: (items) => {
514
+ items.forEach((item) => {
515
+ console.log(chalk.gray(" \u2022"), item);
516
+ });
517
+ },
518
+ code: (code) => {
519
+ console.log(chalk.gray(" $"), chalk.cyan(code));
520
+ },
521
+ box: (title, content) => {
522
+ console.log();
523
+ console.log(chalk.green("\u250C\u2500"), chalk.bold.green(title));
524
+ content.forEach((line) => {
525
+ console.log(chalk.green("\u2502"), line);
526
+ });
527
+ console.log(chalk.green("\u2514\u2500"));
528
+ },
529
+ nextSteps: (projectName, language) => {
530
+ logger.blank();
531
+ logger.box("Next steps:", [
532
+ "",
533
+ ` ${chalk.cyan("cd")} ${projectName}`,
534
+ language === "typescript" ? ` ${chalk.cyan("npm install")}` : ` ${chalk.cyan("pip install -e .")}`,
535
+ language === "typescript" ? ` ${chalk.cyan("npm run dev")}` : ` ${chalk.cyan("python -m src.server")}`,
536
+ ""
537
+ ]);
538
+ }
539
+ };
540
+
541
+ // src/utils/spinner.ts
542
+ import ora from "ora";
543
+ function createSpinner(text) {
544
+ const spinner = ora({
545
+ text,
546
+ spinner: "dots"
547
+ });
548
+ return {
549
+ start: (newText) => {
550
+ if (newText) spinner.text = newText;
551
+ spinner.start();
552
+ return createSpinner(spinner.text);
553
+ },
554
+ stop: () => {
555
+ spinner.stop();
556
+ return createSpinner(spinner.text);
557
+ },
558
+ succeed: (newText) => {
559
+ spinner.succeed(newText);
560
+ return createSpinner(spinner.text);
561
+ },
562
+ fail: (newText) => {
563
+ spinner.fail(newText);
564
+ return createSpinner(spinner.text);
565
+ },
566
+ warn: (newText) => {
567
+ spinner.warn(newText);
568
+ return createSpinner(spinner.text);
569
+ },
570
+ info: (newText) => {
571
+ spinner.info(newText);
572
+ return createSpinner(spinner.text);
573
+ },
574
+ get text() {
575
+ return spinner.text;
576
+ },
577
+ set text(value) {
578
+ spinner.text = value;
579
+ }
580
+ };
581
+ }
582
+ async function withSpinner(text, fn, successText, failText) {
583
+ const spinner = ora({
584
+ text,
585
+ spinner: "dots"
586
+ }).start();
587
+ try {
588
+ const result = await fn();
589
+ spinner.succeed(successText || text);
590
+ return result;
591
+ } catch (error) {
592
+ spinner.fail(failText || `Failed: ${text}`);
593
+ throw error;
594
+ }
595
+ }
596
+
597
+ // src/generators/base.ts
598
+ import path3 from "path";
599
+ import { execa as execa2 } from "execa";
600
+ var BaseGenerator = class {
601
+ config;
602
+ outputDir;
603
+ templateDir;
604
+ constructor(context) {
605
+ this.config = context.config;
606
+ this.outputDir = context.outputDir;
607
+ this.templateDir = context.templateDir;
608
+ }
609
+ async createProjectStructure() {
610
+ await ensureDir(this.outputDir);
611
+ await ensureDir(path3.join(this.outputDir, "src"));
612
+ }
613
+ async renderTemplates() {
614
+ const templateData = this.getTemplateData();
615
+ await walkDir(this.templateDir, async (filePath, isDir) => {
616
+ if (isDir) return;
617
+ const relativePath = path3.relative(this.templateDir, filePath);
618
+ const outputPath = path3.join(this.outputDir, relativePath);
619
+ if (filePath.endsWith(".ejs")) {
620
+ await renderTemplateToFile(filePath, outputPath, templateData);
621
+ } else {
622
+ await copyFile(filePath, outputPath);
623
+ }
624
+ });
625
+ }
626
+ getTemplateData() {
627
+ return {
628
+ name: this.config.name,
629
+ description: this.config.description,
630
+ language: this.config.language,
631
+ transport: this.config.transport,
632
+ tools: this.config.tools,
633
+ resources: this.config.resources,
634
+ includeExampleTool: this.config.includeExampleTool
635
+ };
636
+ }
637
+ async installDependencies() {
638
+ if (this.config.skipInstall) {
639
+ logger.info("Skipping dependency installation (--skip-install)");
640
+ return;
641
+ }
642
+ const isTypescript = this.config.language === "typescript";
643
+ if (isTypescript) {
644
+ await this.installNodeDependencies();
645
+ } else {
646
+ await this.installPythonDependencies();
647
+ }
648
+ }
649
+ async installNodeDependencies() {
650
+ await withSpinner(
651
+ "Installing dependencies...",
652
+ async () => {
653
+ await execa2("npm", ["install"], { cwd: this.outputDir });
654
+ },
655
+ "Dependencies installed",
656
+ "Failed to install dependencies"
657
+ );
658
+ }
659
+ async installPythonDependencies() {
660
+ const hasPip = await this.checkCommand("pip");
661
+ const hasPip3 = await this.checkCommand("pip3");
662
+ if (!hasPip && !hasPip3) {
663
+ logger.warning("pip not found. Please install dependencies manually:");
664
+ logger.code("pip install -r requirements.txt");
665
+ return;
666
+ }
667
+ const pipCommand = hasPip3 ? "pip3" : "pip";
668
+ await withSpinner(
669
+ "Installing dependencies...",
670
+ async () => {
671
+ await execa2(pipCommand, ["install", "-r", "requirements.txt"], {
672
+ cwd: this.outputDir
673
+ });
674
+ },
675
+ "Dependencies installed",
676
+ "Failed to install dependencies"
677
+ );
678
+ }
679
+ async checkCommand(command) {
680
+ try {
681
+ await execa2("which", [command]);
682
+ return true;
683
+ } catch {
684
+ return false;
685
+ }
686
+ }
687
+ async initializeGit() {
688
+ if (!this.config.initGit) {
689
+ return;
690
+ }
691
+ const gitInstalled = await isGitInstalled();
692
+ if (!gitInstalled) {
693
+ logger.warning("Git not found. Skipping git initialization.");
694
+ return;
695
+ }
696
+ await withSpinner(
697
+ "Initializing git repository...",
698
+ async () => {
699
+ await initGitRepository(this.outputDir);
700
+ await createInitialCommit(this.outputDir);
701
+ },
702
+ "Git repository initialized",
703
+ "Failed to initialize git"
704
+ );
705
+ }
706
+ async checkOutputDir() {
707
+ if (await exists(this.outputDir)) {
708
+ const files = await readDir(this.outputDir);
709
+ if (files.length > 0) {
710
+ return false;
711
+ }
712
+ }
713
+ return true;
714
+ }
715
+ };
716
+ function createGeneratorContext(config, outputPath) {
717
+ const outputDir = outputPath || path3.resolve(process.cwd(), config.name);
718
+ const templateDir = path3.join(getTemplateDir(), config.language);
719
+ return {
720
+ config,
721
+ outputDir,
722
+ templateDir
723
+ };
724
+ }
725
+
726
+ // src/generators/from-wizard.ts
727
+ var WizardGenerator = class extends BaseGenerator {
728
+ constructor(context) {
729
+ super(context);
730
+ }
731
+ async generate() {
732
+ logger.title(`Creating ${this.config.name}`);
733
+ const isSafe = await this.checkOutputDir();
734
+ if (!isSafe) {
735
+ throw new Error(
736
+ `Directory ${this.outputDir} already exists and is not empty. Please choose a different name or delete the existing directory.`
737
+ );
738
+ }
739
+ await withSpinner(
740
+ "Creating project structure...",
741
+ async () => {
742
+ await this.createProjectStructure();
743
+ },
744
+ "Project structure created"
745
+ );
746
+ await withSpinner(
747
+ "Generating files from templates...",
748
+ async () => {
749
+ await this.renderTemplates();
750
+ },
751
+ "Files generated"
752
+ );
753
+ await this.installDependencies();
754
+ await this.initializeGit();
755
+ logger.success(`Project ${this.config.name} created successfully!`);
756
+ logger.nextSteps(this.config.name, this.config.language);
757
+ }
758
+ };
759
+ async function generateFromWizard(config, outputPath) {
760
+ const context = createGeneratorContext(config, outputPath);
761
+ const generator = new WizardGenerator(context);
762
+ await generator.generate();
763
+ }
764
+
765
+ // src/parsers/openapi.ts
766
+ import YAML from "yaml";
767
+ import inquirer6 from "inquirer";
768
+ async function parseOpenAPISpec(content) {
769
+ let spec;
770
+ try {
771
+ spec = YAML.parse(content);
772
+ } catch {
773
+ throw new Error("Failed to parse OpenAPI specification. Ensure it is valid YAML or JSON.");
774
+ }
775
+ if (!spec.openapi && !("swagger" in spec)) {
776
+ throw new Error('Invalid OpenAPI specification. Missing "openapi" or "swagger" field.');
777
+ }
778
+ const endpoints = [];
779
+ for (const [path7, pathItem] of Object.entries(spec.paths)) {
780
+ const methods = ["get", "post", "put", "delete", "patch", "options", "head"];
781
+ for (const method of methods) {
782
+ const operation = pathItem[method];
783
+ if (!operation) continue;
784
+ const endpoint = parseOperation(path7, method, operation, spec);
785
+ endpoints.push(endpoint);
786
+ }
787
+ }
788
+ return endpoints;
789
+ }
790
+ function parseOperation(path7, method, operation, spec) {
791
+ const parameters = [];
792
+ if (operation.parameters) {
793
+ for (const param of operation.parameters) {
794
+ const resolvedParam = resolveRef(param, spec);
795
+ parameters.push({
796
+ name: resolvedParam.name,
797
+ in: resolvedParam.in,
798
+ type: resolvedParam.schema?.type || "string",
799
+ description: resolvedParam.description || "",
800
+ required: resolvedParam.required || false
801
+ });
802
+ }
803
+ }
804
+ if (operation.requestBody) {
805
+ const content = operation.requestBody.content;
806
+ const jsonContent = content?.["application/json"];
807
+ if (jsonContent?.schema) {
808
+ const schema = resolveRef(jsonContent.schema, spec);
809
+ const bodyParams = extractSchemaParameters(schema, spec, operation.requestBody.required);
810
+ parameters.push(...bodyParams);
811
+ }
812
+ }
813
+ return {
814
+ path: path7,
815
+ method: method.toUpperCase(),
816
+ operationId: operation.operationId || "",
817
+ summary: operation.summary || "",
818
+ description: operation.description || "",
819
+ parameters,
820
+ tags: operation.tags || []
821
+ };
822
+ }
823
+ function extractSchemaParameters(schema, spec, parentRequired = false) {
824
+ const parameters = [];
825
+ if (schema.properties) {
826
+ for (const [name, propSchema] of Object.entries(schema.properties)) {
827
+ const resolved = resolveRef(propSchema, spec);
828
+ const isRequired = parentRequired && (schema.required?.includes(name) ?? false);
829
+ parameters.push({
830
+ name,
831
+ in: "body",
832
+ type: resolved.type || "string",
833
+ description: resolved.description || "",
834
+ required: isRequired
835
+ });
836
+ }
837
+ }
838
+ return parameters;
839
+ }
840
+ function resolveRef(obj, spec) {
841
+ if (!obj || typeof obj !== "object") {
842
+ return obj;
843
+ }
844
+ const refObj = obj;
845
+ if (!refObj.$ref) {
846
+ return obj;
847
+ }
848
+ const refPath = refObj.$ref.replace("#/", "").split("/");
849
+ let resolved = spec;
850
+ for (const part of refPath) {
851
+ resolved = resolved[part];
852
+ if (!resolved) {
853
+ throw new Error(`Could not resolve reference: ${refObj.$ref}`);
854
+ }
855
+ }
856
+ return resolved;
857
+ }
858
+ async function selectEndpoints(endpoints) {
859
+ if (endpoints.length === 0) {
860
+ return [];
861
+ }
862
+ const choices = endpoints.map((ep) => ({
863
+ name: `${ep.method} ${ep.path} - ${ep.summary || ep.description || "No description"}`,
864
+ value: ep,
865
+ checked: true
866
+ }));
867
+ const { selected } = await inquirer6.prompt([
868
+ {
869
+ type: "checkbox",
870
+ name: "selected",
871
+ message: "Select endpoints to generate tools for:",
872
+ choices,
873
+ pageSize: 15
874
+ }
875
+ ]);
876
+ return selected;
877
+ }
878
+ function endpointToMCPTool(endpoint) {
879
+ const properties = {};
880
+ const required = [];
881
+ for (const param of endpoint.parameters) {
882
+ properties[param.name] = {
883
+ type: param.type,
884
+ description: param.description
885
+ };
886
+ if (param.required) {
887
+ required.push(param.name);
888
+ }
889
+ }
890
+ return {
891
+ name: endpoint.operationId || `${endpoint.method.toLowerCase()}_${endpoint.path.replace(/\//g, "_")}`,
892
+ description: endpoint.summary || endpoint.description,
893
+ inputSchema: {
894
+ type: "object",
895
+ properties,
896
+ required: required.length > 0 ? required : void 0
897
+ }
898
+ };
899
+ }
900
+
901
+ // src/generators/from-openapi.ts
902
+ var OpenAPIGenerator = class extends BaseGenerator {
903
+ _endpoints = [];
904
+ constructor(context) {
905
+ super(context);
906
+ }
907
+ get endpoints() {
908
+ return this._endpoints;
909
+ }
910
+ async generate() {
911
+ logger.title(`Creating ${this.config.name} from OpenAPI`);
912
+ const isSafe = await this.checkOutputDir();
913
+ if (!isSafe) {
914
+ throw new Error(
915
+ `Directory ${this.outputDir} already exists and is not empty.`
916
+ );
917
+ }
918
+ await withSpinner(
919
+ "Creating project structure...",
920
+ async () => {
921
+ await this.createProjectStructure();
922
+ },
923
+ "Project structure created"
924
+ );
925
+ await withSpinner(
926
+ "Generating files from templates...",
927
+ async () => {
928
+ await this.renderTemplates();
929
+ },
930
+ "Files generated"
931
+ );
932
+ await this.installDependencies();
933
+ await this.initializeGit();
934
+ logger.success(`Project ${this.config.name} created successfully!`);
935
+ logger.info(`Generated ${this.config.tools.length} tools from OpenAPI spec`);
936
+ logger.nextSteps(this.config.name, this.config.language);
937
+ }
938
+ setEndpoints(endpoints) {
939
+ this._endpoints = endpoints;
940
+ }
941
+ };
942
+ async function generateFromOpenAPI(specPath, baseConfig) {
943
+ logger.info("Parsing OpenAPI specification...");
944
+ const specContent = await readFile(specPath);
945
+ const endpoints = await parseOpenAPISpec(specContent);
946
+ logger.info(`Found ${endpoints.length} endpoints`);
947
+ const selectedEndpoints = await selectEndpoints(endpoints);
948
+ if (selectedEndpoints.length === 0) {
949
+ throw new Error("No endpoints selected");
950
+ }
951
+ const tools = selectedEndpoints.map((endpoint) => ({
952
+ name: endpointToToolName(endpoint),
953
+ description: endpoint.summary || endpoint.description || `${endpoint.method.toUpperCase()} ${endpoint.path}`,
954
+ parameters: endpoint.parameters.map((param) => ({
955
+ name: param.name,
956
+ type: mapOpenAPIType(param.type),
957
+ description: param.description,
958
+ required: param.required
959
+ }))
960
+ }));
961
+ const config = {
962
+ name: baseConfig.name || "mcp-server",
963
+ description: baseConfig.description || "",
964
+ language: baseConfig.language || "typescript",
965
+ transport: baseConfig.transport || "stdio",
966
+ tools,
967
+ resources: [],
968
+ includeExampleTool: false,
969
+ skipInstall: baseConfig.skipInstall || false,
970
+ initGit: baseConfig.initGit !== false
971
+ };
972
+ const context = createGeneratorContext(config);
973
+ const generator = new OpenAPIGenerator(context);
974
+ generator.setEndpoints(selectedEndpoints);
975
+ await generator.generate();
976
+ }
977
+ function endpointToToolName(endpoint) {
978
+ if (endpoint.operationId) {
979
+ return endpoint.operationId.replace(/[^a-zA-Z0-9]/g, "_").replace(/_+/g, "_").toLowerCase();
980
+ }
981
+ const method = endpoint.method.toLowerCase();
982
+ const pathParts = endpoint.path.split("/").filter((p) => p && !p.startsWith("{")).map((p) => p.replace(/[^a-zA-Z0-9]/g, ""));
983
+ return `${method}_${pathParts.join("_")}`.toLowerCase();
984
+ }
985
+ function mapOpenAPIType(type) {
986
+ switch (type.toLowerCase()) {
987
+ case "integer":
988
+ case "number":
989
+ return "number";
990
+ case "boolean":
991
+ return "boolean";
992
+ case "array":
993
+ return "array";
994
+ case "object":
995
+ return "object";
996
+ default:
997
+ return "string";
998
+ }
999
+ }
1000
+
1001
+ // src/generators/from-prompt.ts
1002
+ import Anthropic from "@anthropic-ai/sdk";
1003
+ import inquirer7 from "inquirer";
1004
+ var SYSTEM_PROMPT = `You are an expert at designing MCP (Model Context Protocol) servers.
1005
+ Given a description of an API or functionality, you generate a list of tools that would be useful for that API.
1006
+
1007
+ For each tool, provide:
1008
+ 1. name: A snake_case name for the tool
1009
+ 2. description: A clear description of what the tool does
1010
+ 3. parameters: An array of parameters, each with:
1011
+ - name: Parameter name
1012
+ - type: One of: string, number, boolean, object, array
1013
+ - description: What the parameter is for
1014
+ - required: Whether it's required (true/false)
1015
+
1016
+ Respond with a JSON array of tools. Example:
1017
+ [
1018
+ {
1019
+ "name": "create_page",
1020
+ "description": "Creates a new page in the workspace",
1021
+ "parameters": [
1022
+ {"name": "title", "type": "string", "description": "Page title", "required": true},
1023
+ {"name": "content", "type": "string", "description": "Page content in markdown", "required": false}
1024
+ ]
1025
+ }
1026
+ ]`;
1027
+ var PromptGenerator = class extends BaseGenerator {
1028
+ constructor(context) {
1029
+ super(context);
1030
+ }
1031
+ async generate() {
1032
+ logger.title(`Creating ${this.config.name}`);
1033
+ const isSafe = await this.checkOutputDir();
1034
+ if (!isSafe) {
1035
+ throw new Error(
1036
+ `Directory ${this.outputDir} already exists and is not empty.`
1037
+ );
1038
+ }
1039
+ await withSpinner(
1040
+ "Creating project structure...",
1041
+ async () => {
1042
+ await this.createProjectStructure();
1043
+ },
1044
+ "Project structure created"
1045
+ );
1046
+ await withSpinner(
1047
+ "Generating files from templates...",
1048
+ async () => {
1049
+ await this.renderTemplates();
1050
+ },
1051
+ "Files generated"
1052
+ );
1053
+ await this.installDependencies();
1054
+ await this.initializeGit();
1055
+ logger.success(`Project ${this.config.name} created successfully!`);
1056
+ logger.info(`Generated ${this.config.tools.length} tools from your description`);
1057
+ logger.nextSteps(this.config.name, this.config.language);
1058
+ }
1059
+ };
1060
+ async function generateFromPrompt(baseConfig) {
1061
+ const { description } = await inquirer7.prompt([
1062
+ {
1063
+ type: "editor",
1064
+ name: "description",
1065
+ message: "Describe your API (opens editor):"
1066
+ }
1067
+ ]);
1068
+ if (!description.trim()) {
1069
+ throw new Error("Description cannot be empty");
1070
+ }
1071
+ const apiKey = process.env.ANTHROPIC_API_KEY;
1072
+ if (!apiKey) {
1073
+ throw new Error(
1074
+ "ANTHROPIC_API_KEY environment variable is required for AI generation. Get your API key at https://console.anthropic.com/"
1075
+ );
1076
+ }
1077
+ let tools = [];
1078
+ await withSpinner(
1079
+ "Generating tools with Claude...",
1080
+ async () => {
1081
+ const anthropic = new Anthropic({ apiKey });
1082
+ const message = await anthropic.messages.create({
1083
+ model: "claude-3-5-sonnet-20241022",
1084
+ max_tokens: 4096,
1085
+ system: SYSTEM_PROMPT,
1086
+ messages: [
1087
+ {
1088
+ role: "user",
1089
+ content: description
1090
+ }
1091
+ ]
1092
+ });
1093
+ const content = message.content[0];
1094
+ if (content.type !== "text") {
1095
+ throw new Error("Unexpected response from Claude");
1096
+ }
1097
+ try {
1098
+ tools = JSON.parse(content.text);
1099
+ } catch {
1100
+ const jsonMatch = content.text.match(/\[[\s\S]*\]/);
1101
+ if (jsonMatch) {
1102
+ tools = JSON.parse(jsonMatch[0]);
1103
+ } else {
1104
+ throw new Error("Could not parse tools from Claude response");
1105
+ }
1106
+ }
1107
+ },
1108
+ "Tools generated",
1109
+ "Failed to generate tools"
1110
+ );
1111
+ logger.blank();
1112
+ logger.info("Generated tools:");
1113
+ tools.forEach((tool, index) => {
1114
+ logger.list([`${index + 1}. ${tool.name} - ${tool.description}`]);
1115
+ });
1116
+ logger.blank();
1117
+ const { confirm } = await inquirer7.prompt([
1118
+ {
1119
+ type: "confirm",
1120
+ name: "confirm",
1121
+ message: "Proceed with these tools?",
1122
+ default: true
1123
+ }
1124
+ ]);
1125
+ if (!confirm) {
1126
+ throw new Error("Generation cancelled by user");
1127
+ }
1128
+ const config = {
1129
+ name: baseConfig.name || "mcp-server",
1130
+ description: baseConfig.description || description.slice(0, 100),
1131
+ language: baseConfig.language || "typescript",
1132
+ transport: baseConfig.transport || "stdio",
1133
+ tools,
1134
+ resources: [],
1135
+ includeExampleTool: false,
1136
+ skipInstall: baseConfig.skipInstall || false,
1137
+ initGit: baseConfig.initGit !== false
1138
+ };
1139
+ const context = createGeneratorContext(config);
1140
+ const generator = new PromptGenerator(context);
1141
+ await generator.generate();
1142
+ }
1143
+
1144
+ // src/commands/create.ts
1145
+ import path4 from "path";
1146
+ async function createCommand(projectName, options) {
1147
+ try {
1148
+ if (options.fromOpenapi) {
1149
+ await handleOpenAPIGeneration(projectName, options);
1150
+ return;
1151
+ }
1152
+ if (options.fromPrompt) {
1153
+ await handlePromptGeneration(projectName, options);
1154
+ return;
1155
+ }
1156
+ await handleWizardGeneration(projectName, options);
1157
+ } catch (error) {
1158
+ if (error instanceof Error) {
1159
+ logger.error(error.message);
1160
+ } else {
1161
+ logger.error("An unexpected error occurred");
1162
+ }
1163
+ process.exit(1);
1164
+ }
1165
+ }
1166
+ async function handleWizardGeneration(projectName, options) {
1167
+ let config;
1168
+ const presetLanguage = options.typescript ? "typescript" : options.python ? "python" : void 0;
1169
+ if (options.yes) {
1170
+ config = await runQuickWizard(projectName, presetLanguage);
1171
+ } else {
1172
+ config = await runWizard({ defaultName: projectName, presetLanguage });
1173
+ }
1174
+ if (options.skipInstall) {
1175
+ config.skipInstall = true;
1176
+ }
1177
+ const outputPath = path4.resolve(process.cwd(), config.name);
1178
+ if (await exists(outputPath)) {
1179
+ const files = await import("fs-extra").then((m) => m.readdir(outputPath));
1180
+ if (files.length > 0) {
1181
+ logger.error(`Directory "${config.name}" already exists and is not empty.`);
1182
+ process.exit(1);
1183
+ }
1184
+ }
1185
+ await generateFromWizard(config);
1186
+ }
1187
+ async function handleOpenAPIGeneration(projectName, options) {
1188
+ const specPath = options.fromOpenapi;
1189
+ if (!await exists(specPath)) {
1190
+ logger.error(`OpenAPI specification file not found: ${specPath}`);
1191
+ process.exit(1);
1192
+ }
1193
+ const name = projectName || path4.basename(specPath, path4.extname(specPath)) + "-mcp";
1194
+ await generateFromOpenAPI(specPath, {
1195
+ name,
1196
+ language: options.typescript ? "typescript" : options.python ? "python" : void 0,
1197
+ skipInstall: options.skipInstall
1198
+ });
1199
+ }
1200
+ async function handlePromptGeneration(projectName, options) {
1201
+ await generateFromPrompt({
1202
+ name: projectName,
1203
+ language: options.typescript ? "typescript" : options.python ? "python" : void 0,
1204
+ skipInstall: options.skipInstall
1205
+ });
1206
+ }
1207
+
1208
+ // src/commands/init.ts
1209
+ import path5 from "path";
1210
+ import inquirer8 from "inquirer";
1211
+ async function initCommand(options) {
1212
+ try {
1213
+ const currentDir = process.cwd();
1214
+ const dirName = path5.basename(currentDir);
1215
+ const hasPackageJson = await exists(path5.join(currentDir, "package.json"));
1216
+ const hasPyproject = await exists(path5.join(currentDir, "pyproject.toml"));
1217
+ if ((hasPackageJson || hasPyproject) && !options.force) {
1218
+ const { proceed } = await inquirer8.prompt([
1219
+ {
1220
+ type: "confirm",
1221
+ name: "proceed",
1222
+ message: "This directory already contains a project. Initialize MCP server here anyway?",
1223
+ default: false
1224
+ }
1225
+ ]);
1226
+ if (!proceed) {
1227
+ logger.info("Initialization cancelled.");
1228
+ return;
1229
+ }
1230
+ }
1231
+ let language;
1232
+ if (options.typescript) {
1233
+ language = "typescript";
1234
+ } else if (options.python) {
1235
+ language = "python";
1236
+ } else if (hasPackageJson) {
1237
+ language = "typescript";
1238
+ logger.info("Detected existing Node.js project, using TypeScript");
1239
+ } else if (hasPyproject) {
1240
+ language = "python";
1241
+ logger.info("Detected existing Python project");
1242
+ } else {
1243
+ language = await promptLanguage();
1244
+ }
1245
+ const transport = await promptTransport();
1246
+ const includeExampleTool = await promptIncludeExampleTool();
1247
+ let projectName = dirName;
1248
+ if (hasPackageJson) {
1249
+ try {
1250
+ const pkgContent = await readFile(path5.join(currentDir, "package.json"));
1251
+ const pkg = JSON.parse(pkgContent);
1252
+ projectName = pkg.name || dirName;
1253
+ } catch {
1254
+ }
1255
+ }
1256
+ const config = {
1257
+ name: projectName,
1258
+ description: "",
1259
+ language,
1260
+ transport,
1261
+ tools: [],
1262
+ resources: [],
1263
+ includeExampleTool,
1264
+ skipInstall: options.skipInstall || false,
1265
+ initGit: false
1266
+ // Don't init git in existing project
1267
+ };
1268
+ await generateFromWizard(config, currentDir);
1269
+ } catch (error) {
1270
+ if (error instanceof Error) {
1271
+ logger.error(error.message);
1272
+ } else {
1273
+ logger.error("An unexpected error occurred");
1274
+ }
1275
+ process.exit(1);
1276
+ }
1277
+ }
1278
+
1279
+ // src/commands/add-tool.ts
1280
+ import path6 from "path";
1281
+ async function addToolCommand(options) {
1282
+ try {
1283
+ const currentDir = process.cwd();
1284
+ const isTypeScript = await exists(path6.join(currentDir, "package.json"));
1285
+ const isPython = await exists(path6.join(currentDir, "pyproject.toml"));
1286
+ if (!isTypeScript && !isPython) {
1287
+ logger.error("No MCP server project found in current directory.");
1288
+ logger.info("Run this command from the root of your MCP server project.");
1289
+ process.exit(1);
1290
+ }
1291
+ let tool;
1292
+ if (options.name) {
1293
+ tool = {
1294
+ name: options.name,
1295
+ description: `Tool ${options.name}`,
1296
+ parameters: []
1297
+ };
1298
+ logger.info(`Creating tool: ${options.name}`);
1299
+ } else {
1300
+ tool = await promptToolConfig();
1301
+ }
1302
+ if (isTypeScript) {
1303
+ await addToolToTypeScript(currentDir, tool);
1304
+ } else {
1305
+ await addToolToPython(currentDir, tool);
1306
+ }
1307
+ logger.success(`Tool "${tool.name}" added successfully!`);
1308
+ logger.info("Remember to implement the tool logic in the generated file.");
1309
+ } catch (error) {
1310
+ if (error instanceof Error) {
1311
+ logger.error(error.message);
1312
+ } else {
1313
+ logger.error("An unexpected error occurred");
1314
+ }
1315
+ process.exit(1);
1316
+ }
1317
+ }
1318
+ async function addToolToTypeScript(projectDir, tool) {
1319
+ const toolsDir = path6.join(projectDir, "src", "tools");
1320
+ const toolFileName = `${tool.name.replace(/_/g, "-")}.ts`;
1321
+ const toolFilePath = path6.join(toolsDir, toolFileName);
1322
+ if (await exists(toolFilePath)) {
1323
+ logger.error(`Tool file already exists: ${toolFilePath}`);
1324
+ process.exit(1);
1325
+ }
1326
+ const content = generateTypeScriptToolFile(tool);
1327
+ await writeFile(toolFilePath, content);
1328
+ logger.info(`Created: src/tools/${toolFileName}`);
1329
+ logger.blank();
1330
+ logger.info("Next steps:");
1331
+ logger.list([
1332
+ `Implement the tool logic in src/tools/${toolFileName}`,
1333
+ "Import and register the tool in src/index.ts"
1334
+ ]);
1335
+ }
1336
+ async function addToolToPython(projectDir, tool) {
1337
+ const toolsDir = path6.join(projectDir, "src", "tools");
1338
+ const toolFileName = `${tool.name}.py`;
1339
+ const toolFilePath = path6.join(toolsDir, toolFileName);
1340
+ if (await exists(toolFilePath)) {
1341
+ logger.error(`Tool file already exists: ${toolFilePath}`);
1342
+ process.exit(1);
1343
+ }
1344
+ const content = generatePythonToolFile(tool);
1345
+ await writeFile(toolFilePath, content);
1346
+ logger.info(`Created: src/tools/${toolFileName}`);
1347
+ logger.blank();
1348
+ logger.info("Next steps:");
1349
+ logger.list([
1350
+ `Implement the tool logic in src/tools/${toolFileName}`,
1351
+ "Import and register the tool in src/server.py"
1352
+ ]);
1353
+ }
1354
+ function generateTypeScriptToolFile(tool) {
1355
+ const interfaceProps = tool.parameters.map((p) => ` ${p.name}${p.required ? "" : "?"}: ${mapTypeToTS(p.type)};`).join("\n");
1356
+ const schemaProps = tool.parameters.map(
1357
+ (p) => ` ${p.name}: {
1358
+ type: "${p.type}",
1359
+ description: "${p.description}",
1360
+ },`
1361
+ ).join("\n");
1362
+ const required = tool.parameters.filter((p) => p.required).map((p) => `"${p.name}"`).join(", ");
1363
+ return `/**
1364
+ * ${tool.description}
1365
+ */
1366
+
1367
+ export interface ${toPascalCase(tool.name)}Input {
1368
+ ${interfaceProps || " // No parameters"}
1369
+ }
1370
+
1371
+ export interface ${toPascalCase(tool.name)}Output {
1372
+ result: string;
1373
+ }
1374
+
1375
+ export async function ${toCamelCase(tool.name)}(
1376
+ input: ${toPascalCase(tool.name)}Input
1377
+ ): Promise<${toPascalCase(tool.name)}Output> {
1378
+ // TODO: Implement tool logic
1379
+ return {
1380
+ result: \`${tool.name} executed with: \${JSON.stringify(input)}\`,
1381
+ };
1382
+ }
1383
+
1384
+ export const ${toCamelCase(tool.name)}Schema = {
1385
+ name: "${tool.name}",
1386
+ description: "${tool.description}",
1387
+ inputSchema: {
1388
+ type: "object" as const,
1389
+ properties: {
1390
+ ${schemaProps}
1391
+ },
1392
+ required: [${required}],
1393
+ },
1394
+ };
1395
+ `;
1396
+ }
1397
+ function generatePythonToolFile(tool) {
1398
+ const imports = tool.parameters.length > 0 ? "from dataclasses import dataclass\n" : "";
1399
+ const fields = tool.parameters.map((p) => ` ${p.name}: ${mapTypeToPython(p.type)}`).join("\n");
1400
+ const schemaProps = tool.parameters.map(
1401
+ (p) => ` "${p.name}": {
1402
+ "type": "${p.type}",
1403
+ "description": "${p.description}",
1404
+ },`
1405
+ ).join("\n");
1406
+ const required = tool.parameters.filter((p) => p.required).map((p) => `"${p.name}"`).join(", ");
1407
+ return `"""
1408
+ ${tool.description}
1409
+ """
1410
+
1411
+ ${imports}from mcp.types import Tool
1412
+
1413
+
1414
+ @dataclass
1415
+ class ${toPascalCase(tool.name)}Input:
1416
+ ${fields || " pass"}
1417
+
1418
+
1419
+ @dataclass
1420
+ class ${toPascalCase(tool.name)}Output:
1421
+ result: str
1422
+
1423
+
1424
+ async def ${tool.name}(input_data: ${toPascalCase(tool.name)}Input) -> ${toPascalCase(tool.name)}Output:
1425
+ """
1426
+ ${tool.description}
1427
+ """
1428
+ # TODO: Implement tool logic
1429
+ return ${toPascalCase(tool.name)}Output(
1430
+ result=f"${tool.name} executed with: {input_data}"
1431
+ )
1432
+
1433
+
1434
+ ${tool.name.toUpperCase()}_SCHEMA = Tool(
1435
+ name="${tool.name}",
1436
+ description="${tool.description}",
1437
+ inputSchema={
1438
+ "type": "object",
1439
+ "properties": {
1440
+ ${schemaProps}
1441
+ },
1442
+ "required": [${required}],
1443
+ },
1444
+ )
1445
+ `;
1446
+ }
1447
+ function toPascalCase(str) {
1448
+ return str.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
1449
+ }
1450
+ function toCamelCase(str) {
1451
+ const pascal = toPascalCase(str);
1452
+ return pascal.charAt(0).toLowerCase() + pascal.slice(1);
1453
+ }
1454
+ function mapTypeToTS(type) {
1455
+ switch (type) {
1456
+ case "number":
1457
+ return "number";
1458
+ case "boolean":
1459
+ return "boolean";
1460
+ case "array":
1461
+ return "unknown[]";
1462
+ case "object":
1463
+ return "Record<string, unknown>";
1464
+ default:
1465
+ return "string";
1466
+ }
1467
+ }
1468
+ function mapTypeToPython(type) {
1469
+ switch (type) {
1470
+ case "number":
1471
+ return "float";
1472
+ case "boolean":
1473
+ return "bool";
1474
+ case "array":
1475
+ return "list";
1476
+ case "object":
1477
+ return "dict";
1478
+ default:
1479
+ return "str";
1480
+ }
1481
+ }
1482
+
1483
+ export {
1484
+ projectNameRegex,
1485
+ validateProjectName,
1486
+ validateToolName,
1487
+ validateUrl,
1488
+ validateFilePath,
1489
+ parseAndValidate,
1490
+ safeParseAndValidate,
1491
+ promptProjectName,
1492
+ promptProjectDescription,
1493
+ promptLanguage,
1494
+ promptTransport,
1495
+ promptIncludeExampleTool,
1496
+ promptAddTools,
1497
+ promptToolConfig,
1498
+ promptMultipleTools,
1499
+ promptAddResources,
1500
+ promptResourceConfig,
1501
+ promptMultipleResources,
1502
+ runWizard,
1503
+ runQuickWizard,
1504
+ ensureDir,
1505
+ writeFile,
1506
+ readFile,
1507
+ copyFile,
1508
+ copyDir,
1509
+ exists,
1510
+ remove,
1511
+ readDir,
1512
+ isDirectory,
1513
+ renderTemplate,
1514
+ renderTemplateToFile,
1515
+ walkDir,
1516
+ getTemplateDir,
1517
+ resolveOutputPath,
1518
+ isGitInstalled,
1519
+ initGitRepository,
1520
+ createInitialCommit,
1521
+ getGitUser,
1522
+ isInsideGitRepository,
1523
+ logger,
1524
+ createSpinner,
1525
+ withSpinner,
1526
+ BaseGenerator,
1527
+ createGeneratorContext,
1528
+ WizardGenerator,
1529
+ generateFromWizard,
1530
+ parseOpenAPISpec,
1531
+ selectEndpoints,
1532
+ endpointToMCPTool,
1533
+ OpenAPIGenerator,
1534
+ generateFromOpenAPI,
1535
+ PromptGenerator,
1536
+ generateFromPrompt,
1537
+ createCommand,
1538
+ initCommand,
1539
+ addToolCommand
1540
+ };