📚guideadvanced

Advanced MCP Server Patterns: Professional Practices

Master advanced Model Context Protocol patterns including error handling, resource management, security implementation, and production deployment strategies.

ByMCP Directory Team
Published
⏱️45 minutes
advanced-patternserror-handlingresource-managementsecurityproductionarchitecture

Advanced MCP Server Patterns: Professional Development Practices

This comprehensive guide explores advanced architectural patterns and professional development practices for building production-ready Model Context Protocol (MCP) servers. Learn how to implement robust error handling, efficient resource management, and scalable architectures that meet enterprise requirements.

Table of Contents

  1. Advanced Architecture Patterns
  2. Error Handling and Recovery
  3. Resource Management and Performance
  4. Security Implementation Patterns
  5. State Management and Persistence
  6. Monitoring and Observability
  7. Testing Strategies
  8. Production Deployment Patterns

Advanced Architecture Patterns

Server Factory Pattern

Implement a factory pattern for creating MCP servers with consistent configuration and middleware:

interface MCPServerConfig {
  name: string;
  version: string;
  capabilities: ServerCapabilities;
  middleware?: Middleware[];
}

class MCPServerFactory {
  static create(config: MCPServerConfig): Server {
    const server = new Server(config);
    
    // Apply common middleware
    config.middleware?.forEach(middleware => {
      server.use(middleware);
    });
    
    return server;
  }
}

Plugin Architecture Pattern

Design extensible servers using a plugin system:

interface MCPPlugin {
  name: string;
  version: string;
  register(server: Server): void;
  unregister?(server: Server): void;
}

class PluginManager {
  private plugins: Map<string, MCPPlugin> = new Map();
  
  register(plugin: MCPPlugin, server: Server): void {
    this.plugins.set(plugin.name, plugin);
    plugin.register(server);
  }
  
  unregister(pluginName: string, server: Server): void {
    const plugin = this.plugins.get(pluginName);
    if (plugin?.unregister) {
      plugin.unregister(server);
    }
    this.plugins.delete(pluginName);
  }
}

Resource Pool Pattern

Manage expensive resources efficiently:

class ResourcePool<T> {
  private available: T[] = [];
  private inUse: Set<T> = new Set();
  private factory: () => Promise<T>;
  private maxSize: number;
  
  constructor(factory: () => Promise<T>, maxSize: number = 10) {
    this.factory = factory;
    this.maxSize = maxSize;
  }
  
  async acquire(): Promise<T> {
    if (this.available.length > 0) {
      const resource = this.available.pop()!;
      this.inUse.add(resource);
      return resource;
    }
    
    if (this.inUse.size < this.maxSize) {
      const resource = await this.factory();
      this.inUse.add(resource);
      return resource;
    }
    
    throw new Error('Resource pool exhausted');
  }
  
  release(resource: T): void {
    this.inUse.delete(resource);
    this.available.push(resource);
  }
}

Error Handling and Recovery

Structured Error Response Pattern

Implement consistent error handling across your MCP server:

enum MCPErrorType {
  VALIDATION = 'validation',
  AUTHORIZATION = 'authorization',
  RESOURCE_NOT_FOUND = 'resource_not_found',
  INTERNAL_SERVER_ERROR = 'internal_server_error',
  RATE_LIMIT_EXCEEDED = 'rate_limit_exceeded'
}

interface MCPError {
  type: MCPErrorType;
  message: string;
  code: number;
  details?: Record<string, any>;
  timestamp: string;
  requestId?: string;
}

class MCPErrorHandler {
  static createError(
    type: MCPErrorType, 
    message: string, 
    details?: Record<string, any>
  ): MCPError {
    return {
      type,
      message,
      code: this.getErrorCode(type),
      details,
      timestamp: new Date().toISOString(),
      requestId: this.generateRequestId()
    };
  }
  
  static handle(error: unknown): MCPError {
    if (error instanceof ValidationError) {
      return this.createError(
        MCPErrorType.VALIDATION,
        error.message,
        { field: error.field }
      );
    }
    
    // Handle other error types...
    return this.createError(
      MCPErrorType.INTERNAL_SERVER_ERROR,
      'An unexpected error occurred'
    );
  }
}

Circuit Breaker Pattern

Implement circuit breakers for external service calls:

enum CircuitState {
  CLOSED,
  OPEN,
  HALF_OPEN
}

