Skip to content

Commit 8e1191e

Browse files
committed
feat: add CLI tool for creating MCP server projects and update package.json
1 parent 4c12f4b commit 8e1191e

2 files changed

Lines changed: 213 additions & 7 deletions

File tree

bin/create-mcp-server.js

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
#!/usr/bin/env node
2+
3+
import fs from 'fs';
4+
import path from 'path';
5+
import { fileURLToPath } from 'url';
6+
import { execSync } from 'child_process';
7+
8+
// Get the directory where the source files are stored
9+
const __filename = fileURLToPath(import.meta.url);
10+
const rootDir = path.join(path.dirname(__filename), '..');
11+
const sourceDir = path.join(rootDir, 'src');
12+
const targetDir = process.cwd();
13+
14+
// Check if the target directory is empty
15+
const isDirectoryEmpty = () => {
16+
const files = fs.readdirSync(targetDir);
17+
return files.length === 0 || (files.length === 1 && files[0] === '.git');
18+
};
19+
20+
// Print a colorful message
21+
const printColorMessage = (message, color) => {
22+
const colors = {
23+
red: '\x1b[31m',
24+
green: '\x1b[32m',
25+
yellow: '\x1b[33m',
26+
blue: '\x1b[34m',
27+
magenta: '\x1b[35m',
28+
cyan: '\x1b[36m',
29+
reset: '\x1b[0m'
30+
};
31+
32+
console.log(`${colors[color]}${message}${colors.reset}`);
33+
};
34+
35+
// Main function
36+
async function main() {
37+
console.log('\n');
38+
printColorMessage('🚀 Creating a new MCP server project...', 'cyan');
39+
console.log('\n');
40+
41+
// Check if the directory is empty
42+
if (!isDirectoryEmpty()) {
43+
printColorMessage('⚠️ The current directory is not empty!', 'yellow');
44+
console.log('To avoid overwriting existing files, please run this command in an empty directory.');
45+
console.log('You can create a new directory and run the command there:');
46+
console.log('\n mkdir my-mcp-server && cd my-mcp-server && npx @mcpdotdirect/create-mcp-server\n');
47+
process.exit(1);
48+
}
49+
50+
// Check if source directory exists
51+
if (!fs.existsSync(sourceDir)) {
52+
printColorMessage('⚠️ Source directory not found!', 'red');
53+
console.log('This is likely an issue with the package installation.');
54+
console.log('Please report this issue at: https://github.com/mcpdotdirect/create-mcp-server/issues');
55+
process.exit(1);
56+
}
57+
58+
try {
59+
// Copy source files to target directory
60+
copyFiles(sourceDir, path.join(targetDir, 'src'));
61+
62+
// Copy other important files
63+
const filesToCopy = [
64+
'.gitignore',
65+
'tsconfig.json',
66+
'README.md'
67+
];
68+
69+
for (const file of filesToCopy) {
70+
const srcPath = path.join(rootDir, file);
71+
const destPath = path.join(targetDir, file);
72+
73+
if (fs.existsSync(srcPath)) {
74+
fs.copyFileSync(srcPath, destPath);
75+
console.log(`📄 Created ${destPath}`);
76+
}
77+
}
78+
79+
// Create a package.json for the new project
80+
createProjectPackageJson();
81+
82+
printColorMessage('✅ Source files copied successfully!', 'green');
83+
84+
printColorMessage('\n🎉 MCP server project created successfully!', 'green');
85+
console.log('\nNext steps:');
86+
console.log(' 1. Install dependencies:');
87+
console.log(' npm install');
88+
console.log(' # or with yarn');
89+
console.log(' yarn');
90+
console.log(' # or with pnpm');
91+
console.log(' pnpm install');
92+
console.log(' # or with bun');
93+
console.log(' bun install');
94+
console.log(' 2. Review the README.md file for usage instructions');
95+
console.log(' 3. Run "npm start" or "npm run dev" to start the server');
96+
console.log('\nHappy coding! 🚀\n');
97+
} catch (error) {
98+
printColorMessage(`\n❌ Error creating MCP server project: ${error.message}`, 'red');
99+
console.log('Please report this issue at: https://github.com/mcpdotdirect/create-mcp-server/issues');
100+
process.exit(1);
101+
}
102+
}
103+
104+
// Function to copy files recursively
105+
function copyFiles(source, destination) {
106+
// Create destination directory if it doesn't exist
107+
if (!fs.existsSync(destination)) {
108+
fs.mkdirSync(destination, { recursive: true });
109+
}
110+
111+
// Read all files/folders in the source directory
112+
const entries = fs.readdirSync(source, { withFileTypes: true });
113+
114+
for (const entry of entries) {
115+
const srcPath = path.join(source, entry.name);
116+
const destPath = path.join(destination, entry.name);
117+
118+
// Skip node_modules, package-lock.json, .git, and other unnecessary directories/files
119+
// This ensures we don't copy any lock files or node_modules, letting the user generate their own
120+
if (entry.name === 'node_modules' ||
121+
entry.name === 'package-lock.json' ||
122+
entry.name === 'npm-debug.log' ||
123+
entry.name === 'yarn.lock' ||
124+
entry.name === 'pnpm-lock.yaml' ||
125+
entry.name === 'bun.lock' ||
126+
entry.name === '.git' ||
127+
entry.name === 'bin' ||
128+
entry.name === '.cursor' ||
129+
entry.name === 'LICENSE' ||
130+
entry.name === 'build') {
131+
continue;
132+
}
133+
134+
if (entry.isDirectory()) {
135+
// Recursively copy directories
136+
copyFiles(srcPath, destPath);
137+
} else {
138+
// Copy files
139+
fs.copyFileSync(srcPath, destPath);
140+
console.log(`📄 Created ${destPath}`);
141+
}
142+
}
143+
}
144+
145+
// Create a package.json for the new project
146+
function createProjectPackageJson() {
147+
const packageJsonPath = path.join(targetDir, 'package.json');
148+
149+
const projectPackageJson = {
150+
name: "mcp-server",
151+
module: "src/index.ts",
152+
type: "module",
153+
version: "1.0.0",
154+
description: "Model Context Protocol (MCP) Server",
155+
private: true,
156+
scripts: {
157+
"start": "bun run src/index.ts",
158+
"build": "bun build src/index.ts --outdir build --target node",
159+
"build:http": "bun build src/server/http-server.ts --outdir build --target node",
160+
"dev": "bun --watch src/index.ts",
161+
"start:http": "bun run src/server/http-server.ts",
162+
"dev:http": "bun --watch src/server/http-server.ts"
163+
},
164+
devDependencies: {
165+
"@types/bun": "latest",
166+
"@types/cors": "^2.8.17",
167+
"@types/express": "^5.0.0",
168+
"@types/node": "^20.11.0"
169+
},
170+
peerDependencies: {
171+
"typescript": "^5.8.2"
172+
},
173+
dependencies: {
174+
"@modelcontextprotocol/sdk": "^1.7.0",
175+
"cors": "^2.8.5",
176+
"express": "^4.21.2",
177+
"zod": "^3.24.2"
178+
}
179+
};
180+
181+
fs.writeFileSync(
182+
packageJsonPath,
183+
JSON.stringify(projectPackageJson, null, 2)
184+
);
185+
console.log(`📄 Created ${packageJsonPath}`);
186+
}
187+
188+
// Run the main function
189+
main().catch(error => {
190+
printColorMessage(`\n❌ Unexpected error: ${error.message}`, 'red');
191+
process.exit(1);
192+
});

