Skip to main content
Glama

MCP File Context Server

by bsmi021
# LoggingService Technical Specification ## Interface Definition ### Core Types ```typescript import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { LoggingLevel } from '@modelcontextprotocol/sdk/types.js'; export interface LoggingConfig { defaultLevel: LoggingLevel; enableConsoleLogging: boolean; loggerName: string; performance: { enabled: boolean; slowOperationThreshold: number; // milliseconds }; serialization: { maxDepth: number; maxLength: number; includeStackTrace: boolean; }; } export interface LogContext { [key: string]: unknown; timestamp?: number; operation?: string; filePath?: string; toolName?: string; requestId?: string; duration?: number; } export interface PerformanceTimer { start(): void; end(operation: string, context?: LogContext): Promise<void>; } ``` ### LoggingService Class ```typescript export class LoggingService { private server: Server; private config: LoggingConfig; private currentLevel: LoggingLevel; private readonly levelPriority: Record<LoggingLevel, number>; constructor(server: Server, config: Partial<LoggingConfig> = {}); // Core logging methods async debug(message: string, context?: LogContext): Promise<void>; async info(message: string, context?: LogContext): Promise<void>; async notice(message: string, context?: LogContext): Promise<void>; async warning(message: string, context?: LogContext): Promise<void>; async error(message: string, error?: Error, context?: LogContext): Promise<void>; async critical(message: string, error?: Error, context?: LogContext): Promise<void>; // Configuration methods setLevel(level: LoggingLevel): void; getLevel(): LoggingLevel; shouldLog(level: LoggingLevel): boolean; // Utility methods createTimer(): PerformanceTimer; private sanitizeContext(context: LogContext): LogContext; private formatError(error: Error): object; private fallbackLog(level: LoggingLevel, message: string, data?: unknown): void; } ``` ## Implementation Details ### Level Priority System ```typescript private readonly levelPriority: Record<LoggingLevel, number> = { debug: 0, info: 1, notice: 2, warning: 3, error: 4, critical: 5, alert: 6, emergency: 7 }; ``` ### Core Logging Implementation ```typescript private async log(level: LoggingLevel, message: string, context?: LogContext, error?: Error): Promise<void> { if (!this.shouldLog(level)) { return; } try { const logData = { message, timestamp: Date.now(), level, logger: this.config.loggerName, ...this.sanitizeContext(context || {}), ...(error && { error: this.formatError(error) }) }; await this.server.sendLoggingMessage({ level, logger: this.config.loggerName, data: logData }); } catch (loggingError) { // Fallback to console if MCP logging fails this.fallbackLog(level, message, { context, error, loggingError }); } } ``` ### Performance Timer Implementation ```typescript class PerformanceTimerImpl implements PerformanceTimer { private startTime: number; constructor(private loggingService: LoggingService) {} start(): void { this.startTime = performance.now(); } async end(operation: string, context: LogContext = {}): Promise<void> { const duration = performance.now() - this.startTime; const logContext = { ...context, operation, duration: Math.round(duration * 100) / 100 // Round to 2 decimal places }; if (duration > this.loggingService.config.performance.slowOperationThreshold) { await this.loggingService.warning(`Slow operation detected: ${operation}`, logContext); } else if (this.loggingService.config.performance.enabled) { await this.loggingService.debug(`Operation completed: ${operation}`, logContext); } } } ``` ### Context Sanitization ```typescript private sanitizeContext(context: LogContext): LogContext { try { // Remove circular references and limit depth const sanitized = JSON.parse(JSON.stringify(context, this.getCircularReplacer(), 2)); // Truncate large strings return this.truncateValues(sanitized); } catch (error) { return { contextError: 'Failed to serialize context', originalKeys: Object.keys(context) }; } } private getCircularReplacer() { const seen = new WeakSet(); return (key: string, value: unknown) => { if (typeof value === 'object' && value !== null) { if (seen.has(value)) { return '[Circular Reference]'; } seen.add(value); } return value; }; } private truncateValues(obj: unknown, maxLength = 1000): unknown { if (typeof obj === 'string') { return obj.length > maxLength ? obj.substring(0, maxLength) + '...' : obj; } if (Array.isArray(obj)) { return obj.map(item => this.truncateValues(item, maxLength)); } if (typeof obj === 'object' && obj !== null) { const result: Record<string, unknown> = {}; for (const [key, value] of Object.entries(obj)) { result[key] = this.truncateValues(value, maxLength); } return result; } return obj; } ``` ### Error Formatting ```typescript private formatError(error: Error): object { return { name: error.name, message: error.message, stack: this.config.serialization.includeStackTrace ? error.stack : undefined, ...(error.cause && { cause: this.formatError(error.cause as Error) }) }; } ``` ## Integration Pattern ### Server Integration ```typescript // In FileContextServer constructor export class FileContextServer { private loggingService: LoggingService; constructor(config: Partial<StreamConfig> = {}) { // ... existing initialization // Initialize logging service this.loggingService = new LoggingService(this.server, { defaultLevel: process.env.MCP_LOG_LEVEL as LoggingLevel || 'info', enableConsoleLogging: process.env.NODE_ENV === 'development', loggerName: 'file-context-server', performance: { enabled: process.env.MCP_LOG_PERFORMANCE === 'true', slowOperationThreshold: 1000 }, serialization: { maxDepth: 5, maxLength: 2000, includeStackTrace: true } }); // Handle client logging level requests this.server.setRequestHandler(SetLevelRequestSchema, async (request) => { this.loggingService.setLevel(request.params.level); await this.loggingService.info('Log level changed', { newLevel: request.params.level, requestedBy: 'client' }); return {}; }); // Pass logging service to other services this.fileWatcherService = new FileWatcherService(this.loggingService); this.profileService = new ProfileService(process.cwd(), this.loggingService); // ... etc } } ``` ### Service Integration Pattern ```typescript // Example: FileWatcherService integration export class FileWatcherService extends EventEmitter { constructor(private logger?: LoggingService) { super(); // ... existing initialization } async watch(targetPath: string): Promise<void> { const timer = this.logger?.createTimer(); timer?.start(); try { // ... existing watch logic await this.logger?.info('File watcher started', { targetPath, operation: 'watch_start' }); timer?.end('watch_initialization', { targetPath }); } catch (error) { await this.logger?.error('Failed to start file watcher', error, { targetPath, operation: 'watch_start' }); throw error; } } } ``` ## Migration Examples ### Before/After Comparisons #### Simple Error Logging ```typescript // Before console.error(`Error reading file ${filePath}:`, error); // After await this.loggingService.error('Failed to read file', error, { filePath, operation: 'read_file', encoding: 'utf8' }); ``` #### Debug Information ```typescript // Before console.error('[FileContextServer] Services initialized'); // After await this.loggingService.info('Server services initialized', { operation: 'server_startup', services: ['fileWatcher', 'profile', 'template', 'codeAnalysis'] }); ``` #### Performance Sensitive Operations ```typescript // Before const files = await this.listFiles(dirPath); console.error(`Found files: ${files.length}`); // After const timer = this.loggingService.createTimer(); timer.start(); const files = await this.listFiles(dirPath); await timer.end('list_files', { directory: dirPath, fileCount: files.length, recursive: options.recursive }); ``` ## Default Configuration ```typescript export const DEFAULT_LOGGING_CONFIG: LoggingConfig = { defaultLevel: 'info', enableConsoleLogging: false, loggerName: 'file-context-server', performance: { enabled: false, slowOperationThreshold: 1000 }, serialization: { maxDepth: 5, maxLength: 2000, includeStackTrace: true } }; ``` ## Error Handling Strategy ### Logging Service Failures ```typescript private fallbackLog(level: LoggingLevel, message: string, data?: unknown): void { if (this.config.enableConsoleLogging) { const timestamp = new Date().toISOString(); const logLine = `[${timestamp}] ${level.toUpperCase()}: ${message}`; if (level === 'error' || level === 'critical') { console.error(logLine, data); } else { console.log(logLine, data); } } } ``` ### Graceful Degradation 1. **MCP Connection Issues**: Fall back to console logging 2. **Serialization Errors**: Log error with minimal context 3. **Performance Timer Failures**: Continue operation without timing 4. **Configuration Errors**: Use default configuration with warning ## Testing Strategy ### Unit Tests ```typescript describe('LoggingService', () => { let mockServer: jest.Mocked<Server>; let loggingService: LoggingService; beforeEach(() => { mockServer = { sendLoggingMessage: jest.fn().mockResolvedValue(undefined) } as any; loggingService = new LoggingService(mockServer, { defaultLevel: 'debug', enableConsoleLogging: false, loggerName: 'test-logger' }); }); test('should filter messages based on log level', async () => { loggingService.setLevel('warning'); await loggingService.debug('debug message'); await loggingService.warning('warning message'); expect(mockServer.sendLoggingMessage).toHaveBeenCalledTimes(1); expect(mockServer.sendLoggingMessage).toHaveBeenCalledWith({ level: 'warning', logger: 'test-logger', data: expect.objectContaining({ message: 'warning message' }) }); }); test('should handle circular references in context', async () => { const circular: any = { name: 'test' }; circular.self = circular; await loggingService.info('test message', { circular }); expect(mockServer.sendLoggingMessage).toHaveBeenCalledWith({ level: 'info', logger: 'test-logger', data: expect.objectContaining({ circular: expect.objectContaining({ self: '[Circular Reference]' }) }) }); }); }); ``` This implementation provides a robust, feature-complete logging service that integrates seamlessly with the MCP protocol while maintaining performance and reliability.

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/bsmi021/mcp-file-context-server'

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