class CircuitBreaker {
  private state: CircuitState = CircuitState.CLOSED;
  private failureCount: number = 0;
  private lastFailureTime: number = 0;
  private successCount: number = 0;
  
  constructor(
    private failureThreshold: number = 5,
    private recoveryTimeout: number = 60000,
    private successThreshold: number = 3
  ) {}
  
  async execute<T>(operation: () => Promise<T>): Promise<T> {
    if (this.state === CircuitState.OPEN) {
      if (Date.now() - this.lastFailureTime < this.recoveryTimeout) {
        throw new Error('Circuit breaker is open');
      }
      this.state = CircuitState.HALF_OPEN;
      this.successCount = 0;
    }
    
    try {
      const result = await operation();
      this.onSuccess();
      return result;
    } catch (error) {
      this.onFailure();
      throw error;
    }
  }
  
  private onSuccess(): void {
    if (this.state === CircuitState.HALF_OPEN) {
      this.successCount++;
      if (this.successCount >= this.successThreshold) {
        this.state = CircuitState.CLOSED;
        this.failureCount = 0;
      }
    }
  }
  
  private onFailure(): void {
    this.failureCount++;
    this.lastFailureTime = Date.now();
    
    if (this.failureCount >= this.failureThreshold) {
      this.state = CircuitState.OPEN;
    }
  }
}

Resource Management and Performance

Request Deduplication Pattern

Prevent duplicate expensive operations:

class RequestDeduplicator {
  private pendingRequests: Map<string, Promise<any>> = new Map();
  
  async execute<T>(
    key: string, 
    operation: () => Promise<T>,
    ttl: number = 5000
  ): Promise<T> {
    if (this.pendingRequests.has(key)) {
      return this.pendingRequests.get(key) as Promise<T>;
    }
    
    const promise = operation();
    this.pendingRequests.set(key, promise);
    
    // Clean up after completion or timeout
    setTimeout(() => {
      this.pendingRequests.delete(key);
    }, ttl);
    
    try {
      const result = await promise;
      return result;
    } catch (error) {
      this.pendingRequests.delete(key);
      throw error;
    }
  }
}

Caching Strategy Pattern

Implement multi-level caching:

interface CacheProvider {
  get<T>(key: string): Promise<T | null>;
  set<T>(key: string, value: T, ttl?: number): Promise<void>;
  delete(key: string): Promise<void>;
}

class MultiLevelCache {
  constructor(
    private l1Cache: CacheProvider, // Memory cache
    private l2Cache: CacheProvider  // Redis/external cache
  ) {}
  
  async get<T>(key: string): Promise<T | null> {
    // Try L1 cache first
    let value = await this.l1Cache.get<T>(key);
    if (value) return value;
    
    // Try L2 cache
    value = await this.l2Cache.get<T>(key);
    if (value) {
      // Promote to L1 cache
      await this.l1Cache.set(key, value, 300); // 5 minutes
      return value;
    }
    
    return null;
  }
  
  async set<T>(key: string, value: T, ttl?: number): Promise<void> {
    await Promise.all([
      this.l1Cache.set(key, value, ttl),
      this.l2Cache.set(key, value, ttl)
    ]);
  }
}

Rate Limiting Pattern

Implement sophisticated rate limiting:

interface RateLimiter {
  checkLimit(identifier: string): Promise<boolean>;
  getRemainingRequests(identifier: string): Promise<number>;
}

class SlidingWindowRateLimiter implements RateLimiter {
  private windows: Map<string, number[]> = new Map();
  
  constructor(
    private maxRequests: number,
    private windowSizeMs: number
  ) {}
  
  async checkLimit(identifier: string): Promise<boolean> {
    const now = Date.now();
    const windowStart = now - this.windowSizeMs;
    
    let requests = this.windows.get(identifier) || [];
    
    // Remove expired requests
    requests = requests.filter(timestamp => timestamp > windowStart);
    
    if (requests.length >= this.maxRequests) {
      return false;
    }
    
    // Add current request
    requests.push(now);
    this.windows.set(identifier, requests);
    
    return true;
  }
  
  async getRemainingRequests(identifier: string): Promise<number> {
    const now = Date.now();
    const windowStart = now - this.windowSizeMs;
    
    const requests = this.windows.get(identifier) || [];
    const validRequests = requests.filter(timestamp => timestamp > windowStart);
    
    return Math.max(0, this.maxRequests - validRequests.length);
  }
}

