MCP Utils

MCP Utils

A comprehensive utilities library for building Model Context Protocol servers, providing standardized tools for response formatting, caching, rate limiting, logging, and WebSocket management.

Category
Visit Server

README

@haakco/mcp-utils

Comprehensive shared utilities library for MCP (Model Context Protocol) servers

Eliminates code duplication and provides standardized, production-ready utilities for building robust MCP servers.

Tests TypeScript Lint

Installation

npm install @haakco/mcp-utils

Quick Start

import { 
  BaseToolHandler, 
  ResponseBuilder, 
  createTextResponse,
  formatSuccess,
  createLogger
} from '@haakco/mcp-utils';

class MyMCPServer extends BaseToolHandler {
  private logger = createLogger('my-server');

  async handleTool(args: unknown) {
    try {
      const result = await this.performOperation(args);
      this.logger.info('Operation completed successfully');
      return createTextResponse(formatSuccess(result));
    } catch (error) {
      this.logger.error('Operation failed:', error);
      return createErrorResponse(error);
    }
  }
}

🚀 Core Features

🏗️ MCP Architecture Components

  • BaseToolHandler - Standardized tool execution patterns
  • ResponseBuilder - Consistent MCP response formatting
  • InstanceManager - Multi-instance server support
  • ToolRegistry - Tool registration and management

🎨 Formatting & Presentation

  • Message Formatters - ✅ ❌ ⚠️ ℹ️ status indicators
  • Size Formatters - Bytes, storage units (KB, MB, GB, Ki, Mi, Gi)
  • Table Formatters - Structured data presentation
  • JSON/Text Utilities - Safe serialization and truncation

📅 DateTime Operations

  • Duration Formatting - Human-readable time spans
  • Age Calculation - "2 hours ago", "3 days old"
  • Relative Time - "in 5 minutes", "1 week ago"
  • Duration Parsing - "1h30m" → milliseconds

🧠 Advanced Caching

  • SimpleCache - TTL-based caching
  • LRUCache - Least Recently Used eviction
  • TTLCache - Combined TTL + LRU strategies
  • DebouncedCache - Batched operation caching
  • Memoization - Function result caching

🚦 Rate Limiting

  • Token Bucket - Burst capacity with refill
  • Sliding Window - Rolling time-based limits
  • Fixed Window - Period-based rate limiting
  • Leaky Bucket - Smooth rate control
  • Multi-Tier - Multiple simultaneous limits
  • Keyed Limiters - Per-user/per-resource limits

🔌 WebSocket Utilities

  • ReconnectingWebSocket - Auto-reconnection with backoff
  • Message Router - Type-based message handling
  • RPC Client - Request/response over WebSocket
  • Connection Pool - Managed connection lifecycle

📊 Logging & Monitoring

  • Structured Logging - Context-aware log entries
  • Performance Timing - Operation duration tracking
  • Environment-based - Debug/production configurations
  • Multiple Outputs - Console, silent, custom loggers

📚 API Reference

Core MCP Utilities

Response Creation

import { createTextResponse, createErrorResponse, createSuccessResponse } from '@haakco/mcp-utils';

// Basic responses
createTextResponse('Operation completed');
createErrorResponse(new Error('Failed to connect'));
createSuccessResponse('User created successfully');

// Multi-part responses
createMultipartResponse(['Header', 'Content', 'Footer']);

// Progress indicators
createProgressResponse(75, 100, 'Processing data');

Argument Validation

import { validateToolArgs, validateRequiredArgs, extractPagination } from '@haakco/mcp-utils';

// Schema validation
const args = validateToolArgs(input, (data) => userSchema.parse(data), 'create_user');

// Required field validation
const validated = validateRequiredArgs(input, ['name', 'email'], 'User creation');

// Pagination extraction
const { page, perPage, offset, limit } = extractPagination(args);

Formatting Utilities

Message Formatting

import { formatSuccess, formatError, formatWarning, formatInfo } from '@haakco/mcp-utils';

formatSuccess('Operation completed'); // ✅ Operation completed
formatError('Connection failed');     // ❌ Error: Connection failed
formatWarning('Low disk space');      // ⚠️ Warning: Low disk space
formatInfo('System status');          // ℹ️ System status

Size & Number Formatting

import { formatBytes, formatStorageSize, formatPercentage, formatCPU } from '@haakco/mcp-utils';

