create-mcp-use-app 0.3.4 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +153 -73
- package/dist/templates/apps_sdk/README.md +398 -0
- package/dist/templates/apps_sdk/index.ts +11 -0
- package/dist/templates/apps_sdk/package.json +42 -0
- package/dist/templates/apps_sdk/src/server.ts +239 -0
- package/dist/templates/apps_sdk/src/widgets.ts +180 -0
- package/dist/templates/apps_sdk/tsconfig.json +20 -0
- package/dist/templates/ui/README.md +21 -26
- package/dist/templates/ui/src/server.ts +2 -2
- package/dist/templates/uiresource/README.md +54 -34
- package/dist/templates/uiresource/src/server.ts +6 -6
- package/package.json +5 -2
package/dist/index.js
CHANGED
|
@@ -1,15 +1,42 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
import { Command } from "commander";
|
|
6
|
+
import inquirer from "inquirer";
|
|
7
|
+
import { spawn } from "child_process";
|
|
5
8
|
import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "fs";
|
|
6
9
|
import { dirname, join, resolve } from "path";
|
|
7
10
|
import { fileURLToPath } from "url";
|
|
8
|
-
import
|
|
9
|
-
import { Command } from "commander";
|
|
11
|
+
import ora from "ora";
|
|
10
12
|
var __filename = fileURLToPath(import.meta.url);
|
|
11
13
|
var __dirname = dirname(__filename);
|
|
14
|
+
function runPackageManager(packageManager, args, cwd) {
|
|
15
|
+
return new Promise((resolve2, reject) => {
|
|
16
|
+
const child = spawn(packageManager, args, {
|
|
17
|
+
cwd,
|
|
18
|
+
stdio: "inherit",
|
|
19
|
+
shell: false
|
|
20
|
+
// Disable shell to prevent command injection
|
|
21
|
+
});
|
|
22
|
+
child.on("close", (code) => {
|
|
23
|
+
if (code === 0) {
|
|
24
|
+
resolve2();
|
|
25
|
+
} else {
|
|
26
|
+
reject(new Error(`Process exited with code ${code}`));
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
child.on("error", (err) => {
|
|
30
|
+
reject(err);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
}
|
|
12
34
|
var program = new Command();
|
|
35
|
+
function renderLogo() {
|
|
36
|
+
console.log(chalk.cyan("\u259B\u259B\u258C\u259B\u2598\u259B\u258C\u2584\u2596\u258C\u258C\u259B\u2598\u2588\u258C"));
|
|
37
|
+
console.log(chalk.cyan("\u258C\u258C\u258C\u2599\u2596\u2599\u258C \u2599\u258C\u2584\u258C\u2599\u2596"));
|
|
38
|
+
console.log(chalk.cyan(" \u258C "));
|
|
39
|
+
}
|
|
13
40
|
var packageJson = JSON.parse(
|
|
14
41
|
readFileSync(join(__dirname, "../package.json"), "utf-8")
|
|
15
42
|
);
|
|
@@ -49,8 +76,13 @@ function getCurrentPackageVersions() {
|
|
|
49
76
|
);
|
|
50
77
|
versions["@mcp-use/inspector"] = inspectorPackage.version;
|
|
51
78
|
} catch (error) {
|
|
52
|
-
|
|
53
|
-
|
|
79
|
+
if (process.env.NODE_ENV === "development") {
|
|
80
|
+
console.warn("\u26A0\uFE0F Could not read workspace package versions, using defaults");
|
|
81
|
+
console.warn(` Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
82
|
+
if (error instanceof Error && error.stack) {
|
|
83
|
+
console.warn(` Stack: ${error.stack}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
54
86
|
}
|
|
55
87
|
return versions;
|
|
56
88
|
}
|
|
@@ -73,88 +105,122 @@ function processTemplateFile(filePath, versions, isDevelopment = false) {
|
|
|
73
105
|
}
|
|
74
106
|
return processedContent;
|
|
75
107
|
}
|
|
76
|
-
program.name("create-mcp-use-app").description("Create a new MCP server project").version(packageJson.version).argument("[project-name]", "Name of the MCP server project").option("-t, --template <template>", "Template to use", "
|
|
108
|
+
program.name("create-mcp-use-app").description("Create a new MCP server project").version(packageJson.version).argument("[project-name]", "Name of the MCP server project").option("-t, --template <template>", "Template to use", "simple").option("--no-install", "Skip installing dependencies").option("--dev", "Use workspace dependencies for development").action(async (projectName, options) => {
|
|
77
109
|
try {
|
|
110
|
+
let selectedTemplate = options.template;
|
|
78
111
|
if (!projectName) {
|
|
79
|
-
console.log("\u{1F3AF} Welcome to create-mcp-use-app!");
|
|
80
112
|
console.log("");
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
113
|
+
renderLogo();
|
|
114
|
+
console.log("");
|
|
115
|
+
console.log(chalk.bold("Welcome to create-mcp-use-app!"));
|
|
116
|
+
console.log("");
|
|
117
|
+
projectName = await promptForProjectName();
|
|
118
|
+
console.log("");
|
|
119
|
+
selectedTemplate = await promptForTemplate();
|
|
120
|
+
}
|
|
121
|
+
const sanitizedProjectName = projectName.trim();
|
|
122
|
+
if (!sanitizedProjectName) {
|
|
123
|
+
console.error(chalk.red("\u274C Project name cannot be empty"));
|
|
124
|
+
process.exit(1);
|
|
87
125
|
}
|
|
88
|
-
|
|
89
|
-
|
|
126
|
+
if (sanitizedProjectName.includes("..") || sanitizedProjectName.includes("/") || sanitizedProjectName.includes("\\")) {
|
|
127
|
+
console.error(chalk.red('\u274C Project name cannot contain path separators or ".."'));
|
|
128
|
+
console.error(chalk.yellow(' Use simple names like "my-mcp-server"'));
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
const protectedNames = ["node_modules", ".git", ".env", "package.json", "src", "dist"];
|
|
132
|
+
if (protectedNames.includes(sanitizedProjectName.toLowerCase())) {
|
|
133
|
+
console.error(chalk.red(`\u274C Cannot use protected name "${sanitizedProjectName}"`));
|
|
134
|
+
console.error(chalk.yellow(" Please choose a different project name"));
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
137
|
+
console.log(chalk.cyan(`\u{1F680} Creating MCP server "${sanitizedProjectName}"...`));
|
|
138
|
+
const projectPath = resolve(process.cwd(), sanitizedProjectName);
|
|
90
139
|
if (existsSync(projectPath)) {
|
|
91
|
-
console.error(`\u274C Directory "${
|
|
140
|
+
console.error(chalk.red(`\u274C Directory "${sanitizedProjectName}" already exists!`));
|
|
141
|
+
console.error(chalk.yellow(" Please choose a different name or remove the existing directory"));
|
|
92
142
|
process.exit(1);
|
|
93
143
|
}
|
|
94
144
|
mkdirSync(projectPath, { recursive: true });
|
|
145
|
+
const validatedTemplate = validateTemplateName(selectedTemplate);
|
|
95
146
|
const versions = getCurrentPackageVersions();
|
|
96
|
-
await copyTemplate(projectPath,
|
|
97
|
-
updatePackageJson(projectPath,
|
|
147
|
+
await copyTemplate(projectPath, validatedTemplate, versions, options.dev);
|
|
148
|
+
updatePackageJson(projectPath, sanitizedProjectName);
|
|
98
149
|
if (options.install) {
|
|
99
|
-
|
|
150
|
+
const spinner = ora("Installing packages...").start();
|
|
100
151
|
try {
|
|
101
|
-
|
|
152
|
+
await runPackageManager("pnpm", ["install"], projectPath);
|
|
153
|
+
spinner.succeed("Packages installed successfully");
|
|
102
154
|
} catch {
|
|
103
|
-
|
|
155
|
+
spinner.text = "pnpm not found, trying npm...";
|
|
104
156
|
try {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
157
|
+
await runPackageManager("npm", ["install"], projectPath);
|
|
158
|
+
spinner.succeed("Packages installed successfully");
|
|
159
|
+
} catch (error) {
|
|
160
|
+
spinner.fail("Package installation failed");
|
|
161
|
+
console.log('\u26A0\uFE0F Please run "npm install" or "pnpm install" manually');
|
|
108
162
|
}
|
|
109
163
|
}
|
|
110
164
|
}
|
|
111
|
-
console.log("
|
|
165
|
+
console.log("");
|
|
166
|
+
console.log(chalk.green("\u2705 MCP server created successfully!"));
|
|
112
167
|
if (options.dev) {
|
|
113
|
-
console.log("\u{1F527} Development mode: Using workspace dependencies");
|
|
168
|
+
console.log(chalk.yellow("\u{1F527} Development mode: Using workspace dependencies"));
|
|
114
169
|
}
|
|
115
170
|
console.log("");
|
|
116
|
-
console.log("\u{1F4C1} Project structure:");
|
|
117
|
-
console.log(` ${
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
171
|
+
console.log(chalk.bold("\u{1F4C1} Project structure:"));
|
|
172
|
+
console.log(` ${sanitizedProjectName}/`);
|
|
173
|
+
console.log(" \u251C\u2500\u2500 src/");
|
|
174
|
+
console.log(" \u2502 \u2514\u2500\u2500 server.ts");
|
|
175
|
+
if (validatedTemplate === "ui") {
|
|
121
176
|
console.log(" \u251C\u2500\u2500 resources/");
|
|
122
177
|
console.log(" \u2502 \u251C\u2500\u2500 data-visualization.tsx");
|
|
123
178
|
console.log(" \u2502 \u251C\u2500\u2500 kanban-board.tsx");
|
|
124
179
|
console.log(" \u2502 \u2514\u2500\u2500 todo-list.tsx");
|
|
125
|
-
console.log(" \u251C\u2500\u2500 index.ts");
|
|
126
|
-
console.log(" \u251C\u2500\u2500 package.json");
|
|
127
|
-
console.log(" \u251C\u2500\u2500 tsconfig.json");
|
|
128
|
-
console.log(" \u2514\u2500\u2500 README.md");
|
|
129
|
-
} else {
|
|
130
|
-
console.log(" \u251C\u2500\u2500 src/");
|
|
131
|
-
console.log(" \u2502 \u2514\u2500\u2500 server.ts");
|
|
132
|
-
console.log(" \u251C\u2500\u2500 package.json");
|
|
133
|
-
console.log(" \u251C\u2500\u2500 tsconfig.json");
|
|
134
|
-
console.log(" \u2514\u2500\u2500 README.md");
|
|
135
180
|
}
|
|
181
|
+
console.log(" \u251C\u2500\u2500 index.ts");
|
|
182
|
+
console.log(" \u251C\u2500\u2500 package.json");
|
|
183
|
+
console.log(" \u251C\u2500\u2500 tsconfig.json");
|
|
184
|
+
console.log(" \u2514\u2500\u2500 README.md");
|
|
136
185
|
console.log("");
|
|
137
|
-
console.log("\u{1F680} To get started:");
|
|
138
|
-
console.log(` cd ${
|
|
186
|
+
console.log(chalk.bold("\u{1F680} To get started:"));
|
|
187
|
+
console.log(chalk.cyan(` cd ${sanitizedProjectName}`));
|
|
139
188
|
if (!options.install) {
|
|
140
|
-
console.log(" npm install");
|
|
189
|
+
console.log(chalk.cyan(" npm install"));
|
|
141
190
|
}
|
|
142
|
-
console.log(" npm run dev");
|
|
191
|
+
console.log(chalk.cyan(" npm run dev"));
|
|
143
192
|
console.log("");
|
|
144
193
|
if (options.dev) {
|
|
145
|
-
console.log("\u{1F4A1} Development mode: Your project uses workspace dependencies");
|
|
146
|
-
console.log(" Make sure you're in the mcp-use workspace root for development");
|
|
194
|
+
console.log(chalk.yellow("\u{1F4A1} Development mode: Your project uses workspace dependencies"));
|
|
195
|
+
console.log(chalk.yellow(" Make sure you're in the mcp-use workspace root for development"));
|
|
196
|
+
console.log("");
|
|
147
197
|
}
|
|
148
|
-
console.log("\u{1F4DA} Learn more: https://docs.mcp-use.
|
|
198
|
+
console.log(chalk.blue("\u{1F4DA} Learn more: https://docs.mcp-use.com"));
|
|
199
|
+
console.log(chalk.gray("\u{1F4AC} For feedback and bug reporting visit:"));
|
|
200
|
+
console.log(chalk.gray(" https://github.com/mcp-use/mcp-use or https://mcp-use.com"));
|
|
149
201
|
} catch (error) {
|
|
150
202
|
console.error("\u274C Error creating MCP server:", error);
|
|
151
203
|
process.exit(1);
|
|
152
204
|
}
|
|
153
205
|
});
|
|
206
|
+
function validateTemplateName(template) {
|
|
207
|
+
const sanitized = template.trim();
|
|
208
|
+
if (sanitized.includes("..") || sanitized.includes("/") || sanitized.includes("\\")) {
|
|
209
|
+
console.error(chalk.red("\u274C Invalid template name"));
|
|
210
|
+
console.error(chalk.yellow(" Template name cannot contain path separators"));
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}
|
|
213
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(sanitized)) {
|
|
214
|
+
console.error(chalk.red("\u274C Invalid template name"));
|
|
215
|
+
console.error(chalk.yellow(" Template name can only contain letters, numbers, hyphens, and underscores"));
|
|
216
|
+
process.exit(1);
|
|
217
|
+
}
|
|
218
|
+
return sanitized;
|
|
219
|
+
}
|
|
154
220
|
async function copyTemplate(projectPath, template, versions, isDevelopment = false) {
|
|
155
221
|
const templatePath = join(__dirname, "templates", template);
|
|
156
222
|
if (!existsSync(templatePath)) {
|
|
157
|
-
console.error(`\u274C Template "${template}" not found!`);
|
|
223
|
+
console.error(chalk.red(`\u274C Template "${template}" not found!`));
|
|
158
224
|
const templatesDir = join(__dirname, "templates");
|
|
159
225
|
if (existsSync(templatesDir)) {
|
|
160
226
|
const availableTemplates = readdirSync(templatesDir, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name).sort();
|
|
@@ -193,35 +259,49 @@ function updatePackageJson(projectPath, projectName) {
|
|
|
193
259
|
packageJsonContent.description = `MCP server: ${projectName}`;
|
|
194
260
|
writeFileSync(packageJsonPath, JSON.stringify(packageJsonContent, null, 2));
|
|
195
261
|
}
|
|
196
|
-
function promptForProjectName() {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
const trimmed = answer.trim();
|
|
262
|
+
async function promptForProjectName() {
|
|
263
|
+
const { projectName } = await inquirer.prompt([
|
|
264
|
+
{
|
|
265
|
+
type: "input",
|
|
266
|
+
name: "projectName",
|
|
267
|
+
message: "What is your project name?",
|
|
268
|
+
validate: (input) => {
|
|
269
|
+
const trimmed = input.trim();
|
|
205
270
|
if (!trimmed) {
|
|
206
|
-
|
|
207
|
-
askForName();
|
|
208
|
-
return;
|
|
271
|
+
return "Project name is required";
|
|
209
272
|
}
|
|
210
273
|
if (!/^[a-zA-Z0-9-_]+$/.test(trimmed)) {
|
|
211
|
-
|
|
212
|
-
askForName();
|
|
213
|
-
return;
|
|
274
|
+
return "Project name can only contain letters, numbers, hyphens, and underscores";
|
|
214
275
|
}
|
|
215
276
|
if (existsSync(join(process.cwd(), trimmed))) {
|
|
216
|
-
|
|
217
|
-
askForName();
|
|
218
|
-
return;
|
|
277
|
+
return `Directory "${trimmed}" already exists! Please choose a different name.`;
|
|
219
278
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
279
|
+
return true;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
]);
|
|
283
|
+
return projectName;
|
|
284
|
+
}
|
|
285
|
+
async function promptForTemplate() {
|
|
286
|
+
const templatesDir = join(__dirname, "templates");
|
|
287
|
+
const availableTemplates = existsSync(templatesDir) ? readdirSync(templatesDir, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name).sort() : ["simple", "ui", "uiresource"];
|
|
288
|
+
const templateDescriptions = {
|
|
289
|
+
"simple": "Simple MCP server with a basic calculator tool (add numbers)",
|
|
290
|
+
"ui": "MCP Server with mcp-ui resources returned from tools",
|
|
291
|
+
"uiresource": "MCP Server with mcp-ui resources"
|
|
292
|
+
};
|
|
293
|
+
const { template } = await inquirer.prompt([
|
|
294
|
+
{
|
|
295
|
+
type: "list",
|
|
296
|
+
name: "template",
|
|
297
|
+
message: "Select a template:",
|
|
298
|
+
default: "simple",
|
|
299
|
+
choices: availableTemplates.map((template2) => ({
|
|
300
|
+
name: `${template2} - ${templateDescriptions[template2] || "MCP server template"}`,
|
|
301
|
+
value: template2
|
|
302
|
+
}))
|
|
303
|
+
}
|
|
304
|
+
]);
|
|
305
|
+
return template;
|
|
226
306
|
}
|
|
227
307
|
program.parse();
|