mcp-new 0.1.0 → 1.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.
package/README.md CHANGED
@@ -1,17 +1,17 @@
1
- # mcp-new
1
+ # mcp-generator
2
2
 
3
3
  > CLI generator for MCP servers in seconds. Like `create-react-app`, but for MCP.
4
4
 
5
5
  ## Quick Start
6
6
 
7
7
  ```bash
8
- npx mcp-new my-server
8
+ npx mcp-generator my-server
9
9
  ```
10
10
 
11
11
  ## Features
12
12
 
13
13
  - **Interactive wizard** — create MCP server through step-by-step prompts
14
- - **TypeScript and Python** — support for both languages
14
+ - **Multi-language support** — TypeScript, Python, Go, and Rust
15
15
  - **OpenAPI generation** — auto-create tools from OpenAPI/Swagger specification
16
16
  - **AI generation** — create tools from text description using Claude
17
17
  - **Ready-to-use templates** — working code with examples out of the box
@@ -19,13 +19,13 @@ npx mcp-new my-server
19
19
  ## Installation
20
20
 
21
21
  ```bash
22
- npm install -g mcp-new
22
+ npm install -g mcp-generator
23
23
  ```
24
24
 
25
25
  Or use directly via npx:
26
26
 
27
27
  ```bash
28
- npx mcp-new my-server
28
+ npx mcp-generator my-server
29
29
  ```
30
30
 
31
31
  ## Usage
@@ -33,7 +33,7 @@ npx mcp-new my-server
33
33
  ### Basic creation
34
34
 
35
35
  ```bash
36
- npx mcp-new my-weather-api
36
+ npx mcp-generator my-weather-api
37
37
 
38
38
  # Answer the questions:
39
39
  # ? Project name: my-weather-api
@@ -51,22 +51,28 @@ npm run dev
51
51
 
52
52
  ```bash
53
53
  # TypeScript project (skips language prompt)
54
- npx mcp-new my-server -t
54
+ npx mcp-generator my-server -t
55
55
 
56
56
  # Python project (skips language prompt)
57
- npx mcp-new my-server -p
57
+ npx mcp-generator my-server -p
58
+
59
+ # Go project (skips language prompt)
60
+ npx mcp-generator my-server -g
61
+
62
+ # Rust project (skips language prompt)
63
+ npx mcp-generator my-server -r
58
64
 
59
65
  # Skip dependency installation
60
- npx mcp-new my-server --skip-install
66
+ npx mcp-generator my-server --skip-install
61
67
 
62
68
  # Use default values
63
- npx mcp-new my-server -y
69
+ npx mcp-generator my-server -y
64
70
  ```
65
71
 
66
72
  ### From OpenAPI specification
67
73
 
68
74
  ```bash
69
- npx mcp-new stripe-mcp --from-openapi ./stripe-api.yaml
75
+ npx mcp-generator stripe-mcp --from-openapi ./stripe-api.yaml
70
76
 
71
77
  # CLI will show found endpoints and let you select the ones you need
72
78
  ```
@@ -74,7 +80,7 @@ npx mcp-new stripe-mcp --from-openapi ./stripe-api.yaml
74
80
  ### From text description (AI)
75
81
 
76
82
  ```bash
77
- npx mcp-new notion-mcp --from-prompt
83
+ npx mcp-generator notion-mcp --from-prompt
78
84
 
79
85
  # Describe your API in the editor
80
86
  # Claude will generate tools automatically
@@ -86,23 +92,23 @@ Requires `ANTHROPIC_API_KEY` environment variable.
86
92
 
87
93
  ```bash
88
94
  cd my-existing-project
89
- npx mcp-new init
95
+ npx mcp-generator init
90
96
  ```
91
97
 
92
98
  ### Add new tool
93
99
 
94
100
  ```bash
95
101
  cd my-mcp-server
96
- npx mcp-new add-tool
102
+ npx mcp-generator add-tool
97
103
  ```
98
104
 
99
105
  ## CLI Commands
100
106
 
101
107
  | Command | Description |
102
108
  |---------|-------------|
103
- | `mcp-new <name>` | Create new MCP server |
104
- | `mcp-new init` | Initialize MCP in current directory |
105
- | `mcp-new add-tool` | Add tool to existing server |
109
+ | `mcp-generator <name>` | Create new MCP server |
110
+ | `mcp-generator init` | Initialize MCP in current directory |
111
+ | `mcp-generator add-tool` | Add tool to existing server |
106
112
 
107
113
  ## Options
108
114
 
@@ -110,6 +116,8 @@ npx mcp-new add-tool
110
116
  |------|-------------|
111
117
  | `-t, --typescript` | Use TypeScript (skips language prompt) |
112
118
  | `-p, --python` | Use Python (skips language prompt) |
119
+ | `-g, --go` | Use Go (skips language prompt) |
120
+ | `-r, --rust` | Use Rust (skips language prompt) |
113
121
  | `--skip-install` | Skip dependency installation |
114
122
  | `--from-openapi <path>` | Create from OpenAPI specification |
115
123
  | `--from-prompt` | Create via AI from description |
@@ -149,12 +157,41 @@ my-server/
149
157
  └── example_tool.py
150
158
  ```
151
159
 
160
+ ### Go
161
+
162
+ ```
163
+ my-server/
164
+ ├── go.mod
165
+ ├── README.md
166
+ ├── .gitignore
167
+ ├── .env.example
168
+ ├── cmd/
169
+ │ └── server/
170
+ │ └── main.go # Main server file
171
+ └── internal/
172
+ └── tools/
173
+ └── example.go
174
+ ```
175
+
176
+ ### Rust
177
+
178
+ ```
179
+ my-server/
180
+ ├── Cargo.toml
181
+ ├── README.md
182
+ ├── .gitignore
183
+ ├── .env.example
184
+ └── src/
185
+ ├── main.rs # Main server file
186
+ └── tools.rs
187
+ ```
188
+
152
189
  ## Development
153
190
 
154
191
  ```bash
155
192
  # Clone repository
