Skip to main content
Glama
server.ts11 kB
/** * Main MCP Color Server implementation */ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; import { ServerConfig } from './types/index'; import { toolRegistry } from './tools/index'; import { logger } from './utils/logger'; import { performanceMonitor, resourceManager } from './utils/index'; import { securityMiddleware, securityAuditor } from './security/index'; import { getVersionInfo } from './version'; // Import tools/index to ensure all tools are registered import './tools/index'; export class ColorServer { private server: Server; private config: ServerConfig; private performanceStatsInterval: ReturnType<typeof setInterval> | null = null; private cleanupInterval: ReturnType<typeof setInterval> | null = null; constructor(config?: Partial<ServerConfig>) { const versionInfo = getVersionInfo(); this.config = { name: versionInfo.name, description: versionInfo.description, ...config, // Ensure centralized version always takes precedence version: versionInfo.version, }; // Initialize MCP server this.server = new Server( { name: this.config.name, version: this.config.version, }, { capabilities: { tools: {}, }, } ); // Only setup request handlers in non-test environments const isTestEnvironment = process.env['NODE_ENV'] === 'test' || process.env['JEST_WORKER_ID'] !== undefined || process.env['CI'] === 'true' || typeof jest !== 'undefined' || (typeof global !== 'undefined' && 'jest' in global); if (!isTestEnvironment) { this.setupRequestHandlers(); } this.registerTools(); if (!isTestEnvironment) { this.setupPerformanceMonitoring(); } logger.info(`${this.config.name} v${this.config.version} initialized`); } private setupRequestHandlers(): void { // Handle tool listing requests this.server.setRequestHandler(ListToolsRequestSchema, async () => { const tools = toolRegistry.getAllTools(); logger.debug(`Listing ${tools.length} available tools`); return { tools: tools.map(tool => ({ name: tool.name, description: tool.description, inputSchema: tool.parameters, })), }; }); // Handle tool execution requests with security middleware this.server.setRequestHandler(CallToolRequestSchema, async request => { const { name, arguments: args } = request.params; const startTime = Date.now(); const operationId = performanceMonitor.startOperation(name); logger.info(`Executing tool: ${name}`, { tool: name }); try { // Security check before tool execution const securityCheck = await securityMiddleware.checkSecurity({ operation: name, clientId: 'mcp-client', // Could be extracted from request context parameters: args || {}, }); if (!securityCheck.allowed) { const executionTime = Date.now() - startTime; performanceMonitor.endOperation(operationId, name, false); logger.warn(`Tool execution blocked by security: ${name}`, { tool: name, executionTime, errors: securityCheck.errors, }); return { content: [ { type: 'text', text: JSON.stringify( { success: false, error: { code: 'SECURITY_VIOLATION', message: 'Request blocked by security policy', details: securityCheck.errors, suggestions: [ 'Check your input parameters for invalid characters', 'Ensure URLs use HTTPS protocol', 'Reduce request rate if hitting limits', ], }, metadata: { execution_time: executionTime, tool: name, timestamp: new Date().toISOString(), rate_limit: securityCheck.rateLimitInfo, }, }, null, 2 ), }, ], }; } const tool = toolRegistry.getTool(name); if (!tool) { const executionTime = Date.now() - startTime; performanceMonitor.endOperation(operationId, name, false); logger.warn(`Tool not found: ${name}`, { tool: name, executionTime }); return { content: [ { type: 'text', text: JSON.stringify( { success: false, error: { code: 'TOOL_NOT_FOUND', message: `Tool '${name}' not found`, suggestions: [ `Available tools: ${toolRegistry.getToolNames().join(', ')}`, 'Check the tool name spelling and try again', ], }, metadata: { execution_time: executionTime, tool: name, timestamp: new Date().toISOString(), }, }, null, 2 ), }, ], }; } // Execute the tool with sanitized parameters const result = await tool.handler( securityCheck.sanitizedParameters || {} ); const executionTime = Date.now() - startTime; performanceMonitor.endOperation(operationId, name, true); logger.logToolExecution( name, 'Tool executed successfully', executionTime ); // Add security warnings to response if any if (securityCheck.warnings.length > 0) { if (result.metadata) { (result.metadata as Record<string, unknown>)['security_warnings'] = securityCheck.warnings; } } return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } catch (error) { const executionTime = Date.now() - startTime; performanceMonitor.endOperation(operationId, name, false); logger.error(`Tool execution failed: ${name}`, { tool: name, executionTime, error: error as Error, }); // Log security event for execution failures securityAuditor.logSecurityEvent({ type: 'suspicious_activity', severity: 'medium', operation: name, clientId: 'mcp-client', details: { error: (error as Error).message, executionTime, }, }); return { content: [ { type: 'text', text: JSON.stringify( { success: false, error: { code: 'EXECUTION_ERROR', message: 'Tool execution failed', details: process.env['NODE_ENV'] === 'development' ? (error as Error).message : 'Internal server error', }, metadata: { execution_time: executionTime, tool: name, timestamp: new Date().toISOString(), }, }, null, 2 ), }, ], }; } }); } private registerTools(): void { // All tools are automatically registered when tools/index.ts is imported // No need to manually register individual tools here const toolCount = toolRegistry.getAllTools().length; logger.info( `Registered ${toolCount} tools: ${toolRegistry.getToolNames().join(', ')}` ); } public async start(): Promise<void> { // Skip actual server startup in test environments const isTestEnvironment = process.env['NODE_ENV'] === 'test' || process.env['JEST_WORKER_ID'] !== undefined || typeof jest !== 'undefined'; if (isTestEnvironment) { // In test mode, just simulate the startup logger.info(`${this.config.name} started in test mode`); return; } try { const transport = new StdioServerTransport(); await this.server.connect(transport); logger.info( `${this.config.name} started successfully on stdio transport` ); } catch (error) { logger.error('Failed to start server', { error: error as Error }); throw error; } } public getConfig(): ServerConfig { return { ...this.config }; } public getToolCount(): number { return toolRegistry.getAllTools().length; } private setupPerformanceMonitoring(): void { // Log performance stats periodically this.performanceStatsInterval = setInterval(() => { const stats = performanceMonitor.getStats(); const resourceStatus = resourceManager.getResourceStatus(); logger.info('Performance stats', { totalRequests: stats.totalRequests, averageResponseTime: Math.round(stats.averageResponseTime), memoryUsage: Math.round(stats.memoryUsage.current / 1024 / 1024), cacheHitRate: Math.round(stats.cacheStats.hitRate * 100), concurrentRequests: stats.concurrentRequests, resourceStatus: resourceStatus.status, }); }, 60000); // Log every minute // Periodic cleanup this.cleanupInterval = setInterval(async () => { await resourceManager.cleanup(); }, 300000); // Cleanup every 5 minutes } public getPerformanceStats() { return { performance: performanceMonitor.getStats(), resources: resourceManager.getResourceStatus(), }; } public getSecurityStats() { return { audit: securityAuditor.getMetrics(), rateLimits: { // Add rate limiter stats if needed }, }; } public generateSecurityReport() { return securityAuditor.generateSecurityReport(); } public destroy(): void { // Clear intervals if (this.performanceStatsInterval) { clearInterval(this.performanceStatsInterval); this.performanceStatsInterval = null; } if (this.cleanupInterval) { clearInterval(this.cleanupInterval); this.cleanupInterval = null; } } } // Export for testing export { toolRegistry };

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/keyurgolani/ColorMcp'

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