Security Implementation Patterns

Request Validation Pattern

Implement comprehensive input validation:

import { z } from 'zod';

const ResourceRequestSchema = z.object({
  uri: z.string().url().refine(uri => {
    // Only allow specific protocols
    return uri.startsWith('file://') || uri.startsWith('https://');
  }),
  parameters: z.record(z.string()).optional(),
  metadata: z.object({
    requestId: z.string().uuid(),
    timestamp: z.string().datetime()
  })
});

class RequestValidator {
  static validate<T>(schema: z.ZodSchema<T>, data: unknown): T {
    try {
      return schema.parse(data);
    } catch (error) {
      if (error instanceof z.ZodError) {
        throw new ValidationError('Invalid request format', error.errors);
      }
      throw error;
    }
  }
}

Authorization Middleware Pattern

Implement role-based access control:

enum Permission {
  READ_RESOURCES = 'read:resources',
  WRITE_RESOURCES = 'write:resources',
  EXECUTE_TOOLS = 'execute:tools',
  ADMIN_ACCESS = 'admin:access'
}

interface AuthContext {
  userId: string;
  roles: string[];
  permissions: Permission[];
  sessionId: string;
}

class AuthorizationMiddleware {
  constructor(private requiredPermissions: Permission[]) {}
  
  async authorize(context: AuthContext, resource?: string): Promise<boolean> {
    // Check if user has required permissions
    const hasPermissions = this.requiredPermissions.every(
      permission => context.permissions.includes(permission)
    );
    
    if (!hasPermissions) {
      return false;
    }
    
    // Resource-specific authorization
    if (resource) {
      return this.checkResourceAccess(context, resource);
    }
    
    return true;
  }
  
  private async checkResourceAccess(
    context: AuthContext, 
    resource: string
  ): Promise<boolean> {
    // Implement resource-specific access control
    // This could involve checking ownership, group membership, etc.
    return true;
  }
}

State Management and Persistence

Event Sourcing Pattern

Implement event sourcing for audit trails and state reconstruction:

interface Event {
  id: string;
  type: string;
  aggregateId: string;
  data: Record<string, any>;
  timestamp: Date;
  version: number;
}

class EventStore {
  private events: Map<string, Event[]> = new Map();
  
  async appendEvent(aggregateId: string, event: Omit<Event, 'id' | 'timestamp'>): Promise<void> {
    const events = this.events.get(aggregateId) || [];
    
    const newEvent: Event = {
      ...event,
      id: this.generateId(),
      timestamp: new Date(),
      version: events.length + 1
    };
    
    events.push(newEvent);
    this.events.set(aggregateId, events);
  }
  
  async getEvents(aggregateId: string, fromVersion?: number): Promise<Event[]> {
    const events = this.events.get(aggregateId) || [];
    
    if (fromVersion) {
      return events.filter(event => event.version >= fromVersion);
    }
    
    return events;
  }
  
  private generateId(): string {
    return Math.random().toString(36).substring(2, 15);
  }
}

Snapshot Pattern

Implement snapshots for performance optimization:

interface Snapshot<T> {
  aggregateId: string;
  data: T;
  version: number;
  timestamp: Date;
}

class SnapshotStore<T> {
  private snapshots: Map<string, Snapshot<T>> = new Map();
  
  async saveSnapshot(aggregateId: string, data: T, version: number): Promise<void> {
    const snapshot: Snapshot<T> = {
      aggregateId,
      data,
      version,
      timestamp: new Date()
    };
    
    this.snapshots.set(aggregateId, snapshot);
  }
  
  async getSnapshot(aggregateId: string): Promise<Snapshot<T> | null> {
    return this.snapshots.get(aggregateId) || null;
  }
  
  async reconstructState(aggregateId: string): Promise<T | null> {
    const snapshot = await this.getSnapshot(aggregateId);
    
    if (!snapshot) {
      return null;
    }
    
    // Apply events since snapshot
    const eventStore = new EventStore();
    const recentEvents = await eventStore.getEvents(aggregateId, snapshot.version + 1);
    
    let state = snapshot.data;
    for (const event of recentEvents) {
      state = this.applyEvent(state, event);
    }
    
    return state;
  }
  
