create-mcp-use-app 0.9.4 → 0.10.0-canary.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 +28 -0
- package/dist/index.js +155 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -112,6 +112,11 @@ npx create-mcp-use-app my-project
|
|
|
112
112
|
npx create-mcp-use-app my-project --template apps-sdk
|
|
113
113
|
npx create-mcp-use-app my-project --template mcp-ui
|
|
114
114
|
|
|
115
|
+
# Use a GitHub repository as a template
|
|
116
|
+
npx create-mcp-use-app my-project --template owner/repo
|
|
117
|
+
npx create-mcp-use-app my-project --template https://github.com/owner/repo
|
|
118
|
+
npx create-mcp-use-app my-project --template owner/repo#branch-name
|
|
119
|
+
|
|
115
120
|
# Use a specific package manager
|
|
116
121
|
npx create-mcp-use-app my-project --npm
|
|
117
122
|
npx create-mcp-use-app my-project --yarn
|
|
@@ -159,6 +164,29 @@ The mcp-ui template includes:
|
|
|
159
164
|
|
|
160
165
|
Best for building MCP servers with rich interactive UI components.
|
|
161
166
|
|
|
167
|
+
### GitHub Repository Templates
|
|
168
|
+
|
|
169
|
+
You can use any GitHub repository as a template by providing the repository URL:
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
# Short format (owner/repo)
|
|
173
|
+
npx create-mcp-use-app my-project --template owner/repo
|
|
174
|
+
|
|
175
|
+
# Full URL format
|
|
176
|
+
npx create-mcp-use-app my-project --template https://github.com/owner/repo
|
|
177
|
+
|
|
178
|
+
# With specific branch
|
|
179
|
+
npx create-mcp-use-app my-project --template owner/repo#branch-name
|
|
180
|
+
npx create-mcp-use-app my-project --template https://github.com/owner/repo#branch-name
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
The repository will be cloned and its contents will be used to initialize your project. This is useful for:
|
|
184
|
+
- Using community templates
|
|
185
|
+
- Sharing custom templates within your organization
|
|
186
|
+
- Creating projects from existing repositories
|
|
187
|
+
|
|
188
|
+
**Note:** Git must be installed and available in your PATH to use GitHub repository templates.
|
|
189
|
+
|
|
162
190
|
---
|
|
163
191
|
|
|
164
192
|
## 🏗️ What Gets Installed
|
package/dist/index.js
CHANGED
|
@@ -6,11 +6,12 @@ import { Command } from "commander";
|
|
|
6
6
|
import { render } from "ink";
|
|
7
7
|
import SelectInput from "ink-select-input";
|
|
8
8
|
import TextInput from "ink-text-input";
|
|
9
|
-
import { execSync, spawn } from "child_process";
|
|
9
|
+
import { execSync, spawn, spawnSync } from "child_process";
|
|
10
10
|
import {
|
|
11
11
|
copyFileSync,
|
|
12
12
|
existsSync,
|
|
13
13
|
mkdirSync,
|
|
14
|
+
mkdtempSync,
|
|
14
15
|
readdirSync,
|
|
15
16
|
readFileSync,
|
|
16
17
|
rmSync,
|
|
@@ -18,6 +19,7 @@ import {
|
|
|
18
19
|
} from "fs";
|
|
19
20
|
import { dirname, join, resolve } from "path";
|
|
20
21
|
import { fileURLToPath } from "url";
|
|
22
|
+
import { tmpdir } from "os";
|
|
21
23
|
import ora from "ora";
|
|
22
24
|
import React, { useState } from "react";
|
|
23
25
|
import { Box, Text } from "ink";
|
|
@@ -279,7 +281,7 @@ function listTemplates() {
|
|
|
279
281
|
}
|
|
280
282
|
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(
|
|
281
283
|
"-t, --template <template>",
|
|
282
|
-
"Template to use (starter, mcp-ui, apps-sdk)",
|
|
284
|
+
"Template to use (starter, mcp-ui, apps-sdk) or GitHub repo URL (owner/repo or https://github.com/owner/repo)",
|
|
283
285
|
"starter"
|
|
284
286
|
).option("--list-templates", "List all available templates").option("--install", "Install dependencies after creating project").option("--no-git", "Skip initializing a git repository").option("--dev", "Use workspace dependencies for development").option("--canary", "Use canary versions of packages").option("--yarn", "Use yarn as package manager").option("--npm", "Use npm as package manager").option("--pnpm", "Use pnpm as package manager").action(
|
|
285
287
|
async (projectName, options) => {
|
|
@@ -479,7 +481,7 @@ program.name("create-mcp-use-app").description("Create a new MCP server project"
|
|
|
479
481
|
);
|
|
480
482
|
}
|
|
481
483
|
}
|
|
482
|
-
if (options.git) {
|
|
484
|
+
if (options.git && !isGitHubRepoUrl(validatedTemplate)) {
|
|
483
485
|
tryGitInit(projectPath);
|
|
484
486
|
}
|
|
485
487
|
console.log("");
|
|
@@ -554,8 +556,127 @@ program.name("create-mcp-use-app").description("Create a new MCP server project"
|
|
|
554
556
|
}
|
|
555
557
|
}
|
|
556
558
|
);
|
|
559
|
+
function parseGitHubRepoUrl(url) {
|
|
560
|
+
const trimmed = url.trim();
|
|
561
|
+
let match = trimmed.match(
|
|
562
|
+
/^https?:\/\/(?:www\.)?github\.com\/([^/]+)\/([^/#]+?)(?:\.git)?(?:\/tree\/([^/]+))?(?:#(.+))?$/
|
|
563
|
+
);
|
|
564
|
+
if (match) {
|
|
565
|
+
const result = {
|
|
566
|
+
owner: match[1],
|
|
567
|
+
repo: match[2],
|
|
568
|
+
branch: match[3] || match[4] || void 0
|
|
569
|
+
};
|
|
570
|
+
return validateGitHubRepoInfo(result) ? result : null;
|
|
571
|
+
}
|
|
572
|
+
match = trimmed.match(
|
|
573
|
+
/^(?:www\.)?github\.com\/([^/]+)\/([^/#]+?)(?:\.git)?(?:#(.+))?$/
|
|
574
|
+
);
|
|
575
|
+
if (match) {
|
|
576
|
+
const result = {
|
|
577
|
+
owner: match[1],
|
|
578
|
+
repo: match[2],
|
|
579
|
+
branch: match[3] || void 0
|
|
580
|
+
};
|
|
581
|
+
return validateGitHubRepoInfo(result) ? result : null;
|
|
582
|
+
}
|
|
583
|
+
match = trimmed.match(/^([^/#]+)\/([^/#]+?)(?:#(.+))?$/);
|
|
584
|
+
if (match) {
|
|
585
|
+
const result = {
|
|
586
|
+
owner: match[1],
|
|
587
|
+
repo: match[2],
|
|
588
|
+
branch: match[3] || void 0
|
|
589
|
+
};
|
|
590
|
+
return validateGitHubRepoInfo(result) ? result : null;
|
|
591
|
+
}
|
|
592
|
+
return null;
|
|
593
|
+
}
|
|
594
|
+
function validateGitHubRepoInfo(info) {
|
|
595
|
+
const validIdentifier = /^[a-zA-Z0-9_-]+$/;
|
|
596
|
+
const validBranch = /^[a-zA-Z0-9_./-]+$/;
|
|
597
|
+
if (!validIdentifier.test(info.owner) || !validIdentifier.test(info.repo)) {
|
|
598
|
+
return false;
|
|
599
|
+
}
|
|
600
|
+
if (info.branch && !validBranch.test(info.branch)) {
|
|
601
|
+
return false;
|
|
602
|
+
}
|
|
603
|
+
return true;
|
|
604
|
+
}
|
|
605
|
+
function isGitHubRepoUrl(template) {
|
|
606
|
+
return parseGitHubRepoUrl(template) !== null;
|
|
607
|
+
}
|
|
608
|
+
async function cloneGitHubRepo(repoInfo, tempDir) {
|
|
609
|
+
const versionCheck = spawnSync("git", ["--version"], {
|
|
610
|
+
stdio: "ignore",
|
|
611
|
+
shell: false
|
|
612
|
+
});
|
|
613
|
+
if (versionCheck.status !== 0 || versionCheck.error) {
|
|
614
|
+
console.error(
|
|
615
|
+
chalk.red("\u274C Git is not installed or not available in PATH")
|
|
616
|
+
);
|
|
617
|
+
console.error(
|
|
618
|
+
chalk.yellow(" Please install Git to use GitHub repository templates")
|
|
619
|
+
);
|
|
620
|
+
process.exit(1);
|
|
621
|
+
}
|
|
622
|
+
const repoUrl = `https://github.com/${repoInfo.owner}/${repoInfo.repo}.git`;
|
|
623
|
+
const branch = repoInfo.branch || "main";
|
|
624
|
+
const spinner = ora(`Cloning repository from GitHub...`).start();
|
|
625
|
+
const cloneWithBranch = (branchName) => {
|
|
626
|
+
const result = spawnSync(
|
|
627
|
+
"git",
|
|
628
|
+
["clone", "--depth", "1", "--branch", branchName, repoUrl, tempDir],
|
|
629
|
+
{
|
|
630
|
+
stdio: "pipe",
|
|
631
|
+
shell: false
|
|
632
|
+
}
|
|
633
|
+
);
|
|
634
|
+
if (result.status !== 0) {
|
|
635
|
+
const errorMessage2 = result.stderr?.toString() || result.stdout?.toString() || "Unknown error";
|
|
636
|
+
return {
|
|
637
|
+
success: false,
|
|
638
|
+
error: new Error(errorMessage2)
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
return { success: true };
|
|
642
|
+
};
|
|
643
|
+
const cloneResult = cloneWithBranch(branch);
|
|
644
|
+
if (cloneResult.success) {
|
|
645
|
+
spinner.succeed("Repository cloned successfully");
|
|
646
|
+
return tempDir;
|
|
647
|
+
}
|
|
648
|
+
if (!repoInfo.branch) {
|
|
649
|
+
spinner.text = `Branch "${branch}" not found, trying "main"...`;
|
|
650
|
+
const mainResult = cloneWithBranch("main");
|
|
651
|
+
if (mainResult.success) {
|
|
652
|
+
spinner.succeed("Repository cloned successfully (using main branch)");
|
|
653
|
+
return tempDir;
|
|
654
|
+
}
|
|
655
|
+
spinner.text = `Branch "main" not found, trying "master"...`;
|
|
656
|
+
const masterResult = cloneWithBranch("master");
|
|
657
|
+
if (masterResult.success) {
|
|
658
|
+
spinner.succeed("Repository cloned successfully (using master branch)");
|
|
659
|
+
return tempDir;
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
spinner.fail("Failed to clone repository");
|
|
663
|
+
console.error(chalk.red(`\u274C Error cloning repository: ${repoUrl}`));
|
|
664
|
+
if (repoInfo.branch) {
|
|
665
|
+
console.error(chalk.yellow(` Branch "${repoInfo.branch}" may not exist`));
|
|
666
|
+
}
|
|
667
|
+
const errorMessage = cloneResult.error?.message || "Unknown error";
|
|
668
|
+
if (errorMessage.includes("not found")) {
|
|
669
|
+
console.error(chalk.yellow(` Repository may not exist or is private`));
|
|
670
|
+
} else {
|
|
671
|
+
console.error(chalk.yellow(` ${errorMessage}`));
|
|
672
|
+
}
|
|
673
|
+
throw cloneResult.error || new Error("Failed to clone repository");
|
|
674
|
+
}
|
|
557
675
|
function validateTemplateName(template) {
|
|
558
676
|
const sanitized = template.trim();
|
|
677
|
+
if (isGitHubRepoUrl(sanitized)) {
|
|
678
|
+
return sanitized;
|
|
679
|
+
}
|
|
559
680
|
if (sanitized.includes("..") || sanitized.includes("/") || sanitized.includes("\\")) {
|
|
560
681
|
console.error(chalk.red("\u274C Invalid template name"));
|
|
561
682
|
console.error(
|
|
@@ -575,6 +696,31 @@ function validateTemplateName(template) {
|
|
|
575
696
|
return sanitized;
|
|
576
697
|
}
|
|
577
698
|
async function copyTemplate(projectPath, template, versions, isDevelopment = false, useCanary = false) {
|
|
699
|
+
const repoInfo = parseGitHubRepoUrl(template);
|
|
700
|
+
if (repoInfo) {
|
|
701
|
+
const tempDir = mkdtempSync(join(tmpdir(), "create-mcp-use-app-"));
|
|
702
|
+
try {
|
|
703
|
+
await cloneGitHubRepo(repoInfo, tempDir);
|
|
704
|
+
copyDirectoryWithProcessing(
|
|
705
|
+
tempDir,
|
|
706
|
+
projectPath,
|
|
707
|
+
versions,
|
|
708
|
+
isDevelopment,
|
|
709
|
+
useCanary
|
|
710
|
+
);
|
|
711
|
+
} finally {
|
|
712
|
+
try {
|
|
713
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
714
|
+
} catch (error) {
|
|
715
|
+
if (process.env.NODE_ENV === "development") {
|
|
716
|
+
console.warn(
|
|
717
|
+
`Warning: Failed to clean up temp directory: ${tempDir}`
|
|
718
|
+
);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
578
724
|
const templatePath = join(__dirname, "templates", template);
|
|
579
725
|
if (!existsSync(templatePath)) {
|
|
580
726
|
console.error(chalk.red(`\u274C Template "${template}" not found!`));
|
|
@@ -596,6 +742,9 @@ async function copyTemplate(projectPath, template, versions, isDevelopment = fal
|
|
|
596
742
|
console.log(
|
|
597
743
|
'\u{1F4A1} Tip: Use "apps-sdk" template for a MCP server with OpenAI Apps SDK integration'
|
|
598
744
|
);
|
|
745
|
+
console.log(
|
|
746
|
+
'\u{1F4A1} Tip: Use a GitHub repo URL like "owner/repo" or "https://github.com/owner/repo" to use a custom template'
|
|
747
|
+
);
|
|
599
748
|
process.exit(1);
|
|
600
749
|
}
|
|
601
750
|
copyDirectoryWithProcessing(
|
|
@@ -609,6 +758,9 @@ async function copyTemplate(projectPath, template, versions, isDevelopment = fal
|
|
|
609
758
|
function copyDirectoryWithProcessing(src, dest, versions, isDevelopment, useCanary = false) {
|
|
610
759
|
const entries = readdirSync(src, { withFileTypes: true });
|
|
611
760
|
for (const entry of entries) {
|
|
761
|
+
if (entry.name === ".git") {
|
|
762
|
+
continue;
|
|
763
|
+
}
|
|
612
764
|
const srcPath = join(src, entry.name);
|
|
613
765
|
const destPath = join(dest, entry.name);
|
|
614
766
|
if (entry.isDirectory()) {
|