Skip to main content
Glama
mcp-database-handler.ts3.82 kB
/** * MCP Database Handler * Single responsibility: handle database-related MCP requests * FIXED: Race condition protection and proper validation */ import { DIContainer } from '../../container/di-container'; import { MCPDatabaseError, MCPValidationError, MCPErrorCodes } from '../../infrastructure/errors'; export class McpDatabaseHandler { private container: DIContainer; private initializationPromise: Promise<void> | null = null; constructor() { this.container = DIContainer.getInstance(); } /** * ZERO-FALLBACK: Thread-safe database initialization * Uses Promise-based singleton pattern to prevent race conditions */ private async ensureDatabaseInitialized(): Promise<void> { if (!this.initializationPromise) { this.initializationPromise = this.container.initializeDatabase() .catch((error) => { // Reset promise on failure to allow retry this.initializationPromise = null; throw error; }); } return this.initializationPromise; } /** * Handle database switch with validation and proper error handling */ async handleDatabaseSwitch(databaseName: string): Promise<any> { // Input validation - fail fast this.validateDatabaseName(databaseName); await this.ensureDatabaseInitialized(); try { const databaseManager = this.container.getDatabaseManager(); return await databaseManager.switchDatabase(databaseName); } catch (error) { // Specific error types based on actual failure if (error instanceof Error) { if (error.message.includes('Invalid database name')) { throw new MCPValidationError( `Invalid database name: ${databaseName}`, MCPErrorCodes.INVALID_DATABASE_NAME, { databaseName, reason: 'invalid_name' } ); } if (error.message.includes('Connection failed') || error.message.includes('ServiceUnavailable')) { throw new MCPDatabaseError( `Database connection failed: ${error.message}`, MCPErrorCodes.DATABASE_UNAVAILABLE ); } } // Fallback for unknown errors throw new MCPDatabaseError( `Database switch to '${databaseName}' failed: ${error instanceof Error ? error.message : String(error)}`, MCPErrorCodes.DATABASE_OPERATION_FAILED ); } } /** * Validate database name according to Neo4j constraints * ZERO-FALLBACK: Invalid names throw immediately */ private validateDatabaseName(databaseName: string): void { if (!databaseName || typeof databaseName !== 'string') { throw new MCPValidationError( 'Database name must be a non-empty string', MCPErrorCodes.INVALID_DATABASE_NAME ); } if (databaseName.trim() !== databaseName) { throw new MCPValidationError( 'Database name cannot have leading or trailing whitespace', MCPErrorCodes.INVALID_DATABASE_NAME ); } if (databaseName.length === 0) { throw new MCPValidationError( 'Database name cannot be empty', MCPErrorCodes.INVALID_DATABASE_NAME ); } if (databaseName.length > 63) { throw new MCPValidationError( 'Database name cannot exceed 63 characters', MCPErrorCodes.INVALID_DATABASE_NAME, { length: databaseName.length, maxLength: 63 } ); } // Neo4j database name constraints (before normalization) if (!/^[a-zA-Z0-9\s\-_]+$/.test(databaseName)) { throw new MCPValidationError( 'Database name contains invalid characters. Only letters, numbers, spaces, hyphens, and underscores are allowed', MCPErrorCodes.INVALID_DATABASE_NAME, { invalidName: databaseName } ); } } }

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/sylweriusz/mcp-neo4j-memory-server'

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