156
- git clone https://github.com/d1maash/mcp-new.git
157
- cd mcp-new
193
+ git clone https://github.com/d1maash/mcp-generator.git
194
+ cd mcp-generator
158
195
 
159
196
  # Install dependencies
160
197
  npm install
@@ -170,7 +207,7 @@ npm test
170
207
 
171
208
  # Local CLI testing
172
209
  npm link
173
- mcp-new test-project
210
+ mcp-generator test-project
174
211
  ```
175
212
 
176
213
  ## Links
@@ -178,6 +215,8 @@ mcp-new test-project
178
215
  - [MCP Specification](https://spec.modelcontextprotocol.io/)
179
216
  - [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk)
180
217
  - [MCP Python SDK](https://github.com/modelcontextprotocol/python-sdk)
218
+ - [MCP Go SDK](https://github.com/mark3labs/mcp-go)
219
+ - [MCP Rust SDK](https://github.com/modelcontextprotocol/rust-sdk)
181
220
 
182
221
  ## License
183
222
 
@@ -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
  }
@@ -639,11 +641,19 @@ var BaseGenerator = class {
639
641
  logger.info("Skipping dependency installation (--skip-install)");
640
642
  return;
641
643
  }
642
- const isTypescript = this.config.language === "typescript";
643
- if (isTypescript) {
644
- await this.installNodeDependencies();
645
- } else {
646
- await this.installPythonDependencies();
644
+ switch (this.config.language) {
645
+ case "typescript":
646
+ await this.installNodeDependencies();
647
+ break;
648
+ case "python":
649
+ await this.installPythonDependencies();
650
+ break;
651
+ case "go":
652
+ await this.installGoDependencies();
653
+ break;
654
+ case "rust":
655
+ await this.installRustDependencies();
656
+ break;
647
657
  }
648
658
  }
649
659
  async installNodeDependencies() {
@@ -676,9 +686,49 @@ var BaseGenerator = class {
676
686
  "Failed to install dependencies"
677
687
  );
678
688
  }
689
+ async installGoDependencies() {
690
+ const hasGo = await this.checkCommand("go");
691
+ if (!hasGo) {
692
+ logger.warning("Go not found. Please install dependencies manually:");
693
+ logger.code("go mod download");
694
+ return;
695
+ }
696
+ await withSpinner(
697
+ "Installing Go dependencies...",
698
+ async () => {
699
+ await execa2("go", ["mod", "download"], {
700
+ cwd: this.outputDir
701
+ });
702
+ await execa2("go", ["mod", "tidy"], {
703
+ cwd: this.outputDir
704
+ });
705
+ },
706
+ "Dependencies installed",
707
+ "Failed to install dependencies"
708
+ );
709
+ }
710
+ async installRustDependencies() {
711
+ const hasCargo = await this.checkCommand("cargo");
712
+ if (!hasCargo) {
713
+ logger.warning("Cargo not found. Please install dependencies manually:");
714
+ logger.code("cargo build");
715
+ return;
716
+ }
717
+ await withSpinner(
718
+ "Building Rust project...",
719
+ async () => {
720
+ await execa2("cargo", ["build"], {
721
+ cwd: this.outputDir
722
+ });
723
+ },
724
+ "Project built successfully",
725
+ "Failed to build project"
726
+ );
727
+ }
679
728
  async checkCommand(command) {
680
729
  try {
681
- await execa2("which", [command]);
730
+ const checkCmd = process.platform === "win32" ? "where" : "which";
731
+ await execa2(checkCmd, [command]);
682
732
  return true;
683
733
  } catch {
684
734
  return false;
@@ -1165,7 +1215,7 @@ async function createCommand(projectName, options) {
1165
1215
  }
1166
1216
  async function handleWizardGeneration(projectName, options) {
1167
1217
  let config;
1168
- const presetLanguage = options.typescript ? "typescript" : options.python ? "python" : void 0;
1218
+ const presetLanguage = options.typescript ? "typescript" : options.python ? "python" : options.go ? "go" : options.rust ? "rust" : void 0;
1169
1219
  if (options.yes) {
1170
1220
  config = await runQuickWizard(projectName, presetLanguage);
1171
1221
  } else {
@@ -1193,14 +1243,14 @@ async function handleOpenAPIGeneration(projectName, options) {
1193
1243
  const name = projectName || path4.basename(specPath, path4.extname(specPath)) + "-mcp";
1194
1244
  await generateFromOpenAPI(specPath, {
1195
1245
  name,
1196
- language: options.typescript ? "typescript" : options.python ? "python" : void 0,
1246
+ language: options.typescript ? "typescript" : options.python ? "python" : options.go ? "go" : options.rust ? "rust" : void 0,
1197
1247
  skipInstall: options.skipInstall
1198
1248
  });
1199
1249
  }
1200
1250
  async function handlePromptGeneration(projectName, options) {
1201
1251
  await generateFromPrompt({
1202
1252
  name: projectName,
1203
- language: options.typescript ? "typescript" : options.python ? "python" : void 0,
1253
+ language: options.typescript ? "typescript" : options.python ? "python" : options.go ? "go" : options.rust ? "rust" : void 0,
1204
1254
  skipInstall: options.skipInstall
1205
1255
  });
1206
1256
  }
@@ -1214,7 +1264,9 @@ async function initCommand(options) {
1214
1264
  const dirName = path5.basename(currentDir);
1215
1265
  const hasPackageJson = await exists(path5.join(currentDir, "package.json"));
1216
1266
  const hasPyproject = await exists(path5.join(currentDir, "pyproject.toml"));
1217
- if ((hasPackageJson || hasPyproject) && !options.force) {
1267
+ const hasGoMod = await exists(path5.join(currentDir, "go.mod"));
1268
+ const hasCargoToml = await exists(path5.join(currentDir, "Cargo.toml"));
1269
+ if ((hasPackageJson || hasPyproject || hasGoMod || hasCargoToml) && !options.force) {
1218
1270
  const { proceed } = await inquirer8.prompt([
1219
1271
  {
1220
1272
  type: "confirm",
@@ -1233,12 +1285,22 @@ async function initCommand(options) {
1233
1285
  language = "typescript";
1234
1286
  } else if (options.python) {
1235
1287
  language = "python";
1288
+ } else if (options.go) {
1289
+ language = "go";
1290
+ } else if (options.rust) {
1291
+ language = "rust";
1236
1292
  } else if (hasPackageJson) {
1237
1293
  language = "typescript";
1238
1294
  logger.info("Detected existing Node.js project, using TypeScript");
1239
1295
  } else if (hasPyproject) {
1240
1296
  language = "python";
1241
1297
  logger.info("Detected existing Python project");
1298
+ } else if (hasGoMod) {
1299
+ language = "go";
1300
+ logger.info("Detected existing Go project");
1301
+ } else if (hasCargoToml) {
1302
+ language = "rust";
1303
+ logger.info("Detected existing Rust project");
1242
1304
  } else {
1243
1305
  language = await promptLanguage();
1244
1306
  }
@@ -1283,7 +1345,9 @@ async function addToolCommand(options) {
1283
1345
  const currentDir = process.cwd();
1284
1346
  const isTypeScript = await exists(path6.join(currentDir, "package.json"));
1285
1347
  const isPython = await exists(path6.join(currentDir, "pyproject.toml"));
1286
- if (!isTypeScript && !isPython) {
1348
+ const isGo = await exists(path6.join(currentDir, "go.mod"));
1349
+ const isRust = await exists(path6.join(currentDir, "Cargo.toml"));
1350
+ if (!isTypeScript && !isPython && !isGo && !isRust) {
1287
1351
  logger.error("No MCP server project found in current directory.");
1288
1352
  logger.info("Run this command from the root of your MCP server project.");
1289
1353
  process.exit(1);
@@ -1301,8 +1365,12 @@ async function addToolCommand(options) {
1301
1365
  }
1302
1366
  if (isTypeScript) {
1303
1367
  await addToolToTypeScript(currentDir, tool);
1304
- } else {
1368
+ } else if (isPython) {
1305
1369
  await addToolToPython(currentDir, tool);
1370
+ } else if (isGo) {
1371
+ await addToolToGo(currentDir, tool);
1372
+ } else if (isRust) {
1373
+ await addToolToRust(currentDir, tool);
1306
1374
  }
1307
1375
  logger.success(`Tool "${tool.name}" added successfully!`);
1308
1376
  logger.info("Remember to implement the tool logic in the generated file.");
@@ -1479,6 +1547,173 @@ function mapTypeToPython(type) {
1479
1547
  return "str";
1480
1548
  }
1481
1549
  }
1550
+ async function addToolToGo(projectDir, tool) {
1551
+ const toolsDir = path6.join(projectDir, "internal", "tools");
1552
+ const toolFileName = `${tool.name}.go`;
1553
+ const toolFilePath = path6.join(toolsDir, toolFileName);
1554
+ if (await exists(toolFilePath)) {
1555
+ logger.error(`Tool file already exists: ${toolFilePath}`);
1556
+ process.exit(1);
1557
+ }
1558
+ const content = generateGoToolFile(tool);
1559
+ await writeFile(toolFilePath, content);
1560
+ logger.info(`Created: internal/tools/${toolFileName}`);
1561
+ logger.blank();
1562
+ logger.info("Next steps:");
1563
+ logger.list([
1564
+ `Implement the tool logic in internal/tools/${toolFileName}`,
1565
+ "Import and register the tool in cmd/server/main.go"
1566
+ ]);
1567
+ }
1568
+ async function addToolToRust(projectDir, tool) {
1569
+ const srcDir = path6.join(projectDir, "src");
1570
+ const toolFileName = `${tool.name}.rs`;
1571
+ const toolFilePath = path6.join(srcDir, toolFileName);
1572
+ if (await exists(toolFilePath)) {
1573
+ logger.error(`Tool file already exists: ${toolFilePath}`);
1574
+ process.exit(1);
1575
+ }
1576
+ const content = generateRustToolFile(tool);
1577
+ await writeFile(toolFilePath, content);
1578
+ logger.info(`Created: src/${toolFileName}`);
1579
+ logger.blank();
1580
+ logger.info("Next steps:");
1581
+ logger.list([
1582
+ `Implement the tool logic in src/${toolFileName}`,
1583
+ `Add "mod ${tool.name};" to src/main.rs`,
1584
+ "Register the tool in the server builder"
1585
+ ]);
1586
+ }
1587
+ function generateGoToolFile(tool) {
1588
+ const structFields = tool.parameters.map((p) => ` ${toPascalCase(p.name)} ${mapTypeToGo(p.type)} \`json:"${p.name}"\``).join("\n");
1589
+ return `package tools
1590
+
1591
+ import (
1592
+ "context"
1593
+ "fmt"
1594
+
1595
+ "github.com/mark3labs/mcp-go/mcp"
1596
+ )
1597
+
1598
+ // ${toPascalCase(tool.name)}Input represents the input for the ${tool.name} tool
1599
+ type ${toPascalCase(tool.name)}Input struct {
1600
+ ${structFields || " // No parameters"}
1601
+ }
1602
+
1603
+ // ${toPascalCase(tool.name)}Tool creates the ${tool.name} tool definition
1604
+ func ${toPascalCase(tool.name)}Tool() mcp.Tool {
1605
+ return mcp.NewTool("${tool.name}",
1606
+ mcp.WithDescription("${tool.description}"),
1607
+ ${tool.parameters.map((p) => {
1608
+ const mcpType = p.type === "number" ? "WithNumber" : p.type === "boolean" ? "WithBoolean" : "WithString";
1609
+ return ` mcp.${mcpType}("${p.name}",
1610
+ ${p.required ? " mcp.Required(),\n" : ""} mcp.Description("${p.description}"),
1611
+ ),`;
1612
+ }).join("\n")}
1613
+ )
1614
+ }
1615
+
1616
+ // ${toPascalCase(tool.name)}Handler handles the ${tool.name} tool execution
1617
+ func ${toPascalCase(tool.name)}Handler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
1618
+ // TODO: Implement ${tool.name} logic
1619
+ ${tool.parameters.map((p) => {
1620
+ const goType = mapTypeToGo(p.type);
1621
+ if (goType === "float64") {
1622
+ return ` ${p.name}, _ := request.Params.Arguments["${p.name}"].(float64)`;
1623
+ } else if (goType === "bool") {
1624
+ return ` ${p.name}, _ := request.Params.Arguments["${p.name}"].(bool)`;
1625
+ } else {
1626
+ return ` ${p.name}, _ := request.Params.Arguments["${p.name}"].(string)`;
1627
+ }
1628
+ }).join("\n")}
1629
+
1630
+ return mcp.NewToolResultText(fmt.Sprintf("${tool.name} called with: %v", request.Params.Arguments)), nil
1631
+ }
1632
+ `;
1633
+ }
1634
+ function generateRustToolFile(tool) {
1635
+ const structFields = tool.parameters.map((p) => ` pub ${p.name}: ${mapTypeToRust(p.type)},`).join("\n");
1636
+ const required = tool.parameters.filter((p) => p.required).map((p) => `"${p.name}".to_string()`).join(", ");
1637
+ return `use rmcp::{
1638
+ model::{CallToolResult, Content, Tool, ToolInputSchema},
1639
+ tool,
1640
+ };
1641
+ use serde::Deserialize;
1642
+ use serde_json::{json, Value};
1643
+ use std::collections::HashMap;
1644
+
1645
+ /// ${tool.description}
1646
+ #[derive(Debug, Deserialize)]
1647
+ pub struct ${toPascalCase(tool.name)}Input {
1648
+ ${structFields || " // No parameters"}
1649
+ }
1650
+
1651
+ pub fn ${tool.name}_tool() -> Tool {
1652
+ Tool {
1653
+ name: "${tool.name}".to_string(),
1654
+ description: Some("${tool.description}".to_string()),
1655
+ input_schema: ToolInputSchema {
1656
+ r#type: "object".to_string(),
1657
+ properties: Some({
1658
+ let mut props = HashMap::new();
1659
+ ${tool.parameters.map(
1660
+ (p) => ` props.insert(
1661
+ "${p.name}".to_string(),
1662
+ json!({
1663
+ "type": "${p.type}",
1664
+ "description": "${p.description}"
1665
+ }),
1666
+ );`
1667
+ ).join("\n")}
1668
+ props
1669
+ }),
1670
+ required: Some(vec![${required}]),
1671
+ },
1672
+ handler: Box::new(|args| {
1673
+ Box::pin(async move {
1674
+ // TODO: Implement ${tool.name} logic
1675
+ let input: ${toPascalCase(tool.name)}Input = serde_json::from_value(args.clone())?;
1676
+
1677
+ Ok(CallToolResult {
1678
+ content: vec![Content::Text {
1679
+ text: format!("${tool.name} called with: {:?}", args),
1680
+ }],
1681
+ is_error: None,
1682
+ })
1683
+ })
1684
+ }),
1685
+ }
1686
+ }
1687
+ `;
1688
+ }
1689
+ function mapTypeToGo(type) {
1690
+ switch (type) {
1691
+ case "number":
1692
+ return "float64";
1693
+ case "boolean":
1694
+ return "bool";
1695
+ case "array":
1696
+ return "[]interface{}";
1697
+ case "object":
1698
+ return "map[string]interface{}";
1699
+ default:
1700
+ return "string";
1701
+ }
1702
+ }
1703
+ function mapTypeToRust(type) {
1704
+ switch (type) {
1705
+ case "number":
1706
+ return "f64";
1707
+ case "boolean":
1708
+ return "bool";
1709
+ case "array":
1710
+ return "Vec<Value>";
1711
+ case "object":
1712
+ return "HashMap<String, Value>";
1713
+ default:
1714
+ return "String";
1715
+ }
1716
+ }
1482
1717
 
