├── .gitignore ├── .prettierrc ├── README.md ├── package-lock.json ├── package.json ├── packages ├── create-mcp-ts │ ├── .gitignore │ ├── .npmignore │ ├── README.md │ ├── README.template.md │ ├── package.json │ ├── src │ │ ├── index.test.ts │ │ ├── index.ts │ │ └── utils.ts │ └── tsconfig.json ├── mcp-scripts │ ├── .gitignore │ ├── .npmignore │ ├── README.md │ ├── package.json │ ├── src │ │ ├── eject.js │ │ ├── index.ts │ │ └── setup.ts │ └── tsconfig.json └── mcp-ts-template-default │ ├── .gitignore │ ├── .npmignore │ ├── README.md │ ├── gitignore │ ├── package.json │ ├── src │ └── index.ts │ └── tsconfig.json └── scripts └── updateTemplatesDeps.cjs /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | 3 | # System 4 | .DS_Store 5 | .idea/ 6 | .vscode/ 7 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "tabWidth": 2 4 | } 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | packages/create-mcp-ts/README.md -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "workspaces": [ 4 | "packages/*" 5 | ], 6 | "scripts": { 7 | "build": "npm run build -ws", 8 | "version": "npm version -ws", 9 | "postversion": "node scripts/updateTemplatesDeps.cjs", 10 | "version:major": "npm run version major && npm run build", 11 | "version:minor": "npm run version minor && npm run build", 12 | "version:patch": "npm run version patch && npm run build", 13 | "publish": "npm publish -ws" 14 | }, 15 | "devDependencies": { 16 | "glob": "^11.0.1" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/create-mcp-ts/.gitignore: -------------------------------------------------------------------------------- 1 | .mcp-ts/ 2 | dist/ 3 | node_modules/ 4 | 5 | # System 6 | .DS_Store 7 | .idea/ 8 | .vscode/ 9 | -------------------------------------------------------------------------------- /packages/create-mcp-ts/.npmignore: -------------------------------------------------------------------------------- 1 | .mcp-ts/ 2 | node_modules/ 3 | 4 | # System 5 | .DS_Store 6 | .idea/ 7 | .vscode/ 8 | -------------------------------------------------------------------------------- /packages/create-mcp-ts/README.md: -------------------------------------------------------------------------------- 1 | # create-mcp-ts ![NPM Version](https://img.shields.io/npm/v/create-mcp-ts) 2 | 3 | Create a new MCP server in TypeScript, batteries included. 4 | 5 | ## Getting started 6 | 7 | ```shell 8 | npm init mcp-ts your-server 9 | cd your-server 10 | npm run dev 11 | ``` 12 | 13 | `create-mcp-ts` requires **zero build configuration** - it will automatically install everything you need to develop, build, and set up your MCP server. 14 | 15 | ### Set up your MCP server in Cursor, Windsurf, and Claude Desktop 16 | 17 | `create-mcp-ts` can automatically configure your MCP server in Cursor, Windsurf, and Claude Desktop: 18 | 19 | ```shell 20 | npm run setup 21 | ``` 22 | 23 | This script checks if `your-server` already exists in each client's respective MCP configuration file. If not, it adds an entry pointing to the server script (`dist/index.js`): 24 | 25 | ```json 26 | { 27 | "mcpConfig": { 28 | "your-server": { 29 | "command": "node", 30 | "args": ["/path/to/your-server/dist/index.js"] 31 | } 32 | } 33 | } 34 | ``` 35 | 36 | For more details on how to set up MCP servers in Cursor, Windsurf, and Claude Desktop, see: 37 | 38 | - [MCP docs for Cursor](https://docs.cursor.com/integrations/mcp) 39 | - [MCP docs for Windsurf](https://docs.windsurf.com/windsurf/mcp) 40 | - [MCP docs for Claude Desktop](https://modelcontextprotocol.io/quickstart/user) 41 | 42 | ### Custom templates 43 | 44 | If you'd like to use a custom template, you can do so by passing the template npm package name or file path to the `npx create-mcp-ts` command: 45 | 46 | ```shell 47 | npx create-mcp-ts your-server --template=mcp-ts-template-default 48 | npx create-mcp-ts your-server --template=file:/path/to/mcp-ts-template 49 | ``` 50 | 51 | ### Publishing your MCP server 52 | 53 | If you plan to share your MCP server, you can publish it to npm: 54 | 55 | 1. Set `"version"` in `package.json` and ensure `"private"` is set to `false`. 56 | 2. Run `npm install`. 57 | 3. Run `npm run build`. 58 | 4. Run `npm login` (if needed). 59 | 5. Run `npm publish`. 60 | 61 | ## Ejecting from `mcp-scripts` 62 | 63 | The `mcp-scripts` package contains build tools for `create-mcp-ts` projects - everything you'll need to develop, build, and set up an MCP server **without any additional configuration**. 64 | 65 | Under the hood `mcp-scripts` uses [tsup](https://github.com/egoist/tsup) and [esbuild](https://github.com/evanw/esbuild), lightweight, battle-tested utilities that are well-suited for production-grade TypeScript projects. 66 | 67 | If you'd like to eject from `mcp-scripts`, run the following command: 68 | 69 | ```shell 70 | npm run eject 71 | ``` 72 | 73 | This will remove the `mcp-scripts` dependency and replace any related commands from your project's `package.json` file. 74 | 75 | ## Troubleshooting your MCP server 76 | 77 | ### Confirm you have Node.js installed 78 | 79 | If you experience issues running your MCP server, the first thing to check is that you have Node.js installed globally. You can check this by running: 80 | 81 | ```shell 82 | node --version 83 | ``` 84 | 85 | If you don't have Node.js installed, you can install it by following the instructions [here](https://nodejs.org/en/download/). 86 | 87 | ### Usage with Node.js version managers 88 | 89 | Some MCP client environments may not have access to the full system PATH, which can cause issues when using Node.js version managers like `nodenv` or `nvm`. In these cases, you'll need to specify the full path to the Node.js binary in your MCP config: 90 | 91 | ```json 92 | { 93 | "mcpConfig": { 94 | "your-server": { 95 | "command": "/absolute/path/to/node", 96 | "args": ["/path/to/your-mcp-server/dist/index.js"] 97 | } 98 | } 99 | } 100 | ``` 101 | 102 | To find the absolute path to your Node.js binary, you can run: 103 | 104 | ```shell 105 | which node 106 | ``` 107 | 108 | Make sure to update your MCP config in each client (Cursor, Windsurf, or Claude Desktop) with the correct absolute path to the Node.js binary you want to use. 109 | 110 | For any other issues, please [open an issue here](https://github.com/stephencme/create-mcp-ts/issues/new). 111 | 112 | ## Philosophy 113 | 114 | `create-mcp-ts` is designed to be a batteries-included, "it just works" experience for MCP server developers. 115 | 116 | - **Batteries included**: there is only one build dependency, `mcp-scripts`. It uses tsup, esbuild, and other amazing open source projects, but provides a curated experience on top of them. 117 | - **Zero configuration**: you don't need to configure anything. A reasonably good configuration is handled for you so you can focus on writing code. 118 | - **No lock-in**: you can "eject" to a custom setup at any time. Run a single command, and all the configuration and build dependencies will be moved directly into your project, so you can pick up right where you left off. 119 | 120 | This project is inspired by [create-react-app](https://github.com/facebook/create-react-app). 121 | 122 | ## Related packages 123 | 124 | - **[packages/mcp-scripts](https://github.com/stephencme/create-mcp-ts/tree/main/packages/mcp-scripts)**: The build tools that power `create-mcp-ts` projects. 125 | - **[packages/mcp-ts-template-default](https://github.com/stephencme/create-mcp-ts/tree/main/packages/mcp-ts-template-default)**: Default project template for `create-mcp-ts`. 126 | -------------------------------------------------------------------------------- /packages/create-mcp-ts/README.template.md: -------------------------------------------------------------------------------- 1 | # Getting started with your MCP server 2 | 3 | This project was bootstrapped with [create-mcp-ts](https://github.com/stephencme/create-mcp-ts). 4 | 5 | It provides a basic template for building an MCP (Model Context Protocol) server using TypeScript. 6 | 7 | ## Quick start 8 | 9 | ```shell 10 | npm run setup 11 | npm run dev 12 | ``` 13 | 14 | ## Available scripts 15 | 16 | In the project directory, you can run: 17 | 18 | ### `npm run dev` 19 | 20 | Builds the MCP server to the `dist` folder and watches source files for changes.\ 21 | Changes will trigger a rebuild of the server. 22 | 23 | ### `npm run build` 24 | 25 | Builds the MCP server to the `dist` folder.\ 26 | It bundles your code for production use e.g. publishing to npm. 27 | 28 | ### `npm run setup` 29 | 30 | This script helps configure your MCP server in Cursor, Windsurf, and Claude Desktop.\ 31 | It checks the respective configuration files (`.json`) and adds an entry for your server if it doesn't exist, pointing to the server script (`dist/index.js`). 32 | 33 | ```json 34 | { 35 | "mcpConfig": { 36 | "your-mcp-server": { 37 | "command": "node", 38 | "args": ["/path/to/your-mcp-server/dist/index.js"] 39 | } 40 | } 41 | } 42 | ``` 43 | 44 | **Note**: You might need to adjust the `"command"` if your Node.js installation is not in the default system PATH, especially when using version managers like `nvm` or `nodenv`. See the [Troubleshooting](#troubleshooting-your-mcp-server-configuration) section in the `create-mcp-ts` README for details. 45 | 46 | ### `npm run eject` 47 | 48 | **This is a one-way operation. Once you `eject`, you can't go back!** 49 | 50 | If you aren't satisfied with the included build tools (`mcp-scripts`), you can `eject` at any time. This command will remove the `mcp-scripts` dependency from your project. 51 | 52 | Instead, it will copy any configuration files and the transitive dependencies right into your project so you have full control over them. All commands except `eject` will still work, but they will point to the copied configurations. At this point you're on your own. 53 | 54 | You don't have to ever use `eject`. The curated feature set is suitable for many deployments. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it. 55 | 56 | ## Publishing your MCP server 57 | 58 | If you plan to share your MCP server, you can publish it to npm: 59 | 60 | 1. Set `"version"` in `package.json` and ensure `"private"` is set to `false`. 61 | 2. Run `npm install`. 62 | 3. Run `npm run build`. 63 | 4. Run `npm login` (if needed). 64 | 5. Run `npm publish`. 65 | 66 | ## Troubleshooting 67 | 68 | See the [Troubleshooting](https://github.com/stephencme/create-mcp-ts#troubleshooting-your-mcp-server) section in the `create-mcp-ts` README for common issues. 69 | 70 | ## Learn more 71 | 72 | - **Model Context Protocol (MCP)**: Learn more about the protocol at [modelcontextprotocol.io](https://modelcontextprotocol.io/). 73 | - **create-mcp-ts**: Check out the tool that created this template [here](https://github.com/stephencme/create-mcp-ts). 74 | - **TypeScript**: Learn more about TypeScript [here](https://www.typescriptlang.org/). 75 | -------------------------------------------------------------------------------- /packages/create-mcp-ts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create-mcp-ts", 3 | "version": "0.3.4", 4 | "description": "Create a new MCP server in TypeScript, batteries included.", 5 | "author": "stephencme", 6 | "license": "MIT", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/stephencme/create-mcp-ts.git", 10 | "directory": "packages/create-mcp-ts" 11 | }, 12 | "keywords": [ 13 | "create", 14 | "mcp", 15 | "server", 16 | "init", 17 | "model-context-protocol", 18 | "cli", 19 | "generator" 20 | ], 21 | "type": "module", 22 | "bin": "./dist/index.js", 23 | "scripts": { 24 | "build": "tsup src/index.ts --format esm --dts --clean", 25 | "build:watch": "tsup src/index.ts --format esm --dts --watch", 26 | "test": "NODE_OPTIONS=--experimental-vm-modules jest" 27 | }, 28 | "jest": { 29 | "preset": "ts-jest/presets/default-esm" 30 | }, 31 | "dependencies": { 32 | "chalk": "^4.1.2", 33 | "commander": "^12.0.0", 34 | "fs-extra": "^11.2.0", 35 | "validate-npm-package-name": "^5.0.0" 36 | }, 37 | "devDependencies": { 38 | "@types/fs-extra": "^11.0.4", 39 | "@types/jest": "^29.5.14", 40 | "@types/node": "^20.11.28", 41 | "@types/validate-npm-package-name": "^4.0.2", 42 | "jest": "^29.7.0", 43 | "ts-jest": "^29.3.1", 44 | "tsup": "^8.4.0", 45 | "typescript": "^5.4.2" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/create-mcp-ts/src/index.test.ts: -------------------------------------------------------------------------------- 1 | import { execSync } from "child_process"; 2 | import fs from "fs-extra"; 3 | import path from "path"; 4 | 5 | describe("create-mcp-ts", () => { 6 | const testProjectDir = "./.mcp-ts/test-mcp-server"; 7 | const testProjectName = path.basename(testProjectDir); 8 | const templatePath = "../mcp-ts-template-default"; 9 | 10 | afterEach(() => { 11 | // Clean up test directory after each test 12 | if (fs.existsSync(testProjectDir)) { 13 | fs.removeSync(testProjectDir); 14 | } 15 | }); 16 | 17 | it("should create a new MCP server project with local template", () => { 18 | // Run the command 19 | const command = `node dist/index.js ${testProjectDir} --template=file:${templatePath}`; 20 | execSync(command, { stdio: "inherit" }); 21 | 22 | // Verify project structure 23 | expect(fs.existsSync(testProjectDir)).toBe(true); 24 | expect(fs.existsSync(path.join(testProjectDir, "package.json"))).toBe(true); 25 | expect(fs.existsSync(path.join(testProjectDir, "src"))).toBe(true); 26 | expect(fs.existsSync(path.join(testProjectDir, ".git"))).toBe(true); 27 | expect(fs.existsSync(path.join(testProjectDir, ".gitignore"))).toBe(true); 28 | 29 | // Verify package.json content 30 | const packageJson = JSON.parse( 31 | fs.readFileSync(path.join(testProjectDir, "package.json"), "utf-8") 32 | ); 33 | expect(packageJson.name).toBe(testProjectName); 34 | expect(packageJson.scripts).toBeDefined(); 35 | expect(packageJson.scripts.dev).toBeDefined(); 36 | expect(packageJson.scripts.build).toBeDefined(); 37 | expect(packageJson.scripts.setup).toBeDefined(); 38 | expect(packageJson.scripts.eject).toBeDefined(); 39 | }); 40 | 41 | it("should eject the project correctly", () => { 42 | // 1. Initialize the project first 43 | const initCommand = `node dist/index.js ${testProjectDir} --template=file:${templatePath}`; 44 | execSync(initCommand, { stdio: "ignore" }); // ignore stdio for less noisy tests 45 | 46 | // 2. Run the eject command within the test project directory 47 | // Need to install dependencies first so mcp-scripts is available 48 | execSync("npm install", { cwd: testProjectDir, stdio: "ignore" }); 49 | // Run eject using the installed script 50 | const ejectCommand = "npm run eject"; // This assumes eject is defined in the template's package.json 51 | execSync(ejectCommand, { cwd: testProjectDir, stdio: "ignore" }); 52 | 53 | // 3. Verify package.json changes 54 | const packageJsonPath = path.join(testProjectDir, "package.json"); 55 | const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8")); 56 | 57 | // Check scripts 58 | expect(packageJson.scripts.dev).toBe( 59 | "tsup src/index.ts --format esm --dts --watch" 60 | ); 61 | expect(packageJson.scripts.build).toBe( 62 | "tsup src/index.ts --format esm --dts --clean" 63 | ); 64 | // Check setup script path (relative) 65 | expect(packageJson.scripts.setup).toMatch( 66 | /^node mcp-scripts[\\/]setup\.js$/ 67 | ); // Match both / and \\ for cross-platform 68 | expect(packageJson.scripts.eject).toBeUndefined(); // Eject script should remove itself if defined 69 | 70 | // Check dependencies 71 | expect(packageJson.dependencies?.["mcp-scripts"]).toBeUndefined(); 72 | expect(packageJson.devDependencies?.["mcp-scripts"]).toBeUndefined(); 73 | expect(packageJson.devDependencies?.tsup).toBeDefined(); // tsup should be added 74 | 75 | // 4. Verify copied files 76 | expect( 77 | fs.existsSync(path.join(testProjectDir, "mcp-scripts/setup.js")) 78 | ).toBe(true); 79 | 80 | // 5. Verify mcp-scripts is removed from node_modules (optional, but good check) 81 | // This check assumes npm prune or similar isn't run automatically, 82 | // but the dependency removal in package.json is the main goal. 83 | // expect(fs.existsSync(path.join(testProjectDir, 'node_modules/mcp-scripts'))).toBe(false); // This might fail depending on npm/yarn behavior after script execution 84 | }); 85 | }); 86 | -------------------------------------------------------------------------------- /packages/create-mcp-ts/src/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import { Command } from "commander"; 4 | import fs from "fs-extra"; 5 | import path from "path"; 6 | import { fileURLToPath } from "url"; 7 | import validatePackageName from "validate-npm-package-name"; 8 | import { 9 | executeCmd, 10 | printCreatingServer, 11 | printDirectoryExists, 12 | printHelp, 13 | printIsInvalidPackageName, 14 | printSpecifyProjectDirectory, 15 | printSuccess, 16 | printUsingTemplate, 17 | templateDescriptionString, 18 | usageString, 19 | } from "./utils.js"; 20 | 21 | // Load package.json for version and name information 22 | const packageJson = require("../package.json") as any; 23 | 24 | // Get current file directory in ES modules 25 | const __filename = fileURLToPath(import.meta.url); 26 | const __dirname = path.dirname(__filename); 27 | 28 | let projectDir = ""; 29 | 30 | // Setup command line interface using commander 31 | const program = new Command(packageJson.name) 32 | .version(packageJson.version) 33 | .arguments("") 34 | .usage(usageString()) 35 | .option("--template ", templateDescriptionString()) 36 | .allowUnknownOption() 37 | .on("--help", () => printHelp()) 38 | .action((_projectDir) => { 39 | projectDir = _projectDir; 40 | }) 41 | .parse(); 42 | 43 | const options = program.opts(); 44 | 45 | // Helper function to handle special file names during template copying 46 | function getDestinationFileName(srcFileName: string): string { 47 | // Handle special cases for template files 48 | if (srcFileName === "gitignore") { 49 | return ".gitignore"; 50 | } 51 | return srcFileName; 52 | } 53 | 54 | async function run() { 55 | // Clean up project directory string input 56 | if (typeof projectDir === "string") { 57 | projectDir = projectDir.trim(); 58 | } 59 | 60 | // Ensure project directory was provided 61 | if (!projectDir) { 62 | printSpecifyProjectDirectory(program.name()); 63 | process.exit(1); 64 | } 65 | 66 | const resolvedProjectPath = path.resolve(projectDir); 67 | const projectName = path.basename(resolvedProjectPath); 68 | 69 | // Validate the project name is a valid npm package name 70 | const nameValidation = validatePackageName(projectName); 71 | const valid = nameValidation.validForNewPackages; 72 | const problems = [ 73 | ...(nameValidation.errors || []), 74 | ...(nameValidation.warnings || []), 75 | ]; 76 | 77 | if (!valid) { 78 | printIsInvalidPackageName(projectName, problems); 79 | process.exit(1); 80 | } 81 | 82 | // Check if directory already exists 83 | if (fs.existsSync(resolvedProjectPath)) { 84 | printDirectoryExists(resolvedProjectPath); 85 | process.exit(1); 86 | } 87 | 88 | // Create project directory 89 | fs.ensureDirSync(resolvedProjectPath); 90 | 91 | printCreatingServer(resolvedProjectPath); 92 | 93 | // Determine which template to use (default or custom) 94 | const templateName = options.template || "mcp-ts-template-default"; 95 | printUsingTemplate(templateName); 96 | 97 | // Copy template contents 98 | console.log(`Installing template from ${templateName}...`); 99 | 100 | try { 101 | // Handle local template (file:) vs npm package 102 | if (templateName.startsWith("file:")) { 103 | const localTemplatePath = path.resolve(templateName.slice(5)); 104 | if (!fs.existsSync(localTemplatePath)) { 105 | console.error( 106 | `Local template path does not exist: ${localTemplatePath}` 107 | ); 108 | process.exit(1); 109 | } 110 | // Copy files with special file name handling 111 | const templateFiles = fs 112 | .readdirSync(localTemplatePath) 113 | .filter( 114 | (file) => !["node_modules", ".git", "README.md"].includes(file) 115 | ); 116 | 117 | templateFiles.forEach((file) => { 118 | const srcPath = path.join(localTemplatePath, file); 119 | const destPath = path.join( 120 | resolvedProjectPath, 121 | getDestinationFileName(file) 122 | ); 123 | fs.copySync(srcPath, destPath); 124 | }); 125 | } else { 126 | // For npm packages, first create a package.json 127 | const tempPackageJson = { 128 | dependencies: { 129 | [templateName]: "latest", 130 | }, 131 | }; 132 | 133 | fs.writeFileSync( 134 | path.join(resolvedProjectPath, "package.json"), 135 | JSON.stringify(tempPackageJson, null, 2) 136 | ); 137 | 138 | // Install the template package 139 | await executeCmd("npm", ["install"], resolvedProjectPath); 140 | 141 | // Copy template contents from node_modules to project directory 142 | const templatePath = path.join( 143 | resolvedProjectPath, 144 | "node_modules", 145 | templateName 146 | ); 147 | const templateFiles = fs 148 | .readdirSync(templatePath) 149 | .filter( 150 | (file) => !["node_modules", ".git", "README.md"].includes(file) 151 | ); 152 | 153 | // Copy each file/directory from the template with special file name handling 154 | templateFiles.forEach((file) => { 155 | const srcPath = path.join(templatePath, file); 156 | const destPath = path.join( 157 | resolvedProjectPath, 158 | getDestinationFileName(file) 159 | ); 160 | fs.copySync(srcPath, destPath); 161 | }); 162 | 163 | // Clean up after template installation 164 | await executeCmd("npm", ["uninstall", templateName], resolvedProjectPath); 165 | } 166 | 167 | // Copy README.template.md to README.md 168 | const readmeTemplatePath = path.join(__dirname, "..", "README.template.md"); 169 | if (fs.existsSync(readmeTemplatePath)) { 170 | fs.copyFileSync( 171 | readmeTemplatePath, 172 | path.join(resolvedProjectPath, "README.md") 173 | ); 174 | } 175 | 176 | console.log("Template copied successfully."); 177 | console.log(); 178 | } catch (error) { 179 | console.error("Failed to copy template:", error); 180 | process.exit(1); 181 | } 182 | 183 | // Update package.json with project name and version 184 | try { 185 | const projectPackageJson = { 186 | name: projectName, 187 | version: "0.1.0", 188 | private: true, 189 | }; 190 | const packageJsonPath = path.join(resolvedProjectPath, "package.json"); 191 | const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8")); 192 | const { bin, dependencies, devDependencies, scripts } = packageJson; 193 | const updatedPackageJson = { 194 | ...projectPackageJson, 195 | bin, 196 | scripts, 197 | dependencies, 198 | devDependencies, 199 | }; 200 | fs.writeFileSync( 201 | packageJsonPath, 202 | JSON.stringify(updatedPackageJson, null, 2) 203 | ); 204 | console.log("Updated package.json with project name and version."); 205 | console.log(); 206 | } catch (error) { 207 | console.error("Failed to update package.json:", error); 208 | process.exit(1); 209 | } 210 | 211 | // Initialize git repository 212 | try { 213 | await executeCmd("git", ["init"], resolvedProjectPath); 214 | console.log("Initialized a git repository."); 215 | console.log(); 216 | } catch (error) { 217 | console.warn("Failed to initialize git repository"); 218 | } 219 | 220 | // Install dependencies 221 | console.log("Installing template dependencies using npm..."); 222 | try { 223 | await executeCmd("npm", ["install"], resolvedProjectPath); 224 | console.log(); 225 | } catch (error) { 226 | console.error("Failed to install dependencies"); 227 | process.exit(1); 228 | } 229 | 230 | // Create initial git commit 231 | try { 232 | await executeCmd("git", ["add", "-A"], resolvedProjectPath); 233 | await executeCmd( 234 | "git", 235 | ["commit", "-m", "Initial commit from create-mcp-ts"], 236 | resolvedProjectPath 237 | ); 238 | console.log("Created git commit."); 239 | console.log(); 240 | } catch (error) { 241 | console.warn("Failed to create initial git commit"); 242 | } 243 | 244 | // Print success message with next steps 245 | printSuccess(projectName, resolvedProjectPath); 246 | } 247 | 248 | // Execute the main function and handle any unexpected errors 249 | run().catch((error) => { 250 | console.error("Unexpected error:", error); 251 | process.exit(1); 252 | }); 253 | -------------------------------------------------------------------------------- /packages/create-mcp-ts/src/utils.ts: -------------------------------------------------------------------------------- 1 | import chalk from "chalk"; 2 | import { spawn } from "child_process"; 3 | 4 | // Execute a command 5 | export function executeCmd( 6 | cmd: string, 7 | args: string[], 8 | cwd: string 9 | ): Promise { 10 | return new Promise((resolve, reject) => { 11 | const child = spawn(cmd, args, { 12 | stdio: "inherit", 13 | cwd, 14 | }); 15 | 16 | child.on("close", (code) => { 17 | if (code !== 0) { 18 | reject( 19 | new Error(`${cmd} ${args.join(" ")} failed with exit code ${code}`) 20 | ); 21 | return; 22 | } 23 | resolve(); 24 | }); 25 | }); 26 | } 27 | 28 | // -- String utils -- 29 | 30 | // program.usage(...) 31 | export const usageString = () => 32 | `${chalk.green("")} [options]`; 33 | 34 | // program.option("--template ", ...) 35 | export const templateDescriptionString = () => 36 | `specify a template for the created project`; 37 | 38 | // program.on("--help", () => ...) 39 | const helpString = () => `Only ${chalk.green( 40 | "" 41 | )} is required. 42 | 43 | A custom ${chalk.cyan("--template")} can be one of: 44 | - a custom template published on npm: ${chalk.green( 45 | "mcp-ts-template-default" 46 | )} 47 | - a local path relative to the current working directory: ${chalk.green( 48 | "file:../your-custom-template" 49 | )} 50 | 51 | If you have any problems, do not hesitate to file an issue: 52 | ${chalk.cyan("https://github.com/stephencme/create-mcp-ts/issues/new")}`; 53 | 54 | // Specify project directory string 55 | const specifyProjectDirectoryString = (programName: string) => 56 | `Please specify the project directory: 57 | ${chalk.cyan(programName)} ${chalk.green("")} 58 | 59 | For example: 60 | ${chalk.cyan(programName)} ${chalk.green("your-mcp-server")} 61 | 62 | Run ${chalk.cyan(`${programName} --help`)} to see all options.`; 63 | 64 | // Invalid package name string 65 | const invalidPackageNameString = (projectName: string, problems: string[]) => 66 | `Could not create a project called ${chalk.red( 67 | `"${projectName}"` 68 | )} because of npm naming restrictions: 69 | ${problems.map((p) => ` ${chalk.red.bold("*")} ${p}`).join("\n")}`; 70 | 71 | // Directory exists 72 | const directoryExistsString = (path: string) => 73 | `The directory ${chalk.green( 74 | path 75 | )} already exists. Please use a different directory name.`; 76 | 77 | // Creating server string 78 | const creatingServerString = (path: string) => 79 | `Creating a new MCP server in ${chalk.green(path)}.`; 80 | 81 | // Confirm template string 82 | const confirmTemplateString = (templateName: string) => 83 | `Using template ${chalk.green(templateName)}.`; 84 | 85 | // Success string 86 | const successString = (projectName: string, path: string) => 87 | `Success! Created ${chalk.green(projectName)} at ${chalk.green(path)} 88 | Inside that directory, you can run several commands: 89 | 90 | ${chalk.cyan("npm run dev")} 91 | Build MCP server AND watch for changes. 92 | 93 | ${chalk.cyan("npm run build")} 94 | Build MCP server. 95 | 96 | ${chalk.cyan("npm run setup")} 97 | Setup MCP clients: Cursor, Windsurf, Claude Desktop. 98 | 99 | We suggest that you begin by typing: 100 | 101 | ${chalk.cyan("cd")} ${projectName} 102 | 103 | ${chalk.cyan("npm run dev")} 104 | 105 | Happy hacking!`; 106 | 107 | // -- Print utils -- 108 | 109 | type ConsoleMethod = keyof Pick< 110 | Console, 111 | "debug" | "error" | "info" | "log" | "warn" 112 | >; 113 | 114 | // Call console.log separately for each line of string 115 | export function print(text: string, method: ConsoleMethod = "log") { 116 | const lines = text.split("\n"); 117 | lines.forEach((line) => { 118 | console[method](line); 119 | }); 120 | } 121 | 122 | export const printHelp = () => print(helpString()); 123 | 124 | export const printSpecifyProjectDirectory = (programName: string) => 125 | print(specifyProjectDirectoryString(programName)); 126 | 127 | export const printIsInvalidPackageName = ( 128 | projectName: string, 129 | problems: string[] 130 | ) => print(invalidPackageNameString(projectName, problems), "error"); 131 | 132 | export const printDirectoryExists = (path: string) => 133 | print(directoryExistsString(path), "error"); 134 | 135 | export const printCreatingServer = (path: string) => 136 | print(creatingServerString(path)); 137 | 138 | export const printUsingTemplate = (templateName: string) => 139 | print(confirmTemplateString(templateName)); 140 | 141 | export const printSuccess = (projectName: string, path: string) => 142 | print(successString(projectName, path)); 143 | -------------------------------------------------------------------------------- /packages/create-mcp-ts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "Node16", 4 | "moduleResolution": "Node16", 5 | "esModuleInterop": true, 6 | "isolatedModules": true, 7 | "outDir": "./dist", 8 | "rootDir": "./src" 9 | }, 10 | "exclude": ["node_modules", "dist", "**/*.test.ts"], 11 | "include": ["src/**/*", "package.json"] 12 | } 13 | -------------------------------------------------------------------------------- /packages/mcp-scripts/.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules/ 3 | 4 | # System 5 | .DS_Store 6 | .idea/ 7 | .vscode/ 8 | -------------------------------------------------------------------------------- /packages/mcp-scripts/.npmignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | 3 | # System 4 | .DS_Store 5 | .idea/ 6 | .vscode/ 7 | -------------------------------------------------------------------------------- /packages/mcp-scripts/README.md: -------------------------------------------------------------------------------- 1 | # mcp-scripts 2 | 3 | The build tools that power `create-mcp-ts` projects. 4 | 5 | ## Related packages 6 | 7 | - **[packages/create-mcp-ts](https://github.com/stephencme/create-mcp-ts/tree/main/packages/create-mcp-ts)**: Create a new MCP server in TypeScript, batteries included. 8 | - **[packages/mcp-ts-template-default](https://github.com/stephencme/create-mcp-ts/tree/main/packages/mcp-ts-template-default)**: Default project template for `create-mcp-ts`. 9 | -------------------------------------------------------------------------------- /packages/mcp-scripts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mcp-scripts", 3 | "version": "0.3.4", 4 | "description": "The build tools that power create-mcp-ts projects", 5 | "author": "stephencme", 6 | "license": "MIT", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/stephencme/create-mcp-ts.git", 10 | "directory": "packages/mcp-ts-template-default" 11 | }, 12 | "type": "module", 13 | "bin": "./dist/index.js", 14 | "scripts": { 15 | "build": "tsup src/index.ts src/setup.ts src/eject.js --format esm --dts --clean" 16 | }, 17 | "dependencies": { 18 | "@types/node": "^22.13.13", 19 | "tsup": "^8.4.0", 20 | "typescript": "^5.8.2" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/mcp-scripts/src/eject.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import fs from "fs"; 4 | import path from "path"; 5 | import { fileURLToPath } from "url"; 6 | 7 | const __filename = fileURLToPath(import.meta.url); 8 | const __dirname = path.dirname(__filename); 9 | 10 | console.log("Ejecting from mcp-scripts..."); 11 | 12 | const projectRoot = process.cwd(); 13 | const projectPackageJsonPath = path.join(projectRoot, "package.json"); 14 | // Use path.resolve to ensure the path is absolute, especially when run via npx 15 | const scriptsPackageJsonPath = path.resolve(__dirname, "../package.json"); 16 | const setupScriptSourcePath = path.resolve(__dirname, "setup.js"); 17 | const projectScriptsDir = path.join(projectRoot, "mcp-scripts"); // Changed target directory 18 | const projectSetupScriptPath = path.join(projectScriptsDir, "setup.js"); 19 | 20 | try { 21 | // --- Pre-flight checks --- 22 | console.log("Performing pre-flight checks..."); 23 | 24 | // Check project package.json exists 25 | if (!fs.existsSync(projectPackageJsonPath)) { 26 | throw new Error( 27 | `Could not find package.json in project root: ${projectRoot}` 28 | ); 29 | } 30 | const projectPackageJson = JSON.parse( 31 | fs.readFileSync(projectPackageJsonPath, "utf8") 32 | ); 33 | const projectScripts = projectPackageJson.scripts || {}; 34 | 35 | // Check if default scripts were modified 36 | const expectedScripts = { 37 | dev: "mcp-scripts dev", 38 | build: "mcp-scripts build", 39 | setup: "mcp-scripts setup", 40 | }; 41 | for (const [scriptName, expectedCommand] of Object.entries(expectedScripts)) { 42 | if ( 43 | projectScripts[scriptName] && 44 | projectScripts[scriptName] !== expectedCommand 45 | ) { 46 | throw new Error( 47 | `Script '${scriptName}' in your package.json has been modified from the default ('${expectedCommand}'). Ejection cannot proceed automatically. Please revert the script or eject manually.` 48 | ); 49 | } 50 | } 51 | 52 | // Check for file conflicts 53 | if (fs.existsSync(projectSetupScriptPath)) { 54 | throw new Error( 55 | `Conflict: File already exists at ${projectSetupScriptPath}. Ejection cannot proceed.` 56 | ); 57 | } 58 | // Check if the target directory exists *and* is not a directory (edge case) 59 | if ( 60 | fs.existsSync(projectScriptsDir) && 61 | !fs.lstatSync(projectScriptsDir).isDirectory() 62 | ) { 63 | throw new Error( 64 | `Conflict: A file exists at ${projectScriptsDir}, which is needed for the mcp-scripts directory. Ejection cannot proceed.` 65 | ); 66 | } 67 | 68 | console.log("Pre-flight checks passed."); 69 | 70 | // --- Read mcp-scripts' package.json to find dependencies --- 71 | let actualScriptsPackageJsonPath = scriptsPackageJsonPath; 72 | if (!fs.existsSync(actualScriptsPackageJsonPath)) { 73 | // Fallback if running directly from src or __dirname is unexpected 74 | const altScriptsPackageJsonPath = path.join( 75 | projectRoot, 76 | "node_modules/mcp-scripts/package.json" 77 | ); 78 | if (!fs.existsSync(altScriptsPackageJsonPath)) { 79 | throw new Error( 80 | "Could not find mcp-scripts package.json. Ensure it's installed." 81 | ); 82 | } 83 | actualScriptsPackageJsonPath = altScriptsPackageJsonPath; // Adjust path if found in node_modules 84 | } 85 | const scriptsPackageJson = JSON.parse( 86 | fs.readFileSync(actualScriptsPackageJsonPath, "utf8") 87 | ); 88 | const scriptsDependencies = scriptsPackageJson.dependencies || {}; 89 | const tsupVersion = scriptsDependencies.tsup; 90 | if (!tsupVersion) { 91 | console.warn( 92 | "Warning: Could not find 'tsup' in mcp-scripts dependencies. Build scripts might not work." 93 | ); 94 | } 95 | console.log("Read mcp-scripts package.json."); 96 | 97 | // --- Start Ejection Process (Irreversible Changes Below) --- 98 | console.log("Starting irreversible ejection process..."); 99 | 100 | // --- 3. Copy setup.js --- 101 | if (!fs.existsSync(projectScriptsDir)) { 102 | fs.mkdirSync(projectScriptsDir); 103 | console.log(`Created directory: ${projectScriptsDir}`); 104 | } 105 | // Adjust setup script source path if running directly from src 106 | let actualSetupSourcePath = setupScriptSourcePath; 107 | if (!fs.existsSync(actualSetupSourcePath)) { 108 | actualSetupSourcePath = path.resolve( 109 | projectRoot, 110 | "node_modules/mcp-scripts/dist/setup.js" 111 | ); 112 | if (!fs.existsSync(actualSetupSourcePath)) { 113 | // Try one more level up if dist isn't directly in node_modules/mcp-scripts 114 | actualSetupSourcePath = path.resolve( 115 | projectRoot, 116 | "node_modules/mcp-scripts/src/setup.js" 117 | ); 118 | if (!fs.existsSync(actualSetupSourcePath)) { 119 | throw new Error(`Could not find setup.js script to copy.`); 120 | } 121 | } 122 | } 123 | 124 | fs.copyFileSync(actualSetupSourcePath, projectSetupScriptPath); 125 | console.log(`Copied setup.js to ${projectSetupScriptPath}`); 126 | 127 | // --- 4. Update scripts in project's package.json --- 128 | // Re-read package.json just in case, though checks should prevent issues 129 | const freshProjectPackageJson = JSON.parse( 130 | fs.readFileSync(projectPackageJsonPath, "utf8") 131 | ); 132 | const freshProjectScripts = freshProjectPackageJson.scripts || {}; 133 | let scriptsUpdated = false; 134 | 135 | if (freshProjectScripts.dev === "mcp-scripts dev") { 136 | freshProjectScripts.dev = "tsup src/index.ts --format esm --dts --watch"; 137 | scriptsUpdated = true; 138 | } 139 | if (freshProjectScripts.build === "mcp-scripts build") { 140 | freshProjectScripts.build = "tsup src/index.ts --format esm --dts --clean"; 141 | scriptsUpdated = true; 142 | } 143 | if (freshProjectScripts.setup === "mcp-scripts setup") { 144 | // Use relative path for cross-platform compatibility 145 | freshProjectScripts.setup = `node ${path.relative( 146 | projectRoot, 147 | projectSetupScriptPath 148 | )}`; 149 | scriptsUpdated = true; 150 | } 151 | if (freshProjectScripts.eject === "mcp-scripts eject") { 152 | delete freshProjectScripts.eject; 153 | scriptsUpdated = true; 154 | } 155 | 156 | if (scriptsUpdated) { 157 | freshProjectPackageJson.scripts = freshProjectScripts; 158 | console.log("Updated scripts in project package.json object."); 159 | } else { 160 | // This case should ideally not be reached due to pre-flight checks 161 | console.log("No scripts needed updating in project package.json."); 162 | } 163 | 164 | // --- 5. Add tsup as devDependency --- 165 | if (tsupVersion) { 166 | if (!freshProjectPackageJson.devDependencies) { 167 | freshProjectPackageJson.devDependencies = {}; 168 | } 169 | if (!freshProjectPackageJson.devDependencies.tsup) { 170 | freshProjectPackageJson.devDependencies.tsup = tsupVersion; 171 | console.log(`Added tsup@${tsupVersion} to project devDependencies.`); 172 | } else { 173 | console.log(`tsup already exists in project devDependencies.`); 174 | } 175 | } 176 | 177 | // --- 6. Remove mcp-scripts dependency --- 178 | let removed = false; 179 | if ( 180 | freshProjectPackageJson.dependencies && 181 | freshProjectPackageJson.dependencies["mcp-scripts"] 182 | ) { 183 | delete freshProjectPackageJson.dependencies["mcp-scripts"]; 184 | removed = true; 185 | } 186 | if ( 187 | freshProjectPackageJson.devDependencies && 188 | freshProjectPackageJson.devDependencies["mcp-scripts"] 189 | ) { 190 | delete freshProjectPackageJson.devDependencies["mcp-scripts"]; 191 | removed = true; 192 | } 193 | if (removed) { 194 | console.log("Removed mcp-scripts from project dependencies object."); 195 | } else { 196 | // This case should also ideally not be reached 197 | console.log("mcp-scripts not found in project dependencies."); 198 | } 199 | 200 | // --- 7. Write updated package.json --- 201 | fs.writeFileSync( 202 | projectPackageJsonPath, 203 | JSON.stringify(freshProjectPackageJson, null, 2) + "\n" // Add trailing newline 204 | ); 205 | console.log(`Successfully updated ${projectPackageJsonPath}`); 206 | 207 | // --- 8. Instruct user --- 208 | console.log( 209 | "\nEjection successful! \nPlease run 'npm install' (or 'yarn install' or 'pnpm install') to update your dependencies." 210 | ); 211 | console.log( 212 | "Your build, dev, and setup scripts have been updated to run directly." 213 | ); 214 | console.log( 215 | `Configuration files like setup.js have been copied to the '${path.basename( 216 | projectScriptsDir 217 | )}' directory.` 218 | ); 219 | console.log( 220 | "You are now responsible for maintaining these configurations and dependencies." 221 | ); 222 | } catch (error) { 223 | console.error("\nEjection failed:", error.message); 224 | process.exit(1); 225 | } 226 | -------------------------------------------------------------------------------- /packages/mcp-scripts/src/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import { spawn } from "child_process"; 4 | import { join } from "path"; 5 | import { fileURLToPath } from "url"; 6 | 7 | const __filename = fileURLToPath(import.meta.url); 8 | const __dirname = fileURLToPath(new URL(".", import.meta.url)); 9 | 10 | // Define commands 11 | const commands = { 12 | dev: "tsup src/index.ts --format esm --dts --watch", 13 | build: "tsup src/index.ts --format esm --dts --clean", 14 | setup: `node ${join(__dirname, "setup.js")}`, 15 | eject: `node ${join(__dirname, "eject.js")}`, 16 | }; 17 | 18 | // Execute a command 19 | function executeCommand(command: string) { 20 | const [cmd, ...args] = command.split(" "); 21 | const child = spawn(cmd, args, { 22 | stdio: "inherit", 23 | shell: true, 24 | cwd: process.cwd(), 25 | }); 26 | 27 | child.on("error", (error) => { 28 | console.error("Error executing command:", error.message); 29 | process.exit(1); 30 | }); 31 | 32 | child.on("close", (code) => { 33 | process.exit(code || 0); 34 | }); 35 | } 36 | 37 | // Get the command name from arguments 38 | const command = process.argv[2]; 39 | 40 | // Output usage information 41 | if (!command || !(command in commands) || command === "help") { 42 | console.error("Usage: mcp-scripts "); 43 | console.error("Available commands:"); 44 | console.error(" dev build MCP server and watch for changes"); 45 | console.error(" build build MCP server"); 46 | console.error( 47 | " setup set up MCP clients: Cursor, Windsurf, and Claude Desktop" 48 | ); 49 | console.error(" eject eject from mcp-scripts"); 50 | console.error(" help output usage information"); 51 | process.exit(1); 52 | } 53 | 54 | executeCommand(commands[command as keyof typeof commands]); 55 | -------------------------------------------------------------------------------- /packages/mcp-scripts/src/setup.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import { readFileSync, writeFileSync, existsSync } from "fs"; 4 | import { join } from "path"; 5 | import { homedir } from "os"; 6 | 7 | // MCP config JSON interface 8 | interface MCPConfig { 9 | mcpServers: { 10 | [key: string]: { 11 | command: string; 12 | args: string[]; 13 | }; 14 | }; 15 | } 16 | 17 | // Construct config paths for popular MCP clients 18 | const configPaths = { 19 | cursor: join(homedir(), ".cursor", "mcp.json"), 20 | windsurf: join(homedir(), ".codeium", "windsurf", "mcp_config.json"), 21 | claude: join( 22 | homedir(), 23 | "Library", 24 | "Application Support", 25 | "Claude", 26 | "claude_desktop_config.json" 27 | ), 28 | }; 29 | 30 | // Read MCP config file 31 | function readConfig(path: string): MCPConfig { 32 | if (!existsSync(path)) { 33 | return { mcpServers: {} }; 34 | } 35 | return JSON.parse(readFileSync(path, "utf-8")); 36 | } 37 | 38 | // Write MCP config file 39 | function writeConfig(path: string, config: MCPConfig) { 40 | writeFileSync(path, JSON.stringify(config, null, 2)); 41 | } 42 | 43 | // Add MCP config to MCP clients 44 | function addMCPConfig(serverName: string, projectPath: string) { 45 | const newConfig = { 46 | command: process.execPath, 47 | args: [join(projectPath, "dist", "index.js")], 48 | }; 49 | 50 | for (const [client, configPath] of Object.entries(configPaths)) { 51 | const config = readConfig(configPath); 52 | 53 | if (config.mcpServers[serverName]) { 54 | console.log( 55 | `MCP server "${serverName}" already exists in ${client} config. Skipping...` 56 | ); 57 | continue; 58 | } 59 | 60 | config.mcpServers[serverName] = newConfig; 61 | writeConfig(configPath, config); 62 | console.log(`Added "${serverName}" to ${client} config.`); 63 | } 64 | } 65 | 66 | // Infer server name from project path 67 | const projectPath = process.cwd(); 68 | const serverName = projectPath.split("/").pop(); 69 | 70 | if (!serverName) { 71 | console.error("Could not infer MCP server name from current directory"); 72 | process.exit(1); 73 | } 74 | 75 | addMCPConfig(serverName, projectPath); 76 | -------------------------------------------------------------------------------- /packages/mcp-scripts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "Node16", 4 | "moduleResolution": "Node16", 5 | "esModuleInterop": true, 6 | "isolatedModules": true, 7 | "outDir": "./dist", 8 | "rootDir": "./src" 9 | }, 10 | "exclude": ["node_modules", "dist"], 11 | "include": ["src/**/*", "package.json"] 12 | } 13 | -------------------------------------------------------------------------------- /packages/mcp-ts-template-default/.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules/ 3 | 4 | # System 5 | .DS_Store 6 | .idea/ 7 | .vscode/ 8 | -------------------------------------------------------------------------------- /packages/mcp-ts-template-default/.npmignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules/ 3 | 4 | # System 5 | .DS_Store 6 | .idea/ 7 | .vscode/ 8 | -------------------------------------------------------------------------------- /packages/mcp-ts-template-default/README.md: -------------------------------------------------------------------------------- 1 | # mcp-ts-template-default 2 | 3 | Default project template for `create-mcp-ts`. 4 | 5 | ## Related packages 6 | 7 | - **[packages/create-mcp-ts](https://github.com/stephencme/create-mcp-ts/tree/main/packages/create-mcp-ts)**: Create a new MCP server in TypeScript, batteries included. 8 | - **[packages/mcp-scripts](https://github.com/stephencme/create-mcp-ts/tree/main/packages/mcp-scripts)**: The build tools that power `create-mcp-ts` projects. 9 | -------------------------------------------------------------------------------- /packages/mcp-ts-template-default/gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules/ 3 | 4 | # System 5 | .DS_Store 6 | .idea/ 7 | .vscode/ 8 | -------------------------------------------------------------------------------- /packages/mcp-ts-template-default/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mcp-ts-template-default", 3 | "version": "0.3.4", 4 | "description": "Default project template for create-mcp-ts.", 5 | "author": "stephencme", 6 | "license": "MIT", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/stephencme/create-mcp-ts.git", 10 | "directory": "packages/mcp-ts-template-default" 11 | }, 12 | "type": "module", 13 | "bin": "./dist/index.js", 14 | "scripts": { 15 | "dev": "mcp-scripts dev", 16 | "build": "mcp-scripts build", 17 | "setup": "mcp-scripts setup", 18 | "eject": "mcp-scripts eject" 19 | }, 20 | "dependencies": { 21 | "@modelcontextprotocol/sdk": "^1.8.0", 22 | "zod": "^3.24.2" 23 | }, 24 | "devDependencies": { 25 | "@types/node": "^22.13.13", 26 | "mcp-scripts": "^0.3.4", 27 | "typescript": "^5.8.2" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/mcp-ts-template-default/src/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; 4 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; 5 | import { z } from "zod"; 6 | 7 | const packageJson = require("../package.json") as any; 8 | 9 | // Create a new MCP server 10 | const server = new McpServer( 11 | { 12 | name: packageJson.name, 13 | version: packageJson.version, 14 | }, 15 | { 16 | instructions: 17 | "These tools communicate with a reference Model Context Protocol (MCP) server.", 18 | } 19 | ); 20 | 21 | // Implement the ping_pong tool 22 | server.tool( 23 | "ping_pong", 24 | "Ping the server and receive a pong back", 25 | {}, 26 | async () => { 27 | return { 28 | content: [{ type: "text", text: "pong" }], 29 | }; 30 | } 31 | ); 32 | 33 | // Implement the echo tool 34 | server.tool( 35 | "echo", 36 | "Send a message to the server and receive the message back", 37 | { message: z.string() }, 38 | async (params) => { 39 | return { 40 | content: [{ type: "text", text: params.message }], 41 | }; 42 | } 43 | ); 44 | 45 | // Start the server 46 | async function run() { 47 | try { 48 | // Use stdio for transport 49 | const transport = new StdioServerTransport(); 50 | await server.connect(transport); 51 | // Since stdout is used for MCP messages, use stderr for logging 52 | console.error("MCP server connected via stdio"); 53 | } catch (error) { 54 | console.error("Error starting MCP server:", error); 55 | process.exit(1); 56 | } 57 | } 58 | 59 | run(); 60 | -------------------------------------------------------------------------------- /packages/mcp-ts-template-default/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "Node16", 4 | "moduleResolution": "Node16", 5 | "esModuleInterop": true, 6 | "isolatedModules": true, 7 | "outDir": "./dist", 8 | "rootDir": "./src" 9 | }, 10 | "exclude": ["node_modules", "dist"], 11 | "include": ["src/**/*", "package.json"] 12 | } 13 | -------------------------------------------------------------------------------- /scripts/updateTemplatesDeps.cjs: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const glob = require("glob"); 3 | 4 | try { 5 | // Read mcp-scripts package.json to get the version 6 | const mcpScriptsPackage = JSON.parse( 7 | fs.readFileSync("packages/mcp-scripts/package.json", "utf8") 8 | ); 9 | const version = mcpScriptsPackage.version; 10 | 11 | console.log(`Detected mcp-scripts version: ${version}`); 12 | 13 | // Find all template package.json files 14 | const templatePackageFiles = glob.sync( 15 | "packages/mcp-ts-template-*/package.json" 16 | ); 17 | 18 | // Update mcp-scripts dependency in each template package 19 | templatePackageFiles.forEach((packagePath) => { 20 | const packageJson = JSON.parse(fs.readFileSync(packagePath, "utf8")); 21 | let updated = false; 22 | 23 | // Check and update in dependencies 24 | if (packageJson.dependencies?.["mcp-scripts"]) { 25 | packageJson.dependencies["mcp-scripts"] = `^${version}`; 26 | updated = true; 27 | } 28 | 29 | // Check and update in devDependencies 30 | if (packageJson.devDependencies?.["mcp-scripts"]) { 31 | packageJson.devDependencies["mcp-scripts"] = `^${version}`; 32 | updated = true; 33 | } 34 | 35 | if (updated) { 36 | fs.writeFileSync( 37 | packagePath, 38 | JSON.stringify(packageJson, null, 2) + "\n" 39 | ); 40 | console.log( 41 | `Updated mcp-scripts dependency to ${version} in ${packagePath}` 42 | ); 43 | } 44 | }); 45 | } catch (error) { 46 | console.error("Error:", error.message); 47 | process.exit(1); 48 | } 49 | --------------------------------------------------------------------------------