  private applyEvent(state: T, event: Event): T {
    // Implement event application logic
    return state;
  }
}

Monitoring and Observability

Metrics Collection Pattern

Implement comprehensive metrics collection:

enum MetricType {
  COUNTER = 'counter',
  GAUGE = 'gauge',
  HISTOGRAM = 'histogram'
}

interface Metric {
  name: string;
  type: MetricType;
  value: number;
  labels: Record<string, string>;
  timestamp: Date;
}

class MetricsCollector {
  private metrics: Metric[] = [];
  
  counter(name: string, labels: Record<string, string> = {}): void {
    this.recordMetric(name, MetricType.COUNTER, 1, labels);
  }
  
  gauge(name: string, value: number, labels: Record<string, string> = {}): void {
    this.recordMetric(name, MetricType.GAUGE, value, labels);
  }
  
  histogram(name: string, value: number, labels: Record<string, string> = {}): void {
    this.recordMetric(name, MetricType.HISTOGRAM, value, labels);
  }
  
  private recordMetric(
    name: string, 
    type: MetricType, 
    value: number, 
    labels: Record<string, string>
  ): void {
    this.metrics.push({
      name,
      type,
      value,
      labels,
      timestamp: new Date()
    });
  }
  
  getMetrics(): Metric[] {
    return [...this.metrics];
  }
  
  clearMetrics(): void {
    this.metrics = [];
  }
}

Distributed Tracing Pattern

Implement distributed tracing for complex operations:

interface Span {
  traceId: string;
  spanId: string;
  parentSpanId?: string;
  operationName: string;
  startTime: Date;
  endTime?: Date;
  tags: Record<string, any>;
  logs: Array<{ timestamp: Date; message: string; level: string }>;
}

class DistributedTracer {
  private activeSpans: Map<string, Span> = new Map();
  
  startSpan(operationName: string, parentSpanId?: string): string {
    const span: Span = {
      traceId: parentSpanId ? this.getTraceId(parentSpanId) : this.generateId(),
      spanId: this.generateId(),
      parentSpanId,
      operationName,
      startTime: new Date(),
      tags: {},
      logs: []
    };
    
    this.activeSpans.set(span.spanId, span);
    return span.spanId;
  }
  
  finishSpan(spanId: string): void {
    const span = this.activeSpans.get(spanId);
    if (span) {
      span.endTime = new Date();
    }
  }
  
  addTag(spanId: string, key: string, value: any): void {
    const span = this.activeSpans.get(spanId);
    if (span) {
      span.tags[key] = value;
    }
  }
  
  log(spanId: string, message: string, level: string = 'info'): void {
    const span = this.activeSpans.get(spanId);
    if (span) {
      span.logs.push({
        timestamp: new Date(),
        message,
        level
      });
    }
  }
  
  private getTraceId(spanId: string): string {
    const span = this.activeSpans.get(spanId);
    return span ? span.traceId : this.generateId();
  }
  
  private generateId(): string {
    return Math.random().toString(36).substring(2, 15);
  }
}

Testing Strategies

Test Double Pattern for MCP

Create test doubles for MCP components:

class MockMCPClient implements MCPClient {
  private responses: Map<string, any> = new Map();
  private callLog: Array<{ method: string; params: any }> = [];
  
  setResponse(method: string, response: any): void {
    this.responses.set(method, response);
  }
  
  async callTool(name: string, arguments_: Record<string, any>): Promise<any> {
    this.callLog.push({ method: 'callTool', params: { name, arguments_ } });
    return this.responses.get('callTool') || { success: true };
  }
  
  async getResource(uri: string): Promise<any> {
    this.callLog.push({ method: 'getResource', params: { uri } });
    return this.responses.get('getResource') || { content: 'mock content' };
  }
  
  getCallLog(): Array<{ method: string; params: any }> {
    return [...this.callLog];
  }
  
  clearCallLog(): void {
    this.callLog = [];
  }
}

Integration Test Pattern

Implement comprehensive integration testing:

class MCPIntegrationTest {
  private server: Server;
  private client: MCPClient;
  
  async setup(): Promise<void> {
    // Start server
    this.server = new Server({
      name: 'test-server',
      version: '1.0.0'
    });
    
    await this.server.start();
    
    // Connect client
    this.client = new MCPClient({
      serverUrl: 'http://localhost:3000'
    });
    
    await this.client.connect();
  }
  