1483
1718
  export {
1484
1719
  projectNameRegex,
package/dist/cli.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  addToolCommand,
4
4
  createCommand,
5
5
  initCommand
6
- } from "./chunk-QRUHMGU5.js";
6
+ } from "./chunk-K3TOM22I.js";
7
7
 
8
8
  // src/cli.ts
9
9
  import { Command } from "commander";
@@ -11,13 +11,13 @@ import chalk from "chalk";
11
11
  var program = new Command();
12
12
  var logo = `
13
13
  ${chalk.cyan("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557")}
14
- ${chalk.cyan("\u2551")} ${chalk.bold.white("mcp-new")} ${chalk.cyan("\u2551")}
14
+ ${chalk.cyan("\u2551")} ${chalk.bold.white("mcp-generator")} ${chalk.cyan("\u2551")}
15
15
  ${chalk.cyan("\u2551")} ${chalk.gray("Generate MCP servers in seconds")} ${chalk.cyan("\u2551")}
16
16
  ${chalk.cyan("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D")}
17
17
  `;
18
- program.name("mcp-new").description("CLI tool for generating MCP (Model Context Protocol) servers").version("0.1.0").addHelpText("beforeAll", logo);
19
- program.argument("[project-name]", "Name of the project to create").option("-t, --typescript", "Use TypeScript template").option("-p, --python", "Use Python template").option("--skip-install", "Skip dependency installation").option("--from-openapi <path>", "Generate from OpenAPI/Swagger specification").option("--from-prompt", "Generate tools using AI from text description").option("-y, --yes", "Skip prompts and use defaults").action(createCommand);
20
- program.command("init").description("Initialize MCP server in the current directory").option("-t, --typescript", "Use TypeScript template").option("-p, --python", "Use Python template").option("--skip-install", "Skip dependency installation").option("-f, --force", "Initialize even if directory contains files").action(initCommand);
18
+ program.name("mcp-generator").description("CLI tool for generating MCP (Model Context Protocol) servers").version("0.1.0").addHelpText("beforeAll", logo);
19
+ program.argument("[project-name]", "Name of the project to create").option("-t, --typescript", "Use TypeScript template").option("-p, --python", "Use Python template").option("-g, --go", "Use Go template").option("-r, --rust", "Use Rust template").option("--skip-install", "Skip dependency installation").option("--from-openapi <path>", "Generate from OpenAPI/Swagger specification").option("--from-prompt", "Generate tools using AI from text description").option("-y, --yes", "Skip prompts and use defaults").action(createCommand);
20
+ program.command("init").description("Initialize MCP server in the current directory").option("-t, --typescript", "Use TypeScript template").option("-p, --python", "Use Python template").option("-g, --go", "Use Go template").option("-r, --rust", "Use Rust template").option("--skip-install", "Skip dependency installation").option("-f, --force", "Initialize even if directory contains files").action(initCommand);
21
21
  program.command("add-tool").description("Add a new tool to an existing MCP server").option("-n, --name <name>", "Tool name (snake_case)").action(addToolCommand);
