MCP Server Template
A production-ready template for building Model Context Protocol (MCP) servers with TypeScript.
Features
MCP SDK 1.24.3 - Latest SDK with 2025-11-25 spec support
Dual Transport - Stdio (Claude Desktop) and HTTP (cloud deployment)
OAuth 2.1 Foundations - Protected resource metadata, bearer token structure
SQLite Caching - TTL-based caching with sql.js (WebAssembly)
Observability - Sentry error tracking + OpenTelemetry tracing
Security - PII sanitization, rate limiting, DNS rebinding protection
Type Safety - Strict TypeScript with Zod validation
Quick Start
# Clone and install
git clone <this-repo> my-mcp-server
cd my-mcp-server
npm install
# Development mode (hot reload)
npm run dev
# Build and run
npm run build
npm start
# Test with MCP Inspector
npm run inspector
Project Structure
src/
├── index.ts # CLI entry point
├── server.ts # MCP server implementation
├── instrumentation.ts # Sentry + OpenTelemetry setup
├── config/
│ └── index.ts # Environment configuration
├── db/
│ ├── database.ts # SQLite connection (sql.js)
│ └── cache.ts # TTL-based caching
├── tools/
│ ├── registry.ts # Tool registration pattern
│ └── examples.ts # Example tool implementations
├── transport/
│ └── http-transport.ts # HTTP with OAuth foundations
├── shared/
│ ├── logger.ts # Structured logging
│ ├── errors.ts # Custom error classes
│ ├── rate-limiter.ts # Per-source rate limiting
│ ├── pii-sanitizer.ts # PII detection/removal
│ └── tracing.ts # OpenTelemetry utilities
└── types/
└── index.ts # TypeScript type definitions
Configuration
Environment variables (prefix with MCP_SERVER_):
Variable | Default | Description |
MCP_SERVER_NAME
| mcp-server-template
| Server name |
MCP_SERVER_VERSION
| 0.1.0
| Server version |
MCP_SERVER_LOG_LEVEL
| info
| Log level: debug, info, warning, error |
MCP_SERVER_DB_PATH
| .data/cache.db
| SQLite database path |
MCP_SERVER_CACHE_ENABLED
| true
| Enable/disable caching |
MCP_SERVER_CACHE_TTL
| 3600
| Default cache TTL (seconds) |
MCP_SERVER_TIMEOUT
| 30000
| Request timeout (ms) |
MCP_SERVER_TRANSPORT
| stdio
| Transport: stdio
or http
|
MCP_SERVER_PORT
| 3000
| HTTP port (when transport=http) |
MCP_SERVER_HOST
| 127.0.0.1
| HTTP host (when transport=http) |
MCP_SERVER_SENTRY_DSN
| - | Sentry DSN for error tracking |
OTEL_ENABLED
| false
| Enable OpenTelemetry tracing |
OTEL_EXPORTER_OTLP_ENDPOINT
| - | OTLP collector endpoint |
MCP_SERVER_DEBUG
| false
| Debug mode (skips auth) |
Creating Tools
Tools are registered with the ToolRegistry using Zod schemas:
import { z } from 'zod';
import { getToolRegistry } from './tools/registry.js';
// Define input schema
const MyToolInputSchema = z.object({
query: z.string().min(1).describe('Search query'),
limit: z.number().positive().optional().default(10),
});
type MyToolInput = z.infer<typeof MyToolInputSchema>;
// Implement handler
async function myToolHandler(input: MyToolInput) {
// Your implementation here
return {
success: true,
data: { results: [] },
};
}
// Register tool
const registry = getToolRegistry();
registry.register(
'my_tool',
'Description of what this tool does',
MyToolInputSchema,
myToolHandler
);
Transport Modes
Stdio (Default)
For Claude Desktop and local integrations:
Add to Claude Desktop config:
{
"mcpServers": {
"my-server": {
"command": "node",
"args": ["/path/to/dist/index.js"]
}
}
}
HTTP
For cloud deployment:
MCP_SERVER_TRANSPORT=http npm start
Endpoints:
GET /health - Health check
GET /.well-known/mcp - MCP server metadata
GET /.well-known/oauth-protected-resource - OAuth metadata (RFC 9728)
GET /mcp - SSE stream for server events
POST /mcp - JSON-RPC requests
DELETE /mcp - Close session
Security Features
PII Sanitization
Automatically detects and masks sensitive data:
import { sanitizePii } from './shared/pii-sanitizer.js';
const safe = sanitizePii('Contact: user@example.com');
// Output: "Contact: [EMAIL]"
Rate Limiting
Per-source rate limiting with exponential backoff:
import { getRateLimiter } from './shared/rate-limiter.js';
const limiter = getRateLimiter();
limiter.configure('external-api', {
requestsPerWindow: 100,
windowMs: 60000,
});
await limiter.waitForSlot('external-api');
// Make request...
DNS Rebinding Protection
HTTP transport validates Host headers against allowlist.
Observability
Sentry
Error tracking with PII filtering:
MCP_SERVER_SENTRY_DSN=https://xxx@sentry.io/xxx npm start
OpenTelemetry
Distributed tracing:
OTEL_ENABLED=true \
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 \
npm start
Use tracing utilities:
import { withSpan, createApiSpan } from './shared/tracing.js';
const result = await withSpan('my-operation', async (span) => {
span.setAttribute('custom.attr', 'value');
return doWork();
});
Development
# Type check
npm run check
# Lint
npm run lint
npm run lint:fix
# Format
npm run format
# Test
npm test
npm run test:coverage
# Validate (all checks)
npm run validate
MCP 2025-11-25 Spec Compliance
Feature | Status |
Tools | ✅ Implemented |
Resources | 📝 Scaffolded |
Prompts | 📝 Scaffolded |
Streamable HTTP | ✅ Implemented |
.well-known/mcp | ✅ Implemented |
OAuth 2.1 Foundations | ✅ Scaffolded |
Tasks | ❌ Not yet |
Elicitation | ❌ Not yet |
OAuth 2.1 Implementation
The template includes foundations for OAuth 2.1 per the MCP spec:
Protected Resource Metadata (RFC 9728) at /.well-known/oauth-protected-resource
Bearer token middleware structure (implement JWT validation)
WWW-Authenticate headers with resource_metadata reference
Scope checking structure for tool authorization
To complete OAuth integration:
Choose an authorization server (Auth0, Logto, etc.)
Implement JWT validation in bearerAuthMiddleware
Add JWKS fetching and caching
Configure scopes per tool
See MCP Authorization Spec for details.
License
MIT
References