MCP Server Template
  
Production-ready MCP server template for building Model Context Protocol servers with TypeScript and Bun runtime.
What is this?
This is a template project for creating MCP servers. It includes:
- TypeScript with strict type checking
- Bun runtime for fast execution
- Zod schema validation
- Structured logging (stderr only)
- Retry utilities with exponential backoff
- Unit and integration test setup
- Production-ready error handling
Installation
bun install
Quick Start
# Start the MCP server
bun run start
# Development with hot reload
bun run dev
MCP Configuration
Add to your MCP client configuration:
{
"mcpServers": {
"mcp-server-template": {
"command": "bun",
"args": ["run", "/path/to/mcp-server-template/src/server.ts"]
}
}
}
Or use the built version:
{
"mcpServers": {
"mcp-server-template": {
"command": "node",
"args": ["/path/to/mcp-server-template/dist/server.js"]
}
}
}
Adding Tools
1. Define Schema
In src/types/index.ts:
import { z } from "zod";
export const MyToolInputSchema = z.object({
param1: z.string().min(1).describe("Parameter description"),
param2: z.number().optional().describe("Optional parameter"),
});
export type MyToolInput = z.infer<typeof MyToolInputSchema>;
2. Add Tool Definition
In src/tools/index.ts:
export const toolDefinitions = [
{
name: "my_tool",
description: "Description of what the tool does",
inputSchema: {
type: "object" as const,
properties: {
param1: { type: "string", description: "Parameter description" },
param2: { type: "number", description: "Optional parameter" },
},
required: ["param1"],
},
},
];
3. Implement Handler
export async function handleMyTool(input: unknown): Promise<CallToolResult> {
try {
const validated = MyToolInputSchema.parse(input);
log.info('Executing tool', { tool: 'my_tool', params: validated });
// Your logic here
const result = { data: "..." };
log.info('Tool completed', { tool: 'my_tool', success: true });
return createSuccessResult(result);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
return createErrorResult(message, "my_tool");
}
}
4. Add to Router
export async function handleToolCall(name: string, args: unknown): Promise<CallToolResult> {
switch (name) {
case "my_tool":
return handleMyTool(args);
default:
return createErrorResult(`Unknown tool: ${name}`, name);
}
}
5. Write Tests
Create tests/unit/my_tool.test.ts:
import { describe, test, expect } from "bun:test";
import { handleMyTool } from "../../src/tools/index.js";
describe("My Tool", () => {
test("should work correctly", async () => {
const result = await handleMyTool({ param1: "test" });
expect(result.isError).toBe(false);
});
});
6. Document Tool
Create docs/tools/my_tool.md:
# my_tool
Description of what the tool does.
## Parameters
| Name | Type | Required | Description |
|------|------|----------|-------------|
| param1 | string | Yes | Description |
## Examples
**Input:**
\`\`\`json
{ "param1": "value" }
\`\`\`
**Output:**
\`\`\`json
{ "data": "result" }
\`\`\`
Available Scripts
| Script | Description | |--------|-------------| | bun run dev | Start development server with hot reload | | bun run build | Build for production | | bun run start | Run production build | | bun test | Run unit tests | | bun run test:coverage | Run tests with coverage | | bun run test:runtime | Run runtime testcases | | bun run lint | Run Biome linter | | bun run lint:fix | Auto-fix lint issues | | bun run format | Format code with Biome | | bun run typecheck | Type check without emit |
Configuration
Environment Variables
DEBUG=true- Enable debug logging
Troubleshooting
Server won't start
- Ensure Bun is installed:
bun --version - Check TypeScript errors:
bun run typecheck
Tool execution fails
- Check input schema matches the tool definition
- Enable debug mode:
DEBUG=true bun run start
Development
See DEVELOPMENT.md for development setup.
License
MIT License - see LICENSE for details.






