Skip to main content
Glama
chrisleekr

MCP Server Boilerplate

by chrisleekr
middleware.ts4.79 kB
import bodyParser from 'body-parser'; import { Application, NextFunction, Request, Response } from 'express'; import { rateLimit } from 'express-rate-limit'; import helmet from 'helmet'; import pinoHttp from 'pino-http'; import { v4 as uuidv4 } from 'uuid'; import { getIPAddress } from '@/utils/ip'; import { logger } from '@/utils/logger'; import { AsyncLocalStorageLoggingContext, loggingContext } from './context'; // MCP Protocol Version constants const CURRENT_MCP_VERSION = '2025-06-18'; const FALLBACK_MCP_VERSION = '2025-03-26'; const SUPPORTED_MCP_VERSIONS = [CURRENT_MCP_VERSION, FALLBACK_MCP_VERSION]; export function setupMiddleware(app: Application): void { app.use(helmet()); // Rate limit requests globally app.use( // Refer: https://express-rate-limit.mintlify.app/reference/configuration rateLimit({ windowMs: 1 * 60 * 1000, // 1 minute max: 100, // Limit each IP to 100 requests per `windowMs` standardHeaders: true, legacyHeaders: false, // Can use `store` to use a database to store the rate limit data skip: (req: Request) => { // Skip rate limiting for kube-probe requests return req.headers['user-agent']?.includes('kube-probe') ?? false; }, }) ); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); app.use( pinoHttp({ logger: logger.getLogger(), autoLogging: { ignore: (req: Request) => { return req.headers['user-agent']?.includes('kube-probe') ?? false; }, }, }) ); // MCP Protocol Version Enforcement Middleware app.use((req: Request, res: Response, next: NextFunction) => { // Skip version check for non-MCP endpoints if (!req.path.startsWith('/mcp')) { next(); return; } const protocolVersion = req.headers['mcp-protocol-version'] as string; if (!protocolVersion) { // For backward compatibility, default to 2025-03-26 if version can't be detected loggingContext.log( 'warn', 'Missing MCP-Protocol-Version header, defaulting to fallback version', { data: { fallbackVersion: FALLBACK_MCP_VERSION, path: req.path, method: req.method, }, } ); // Set the fallback version in request context req.headers['mcp-protocol-version'] = FALLBACK_MCP_VERSION; } else if (!SUPPORTED_MCP_VERSIONS.includes(protocolVersion)) { loggingContext.log('error', 'Unsupported MCP protocol version', { data: { requestedVersion: protocolVersion, supportedVersions: SUPPORTED_MCP_VERSIONS, path: req.path, }, }); res.status(400).json({ error: 'Unsupported MCP protocol version', supported_versions: SUPPORTED_MCP_VERSIONS, requested_version: protocolVersion, }); return; } next(); }); app.use((req: Request, res: Response, next: NextFunction) => { const requestStartTime = Date.now(); const context: AsyncLocalStorageLoggingContext = { requestId: (req.headers['x-request-id'] as string) || uuidv4(), mcpSessionId: (req.headers['mcp-session-id'] as string) || '', mcpProtocolVersion: (req.headers['mcp-protocol-version'] as string) || FALLBACK_MCP_VERSION, ipAddress: getIPAddress(req), userAgent: req.headers['user-agent'] ?? '', requestStartTime, }; loggingContext.init(context, () => { // clean up on response finish res.on('finish', () => { const requestDuration = Date.now() - requestStartTime; // If the request takes longer than 30 seconds, log a warning if (requestDuration > 30000) { loggingContext.log( 'warn', `Long-running request detected: ${context.requestId} (${requestDuration}ms)`, { requestDuration, } ); } }); res.on('close', () => { // Client disconnected - context will be automatically cleaned up if (!res.writableEnded) { loggingContext.log( 'warn', 'Client disconnected before response completion' ); } }); // Continue with the next middleware immediately next(); }); }); // CORS headers for web clients app.use((req, res, next) => { res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS'); res.header( 'Access-Control-Allow-Headers', 'Content-Type, Mcp-Session-Id, Mcp-Protocol-Version, Last-Event-ID, Authorization' ); if (req.method === 'OPTIONS') { res.sendStatus(200); return; } next(); }); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/chrisleekr/mcp-server-boilerplate'

If you have feedback or need assistance with the MCP directory API, please join our Discord server