package.json

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,20 @@
33
"module": "src/index.ts",
44
"type": "module",
55
"version": "1.0.0",
6-
"description": "Model Context Protocol (MCP) Server Template Generator",
6+
"description": "CLI tool to create a new MCP (Model Context Protocol) server project",
77
"private": false,
88
"main": "build/index.js",
9+
"bin": {
10+
"create-mcp-server": "./bin/create-mcp-server.js"
11+
},
912
"files": [
10-
"build",
11-
"LICENSE",
12-
"README.md"
13+
"bin",
14+
"src/",
15+
"build/",
16+
".gitignore",
17+
"tsconfig.json",
18+
"README.md",
19+
"LICENSE"
1320
],
1421
"scripts": {
1522
"start": "bun run src/index.ts",
@@ -30,10 +37,14 @@
3037
"keywords": [
3138
"mcp",
3239
"model-context-protocol",
33-
"ai",
3440
"template",
35-
"llm",
36-
"generator"
41+
"server",
42+
"ai",
43+
"agent",
44+
"create",
45+
"generator",
46+
"starter",
47+
"boilerplate"
3748
],
3849
"author": "mcpdotdirect",
3950
"license": "MIT",
@@ -55,5 +66,8 @@
5566
"cors": "^2.8.5",
5667
"express": "^4.21.2",
5768
"zod": "^3.24.2"
69+
},
70+
"engines": {
71+
"node": ">=18.0.0"
5872
}
5973
}

0 commit comments

Comments
 (0)