formatBytes(1048576);          // 1 MB
formatStorageSize(1048576);    // 1Mi (Kubernetes format)
formatPercentage(75, 100);     // 75.00%
formatCPU(0.75);              // 75.00%

Table & List Formatting

import { formatTable, formatSimpleTable, formatList, formatBulletList } from '@haakco/mcp-utils';

// Object array to table
const data = [
  { name: 'John', age: 30, city: 'NYC' },
  { name: 'Jane', age: 25, city: 'LA' }
];
formatTable(data);
// name | age | city
// -----|-----|-----
// John | 30  | NYC
// Jane | 25  | LA

// Key-value table
formatSimpleTable({ name: 'Server1', status: 'Running', cpu: '45%' });
// name   : Server1
// status : Running
// cpu    : 45%

// Lists
formatList(['item1', 'item2', 'item3'], ' | ');        // item1 | item2 | item3
formatBulletList(['item1', 'item2']);                   // • item1\n• item2

DateTime Utilities

Duration Formatting

import { formatDuration, formatDurationFromSeconds, formatAge } from '@haakco/mcp-utils';

formatDuration(90061000);           // 1d 1h 1m 1s
formatDurationFromSeconds(3661);    // 1h 1m 1s
formatAge(Date.now() - 3600000);    // 1h (age from timestamp)

Relative Time

import { formatRelativeTime, parseDuration } from '@haakco/mcp-utils';

formatRelativeTime(new Date(Date.now() - 3600000)); // 1 hour ago
formatRelativeTime(new Date(Date.now() + 1800000)); // in 30 minutes

parseDuration('1h30m');     // 5400000 (milliseconds)
parseDuration('2d12h');     // 216000000

Caching System

Simple TTL Cache

import { SimpleCache } from '@haakco/mcp-utils';

const cache = new SimpleCache<string>(60000); // 1 minute TTL

cache.set('key1', 'value1');
cache.get('key1');          // 'value1'
cache.has('key1');          // true

// Custom TTL for specific items
cache.set('key2', 'value2', 30000); // 30 second TTL

LRU Cache

import { LRUCache } from '@haakco/mcp-utils';

const cache = new LRUCache<string>(100); // Max 100 items

cache.set('key1', 'value1');
cache.get('key1');          // Marks as recently used
cache.size();               // Current cache size

Combined TTL + LRU Cache

import { TTLCache } from '@haakco/mcp-utils';

const cache = new TTLCache<string>({
  ttl: 300000,      // 5 minutes
  maxSize: 1000,    // Max 1000 items
  onEvict: (key, value) => console.log(`Evicted ${key}`)
});

cache.set('key1', 'value1');
cache.cleanup();            // Manual cleanup of expired items

Function Memoization

import { memoize } from '@haakco/mcp-utils';

const expensiveOperation = async (id: string) => {
  // Expensive API call or computation
  return await fetch(`/api/data/${id}`).then(r => r.json());
};

const memoized = memoize(expensiveOperation, {
  ttl: 300000,     // 5 minute cache
  keyGenerator: (id) => `data:${id}`
});

// First call - executes function
await memoized('user1');  

// Second call - returns cached result
await memoized('user1');  

Rate Limiting

Token Bucket Rate Limiter

import { TokenBucketRateLimiter } from '@haakco/mcp-utils';

const limiter = new TokenBucketRateLimiter(
  10,     // Bucket capacity (max burst)
  2       // Refill rate (tokens per second)
);

// Try to acquire tokens
if (await limiter.acquire(1)) {
  // Request allowed
  await processRequest();
}

// Wait until tokens available
await limiter.acquireOrWait(1);

Sliding Window Rate Limiter

import { SlidingWindowRateLimiter } from '@haakco/mcp-utils';

const limiter = new SlidingWindowRateLimiter(
  60000,  // 1 minute window
  100     // Max 100 requests per window
);

if (await limiter.acquire()) {
  // Request allowed
} else {
  const waitTime = limiter.getTimeUntilNextRequest();
  console.log(`Rate limited. Wait ${waitTime}ms`);
}

Multi-Tier Rate Limiting

import { MultiTierRateLimiter } from '@haakco/mcp-utils';

