MCP Server Starter Kit
# π MCP Server Starter Kit
**Production-ready Model Context Protocol (MCP) server boilerplate.**
Ship your first MCP server in minutes, not days.
[](https://github.com/srmcguirt/mcp-server-starter-kit/actions/workflows/ci.yml)    
> π **Premium edition** with Python (FastMCP) version, Railway/Render deploy configs, auth patterns, and 1-on-1 setup support β [Get it on Gumroad β](https://srmcguirt.gumroad.com/l/mcp-starter)
---
## What's included
| Feature | Status |
|---------|--------|
| TypeScript with strict mode | β
|
| Proper stderr logging (won't break MCP stdio) | β
|
| Token-bucket rate limiter | β
|
| Environment variable validation (Zod) | β
|
| Centralized error handling | β
|
| 2 example tools (echo + fetch_url) | β
|
| Docker + docker-compose | π Premium |
| Claude Desktop auto-config script | π Premium |
| Unit test setup (Vitest) | β
|
| Python/FastMCP version | π Premium |
| Railway one-click deploy | π Premium |
| API key auth middleware | π Premium |
| OAuth 2.0 integration pattern | π Premium |
| Webhook receiver tool | π Premium |
| Database connection pattern | π Premium |
---
## Why this starter kit?
Every MCP server tutorial shows you a 30-line "hello world." Then you try to build something real and discover:
- **Logging to stdout breaks MCP** β the protocol uses stdout for communication; your `console.log` corrupts it
- **No rate limiting** β a runaway AI agent can hammer your APIs
- **No input validation** β AI can send malformed arguments and crash your server
- **No error handling** β unhandled exceptions crash the whole server
- **No deploy story** β how do you actually run this in production?
This starter kit solves all of that from day one.
---
## Quick start
### Option 1: Use as a template
```bash
# Clone and rename
git clone https://github.com/srmcguirt/mcp-server-starter-kit my-mcp-server
cd my-mcp-server
# Install dependencies
npm install
# Copy env file and fill in your values
cp .env.example .env
# Start in dev mode (hot reload)
npm run dev
```
### Option 2: Scaffold with npx
```bash
npx @srmcguirt/mcp-server-starter init my-server-name
cd my-server-name
npm install && npm run dev
```
### Option 3: Install as a library
```bash
npm install @srmcguirt/mcp-server-starter
```
---
## Add your first tool
Open `src/tools/` and create a new file:
```typescript
// src/tools/my-tool.ts
import { z } from 'zod';
import { toolResult } from '../lib/error-handler.js';
import type { MCPTool } from '../types.js';
const MyInputSchema = z.object({
query: z.string().min(1).max(500),
limit: z.number().int().positive().max(100).default(10),
});
export const myTool: MCPTool = {
name: 'my_tool',
description: 'Search for something and return results. Be specific about what this does β the AI reads this description.',
inputSchema: {
type: 'object',
properties: {
query: { type: 'string', description: 'The search query' },
limit: { type: 'number', description: 'Max results to return', default: 10 },
},
required: ['query'],
},
async execute(args) {
const { query, limit } = MyInputSchema.parse(args);
// Your implementation here
const results = await myApi.search(query, { limit });
return toolResult(JSON.stringify(results, null, 2));
},
};
```
Then register it in `src/tools/index.ts`:
```typescript
import { myTool } from './my-tool.js';
export const tools: MCPTool[] = [
echoTool,
fetchUrlTool,
myTool, // π Add here
];
```
---
## Connect to Claude Desktop
```bash
# Build and add to Claude Desktop config automatically
chmod +x scripts/add-to-claude.sh
./scripts/add-to-claude.sh my-server-name
# Then restart Claude Desktop
```
Or manually add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
```json
{
"mcpServers": {
"my-server-name": {
"command": "node",
"args": ["/absolute/path/to/my-mcp-server/dist/index.js"],
"env": {
"MY_API_KEY": "your-key-here"
}
}
}
}
```
---
## Connect to Cursor / Cline / Windsurf
Add to your editor's MCP settings:
```json
{
"mcp": {
"servers": {
"my-server-name": {
"command": "node",
"args": ["/absolute/path/to/my-mcp-server/dist/index.js"]
}
}
}
}
```
---
## Deploy with Docker
The Dockerfile, docker-compose config, and the Claude Desktop auto-register
script ship with the premium edition:
**[Get the premium edition β](https://srmcguirt.gumroad.com/l/mcp-starter)**
---
## Project structure
```
mcp-server-starter/
βββ src/
β βββ index.ts # Server entry point β wire everything together here
β βββ types.ts # MCPTool interface and shared types
β βββ lib/
β β βββ logger.ts # Winston logger β always logs to stderr
β β βββ rate-limiter.ts # Token-bucket rate limiter
β β βββ env.ts # Environment variable validation (Zod)
β β βββ error-handler.ts # Centralized error handling + toolResult helpers
β βββ tools/
β βββ index.ts # Tool registry β add your tools here
β βββ echo.ts # Example: simple string echo
β βββ fetch-url.ts # Example: HTTP fetch with timeout + size limit
βββ docker/
β βββ Dockerfile # Multi-stage production build
β βββ docker-compose.yml # Local development + production compose
βββ scripts/
β βββ add-to-claude.sh # Auto-add to Claude Desktop config
βββ .env.example # Required environment variables
βββ tsconfig.json # Strict TypeScript config
βββ package.json
```
---
## Key patterns
### β
Always log to stderr
```typescript
// β WRONG β corrupts MCP protocol
console.log('something happened');
// β
CORRECT β logs to stderr, leaves stdout clean
logger.info('something happened');
```
### β
Validate all input with Zod
```typescript
// β WRONG β trusting AI-provided args
const { query } = args as { query: string };
// β
CORRECT β parse and validate
const { query } = MySchema.parse(args); // throws McpError on invalid input
```
### β
Use withErrorHandling for every tool
```typescript
// β WRONG β unhandled exceptions crash the server
async execute(args) {
return await riskyOperation(args);
}
// β
CORRECT β errors logged + safe message returned to AI
return withErrorHandling('my_tool', () => riskyOperation(args));
```
---
## π Premium Edition β $49
The open source version is a solid foundation. The **Gumroad premium download** adds:
- β
Python/FastMCP version (same patterns, same quality)
- β
API key authentication middleware
- β
OAuth 2.0 integration pattern (GitHub, Google, etc.)
- β
Railway + Render one-click deploy configs
- β
Database connection patterns (Postgres, SQLite, Redis)
- β
Webhook receiver tool template
- β
Streaming responses pattern
- β
MCP resources and prompts examples
- β
30-min video walkthrough: building a real production MCP server
- β
6 real-world example servers (GitHub, Notion, Slack, Postgres, filesystem, web search)
- β
Commercial license (use in client work and products)
**[Get the premium edition β](https://srmcguirt.gumroad.com/l/mcp-starter)**
---
## FAQ
**Q: Why TypeScript and not JavaScript?**
A: MCP tool schemas need to match your implementation exactly. TypeScript catches mismatches at build time, not at 2am when an AI passes unexpected input.
**Q: Why log to stderr?**
A: MCP uses stdio transport β stdout carries the JSON-RPC protocol. Anything you write to stdout that isn't valid MCP JSON will corrupt the connection. The logger in this kit always writes to stderr.
**Q: Can I use this with Python?**
A: The Python/FastMCP version is in the premium edition. The patterns are identical β just in Python.
**Q: Is this compatible with all MCP clients?**
A: Yes. Uses the official `@modelcontextprotocol/sdk`. Tested with Claude Desktop, Cursor, Cline, and Windsurf.
---
## Contributing
PRs welcome. See [CONTRIBUTING.md](CONTRIBUTING.md).
---
## License
MIT β free for personal and open source use.
Commercial license (client work, products, resale) included in the [Premium Edition on Gumroad](https://srmcguirt.gumroad.com/l/mcp-starter).
---
## π¬ Stay Updated
Get a free sample prompt + updates when new tools ship:
**β [srmcguirt.dev](https://srmcguirt.dev)**
TDQS
Scored across 2 tools
Echo and fetch_url are completely distinct in purposeβone verifies connectivity, the other retrieves web content. There is no overlap or ambiguity between them.
Both names are imperative and lowercase, but one is a bare verb (echo) while the other follows a verb_noun pattern (fetch_url). This mixed convention, while readable, is not fully consistent.
Two tools is appropriate for a starter kit that aims to demonstrate basic MCP functionality, but the count feels thin compared to typical servers that offer more comprehensive operations.
The tool set covers the intended purpose of a starter kit: verifying server operation and fetching URL content. There are no obvious gaps for this narrow scope, though it lacks broader capabilities.