Scaffold a production-ready MCP server in one command. TypeScript, Vitest tests, GitHub Actions CI, error handling patterns, and best practices — all included.
npx create-mcp-server-pro my-serverThe official MCP scaffolder was archived in November 2024. This is the production-ready replacement — built from patterns used in 4 real MCP servers with 100+ combined tools.
| Feature | Official (archived) | FastMCP template | create-mcp-server-pro |
|---|---|---|---|
| Maintained | Archived Nov 2024 | Yes | Yes |
| Official MCP SDK | Yes | No (FastMCP) | Yes |
| Vitest tests | No | No | Yes |
| GitHub Actions CI | No | No | Yes |
| Error handling patterns | No | No | Yes |
| Agent-friendly README | No | No | Yes |
| Auth boilerplate | No | No | Yes |
| semantic-release | No | No | Yes |
| ESLint 9 + Prettier | No | No | Yes |
npx create-mcp-server-pro my-serverYou'll be asked for:
- Server name — npm package name (defaults to
mcp-server-<directory>) - Description — what your server does
- Authentication — whether it needs an API token
- Author — your name
npx create-mcp-server-pro my-server \
--name mcp-server-weather \
--description "MCP server for weather data" \
--auth \
--author "Your Name"All flags are optional. Provide --name and --description to skip all prompts.
cd my-server
npm install
npm run build
npm testThen add your tools in src/tools.ts and register them in src/index.ts.
my-server/
src/
index.ts # McpServer setup, tool registration, StdioServerTransport
tools.ts # Example tools with proper error handling
tests/
tools.test.ts # Vitest tests with fetch mocking
.github/
workflows/
ci.yml # Node 20 + 22 matrix, lint, typecheck, build, test
release.yml # semantic-release on version tags
FUNDING.yml
package.json # bin field, correct deps, scripts, lint-staged
tsconfig.json # ES2022, NodeNext, strict mode
tsup.config.ts # ESM, node20 target, shebang banner
vitest.config.ts # globals, v8 coverage
eslint.config.js # flat config, typescript-eslint strict
.prettierrc.json
.gitignore
LICENSE # MIT
README.md # Agent-friendly with Quick Start configs
If you're new to MCP (Model Context Protocol), here's what you need to know.
MCP is an open standard by Anthropic that lets AI assistants (Claude, Cursor, GitHub Copilot, Windsurf) connect to external tools and data. Your MCP server exposes tools that AI agents can call.
AI Assistant <--> MCP Protocol <--> Your Server <--> APIs / Data
Every MCP tool has three parts:
import { z } from "zod";
server.tool(
"tool_name", // 1. Name (snake_case)
"What this tool does — be specific for the AI", // 2. Description
{
// 3. Input schema (Zod)
query: z.string().describe("Search query"),
limit: z
.number()
.int()
.min(1)
.max(100)
.default(10)
.describe("Max results to return"),
},
async ({ query, limit }) => {
// 4. Handler
try {
const results = await searchAPI(query, limit);
return {
content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
};
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
return {
content: [{ type: "text", text: `Error: ${message}` }],
isError: true,
};
}
},
);These patterns come from building 4 production MCP servers:
Naming
- Use
snake_casefor tool names (e.g.search_packages,get_user) - Avoid spaces, dots, or mixed case — they cause tokenization issues in LLMs
Descriptions
- Write descriptions for the AI, not for humans
- Be specific: "Search npm packages by keyword and return name, version, and description" is better than "Search packages"
- Every Zod field should have
.describe()— this is how the AI knows what to pass
Error Handling
- Wrap external calls in try/catch
- Return
{ content: [...], isError: true }for recoverable errors - Include actionable context: "API_TOKEN not set. Set it in your MCP client env." not "Auth failed"
Testing
- Test tool logic separately from MCP registration
- Mock
fetchwithvi.spyOn(globalThis, "fetch") - Test both success and error paths
Project Structure
- Keep tool logic in
src/tools.ts(orsrc/tools/for many tools) - Keep MCP registration in
src/index.ts - This separation makes tools testable without MCP transport
After building your server (npm run build), connect it:
Claude Desktop — ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"my-server": {
"command": "npx",
"args": ["-y", "my-server"]
}
}
}Cursor — .cursor/mcp.json in your project:
{
"mcpServers": {
"my-server": {
"command": "npx",
"args": ["-y", "my-server"]
}
}
}VS Code (GitHub Copilot) — .vscode/mcp.json:
{
"servers": {
"my-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "my-server"]
}
}
}These MCP servers were built with the same patterns this scaffolder generates:
| Server | Tools | What it does |
|---|---|---|
| mcp-server-devutils | 17 | Base64, UUID, hash, JWT decode, cron, timestamps, JSON, regex |
| mcp-server-npm | 6 | Search packages, view details, compare, check downloads |
| mcp-server-github-gist | 8 | Create, read, update, list, and search GitHub Gists |
| mcp-server-cloudflare | 13 | Workers, KV, R2, DNS, and cache management |
MIT