const limiter = new MultiTierRateLimiter([
  { windowMs: 1000, maxRequests: 10, name: 'per-second' },
  { windowMs: 60000, maxRequests: 100, name: 'per-minute' },
  { windowMs: 3600000, maxRequests: 1000, name: 'per-hour' }
]);

const result = await limiter.acquire();
if (!result.allowed) {
  console.log(`Limited by: ${result.limitedBy}`);
}

Rate-Limited Functions

import { rateLimitFunction, debounce, throttle } from '@haakco/mcp-utils';

// Rate-limited function wrapper
const limitedAPI = rateLimitFunction(apiCall, {
  requestsPerSecond: 5,
  burst: 10
});

// Debounced function (delays execution)
const debouncedSave = debounce(saveData, 1000);

// Throttled function (limits execution frequency)
const throttledUpdate = throttle(updateUI, 100);

WebSocket Utilities

Reconnecting WebSocket

import { ReconnectingWebSocket } from '@haakco/mcp-utils';

const ws = new ReconnectingWebSocket({
  url: 'wss://api.example.com/ws',
  maxReconnectAttempts: 5,
  reconnectInterval: 5000,
  reconnectBackoff: 1.5,
  pingInterval: 30000
});

ws.on('open', () => console.log('Connected'));
ws.on('message', (data) => console.log('Received:', data));
ws.on('reconnecting', (attempt) => console.log(`Reconnecting... attempt ${attempt}`));

ws.send({ type: 'subscribe', channel: 'updates' });

Message Router

import { WebSocketRouter } from '@haakco/mcp-utils';

const router = new WebSocketRouter<{ type: string; payload?: any }>();

router.on('user.created', async (message) => {
  console.log('New user:', message.payload);
});

router.on('notification', async (message) => {
  await showNotification(message.payload);
});

router.setDefaultHandler((message) => {
  console.log('Unhandled message:', message);
});

// Handle incoming messages
ws.on('message', (data) => router.handle(data));

WebSocket RPC

import { WebSocketRPC } from '@haakco/mcp-utils';

const rpc = new WebSocketRPC(ws, {
  timeout: 30000,
  idGenerator: () => `req-${Date.now()}`
});

// Make RPC calls
const user = await rpc.call('getUser', { id: '123' });
const result = await rpc.call('updateProfile', { name: 'New Name' });

Logging System

Basic Logger

import { createLogger, createStructuredLogger } from '@haakco/mcp-utils';

const logger = createLogger('my-module');

logger.info('Application started');
logger.warn('Low memory warning');
logger.error('Database connection failed');
logger.debug('Debug information');

Structured Logger with Context

const logger = createStructuredLogger('api', 'app', {
  service: 'user-service',
  version: '1.2.3'
});

logger.info('User login successful');
// Output: INFO: User login successful [{"service":"user-service","version":"1.2.3"}]

// Add context for child logger
const requestLogger = logger.child({ requestId: 'req-123', userId: 'user-456' });
requestLogger.info('Processing request');

Performance Logger

import { createPerformanceLogger } from '@haakco/mcp-utils';

const perfLogger = createPerformanceLogger('performance');

// Manual timing
perfLogger.start('database-query');
await queryDatabase();
perfLogger.end('database-query', { query: 'SELECT * FROM users' });

// Automatic timing
const result = await perfLogger.measure(
  'api-call',
  () => fetch('/api/data').then(r => r.json()),
  { endpoint: '/api/data' }
);

Environment-based Logger

import { createLoggerFromEnv, LogLevel, createLevelLogger } from '@haakco/mcp-utils';

// Automatically configures based on DEBUG and LOG_LEVEL env vars
const logger = createLoggerFromEnv('my-app');

// Or explicit level configuration
const logger = createLevelLogger('my-app', LogLevel.INFO);

🏗️ Advanced Usage

Custom Tool Handler

import { BaseToolHandler, createTextResponse, formatSuccess } from '@haakco/mcp-utils';
import { z } from 'zod';

class CustomToolHandler extends BaseToolHandler {
  constructor() {
    super('my-custom-server');
  }

  getTools() {
    return [
      this.createTool(
        'process_data',
        'Process data with validation and formatting',
        z.object({
          data: z.array(z.string()),
          format: z.enum(['json', 'table', 'list']).default('json')
        }),
        async (args) => {
          const processed = await this.processData(args.data);
          
          switch (args.format) {
            case 'table':
              return createTextResponse(formatTable(processed));
            case 'list':
              return createTextResponse(formatBulletList(processed.map(String)));
            default:
              return createTextResponse(formatSuccess(JSON.stringify(processed)));
          }
        }
      )
    ];
  }