22
22
  program.parse();
23
23
  if (process.argv.length === 2) {
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod';
2
2
 
3
- declare const LanguageSchema: z.ZodEnum<["typescript", "python"]>;
3
+ declare const LanguageSchema: z.ZodEnum<["typescript", "python", "go", "rust"]>;
4
4
  type Language = z.infer<typeof LanguageSchema>;
5
5
  declare const TransportSchema: z.ZodEnum<["stdio", "sse"]>;
6
6
  type Transport = z.infer<typeof TransportSchema>;
@@ -80,7 +80,7 @@ type ResourceConfig = z.infer<typeof ResourceConfigSchema>;
80
80
  declare const ProjectConfigSchema: z.ZodObject<{
81
81
  name: z.ZodString;
82
82
  description: z.ZodDefault<z.ZodString>;
83
- language: z.ZodEnum<["typescript", "python"]>;
83
+ language: z.ZodEnum<["typescript", "python", "go", "rust"]>;
84
84
  transport: z.ZodEnum<["stdio", "sse"]>;
85
85
  tools: z.ZodDefault<z.ZodArray<z.ZodObject<{
86
86
  name: z.ZodString;
@@ -142,7 +142,7 @@ declare const ProjectConfigSchema: z.ZodObject<{
142
142
  }, "strip", z.ZodTypeAny, {
143
143
  name: string;
144
144
  description: string;
145
- language: "typescript" | "python";
145
+ language: "typescript" | "python" | "go" | "rust";
146
146
  transport: "stdio" | "sse";
147
147
  tools: {
148
148
  name: string;
@@ -165,7 +165,7 @@ declare const ProjectConfigSchema: z.ZodObject<{
165
165
  initGit: boolean;
166
166
  }, {
167
167
  name: string;
168
- language: "typescript" | "python";
168
+ language: "typescript" | "python" | "go" | "rust";
169
169
  transport: "stdio" | "sse";
170
170
  description?: string | undefined;
171
171
  tools?: {
@@ -197,6 +197,8 @@ interface GeneratorContext {
197
197
  interface CLIOptions {
198
198
  typescript?: boolean;
199
199
  python?: boolean;
200
+ go?: boolean;
201
+ rust?: boolean;
200
202
  skipInstall?: boolean;
201
203
  fromOpenapi?: string;
202
204
  fromPrompt?: boolean;
@@ -354,6 +356,8 @@ declare abstract class BaseGenerator {
354
356
  protected installDependencies(): Promise<void>;
355
357
  private installNodeDependencies;
356
358
  private installPythonDependencies;
359
+ private installGoDependencies;
360
+ private installRustDependencies;
357
361
  private checkCommand;
358
362
  protected initializeGit(): Promise<void>;
359
363
  protected checkOutputDir(): Promise<boolean>;
@@ -493,16 +497,18 @@ interface WizardOptions {
493
497
  defaultName?: string;
494
498
  skipDescription?: boolean;
495
499
  skipAdvanced?: boolean;
496
- presetLanguage?: 'typescript' | 'python';
500
+ presetLanguage?: Language;
497
501
  }
498
502
  declare function runWizard(options?: WizardOptions): Promise<ProjectConfig>;
499
- declare function runQuickWizard(defaultName?: string, presetLanguage?: 'typescript' | 'python'): Promise<ProjectConfig>;
503
+ declare function runQuickWizard(defaultName?: string, presetLanguage?: Language): Promise<ProjectConfig>;
500
504
 
501
505
  declare function createCommand(projectName: string | undefined, options: CLIOptions): Promise<void>;
502
506
 
503
507
  interface InitOptions {
504
508
  typescript?: boolean;
505
509
  python?: boolean;
510
+ go?: boolean;
511
+ rust?: boolean;
506
512
  skipInstall?: boolean;
507
513
  force?: boolean;
508
514
  }
package/dist/index.js CHANGED
@@ -55,11 +55,11 @@ import {
55
55
  walkDir,
56
56
  withSpinner,
57
57
  writeFile
58
- } from "./chunk-QRUHMGU5.js";
58
+ } from "./chunk-K3TOM22I.js";
59
59
 
60
60
  // src/types/config.ts
61
61
  import { z } from "zod";
62
- var LanguageSchema = z.enum(["typescript", "python"]);
62
+ var LanguageSchema = z.enum(["typescript", "python", "go", "rust"]);
63
63
  var TransportSchema = z.enum(["stdio", "sse"]);
64
64
  var ToolParameterSchema = z.object({
65
65
  name: z.string(),
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "mcp-new",
3
- "version": "0.1.0",
3
+ "version": "1.1.0",
4
4
  "description": "CLI generator for MCP servers. Like create-react-app, but for MCP.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
8
8
  "bin": {
9
- "mcp-new": "./bin/mcp-new.js"
9
+ "mcp-generator": "./bin/mcp-generator.js"
10
10
  },
11
11
  "files": [
12
12
  "dist",
@@ -0,0 +1,5 @@
1
+ # Environment variables for the MCP server
2
+ # Copy this file to .env and fill in the values
3
+
4
+ # Add your environment variables here
5
+ # API_KEY=your_api_key
@@ -0,0 +1,30 @@
1
+ # Binaries
2
+ *.exe
3
+ *.exe~
4
+ *.dll
5
+ *.so
6
+ *.dylib
7
+ <%= name %>
8
+
9
+ # Test binary
10
+ *.test
11
+
12
+ # Output of the go coverage tool
13
+ *.out
14
+
15
+ # Dependency directories
16
+ vendor/
17
+
18
+ # IDE
19
+ .idea/
20
+ .vscode/
21
+ *.swp
22
+ *.swo
23
+
24
+ # Environment
25
+ .env
26
+ .env.local
27
+
28
+ # Build
29
+ /bin/
30
+ /dist/
@@ -0,0 +1,88 @@
1
+ # <%= name %>
2
+
3
+ <%= description || 'MCP Server generated by mcp-generator' %>
4
+
5
+ ## Overview
6
+
7
+ This is an MCP (Model Context Protocol) server built with Go using the [mcp-go](https://github.com/mark3labs/mcp-go) SDK.
8
+
9
+ ## Prerequisites
10
+
11
+ - Go 1.21 or higher
12
+ - Git
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ # Install dependencies
18
+ go mod download
19
+
20
+ # Build the server
21
+ go build -o <%= name %> ./cmd/server
22
+ ```
23
+
24
+ ## Running the Server
25
+
26
+ <% if (transport === 'stdio') { %>
27
+ ### STDIO Transport
28
+
29
+ ```bash
30
+ ./<%= name %>
31
+ ```
32
+
33
+ The server communicates via standard input/output.
34
+ <% } else { %>
35
+ ### SSE Transport
36
+
37
+ ```bash
38
+ ./<%= name %>
39
+ ```
40
+
41
+ The server will start an HTTP server with SSE endpoint.
42
+ <% } %>
43
+
44
+ ## Available Tools
45
+
46
+ <% if (includeExampleTool) { %>
47
+ - **example_tool**: An example tool that echoes the input
48
+ <% } %>
49
+ <% tools.forEach(function(tool) { %>
50
+ - **<%= tool.name %>**: <%= tool.description %>
51
+ <% }); %>
52
+
53
+ ## Development
54
+
55
+ ```bash
56
+ # Run in development mode
57
+ go run ./cmd/server
58
+
59
+ # Run tests
60
+ go test ./...
61
+
62
+ # Build for production
63
+ go build -ldflags="-s -w" -o <%= name %> ./cmd/server
64
+ ```
65
+
66
+ ## Testing with MCP Inspector
67
+
68
+ ```bash
69
+ npx @modelcontextprotocol/inspector ./<%= name %>
70
+ ```
71
+
72
+ ## Project Structure
73
+
74
+ ```
75
+ .
76
+ ├── cmd/
77
+ │ └── server/
78
+ │ └── main.go # Server entry point
79
+ ├── internal/
80
+ │ └── tools/ # Tool implementations
81
+ ├── go.mod
82
+ ├── go.sum
83
+ └── README.md
84
+ ```
85
+
86
+ ## License
87
+
88
+ MIT
@@ -0,0 +1,101 @@
1
+ package main
2
+
3
+ import (
4
+ "context"
5
+ "fmt"
6
+ "os"
7
+
8
+ "github.com/mark3labs/mcp-go/mcp"
9
+ "github.com/mark3labs/mcp-go/server"
10
+ )
11
+
12
+ func main() {
13
+ // Create MCP server
14
+ s := server.NewMCPServer(
15
+ "<%= name %>",
16
+ "1.0.0",
17
+ server.WithToolCapabilities(true),
18
+ )
19
+
20
+ <% if (includeExampleTool) { %>
21
+ // Register example tool
22
+ exampleTool := mcp.NewTool("example_tool",
23
+ mcp.WithDescription("An example tool that echoes the input"),
24
+ mcp.WithString("query",
25
+ mcp.Required(),
26
+ mcp.Description("The query to echo"),
27
+ ),
28
+ )
29
+
30
+ s.AddTool(exampleTool, exampleToolHandler)
31
+ <% } %>
32
+
33
+ <% tools.forEach(function(tool) { %>
34
+ // Register <%= tool.name %> tool
35
+ <%= tool.name %>Tool := mcp.NewTool("<%= tool.name %>",
36
+ mcp.WithDescription("<%= tool.description %>"),
37
+ <% tool.parameters.forEach(function(param, index) { %>
38
+ <% if (param.type === 'string') { %>
39
+ mcp.WithString("<%= param.name %>",
40
+ <% } else if (param.type === 'number') { %>
41
+ mcp.WithNumber("<%= param.name %>",
42
+ <% } else if (param.type === 'boolean') { %>
43
+ mcp.WithBoolean("<%= param.name %>",
44
+ <% } else { %>
45
+ mcp.WithString("<%= param.name %>",
46
+ <% } %>
47
+ <% if (param.required) { %>
48
+ mcp.Required(),
49
+ <% } %>
50
+ mcp.Description("<%= param.description %>"),
51
+ ),
52
+ <% }); %>
53
+ )
54
+
55
+ s.AddTool(<%= tool.name %>Tool, <%= tool.name %>Handler)
56
+ <% }); %>
57
+
58
+ <% if (transport === 'stdio') { %>
59
+ // Start STDIO server
60
+ if err := server.ServeStdio(s); err != nil {
61
+ fmt.Fprintf(os.Stderr, "Server error: %v\n", err)
62
+ os.Exit(1)
63
+ }
64
+ <% } else { %>
65
+ // Start SSE server
66
+ if err := server.ServeSSE(s, ":8080", "/messages"); err != nil {
67
+ fmt.Fprintf(os.Stderr, "Server error: %v\n", err)
68
+ os.Exit(1)
69
+ }
70
+ <% } %>
71
+ }
72
+
73
+ <% if (includeExampleTool) { %>
74
+ func exampleToolHandler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
75
+ query, ok := request.Params.Arguments["query"].(string)
76
+ if !ok {
77
+ return mcp.NewToolResultError("query parameter is required"), nil
78
+ }
79
+
80
+ return mcp.NewToolResultText(fmt.Sprintf("Echo: %s", query)), nil
81
+ }
82
+ <% } %>
83
+
84
+ <% tools.forEach(function(tool) { %>
85
+ func <%= tool.name %>Handler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
86
+ // TODO: Implement <%= tool.name %> logic
87
+ <% tool.parameters.forEach(function(param) { %>
88
+ <% if (param.type === 'string') { %>
89
+ <%= param.name %>, _ := request.Params.Arguments["<%= param.name %>"].(string)
90
+ <% } else if (param.type === 'number') { %>
91
+ <%= param.name %>, _ := request.Params.Arguments["<%= param.name %>"].(float64)
92
+ <% } else if (param.type === 'boolean') { %>
93
+ <%= param.name %>, _ := request.Params.Arguments["<%= param.name %>"].(bool)
94
+ <% } else { %>
95
+ <%= param.name %> := request.Params.Arguments["<%= param.name %>"]
96
+ <% } %>
97
+ <% }); %>
98
+
99
+ return mcp.NewToolResultText(fmt.Sprintf("<%= tool.name %> called with: %v", request.Params.Arguments)), nil
100
+ }
101
+ <% }); %>
@@ -0,0 +1,7 @@
1
+ module <%= name %>
2
+
3
+ go 1.21
4
+
5
+ require (
6
+ github.com/mark3labs/mcp-go v0.6.0
7
+ )
@@ -0,0 +1,40 @@
1
+ package tools
2
+
3
+ import (
4
+ "context"
5
+ "fmt"
6
+
7
+ "github.com/mark3labs/mcp-go/mcp"
8
+ )
9
+
10
+ // ExampleInput represents the input for the example tool
11
+ type ExampleInput struct {
12
+ Query string `json:"query"`
13
+ }
14
+
15
+ // ExampleOutput represents the output of the example tool
16
+ type ExampleOutput struct {
17
+ Result string `json:"result"`
18
+ }
19
+
20
+ // ExampleTool creates the example tool definition
21
+ func ExampleTool() mcp.Tool {
22
+ return mcp.NewTool("example_tool",
23
+ mcp.WithDescription("An example tool that echoes the input"),
24
+ mcp.WithString("query",
25
+ mcp.Required(),
26
+ mcp.Description("The query to echo"),
27
+ ),
28
+ )
29
+ }
30
+
31
+ // ExampleHandler handles the example tool execution
32
+ func ExampleHandler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
33
+ query, ok := request.Params.Arguments["query"].(string)
34
+ if !ok {
35
+ return mcp.NewToolResultError("query parameter is required"), nil
36
+ }
37
+
38
+ result := fmt.Sprintf("Echo: %s", query)
39
+ return mcp.NewToolResultText(result), nil
40
+ }
@@ -0,0 +1,8 @@
1
+ # Environment variables for the MCP server
2
+ # Copy this file to .env and fill in the values
3
+
4
+ # Logging level
5
+ RUST_LOG=info
6
+
7
+ # Add your environment variables here
8
+ # API_KEY=your_api_key
@@ -0,0 +1,19 @@
1
+ # Generated by Cargo
2
+ /target/
3
+
4
+ # Cargo.lock for binaries
5
+ Cargo.lock
6
+
7
+ # Environment
8
+ .env
9
+ .env.local
10
+
11
+ # IDE
12
+ .idea/
13
+ .vscode/
14
+ *.swp
15
+ *.swo
16
+
17
+ # OS
18
+ .DS_Store
19
+ Thumbs.db
@@ -0,0 +1,18 @@
1
+ [package]
2
+ name = "<%= name %>"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+ description = "<%= description || 'MCP Server generated by mcp-generator' %>"
6
+
7
+ [dependencies]
8
+ rmcp = { version = "0.1", features = ["server"<% if (transport === 'stdio') { %>, "transport-io"<% } else { %>, "transport-sse-server"<% } %>] }
9
+ tokio = { version = "1", features = ["full"] }
10
+ serde = { version = "1", features = ["derive"] }
11
+ serde_json = "1"
12
+ anyhow = "1"
13
+ tracing = "0.1"
14
+ tracing-subscriber = { version = "0.3", features = ["env-filter"] }
15
+
16
+ [[bin]]
17
+ name = "<%= name %>"
18
+ path = "src/main.rs"
@@ -0,0 +1,92 @@
1
+ # <%= name %>
2
+
3
+ <%= description || 'MCP Server generated by mcp-generator' %>
4
+
5
+ ## Overview
6
+
7
+ This is an MCP (Model Context Protocol) server built with Rust using the [rmcp](https://github.com/anthropics/rmcp) SDK.
8
+
9
+ ## Prerequisites
10
+
11
+ - Rust 1.70 or higher
12
+ - Cargo
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ # Build the project
18
+ cargo build --release
19
+ ```
20
+
21
+ ## Running the Server
22
+
23
+ <% if (transport === 'stdio') { %>
24
+ ### STDIO Transport
25
+
26
+ ```bash
27
+ cargo run --release
28
+ # or
29
+ ./target/release/<%= name %>
30
+ ```
31
+
32
+ The server communicates via standard input/output.
33
+ <% } else { %>
34
+ ### SSE Transport
35
+
36
+ ```bash
37
+ cargo run --release
38
+ # or
39
+ ./target/release/<%= name %>
40
+ ```
41
+
42
+ The server will start an HTTP server with SSE endpoint on port 8080.
43
+ <% } %>
44
+
45
+ ## Available Tools
46
+
47
+ <% if (includeExampleTool) { %>
48
+ - **example_tool**: An example tool that echoes the input
49
+ <% } %>
50
+ <% tools.forEach(function(tool) { %>
51
+ - **<%= tool.name %>**: <%= tool.description %>
52
+ <% }); %>
53
+
54
+ ## Development
55
+
56
+ ```bash
57
+ # Run in development mode
58
+ cargo run
59
+
60
+ # Run tests
61
+ cargo test
62
+
63
+ # Check for issues
64
+ cargo clippy
65
+
66
+ # Format code
67
+ cargo fmt
68
+
69
+ # Build release
70
+ cargo build --release
71
+ ```
72
+
73
+ ## Testing with MCP Inspector
74
+
75
+ ```bash
76
+ npx @modelcontextprotocol/inspector ./target/release/<%= name %>
77
+ ```
78
+
79
+ ## Project Structure
80
+
81
+ ```
82
+ .
83
+ ├── src/
84
+ │ ├── main.rs # Server entry point
85
+ │ └── tools.rs # Tool implementations
86
+ ├── Cargo.toml
87
+ └── README.md
88
+ ```
89
+
90
+ ## License
91
+
92
+ MIT
@@ -0,0 +1,46 @@
1
+ use anyhow::Result;
2
+ use rmcp::{
3
+ model::{CallToolResult, Content, Tool, ToolInputSchema},
4
+ server::Server,
5
+ tool,
6
+ };
7
+ use serde_json::{json, Value};
8
+ use std::collections::HashMap;
9
+ use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
10
+
11
+ mod tools;
12
+
13
+ #[tokio::main]
14
+ async fn main() -> Result<()> {
15
+ // Initialize logging
16
+ tracing_subscriber::registry()
17
+ .with(tracing_subscriber::EnvFilter::new(
18
+ std::env::var("RUST_LOG").unwrap_or_else(|_| "info".into()),
19
+ ))
20
+ .with(tracing_subscriber::fmt::layer().with_writer(std::io::stderr))
21
+ .init();
22
+
23
+ tracing::info!("Starting <%= name %> MCP server");
24
+
25
+ // Create server
26
+ let server = Server::builder()
27
+ .name("<%= name %>")
28
+ .version("1.0.0")
29
+ <% if (includeExampleTool) { %>
30
+ .tool(tools::example_tool())
31
+ <% } %>
32
+ <% tools.forEach(function(tool) { %>
33
+ .tool(tools::<%= tool.name %>_tool())
34
+ <% }); %>
35
+ .build();
36
+
37
+ <% if (transport === 'stdio') { %>
38
+ // Run with STDIO transport
39
+ server.run_stdio().await?;
40
+ <% } else { %>
41
+ // Run with SSE transport
42
+ server.run_sse("0.0.0.0:8080", "/messages").await?;
43
+ <% } %>
44
+
45
+ Ok(())
46
+ }
@@ -0,0 +1,105 @@
1
+ use rmcp::{
2
+ model::{CallToolResult, Content, Tool, ToolInputSchema},
3
+ tool,
4
+ };
5
+ use serde::{Deserialize, Serialize};
6
+ use serde_json::{json, Value};
7
+ use std::collections::HashMap;
8
+
9
+ <% if (includeExampleTool) { %>
10
+ /// Example tool that echoes the input
11
+ #[derive(Debug, Deserialize)]
12
+ pub struct ExampleToolInput {
13
+ pub query: String,
14
+ }
15
+
16
+ pub fn example_tool() -> Tool {
17
+ Tool {
18
+ name: "example_tool".to_string(),
19
+ description: Some("An example tool that echoes the input".to_string()),
20
+ input_schema: ToolInputSchema {
21
+ r#type: "object".to_string(),
22
+ properties: Some({
23
+ let mut props = HashMap::new();
24
+ props.insert(
25
+ "query".to_string(),
26
+ json!({
27
+ "type": "string",
28
+ "description": "The query to echo"
29
+ }),
30
+ );
31
+ props
32
+ }),
33
+ required: Some(vec!["query".to_string()]),
34
+ },
35
+ handler: Box::new(|args| {
36
+ Box::pin(async move {
37
+ let input: ExampleToolInput = serde_json::from_value(args)?;
38
+ Ok(CallToolResult {
39
+ content: vec![Content::Text {
40
+ text: format!("Echo: {}", input.query),
41
+ }],
42
+ is_error: None,
43
+ })
44
+ })
45
+ }),
46
+ }
47
+ }
48
+ <% } %>
49
+
50
+ <% tools.forEach(function(tool) { %>
51
+ /// <%= tool.description %>
52
+ #[derive(Debug, Deserialize)]
53
+ pub struct <%= tool.name.charAt(0).toUpperCase() + tool.name.slice(1).replace(/_([a-z])/g, (_, c) => c.toUpperCase()) %>Input {
54
+ <% tool.parameters.forEach(function(param) { %>
55
+ <% if (param.type === 'number') { %>
56
+ pub <%= param.name %>: f64,
57
+ <% } else if (param.type === 'boolean') { %>
58
+ pub <%= param.name %>: bool,
59
+ <% } else if (param.type === 'array') { %>
60
+ pub <%= param.name %>: Vec<Value>,
61
+ <% } else if (param.type === 'object') { %>
62
+ pub <%= param.name %>: HashMap<String, Value>,
63
+ <% } else { %>
64
+ pub <%= param.name %>: String,
65
+ <% } %>
66
+ <% }); %>
67
+ }
68
+
69
+ pub fn <%= tool.name %>_tool() -> Tool {
70
+ Tool {
71
+ name: "<%= tool.name %>".to_string(),
72
+ description: Some("<%= tool.description %>".to_string()),
73
+ input_schema: ToolInputSchema {
74
+ r#type: "object".to_string(),
75
+ properties: Some({
76
+ let mut props = HashMap::new();
77
+ <% tool.parameters.forEach(function(param) { %>
78
+ props.insert(
79
+ "<%= param.name %>".to_string(),
80
+ json!({
81
+ "type": "<%= param.type %>",
82
+ "description": "<%= param.description %>"
83
+ }),
84
+ );
85
+ <% }); %>
86
+ props
87
+ }),
88
+ required: Some(vec![<%= tool.parameters.filter(p => p.required).map(p => '"' + p.name + '".to_string()').join(', ') %>]),
89
+ },
90
+ handler: Box::new(|args| {
91
+ Box::pin(async move {
92
+ // TODO: Implement <%= tool.name %> logic
93
+ let input: <%= tool.name.charAt(0).toUpperCase() + tool.name.slice(1).replace(/_([a-z])/g, (_, c) => c.toUpperCase()) %>Input = serde_json::from_value(args.clone())?;
94
+
95
+ Ok(CallToolResult {
96
+ content: vec![Content::Text {
97
+ text: format!("<%= tool.name %> called with: {:?}", args),
98
+ }],
99
+ is_error: None,
100
+ })
101
+ })
102
+ }),
103
+ }
104
+ }
105
+ <% }); %>
File without changes