MCP Server Plus
Enhanced MCP server creation with templates, security, deployment, and monitoring.
Features
- Server Templates: Ready-to-use templates for common use cases
- Security Best Practices: Authentication, rate limiting, input validation
- Deployment Guides: Multiple platforms (Docker, Vercel, AWS)
- Monitoring: Health checks, logging, metrics
- Transport Options: stdio, SSE, WebSocket
Quick Reference
| Use Case | Template | Transport | |----------|----------|-----------| | API wrapper | api-wrapper | SSE | | Database access | database | stdio | | File system | filesystem | stdio | | Real-time | websocket | WebSocket |
Server Templates
API Wrapper Server
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
const server = new Server(
{
name: "api-wrapper",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
// Define tools
server.setRequestHandler("tools/list", async () => ({
tools: [
{
name: "fetch_data",
description: "Fetch data from external API",
inputSchema: {
type: "object",
properties: {
endpoint: { type: "string" },
params: { type: "object" },
},
required: ["endpoint"],
},
},
],
}));
// Handle tool calls
server.setRequestHandler("tools/call", async (request) => {
const { name, arguments: args } = request.params;
if (name === "fetch_data") {
const response = await fetch(args.endpoint, {
method: "GET",
headers: {
"Authorization": `Bearer ${process.env.API_KEY}`,
},
});
const data = await response.json();
return {
content: [
{
type: "text",
text: JSON.stringify(data, null, 2),
},
],
};
}
});
// Start server
const transport = new StdioServerTransport();
await server.connect(transport);
Database Server
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { Pool } from "pg";
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
});
const server = new Server(
{
name: "database",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
server.setRequestHandler("tools/list", async () => ({
tools: [
{
name: "query",
description: "Execute SQL query",
inputSchema: {
type: "object",
properties: {
sql: { type: "string" },
params: { type: "array" },
},
required: ["sql"],
},
},
],
}));
server.setRequestHandler("tools/call", async (request) => {
const { name, arguments: args } = request.params;
if (name === "query") {
const result = await pool.query(args.sql, args.params);
return {
content: [
{
type: "text",
text: JSON.stringify({
rows: result.rows,
rowCount: result.rowCount,
}, null, 2),
},
],
};
}
});
const transport = new StdioServerTransport();
await server.connect(transport);
File System Server
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import * as fs from "fs/promises";
import * as path from "path";
const server = new Server(
{
name: "filesystem",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
server.setRequestHandler("tools/list", async () => ({
tools: [
{
name: "read_file",
description: "Read file contents",
inputSchema: {
type: "object",
properties: {
path: { type: "string" },
},
required: ["path"],
},
},
{
name: "write_file",
description: "Write to file",
inputSchema: {
type: "object",
properties: {
path: { type: "string" },
content: { type: "string" },
},
required: ["path", "content"],
},
},
],
}));
server.setRequestHandler("tools/call", async (request) => {
const { name, arguments: args } = request.params;
if (name === "read_file") {
const content = await fs.readFile(args.path, "utf-8");
return {
content: [
{
type: "text",
text: content,
},
],
};
}
if (name === "write_file") {
await fs.writeFile(args.path, args.content);
return {
content: [
{
type: "text",
text: `File written successfully: ${args.path}`,
},
],
};
}
});
const transport = new StdioServerTransport();
await server.connect(transport);
Security Best Practices
Authentication
// API key validation
const validateApiKey = (req) => {
const apiKey = req.headers["x-api-key"];
if (apiKey !== process.env.API_KEY) {
throw new Error("Invalid API key");
}
};
// OAuth2 validation
const validateOAuth = async (req) => {
const token = req.headers.authorization?.split(" ")[1];
if (!token) {
throw new Error("Missing authorization token");
}
const user = await verifyToken(token);
return user;
};
Rate Limiting
import rateLimit from "express-rate-limit";
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
message: "Too many requests",
});
app.use(limiter);
Input Validation
import { z } from "zod";
const ToolInputSchema = z.object({
endpoint: z.string().url(),
params: z.object({}).optional(),
});
const validateInput = (input) => {
return ToolInputSchema.parse(input);
};
Environment Variables
# Required
API_KEY=your-api-key
DATABASE_URL=postgresql://user:pass@host/db
# Optional
PORT=3000
LOG_LEVEL=info
RATE_LIMIT=100
Deployment Guides
Docker
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
# docker-compose.yml
version: "3.8"
services:
mcp-server:
build: .
ports:
- "3000:3000"
environment:
- API_KEY=${API_KEY}
- DATABASE_URL=${DATABASE_URL}
restart: unless-stopped
Vercel
{
"version": 2,
"builds": [
{
"src": "server.js",
"use": "@vercel/node"
}
],
"routes": [
{
"src": "/(.*)",
"dest": "server.js"
}
]
}
AWS Lambda
// handler.js
const { Server } = require("@modelcontextprotocol/sdk/server/index.js");
const { SSEServerTransport } = require("@modelcontextprotocol/sdk/server/sse.js");
exports.handler = async (event, context) => {
const server = new Server(/* ... */);
const transport = new SSEServerTransport("/messages", res);
await server.connect(transport);
return {
statusCode: 200,
body: JSON.stringify({ status: "connected" }),
};
};
Monitoring
Health Checks
app.get("/health", (req, res) => {
res.json({
status: "healthy",
uptime: process.uptime(),
timestamp: new Date().toISOString(),
});
});
Logging
import winston from "winston";
const logger = winston.createLogger({
level: process.env.LOG_LEVEL || "info",
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [
new winston.transports.Console(),
new winston.transports.File({ filename: "error.log", level: "error" }),
new winston.transports.File({ filename: "combined.log" }),
],
});
Metrics
import { Counter, Histogram } from "prom-client";
const requestCounter = new Counter({
name: "mcp_requests_total",
help: "Total number of MCP requests",
labelNames: ["method", "status"],
});
const requestDuration = new Histogram({
name: "mcp_request_duration_seconds",
help: "Duration of MCP requests in seconds",
labelNames: ["method"],
});
Transport Options
stdio
- Best for local communication
- Simple setup
- Low latency
SSE (Server-Sent Events)
- Best for web applications
- HTTP-based
- Easy to proxy
WebSocket
- Best for real-time communication
- Bidirectional
- Persistent connection
Best Practices
- Use TypeScript - Type safety and better tooling
- Validate inputs - Always validate tool inputs
- Handle errors - Return meaningful error messages
- Log operations - Track tool usage and errors
- Rate limit - Prevent abuse
- Use environment variables - Never hardcode secrets
- Health checks - Monitor server status
- Documentation - Document tools and usage
Common Issues
| Issue | Solution | |-------|----------| | Connection refused | Check server is running | | Authentication failed | Verify API key | | Timeout | Increase timeout or check network | | Rate limited | Implement rate limiting | | Memory leak | Monitor and restart periodically |
Scan to join WeChat group