  private async processData(data: string[]): Promise<any[]> {
    // Your custom processing logic
    return data.map(item => ({ original: item, processed: item.toUpperCase() }));
  }
}

Batch Response Builder

import { BatchResponseBuilder } from '@haakco/mcp-utils';

const response = new BatchResponseBuilder()
  .addSuccess('Connection established')
  .addInfo('Processing 150 items')
  .addSeparator()
  .add('Results:')
  .addSuccess('Created 145 items')
  .addWarning('Skipped 3 duplicates')
  .addError('Failed to create 2 items')
  .addEmptyLine()
  .addInfo('Operation completed in 2.3 seconds')
  .build();

return response; // Returns proper CallToolResult

Resource Operation Mixin

import { ResourceOperationMixin } from '@haakco/mcp-utils';
import { z } from 'zod';

interface User {
  id: string;
  name: string;
  email: string;
}

class UserTools extends ResourceOperationMixin<User> {
  constructor(private userClient: UserClient) {
    super('user-management');
  }

  getTools() {
    return this.createResourceTools({
      resourceName: 'User',
      client: this.userClient,
      formatItem: (user) => `${user.name} <${user.email}>`,
      validateCreate: z.object({
        name: z.string().min(1),
        email: z.string().email()
      }),
      validateUpdate: z.object({
        name: z.string().min(1).optional(),
        email: z.string().email().optional()
      })
    });
  }
}

📦 Integration Examples

Express.js Integration

import express from 'express';
import { createLogger, rateLimitFunction, SimpleCache } from '@haakco/mcp-utils';

const app = express();
const logger = createLogger('api-server');
const cache = new SimpleCache<any>(300000); // 5 minute cache

// Rate-limited endpoint
const rateLimitedHandler = rateLimitFunction(
  async (req, res) => {
    const data = await fetchData(req.params.id);
    res.json(data);
  },
  { requestsPerSecond: 10, burst: 20 }
);

app.get('/api/data/:id', async (req, res) => {
  const cached = cache.get(req.params.id);
  if (cached) {
    logger.info('Cache hit', { id: req.params.id });
    return res.json(cached);
  }

  await rateLimitedHandler(req, res);
});

WebSocket Server Integration

import WebSocket from 'ws';
import { WebSocketRouter, createLogger, TokenBucketRateLimiter } from '@haakco/mcp-utils';

const logger = createLogger('ws-server');
const rateLimiter = new TokenBucketRateLimiter(10, 1); // 10 burst, 1/sec refill

const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', (ws) => {
  const router = new WebSocketRouter();
  
  router.on('message', async (data) => {
    if (await rateLimiter.acquire()) {
      await handleMessage(data);
    } else {
      ws.send(JSON.stringify({ error: 'Rate limit exceeded' }));
    }
  });

  ws.on('message', (data) => {
    try {
      const message = JSON.parse(data.toString());
      router.handle(message);
    } catch (error) {
      logger.error('Invalid message format', error);
    }
  });
});

🧪 Testing

The library includes comprehensive test coverage (85/85 tests passing):

# Run all tests
npm test

# Run tests with coverage
npm run test:coverage

# Run specific test suites
npm test -- --testNamePattern="Cache"
npm test -- --testNamePattern="Rate.*Limiter"

Test Structure

tests/
├── base-handler.test.ts      # Core MCP functionality
├── cache.test.ts            # All caching strategies
├── datetime.test.ts         # DateTime utilities
├── formatters.test.ts       # Formatting functions
├── response-builder.test.ts # Response building
├── task-helpers.test.ts     # Task execution
└── validators.test.ts       # Validation utilities

🚀 Development

# Install dependencies
npm install

# Run tests in watch mode
npm run test:watch

# Build the library
npm run build

# Lint and format code
npm run lint
npm run lint:fix

# Type checking
npm run type-check

📄 License

MIT License - see LICENSE file for details.

🤝 Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes with tests
  4. Ensure all tests pass and linting is clean
  5. Submit a pull request

Built with TypeScript, tested with Jest, and designed for production MCP servers.

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured