mcp-new 0.1.0 → 1.2.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/README.md +206 -97
- package/dist/{chunk-QRUHMGU5.js → chunk-BHGUGEHE.js} +746 -22
- package/dist/cli.js +65 -5
- package/dist/index.d.ts +38 -7
- package/dist/index.js +14 -2
- package/package.json +1 -1
- package/templates/go/.env.example +5 -0
- package/templates/go/.gitignore.ejs +30 -0
- package/templates/go/README.md.ejs +151 -0
- package/templates/go/cmd/server/main.go.ejs +101 -0
- package/templates/go/go.mod.ejs +7 -0
- package/templates/go/internal/tools/example.go.ejs +40 -0
- package/templates/python/README.md.ejs +58 -15
- package/templates/rust/.env.example +8 -0
- package/templates/rust/.gitignore.ejs +19 -0
- package/templates/rust/Cargo.toml.ejs +18 -0
- package/templates/rust/README.md.ejs +155 -0
- package/templates/rust/src/main.rs.ejs +46 -0
- package/templates/rust/src/tools.rs.ejs +105 -0
- package/templates/typescript/README.md.ejs +56 -15
|
@@ -100,7 +100,9 @@ async function promptLanguage() {
|
|
|
100
100
|
message: "Select language:",
|
|
101
101
|
choices: [
|
|
102
102
|
{ name: "TypeScript", value: "typescript" },
|
|
103
|
-
{ name: "Python", value: "python" }
|
|
103
|
+
{ name: "Python", value: "python" },
|
|
104
|
+
{ name: "Go", value: "go" },
|
|
105
|
+
{ name: "Rust", value: "rust" }
|
|
104
106
|
],
|
|
105
107
|
default: "typescript"
|
|
106
108
|
}
|
|
@@ -312,6 +314,381 @@ async function promptMultipleResources() {
|
|
|
312
314
|
return resources;
|
|
313
315
|
}
|
|
314
316
|
|
|
317
|
+
// src/prompts/generation-method.ts
|
|
318
|
+
import inquirer6 from "inquirer";
|
|
319
|
+
async function promptGenerationMethod() {
|
|
320
|
+
const { method } = await inquirer6.prompt([
|
|
321
|
+
{
|
|
322
|
+
type: "list",
|
|
323
|
+
name: "method",
|
|
324
|
+
message: "How would you like to create your MCP server?",
|
|
325
|
+
choices: [
|
|
326
|
+
{
|
|
327
|
+
name: "From scratch (wizard)",
|
|
328
|
+
value: "wizard"
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
name: "From a preset template",
|
|
332
|
+
value: "preset"
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
name: "From OpenAPI/Swagger specification",
|
|
336
|
+
value: "openapi"
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
name: "Using AI (describe your API)",
|
|
340
|
+
value: "prompt"
|
|
341
|
+
}
|
|
342
|
+
],
|
|
343
|
+
default: "wizard"
|
|
344
|
+
}
|
|
345
|
+
]);
|
|
346
|
+
return method;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// src/prompts/preset.ts
|
|
350
|
+
import inquirer7 from "inquirer";
|
|
351
|
+
|
|
352
|
+
// src/presets/database.ts
|
|
353
|
+
var DATABASE_PRESET = {
|
|
354
|
+
id: "database",
|
|
355
|
+
name: "Database CRUD",
|
|
356
|
+
description: "Tools for database operations: query, insert, update, delete",
|
|
357
|
+
tools: [
|
|
358
|
+
{
|
|
359
|
+
name: "query",
|
|
360
|
+
description: "Execute a SQL query on the database",
|
|
361
|
+
parameters: [
|
|
362
|
+
{
|
|
363
|
+
name: "sql",
|
|
364
|
+
type: "string",
|
|
365
|
+
description: "SQL query to execute",
|
|
366
|
+
required: true
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
name: "params",
|
|
370
|
+
type: "array",
|
|
371
|
+
description: "Query parameters for prepared statements",
|
|
372
|
+
required: false
|
|
373
|
+
}
|
|
374
|
+
]
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
name: "insert",
|
|
378
|
+
description: "Insert a new record into a table",
|
|
379
|
+
parameters: [
|
|
380
|
+
{
|
|
381
|
+
name: "table",
|
|
382
|
+
type: "string",
|
|
383
|
+
description: "Table name",
|
|
384
|
+
required: true
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
name: "data",
|
|
388
|
+
type: "object",
|
|
389
|
+
description: "Record data as key-value pairs",
|
|
390
|
+
required: true
|
|
391
|
+
}
|
|
392
|
+
]
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
name: "update",
|
|
396
|
+
description: "Update records in a table",
|
|
397
|
+
parameters: [
|
|
398
|
+
{
|
|
399
|
+
name: "table",
|
|
400
|
+
type: "string",
|
|
401
|
+
description: "Table name",
|
|
402
|
+
required: true
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
name: "data",
|
|
406
|
+
type: "object",
|
|
407
|
+
description: "Fields to update as key-value pairs",
|
|
408
|
+
required: true
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
name: "where",
|
|
412
|
+
type: "object",
|
|
413
|
+
description: "WHERE conditions as key-value pairs",
|
|
414
|
+
required: true
|
|
415
|
+
}
|
|
416
|
+
]
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
name: "delete",
|
|
420
|
+
description: "Delete records from a table",
|
|
421
|
+
parameters: [
|
|
422
|
+
{
|
|
423
|
+
name: "table",
|
|
424
|
+
type: "string",
|
|
425
|
+
description: "Table name",
|
|
426
|
+
required: true
|
|
427
|
+
},
|
|
428
|
+
{
|
|
429
|
+
name: "where",
|
|
430
|
+
type: "object",
|
|
431
|
+
description: "WHERE conditions as key-value pairs",
|
|
432
|
+
required: true
|
|
433
|
+
}
|
|
434
|
+
]
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
name: "list_tables",
|
|
438
|
+
description: "List all tables in the database",
|
|
439
|
+
parameters: []
|
|
440
|
+
}
|
|
441
|
+
]
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
// src/presets/rest-api.ts
|
|
445
|
+
var REST_API_PRESET = {
|
|
446
|
+
id: "rest-api",
|
|
447
|
+
name: "REST API Wrapper",
|
|
448
|
+
description: "Tools for making HTTP requests: GET, POST, PUT, DELETE",
|
|
449
|
+
tools: [
|
|
450
|
+
{
|
|
451
|
+
name: "http_get",
|
|
452
|
+
description: "Make an HTTP GET request",
|
|
453
|
+
parameters: [
|
|
454
|
+
{
|
|
455
|
+
name: "url",
|
|
456
|
+
type: "string",
|
|
457
|
+
description: "URL to request (can be relative if base_url is set)",
|
|
458
|
+
required: true
|
|
459
|
+
},
|
|
460
|
+
{
|
|
461
|
+
name: "headers",
|
|
462
|
+
type: "object",
|
|
463
|
+
description: "Request headers as key-value pairs",
|
|
464
|
+
required: false
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
name: "query",
|
|
468
|
+
type: "object",
|
|
469
|
+
description: "Query parameters as key-value pairs",
|
|
470
|
+
required: false
|
|
471
|
+
}
|
|
472
|
+
]
|
|
473
|
+
},
|
|
474
|
+
{
|
|
475
|
+
name: "http_post",
|
|
476
|
+
description: "Make an HTTP POST request",
|
|
477
|
+
parameters: [
|
|
478
|
+
{
|
|
479
|
+
name: "url",
|
|
480
|
+
type: "string",
|
|
481
|
+
description: "URL to request",
|
|
482
|
+
required: true
|
|
483
|
+
},
|
|
484
|
+
{
|
|
485
|
+
name: "body",
|
|
486
|
+
type: "object",
|
|
487
|
+
description: "Request body (will be JSON encoded)",
|
|
488
|
+
required: false
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
name: "headers",
|
|
492
|
+
type: "object",
|
|
493
|
+
description: "Request headers as key-value pairs",
|
|
494
|
+
required: false
|
|
495
|
+
}
|
|
496
|
+
]
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
name: "http_put",
|
|
500
|
+
description: "Make an HTTP PUT request",
|
|
501
|
+
parameters: [
|
|
502
|
+
{
|
|
503
|
+
name: "url",
|
|
504
|
+
type: "string",
|
|
505
|
+
description: "URL to request",
|
|
506
|
+
required: true
|
|
507
|
+
},
|
|
508
|
+
{
|
|
509
|
+
name: "body",
|
|
510
|
+
type: "object",
|
|
511
|
+
description: "Request body (will be JSON encoded)",
|
|
512
|
+
required: false
|
|
513
|
+
},
|
|
514
|
+
{
|
|
515
|
+
name: "headers",
|
|
516
|
+
type: "object",
|
|
517
|
+
description: "Request headers as key-value pairs",
|
|
518
|
+
required: false
|
|
519
|
+
}
|
|
520
|
+
]
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
name: "http_delete",
|
|
524
|
+
description: "Make an HTTP DELETE request",
|
|
525
|
+
parameters: [
|
|
526
|
+
{
|
|
527
|
+
name: "url",
|
|
528
|
+
type: "string",
|
|
529
|
+
description: "URL to request",
|
|
530
|
+
required: true
|
|
531
|
+
},
|
|
532
|
+
{
|
|
533
|
+
name: "headers",
|
|
534
|
+
type: "object",
|
|
535
|
+
description: "Request headers as key-value pairs",
|
|
536
|
+
required: false
|
|
537
|
+
}
|
|
538
|
+
]
|
|
539
|
+
},
|
|
540
|
+
{
|
|
541
|
+
name: "set_base_url",
|
|
542
|
+
description: "Set the base URL for all subsequent requests",
|
|
543
|
+
parameters: [
|
|
544
|
+
{
|
|
545
|
+
name: "base_url",
|
|
546
|
+
type: "string",
|
|
547
|
+
description: "Base URL (e.g., https://api.example.com)",
|
|
548
|
+
required: true
|
|
549
|
+
}
|
|
550
|
+
]
|
|
551
|
+
}
|
|
552
|
+
]
|
|
553
|
+
};
|
|
554
|
+
|
|
555
|
+
// src/presets/filesystem.ts
|
|
556
|
+
var FILESYSTEM_PRESET = {
|
|
557
|
+
id: "filesystem",
|
|
558
|
+
name: "File System Tools",
|
|
559
|
+
description: "Tools for file operations: read, write, list, search",
|
|
560
|
+
tools: [
|
|
561
|
+
{
|
|
562
|
+
name: "read_file",
|
|
563
|
+
description: "Read the contents of a file",
|
|
564
|
+
parameters: [
|
|
565
|
+
{
|
|
566
|
+
name: "path",
|
|
567
|
+
type: "string",
|
|
568
|
+
description: "Path to the file to read",
|
|
569
|
+
required: true
|
|
570
|
+
},
|
|
571
|
+
{
|
|
572
|
+
name: "encoding",
|
|
573
|
+
type: "string",
|
|
574
|
+
description: "File encoding (default: utf-8)",
|
|
575
|
+
required: false
|
|
576
|
+
}
|
|
577
|
+
]
|
|
578
|
+
},
|
|
579
|
+
{
|
|
580
|
+
name: "write_file",
|
|
581
|
+
description: "Write content to a file",
|
|
582
|
+
parameters: [
|
|
583
|
+
{
|
|
584
|
+
name: "path",
|
|
585
|
+
type: "string",
|
|
586
|
+
description: "Path to the file to write",
|
|
587
|
+
required: true
|
|
588
|
+
},
|
|
589
|
+
{
|
|
590
|
+
name: "content",
|
|
591
|
+
type: "string",
|
|
592
|
+
description: "Content to write to the file",
|
|
593
|
+
required: true
|
|
594
|
+
},
|
|
595
|
+
{
|
|
596
|
+
name: "append",
|
|
597
|
+
type: "boolean",
|
|
598
|
+
description: "Append to file instead of overwriting (default: false)",
|
|
599
|
+
required: false
|
|
600
|
+
}
|
|
601
|
+
]
|
|
602
|
+
},
|
|
603
|
+
{
|
|
604
|
+
name: "list_directory",
|
|
605
|
+
description: "List files and directories in a path",
|
|
606
|
+
parameters: [
|
|
607
|
+
{
|
|
608
|
+
name: "path",
|
|
609
|
+
type: "string",
|
|
610
|
+
description: "Directory path to list",
|
|
611
|
+
required: true
|
|
612
|
+
},
|
|
613
|
+
{
|
|
614
|
+
name: "recursive",
|
|
615
|
+
type: "boolean",
|
|
616
|
+
description: "List recursively (default: false)",
|
|
617
|
+
required: false
|
|
618
|
+
}
|
|
619
|
+
]
|
|
620
|
+
},
|
|
621
|
+
{
|
|
622
|
+
name: "search_files",
|
|
623
|
+
description: "Search for files matching a pattern",
|
|
624
|
+
parameters: [
|
|
625
|
+
{
|
|
626
|
+
name: "path",
|
|
627
|
+
type: "string",
|
|
628
|
+
description: "Directory to search in",
|
|
629
|
+
required: true
|
|
630
|
+
},
|
|
631
|
+
{
|
|
632
|
+
name: "pattern",
|
|
633
|
+
type: "string",
|
|
634
|
+
description: "Glob pattern to match (e.g., *.txt)",
|
|
635
|
+
required: true
|
|
636
|
+
},
|
|
637
|
+
{
|
|
638
|
+
name: "recursive",
|
|
639
|
+
type: "boolean",
|
|
640
|
+
description: "Search recursively (default: true)",
|
|
641
|
+
required: false
|
|
642
|
+
}
|
|
643
|
+
]
|
|
644
|
+
},
|
|
645
|
+
{
|
|
646
|
+
name: "file_info",
|
|
647
|
+
description: "Get information about a file or directory",
|
|
648
|
+
parameters: [
|
|
649
|
+
{
|
|
650
|
+
name: "path",
|
|
651
|
+
type: "string",
|
|
652
|
+
description: "Path to the file or directory",
|
|
653
|
+
required: true
|
|
654
|
+
}
|
|
655
|
+
]
|
|
656
|
+
}
|
|
657
|
+
]
|
|
658
|
+
};
|
|
659
|
+
|
|
660
|
+
// src/presets/index.ts
|
|
661
|
+
var PRESETS = {
|
|
662
|
+
database: DATABASE_PRESET,
|
|
663
|
+
"rest-api": REST_API_PRESET,
|
|
664
|
+
filesystem: FILESYSTEM_PRESET
|
|
665
|
+
};
|
|
666
|
+
var PRESET_IDS = Object.keys(PRESETS);
|
|
667
|
+
function getPreset(id) {
|
|
668
|
+
return PRESETS[id];
|
|
669
|
+
}
|
|
670
|
+
function isValidPresetId(id) {
|
|
671
|
+
return id in PRESETS;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// src/prompts/preset.ts
|
|
675
|
+
async function promptPreset() {
|
|
676
|
+
const choices = Object.values(PRESETS).map((preset2) => ({
|
|
677
|
+
name: `${preset2.name} - ${preset2.description}`,
|
|
678
|
+
value: preset2.id
|
|
679
|
+
}));
|
|
680
|
+
const { preset } = await inquirer7.prompt([
|
|
681
|
+
{
|
|
682
|
+
type: "list",
|
|
683
|
+
name: "preset",
|
|
684
|
+
message: "Select a preset template:",
|
|
685
|
+
choices,
|
|
686
|
+
default: "database"
|
|
687
|
+
}
|
|
688
|
+
]);
|
|
689
|
+
return preset;
|
|
690
|
+
}
|
|
691
|
+
|
|
315
692
|
// src/prompts/index.ts
|
|
316
693
|
async function runWizard(options = {}) {
|
|
317
694
|
const name = await promptProjectName(options.defaultName);
|
|
@@ -528,11 +905,34 @@ var logger = {
|
|
|
528
905
|
},
|
|
529
906
|
nextSteps: (projectName, language) => {
|
|
530
907
|
logger.blank();
|
|
908
|
+
let installCmd;
|
|
909
|
+
let runCmd;
|
|
910
|
+
switch (language) {
|
|
911
|
+
case "typescript":
|
|
912
|
+
installCmd = "npm install";
|
|
913
|
+
runCmd = "npm run dev";
|
|
914
|
+
break;
|
|
915
|
+
case "python":
|
|
916
|
+
installCmd = "pip install -e .";
|
|
917
|
+
runCmd = "python -m src.server";
|
|
918
|
+
break;
|
|
919
|
+
case "go":
|
|
920
|
+
installCmd = "go mod download";
|
|
921
|
+
runCmd = "go run ./cmd/server";
|
|
922
|
+
break;
|
|
923
|
+
case "rust":
|
|
924
|
+
installCmd = "cargo build";
|
|
925
|
+
runCmd = "cargo run";
|
|
926
|
+
break;
|
|
927
|
+
default:
|
|
928
|
+
installCmd = "npm install";
|
|
929
|
+
runCmd = "npm run dev";
|
|
930
|
+
}
|
|
531
931
|
logger.box("Next steps:", [
|
|
532
932
|
"",
|
|
533
933
|
` ${chalk.cyan("cd")} ${projectName}`,
|
|
534
|
-
|
|
535
|
-
|
|
934
|
+
` ${chalk.cyan(installCmd)}`,
|
|
935
|
+
` ${chalk.cyan(runCmd)}`,
|
|
536
936
|
""
|
|
537
937
|
]);
|
|
538
938
|
}
|
|
@@ -639,11 +1039,19 @@ var BaseGenerator = class {
|
|
|
639
1039
|
logger.info("Skipping dependency installation (--skip-install)");
|
|
640
1040
|
return;
|
|
641
1041
|
}
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
1042
|
+
switch (this.config.language) {
|
|
1043
|
+
case "typescript":
|
|
1044
|
+
await this.installNodeDependencies();
|
|
1045
|
+
break;
|
|
1046
|
+
case "python":
|
|
1047
|
+
await this.installPythonDependencies();
|
|
1048
|
+
break;
|
|
1049
|
+
case "go":
|
|
1050
|
+
await this.installGoDependencies();
|
|
1051
|
+
break;
|
|
1052
|
+
case "rust":
|
|
1053
|
+
await this.installRustDependencies();
|
|
1054
|
+
break;
|
|
647
1055
|
}
|
|
648
1056
|
}
|
|
649
1057
|
async installNodeDependencies() {
|
|
@@ -676,9 +1084,49 @@ var BaseGenerator = class {
|
|
|
676
1084
|
"Failed to install dependencies"
|
|
677
1085
|
);
|
|
678
1086
|
}
|
|
1087
|
+
async installGoDependencies() {
|
|
1088
|
+
const hasGo = await this.checkCommand("go");
|
|
1089
|
+
if (!hasGo) {
|
|
1090
|
+
logger.warning("Go not found. Please install dependencies manually:");
|
|
1091
|
+
logger.code("go mod download");
|
|
1092
|
+
return;
|
|
1093
|
+
}
|
|
1094
|
+
await withSpinner(
|
|
1095
|
+
"Installing Go dependencies...",
|
|
1096
|
+
async () => {
|
|
1097
|
+
await execa2("go", ["mod", "download"], {
|
|
1098
|
+
cwd: this.outputDir
|
|
1099
|
+
});
|
|
1100
|
+
await execa2("go", ["mod", "tidy"], {
|
|
1101
|
+
cwd: this.outputDir
|
|
1102
|
+
});
|
|
1103
|
+
},
|
|
1104
|
+
"Dependencies installed",
|
|
1105
|
+
"Failed to install dependencies"
|
|
1106
|
+
);
|
|
1107
|
+
}
|
|
1108
|
+
async installRustDependencies() {
|
|
1109
|
+
const hasCargo = await this.checkCommand("cargo");
|
|
1110
|
+
if (!hasCargo) {
|
|
1111
|
+
logger.warning("Cargo not found. Please install dependencies manually:");
|
|
1112
|
+
logger.code("cargo build");
|
|
1113
|
+
return;
|
|
1114
|
+
}
|
|
1115
|
+
await withSpinner(
|
|
1116
|
+
"Building Rust project...",
|
|
1117
|
+
async () => {
|
|
1118
|
+
await execa2("cargo", ["build"], {
|
|
1119
|
+
cwd: this.outputDir
|
|
1120
|
+
});
|
|
1121
|
+
},
|
|
1122
|
+
"Project built successfully",
|
|
1123
|
+
"Failed to build project"
|
|
1124
|
+
);
|
|
1125
|
+
}
|
|
679
1126
|
async checkCommand(command) {
|
|
680
1127
|
try {
|
|
681
|
-
|
|
1128
|
+
const checkCmd = process.platform === "win32" ? "where" : "which";
|
|
1129
|
+
await execa2(checkCmd, [command]);
|
|
682
1130
|
return true;
|
|
683
1131
|
} catch {
|
|
684
1132
|
return false;
|
|
@@ -764,7 +1212,7 @@ async function generateFromWizard(config, outputPath) {
|
|
|
764
1212
|
|
|
765
1213
|
// src/parsers/openapi.ts
|
|
766
1214
|
import YAML from "yaml";
|
|
767
|
-
import
|
|
1215
|
+
import inquirer8 from "inquirer";
|
|
768
1216
|
async function parseOpenAPISpec(content) {
|
|
769
1217
|
let spec;
|
|
770
1218
|
try {
|
|
@@ -864,7 +1312,7 @@ async function selectEndpoints(endpoints) {
|
|
|
864
1312
|
value: ep,
|
|
865
1313
|
checked: true
|
|
866
1314
|
}));
|
|
867
|
-
const { selected } = await
|
|
1315
|
+
const { selected } = await inquirer8.prompt([
|
|
868
1316
|
{
|
|
869
1317
|
type: "checkbox",
|
|
870
1318
|
name: "selected",
|
|
@@ -1000,7 +1448,7 @@ function mapOpenAPIType(type) {
|
|
|
1000
1448
|
|
|
1001
1449
|
// src/generators/from-prompt.ts
|
|
1002
1450
|
import Anthropic from "@anthropic-ai/sdk";
|
|
1003
|
-
import
|
|
1451
|
+
import inquirer9 from "inquirer";
|
|
1004
1452
|
var SYSTEM_PROMPT = `You are an expert at designing MCP (Model Context Protocol) servers.
|
|
1005
1453
|
Given a description of an API or functionality, you generate a list of tools that would be useful for that API.
|
|
1006
1454
|
|
|
@@ -1058,7 +1506,7 @@ var PromptGenerator = class extends BaseGenerator {
|
|
|
1058
1506
|
}
|
|
1059
1507
|
};
|
|
1060
1508
|
async function generateFromPrompt(baseConfig) {
|
|
1061
|
-
const { description } = await
|
|
1509
|
+
const { description } = await inquirer9.prompt([
|
|
1062
1510
|
{
|
|
1063
1511
|
type: "editor",
|
|
1064
1512
|
name: "description",
|
|
@@ -1114,7 +1562,7 @@ async function generateFromPrompt(baseConfig) {
|
|
|
1114
1562
|
logger.list([`${index + 1}. ${tool.name} - ${tool.description}`]);
|
|
1115
1563
|
});
|
|
1116
1564
|
logger.blank();
|
|
1117
|
-
const { confirm } = await
|
|
1565
|
+
const { confirm } = await inquirer9.prompt([
|
|
1118
1566
|
{
|
|
1119
1567
|
type: "confirm",
|
|
1120
1568
|
name: "confirm",
|
|
@@ -1141,10 +1589,85 @@ async function generateFromPrompt(baseConfig) {
|
|
|
1141
1589
|
await generator.generate();
|
|
1142
1590
|
}
|
|
1143
1591
|
|
|
1592
|
+
// src/generators/from-preset.ts
|
|
1593
|
+
var PresetGenerator = class extends BaseGenerator {
|
|
1594
|
+
presetName;
|
|
1595
|
+
constructor(context, presetName) {
|
|
1596
|
+
super(context);
|
|
1597
|
+
this.presetName = presetName;
|
|
1598
|
+
}
|
|
1599
|
+
async generate() {
|
|
1600
|
+
logger.title(`Creating ${this.config.name} from "${this.presetName}" preset`);
|
|
1601
|
+
const isSafe = await this.checkOutputDir();
|
|
1602
|
+
if (!isSafe) {
|
|
1603
|
+
throw new Error(
|
|
1604
|
+
`Directory ${this.outputDir} already exists and is not empty. Please choose a different name or delete the existing directory.`
|
|
1605
|
+
);
|
|
1606
|
+
}
|
|
1607
|
+
await withSpinner(
|
|
1608
|
+
"Creating project structure...",
|
|
1609
|
+
async () => {
|
|
1610
|
+
await this.createProjectStructure();
|
|
1611
|
+
},
|
|
1612
|
+
"Project structure created"
|
|
1613
|
+
);
|
|
1614
|
+
await withSpinner(
|
|
1615
|
+
"Generating files from templates...",
|
|
1616
|
+
async () => {
|
|
1617
|
+
await this.renderTemplates();
|
|
1618
|
+
},
|
|
1619
|
+
"Files generated"
|
|
1620
|
+
);
|
|
1621
|
+
await this.installDependencies();
|
|
1622
|
+
await this.initializeGit();
|
|
1623
|
+
logger.success(`Project ${this.config.name} created successfully!`);
|
|
1624
|
+
logger.info(`Preset: ${this.presetName}`);
|
|
1625
|
+
logger.info(`Tools included: ${this.config.tools.map((t) => t.name).join(", ")}`);
|
|
1626
|
+
logger.nextSteps(this.config.name, this.config.language);
|
|
1627
|
+
}
|
|
1628
|
+
};
|
|
1629
|
+
async function generateFromPreset(options) {
|
|
1630
|
+
const preset = getPreset(options.presetId);
|
|
1631
|
+
if (!preset) {
|
|
1632
|
+
throw new Error(`Invalid preset: ${options.presetId}`);
|
|
1633
|
+
}
|
|
1634
|
+
const name = options.projectName || await promptProjectName();
|
|
1635
|
+
const description = options.useDefaults ? "" : await promptProjectDescription();
|
|
1636
|
+
const language = options.language || (options.useDefaults ? "typescript" : await promptLanguage());
|
|
1637
|
+
const transport = options.useDefaults ? "stdio" : await promptTransport();
|
|
1638
|
+
const config = {
|
|
1639
|
+
name,
|
|
1640
|
+
description,
|
|
1641
|
+
language,
|
|
1642
|
+
transport,
|
|
1643
|
+
tools: preset.tools,
|
|
1644
|
+
resources: [],
|
|
1645
|
+
includeExampleTool: false,
|
|
1646
|
+
skipInstall: options.skipInstall || false,
|
|
1647
|
+
initGit: true
|
|
1648
|
+
};
|
|
1649
|
+
const context = createGeneratorContext(config);
|
|
1650
|
+
const generator = new PresetGenerator(context, preset.name);
|
|
1651
|
+
await generator.generate();
|
|
1652
|
+
}
|
|
1653
|
+
function validatePresetId(presetId) {
|
|
1654
|
+
if (!isValidPresetId(presetId)) {
|
|
1655
|
+
const validPresets = ["database", "rest-api", "filesystem"];
|
|
1656
|
+
throw new Error(
|
|
1657
|
+
`Invalid preset "${presetId}". Valid presets are: ${validPresets.join(", ")}`
|
|
1658
|
+
);
|
|
1659
|
+
}
|
|
1660
|
+
return true;
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1144
1663
|
// src/commands/create.ts
|
|
1145
1664
|
import path4 from "path";
|
|
1146
1665
|
async function createCommand(projectName, options) {
|
|
1147
1666
|
try {
|
|
1667
|
+
if (options.preset) {
|
|
1668
|
+
await handlePresetGeneration(projectName, options);
|
|
1669
|
+
return;
|
|
1670
|
+
}
|
|
1148
1671
|
if (options.fromOpenapi) {
|
|
1149
1672
|
await handleOpenAPIGeneration(projectName, options);
|
|
1150
1673
|
return;
|
|
@@ -1165,7 +1688,7 @@ async function createCommand(projectName, options) {
|
|
|
1165
1688
|
}
|
|
1166
1689
|
async function handleWizardGeneration(projectName, options) {
|
|
1167
1690
|
let config;
|
|
1168
|
-
const presetLanguage = options.typescript ? "typescript" : options.python ? "python" : void 0;
|
|
1691
|
+
const presetLanguage = options.typescript ? "typescript" : options.python ? "python" : options.go ? "go" : options.rust ? "rust" : void 0;
|
|
1169
1692
|
if (options.yes) {
|
|
1170
1693
|
config = await runQuickWizard(projectName, presetLanguage);
|
|
1171
1694
|
} else {
|
|
@@ -1193,29 +1716,42 @@ async function handleOpenAPIGeneration(projectName, options) {
|
|
|
1193
1716
|
const name = projectName || path4.basename(specPath, path4.extname(specPath)) + "-mcp";
|
|
1194
1717
|
await generateFromOpenAPI(specPath, {
|
|
1195
1718
|
name,
|
|
1196
|
-
language: options.typescript ? "typescript" : options.python ? "python" : void 0,
|
|
1719
|
+
language: options.typescript ? "typescript" : options.python ? "python" : options.go ? "go" : options.rust ? "rust" : void 0,
|
|
1197
1720
|
skipInstall: options.skipInstall
|
|
1198
1721
|
});
|
|
1199
1722
|
}
|
|
1200
1723
|
async function handlePromptGeneration(projectName, options) {
|
|
1201
1724
|
await generateFromPrompt({
|
|
1202
1725
|
name: projectName,
|
|
1203
|
-
language: options.typescript ? "typescript" : options.python ? "python" : void 0,
|
|
1726
|
+
language: options.typescript ? "typescript" : options.python ? "python" : options.go ? "go" : options.rust ? "rust" : void 0,
|
|
1204
1727
|
skipInstall: options.skipInstall
|
|
1205
1728
|
});
|
|
1206
1729
|
}
|
|
1730
|
+
async function handlePresetGeneration(projectName, options) {
|
|
1731
|
+
const presetId = options.preset;
|
|
1732
|
+
validatePresetId(presetId);
|
|
1733
|
+
await generateFromPreset({
|
|
1734
|
+
projectName,
|
|
1735
|
+
presetId,
|
|
1736
|
+
language: options.typescript ? "typescript" : options.python ? "python" : options.go ? "go" : options.rust ? "rust" : void 0,
|
|
1737
|
+
skipInstall: options.skipInstall,
|
|
1738
|
+
useDefaults: options.yes
|
|
1739
|
+
});
|
|
1740
|
+
}
|
|
1207
1741
|
|
|
1208
1742
|
// src/commands/init.ts
|
|
1209
1743
|
import path5 from "path";
|
|
1210
|
-
import
|
|
1744
|
+
import inquirer10 from "inquirer";
|
|
1211
1745
|
async function initCommand(options) {
|
|
1212
1746
|
try {
|
|
1213
1747
|
const currentDir = process.cwd();
|
|
1214
1748
|
const dirName = path5.basename(currentDir);
|
|
1215
1749
|
const hasPackageJson = await exists(path5.join(currentDir, "package.json"));
|
|
1216
1750
|
const hasPyproject = await exists(path5.join(currentDir, "pyproject.toml"));
|
|
1217
|
-
|
|
1218
|
-
|
|
1751
|
+
const hasGoMod = await exists(path5.join(currentDir, "go.mod"));
|
|
1752
|
+
const hasCargoToml = await exists(path5.join(currentDir, "Cargo.toml"));
|
|
1753
|
+
if ((hasPackageJson || hasPyproject || hasGoMod || hasCargoToml) && !options.force) {
|
|
1754
|
+
const { proceed } = await inquirer10.prompt([
|
|
1219
1755
|
{
|
|
1220
1756
|
type: "confirm",
|
|
1221
1757
|
name: "proceed",
|
|
@@ -1233,12 +1769,22 @@ async function initCommand(options) {
|
|
|
1233
1769
|
language = "typescript";
|
|
1234
1770
|
} else if (options.python) {
|
|
1235
1771
|
language = "python";
|
|
1772
|
+
} else if (options.go) {
|
|
1773
|
+
language = "go";
|
|
1774
|
+
} else if (options.rust) {
|
|
1775
|
+
language = "rust";
|
|
1236
1776
|
} else if (hasPackageJson) {
|
|
1237
1777
|
language = "typescript";
|
|
1238
1778
|
logger.info("Detected existing Node.js project, using TypeScript");
|
|
1239
1779
|
} else if (hasPyproject) {
|
|
1240
1780
|
language = "python";
|
|
1241
1781
|
logger.info("Detected existing Python project");
|
|
1782
|
+
} else if (hasGoMod) {
|
|
1783
|
+
language = "go";
|
|
1784
|
+
logger.info("Detected existing Go project");
|
|
1785
|
+
} else if (hasCargoToml) {
|
|
1786
|
+
language = "rust";
|
|
1787
|
+
logger.info("Detected existing Rust project");
|
|
1242
1788
|
} else {
|
|
1243
1789
|
language = await promptLanguage();
|
|
1244
1790
|
}
|
|
@@ -1283,7 +1829,9 @@ async function addToolCommand(options) {
|
|
|
1283
1829
|
const currentDir = process.cwd();
|
|
1284
1830
|
const isTypeScript = await exists(path6.join(currentDir, "package.json"));
|
|
1285
1831
|
const isPython = await exists(path6.join(currentDir, "pyproject.toml"));
|
|
1286
|
-
|
|
1832
|
+
const isGo = await exists(path6.join(currentDir, "go.mod"));
|
|
1833
|
+
const isRust = await exists(path6.join(currentDir, "Cargo.toml"));
|
|
1834
|
+
if (!isTypeScript && !isPython && !isGo && !isRust) {
|
|
1287
1835
|
logger.error("No MCP server project found in current directory.");
|
|
1288
1836
|
logger.info("Run this command from the root of your MCP server project.");
|
|
1289
1837
|
process.exit(1);
|
|
@@ -1301,8 +1849,12 @@ async function addToolCommand(options) {
|
|
|
1301
1849
|
}
|
|
1302
1850
|
if (isTypeScript) {
|
|
1303
1851
|
await addToolToTypeScript(currentDir, tool);
|
|
1304
|
-
} else {
|
|
1852
|
+
} else if (isPython) {
|
|
1305
1853
|
await addToolToPython(currentDir, tool);
|
|
1854
|
+
} else if (isGo) {
|
|
1855
|
+
await addToolToGo(currentDir, tool);
|
|
1856
|
+
} else if (isRust) {
|
|
1857
|
+
await addToolToRust(currentDir, tool);
|
|
1306
1858
|
}
|
|
1307
1859
|
logger.success(`Tool "${tool.name}" added successfully!`);
|
|
1308
1860
|
logger.info("Remember to implement the tool logic in the generated file.");
|
|
@@ -1479,6 +2031,173 @@ function mapTypeToPython(type) {
|
|
|
1479
2031
|
return "str";
|
|
1480
2032
|
}
|
|
1481
2033
|
}
|
|
2034
|
+
async function addToolToGo(projectDir, tool) {
|
|
2035
|
+
const toolsDir = path6.join(projectDir, "internal", "tools");
|
|
2036
|
+
const toolFileName = `${tool.name}.go`;
|
|
2037
|
+
const toolFilePath = path6.join(toolsDir, toolFileName);
|
|
2038
|
+
if (await exists(toolFilePath)) {
|
|
2039
|
+
logger.error(`Tool file already exists: ${toolFilePath}`);
|
|
2040
|
+
process.exit(1);
|
|
2041
|
+
}
|
|
2042
|
+
const content = generateGoToolFile(tool);
|
|
2043
|
+
await writeFile(toolFilePath, content);
|
|
2044
|
+
logger.info(`Created: internal/tools/${toolFileName}`);
|
|
2045
|
+
logger.blank();
|
|
2046
|
+
logger.info("Next steps:");
|
|
2047
|
+
logger.list([
|
|
2048
|
+
`Implement the tool logic in internal/tools/${toolFileName}`,
|
|
2049
|
+
"Import and register the tool in cmd/server/main.go"
|
|
2050
|
+
]);
|
|
2051
|
+
}
|
|
2052
|
+
async function addToolToRust(projectDir, tool) {
|
|
2053
|
+
const srcDir = path6.join(projectDir, "src");
|
|
2054
|
+
const toolFileName = `${tool.name}.rs`;
|
|
2055
|
+
const toolFilePath = path6.join(srcDir, toolFileName);
|
|
2056
|
+
if (await exists(toolFilePath)) {
|
|
2057
|
+
logger.error(`Tool file already exists: ${toolFilePath}`);
|
|
2058
|
+
process.exit(1);
|
|
2059
|
+
}
|
|
2060
|
+
const content = generateRustToolFile(tool);
|
|
2061
|
+
await writeFile(toolFilePath, content);
|
|
2062
|
+
logger.info(`Created: src/${toolFileName}`);
|
|
2063
|
+
logger.blank();
|
|
2064
|
+
logger.info("Next steps:");
|
|
2065
|
+
logger.list([
|
|
2066
|
+
`Implement the tool logic in src/${toolFileName}`,
|
|
2067
|
+
`Add "mod ${tool.name};" to src/main.rs`,
|
|
2068
|
+
"Register the tool in the server builder"
|
|
2069
|
+
]);
|
|
2070
|
+
}
|
|
2071
|
+
function generateGoToolFile(tool) {
|
|
2072
|
+
const structFields = tool.parameters.map((p) => ` ${toPascalCase(p.name)} ${mapTypeToGo(p.type)} \`json:"${p.name}"\``).join("\n");
|
|
2073
|
+
return `package tools
|
|
2074
|
+
|
|
2075
|
+
import (
|
|
2076
|
+
"context"
|
|
2077
|
+
"fmt"
|
|
2078
|
+
|
|
2079
|
+
"github.com/mark3labs/mcp-go/mcp"
|
|
2080
|
+
)
|
|
2081
|
+
|
|
2082
|
+
// ${toPascalCase(tool.name)}Input represents the input for the ${tool.name} tool
|
|
2083
|
+
type ${toPascalCase(tool.name)}Input struct {
|
|
2084
|
+
${structFields || " // No parameters"}
|
|
2085
|
+
}
|
|
2086
|
+
|
|
2087
|
+
// ${toPascalCase(tool.name)}Tool creates the ${tool.name} tool definition
|
|
2088
|
+
func ${toPascalCase(tool.name)}Tool() mcp.Tool {
|
|
2089
|
+
return mcp.NewTool("${tool.name}",
|
|
2090
|
+
mcp.WithDescription("${tool.description}"),
|
|
2091
|
+
${tool.parameters.map((p) => {
|
|
2092
|
+
const mcpType = p.type === "number" ? "WithNumber" : p.type === "boolean" ? "WithBoolean" : "WithString";
|
|
2093
|
+
return ` mcp.${mcpType}("${p.name}",
|
|
2094
|
+
${p.required ? " mcp.Required(),\n" : ""} mcp.Description("${p.description}"),
|
|
2095
|
+
),`;
|
|
2096
|
+
}).join("\n")}
|
|
2097
|
+
)
|
|
2098
|
+
}
|
|
2099
|
+
|
|
2100
|
+
// ${toPascalCase(tool.name)}Handler handles the ${tool.name} tool execution
|
|
2101
|
+
func ${toPascalCase(tool.name)}Handler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
2102
|
+
// TODO: Implement ${tool.name} logic
|
|
2103
|
+
${tool.parameters.map((p) => {
|
|
2104
|
+
const goType = mapTypeToGo(p.type);
|
|
2105
|
+
if (goType === "float64") {
|
|
2106
|
+
return ` ${p.name}, _ := request.Params.Arguments["${p.name}"].(float64)`;
|
|
2107
|
+
} else if (goType === "bool") {
|
|
2108
|
+
return ` ${p.name}, _ := request.Params.Arguments["${p.name}"].(bool)`;
|
|
2109
|
+
} else {
|
|
2110
|
+
return ` ${p.name}, _ := request.Params.Arguments["${p.name}"].(string)`;
|
|
2111
|
+
}
|
|
2112
|
+
}).join("\n")}
|
|
2113
|
+
|
|
2114
|
+
return mcp.NewToolResultText(fmt.Sprintf("${tool.name} called with: %v", request.Params.Arguments)), nil
|
|
2115
|
+
}
|
|
2116
|
+
`;
|
|
2117
|
+
}
|
|
2118
|
+
function generateRustToolFile(tool) {
|
|
2119
|
+
const structFields = tool.parameters.map((p) => ` pub ${p.name}: ${mapTypeToRust(p.type)},`).join("\n");
|
|
2120
|
+
const required = tool.parameters.filter((p) => p.required).map((p) => `"${p.name}".to_string()`).join(", ");
|
|
2121
|
+
return `use rmcp::{
|
|
2122
|
+
model::{CallToolResult, Content, Tool, ToolInputSchema},
|
|
2123
|
+
tool,
|
|
2124
|
+
};
|
|
2125
|
+
use serde::Deserialize;
|
|
2126
|
+
use serde_json::{json, Value};
|
|
2127
|
+
use std::collections::HashMap;
|
|
2128
|
+
|
|
2129
|
+
/// ${tool.description}
|
|
2130
|
+
#[derive(Debug, Deserialize)]
|
|
2131
|
+
pub struct ${toPascalCase(tool.name)}Input {
|
|
2132
|
+
${structFields || " // No parameters"}
|
|
2133
|
+
}
|
|
2134
|
+
|
|
2135
|
+
pub fn ${tool.name}_tool() -> Tool {
|
|
2136
|
+
Tool {
|
|
2137
|
+
name: "${tool.name}".to_string(),
|
|
2138
|
+
description: Some("${tool.description}".to_string()),
|
|
2139
|
+
input_schema: ToolInputSchema {
|
|
2140
|
+
r#type: "object".to_string(),
|
|
2141
|
+
properties: Some({
|
|
2142
|
+
let mut props = HashMap::new();
|
|
2143
|
+
${tool.parameters.map(
|
|
2144
|
+
(p) => ` props.insert(
|
|
2145
|
+
"${p.name}".to_string(),
|
|
2146
|
+
json!({
|
|
2147
|
+
"type": "${p.type}",
|
|
2148
|
+
"description": "${p.description}"
|
|
2149
|
+
}),
|
|
2150
|
+
);`
|
|
2151
|
+
).join("\n")}
|
|
2152
|
+
props
|
|
2153
|
+
}),
|
|
2154
|
+
required: Some(vec![${required}]),
|
|
2155
|
+
},
|
|
2156
|
+
handler: Box::new(|args| {
|
|
2157
|
+
Box::pin(async move {
|
|
2158
|
+
// TODO: Implement ${tool.name} logic
|
|
2159
|
+
let input: ${toPascalCase(tool.name)}Input = serde_json::from_value(args.clone())?;
|
|
2160
|
+
|
|
2161
|
+
Ok(CallToolResult {
|
|
2162
|
+
content: vec![Content::Text {
|
|
2163
|
+
text: format!("${tool.name} called with: {:?}", args),
|
|
2164
|
+
}],
|
|
2165
|
+
is_error: None,
|
|
2166
|
+
})
|
|
2167
|
+
})
|
|
2168
|
+
}),
|
|
2169
|
+
}
|
|
2170
|
+
}
|
|
2171
|
+
`;
|
|
2172
|
+
}
|
|
2173
|
+
function mapTypeToGo(type) {
|
|
2174
|
+
switch (type) {
|
|
2175
|
+
case "number":
|
|
2176
|
+
return "float64";
|
|
2177
|
+
case "boolean":
|
|
2178
|
+
return "bool";
|
|
2179
|
+
case "array":
|
|
2180
|
+
return "[]interface{}";
|
|
2181
|
+
case "object":
|
|
2182
|
+
return "map[string]interface{}";
|
|
2183
|
+
default:
|
|
2184
|
+
return "string";
|
|
2185
|
+
}
|
|
2186
|
+
}
|
|
2187
|
+
function mapTypeToRust(type) {
|
|
2188
|
+
switch (type) {
|
|
2189
|
+
case "number":
|
|
2190
|
+
return "f64";
|
|
2191
|
+
case "boolean":
|
|
2192
|
+
return "bool";
|
|
2193
|
+
case "array":
|
|
2194
|
+
return "Vec<Value>";
|
|
2195
|
+
case "object":
|
|
2196
|
+
return "HashMap<String, Value>";
|
|
2197
|
+
default:
|
|
2198
|
+
return "String";
|
|
2199
|
+
}
|
|
2200
|
+
}
|
|
1482
2201
|
|
|
1483
2202
|
export {
|
|
1484
2203
|
projectNameRegex,
|
|
@@ -1499,6 +2218,8 @@ export {
|
|
|
1499
2218
|
promptAddResources,
|
|
1500
2219
|
promptResourceConfig,
|
|
1501
2220
|
promptMultipleResources,
|
|
2221
|
+
promptGenerationMethod,
|
|
2222
|
+
promptPreset,
|
|
1502
2223
|
runWizard,
|
|
1503
2224
|
runQuickWizard,
|
|
1504
2225
|
ensureDir,
|
|
@@ -1534,6 +2255,9 @@ export {
|
|
|
1534
2255
|
generateFromOpenAPI,
|
|
1535
2256
|
PromptGenerator,
|
|
1536
2257
|
generateFromPrompt,
|
|
2258
|
+
PresetGenerator,
|
|
2259
|
+
generateFromPreset,
|
|
2260
|
+
validatePresetId,
|
|
1537
2261
|
createCommand,
|
|
1538
2262
|
initCommand,
|
|
1539
2263
|
addToolCommand
|