Skip to main content
Glama
srmcguirt

MCP Server Starter Kit

by srmcguirt
README.md
# πŸš€ MCP Server Starter Kit

**Production-ready Model Context Protocol (MCP) server boilerplate.**  
Ship your first MCP server in minutes, not days.

[![CI](https://github.com/srmcguirt/mcp-server-starter-kit/actions/workflows/ci.yml/badge.svg)](https://github.com/srmcguirt/mcp-server-starter-kit/actions/workflows/ci.yml) ![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg) ![TypeScript](https://img.shields.io/badge/TypeScript-5.x-3178C6?logo=typescript&logoColor=white) ![Node](https://img.shields.io/badge/Node-%3E%3D18-339933?logo=node.js&logoColor=white) ![MCP](https://img.shields.io/badge/MCP-Compatible-8A2BE2)

> πŸ’Ž **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

A3.8/5.0

Scored across 2 tools

Disambiguation5/5

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.

Naming Consistency3/5

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.

Tool Count3/5

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.

Completeness4/5

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.

Maintenance

ActivityMaintained
ResponsivenessSyncing