  async teardown(): Promise<void> {
    await this.client?.disconnect();
    await this.server?.stop();
  }
  
  async testResourceAccess(): Promise<void> {
    const resource = await this.client.getResource('test://resource/1');
    
    expect(resource).toBeDefined();
    expect(resource.content).toContain('expected content');
  }
  
  async testToolExecution(): Promise<void> {
    const result = await this.client.callTool('test-tool', {
      param1: 'value1'
    });
    
    expect(result.success).toBe(true);
    expect(result.data).toBeDefined();
  }
}

Production Deployment Patterns

Health Check Pattern

Implement comprehensive health checks:

enum HealthStatus {
  HEALTHY = 'healthy',
  DEGRADED = 'degraded',
  UNHEALTHY = 'unhealthy'
}

interface HealthCheck {
  name: string;
  status: HealthStatus;
  message?: string;
  responseTime: number;
  timestamp: Date;
}

class HealthCheckManager {
  private checks: Map<string, () => Promise<HealthCheck>> = new Map();
  
  registerCheck(name: string, check: () => Promise<HealthCheck>): void {
    this.checks.set(name, check);
  }
  
  async runAllChecks(): Promise<HealthCheck[]> {
    const results: HealthCheck[] = [];
    
    for (const [name, check] of this.checks) {
      try {
        const result = await check();
        results.push(result);
      } catch (error) {
        results.push({
          name,
          status: HealthStatus.UNHEALTHY,
          message: error instanceof Error ? error.message : 'Unknown error',
          responseTime: 0,
          timestamp: new Date()
        });
      }
    }
    
    return results;
  }
  
  async getOverallHealth(): Promise<HealthStatus> {
    const checks = await this.runAllChecks();
    
    if (checks.some(check => check.status === HealthStatus.UNHEALTHY)) {
      return HealthStatus.UNHEALTHY;
    }
    
    if (checks.some(check => check.status === HealthStatus.DEGRADED)) {
      return HealthStatus.DEGRADED;
    }
    
    return HealthStatus.HEALTHY;
  }
}

Graceful Shutdown Pattern

Implement graceful shutdown handling:

class GracefulShutdownManager {
  private shutdownHandlers: Array<() => Promise<void>> = [];
  private isShuttingDown: boolean = false;
  
  constructor() {
    this.setupSignalHandlers();
  }
  
  addShutdownHandler(handler: () => Promise<void>): void {
    this.shutdownHandlers.push(handler);
  }
  
  private setupSignalHandlers(): void {
    process.on('SIGTERM', () => this.shutdown('SIGTERM'));
    process.on('SIGINT', () => this.shutdown('SIGINT'));
    process.on('SIGQUIT', () => this.shutdown('SIGQUIT'));
  }
  
  private async shutdown(signal: string): Promise<void> {
    if (this.isShuttingDown) {
      return;
    }
    
    this.isShuttingDown = true;
    console.log(`Received ${signal}, starting graceful shutdown...`);
    
    try {
      await Promise.all(
        this.shutdownHandlers.map(handler => 
          Promise.race([
            handler(),
            this.timeout(30000) // 30 second timeout
          ])
        )
      );
      
      console.log('Graceful shutdown completed');
      process.exit(0);
    } catch (error) {
      console.error('Error during shutdown:', error);
      process.exit(1);
    }
  }
  
  private timeout(ms: number): Promise<never> {
    return new Promise((_, reject) => {
      setTimeout(() => reject(new Error('Shutdown timeout')), ms);
    });
  }
}

Next Steps

These advanced patterns provide the foundation for building enterprise-grade MCP servers. Consider these areas for further exploration:

  1. Microservices Architecture: Split MCP functionality across multiple services
  2. Event-Driven Architecture: Implement async communication patterns
  3. Multi-Tenant Support: Add tenant isolation and resource management
  4. Advanced Security: Implement OAuth 2.0, JWT tokens, and encryption
  5. Performance Optimization: Add connection pooling and query optimization
  6. Monitoring Integration: Connect with Prometheus, Grafana, and ELK stack

Remember that implementing these patterns should be driven by actual requirements rather than premature optimization. Start with simpler implementations and evolve toward these patterns as your system grows and requirements become clearer.