Skip to main content
Glama

@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

Related MCP server: IT Tools MCP Server

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.

-
security - not tested
F
license - not found
-
quality - not tested

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/haakco/mcp-utils'

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