Skip to main content
Glama

MCP Firebird

driver-factory.ts14.4 kB
/** * Driver Factory for MCP Firebird * Provides abstraction layer to support both node-firebird and node-firebird-driver-native */ import { createLogger } from '../utils/logger.js'; import { FirebirdError, ErrorTypes } from '../utils/errors.js'; import type { ConfigOptions, FirebirdDatabase } from './connection.js'; import { createRequire } from 'module'; const logger = createLogger('db:driver-factory'); // Create require function for ES modules to load CommonJS modules const require = createRequire(import.meta.url); /** * Driver types available */ export enum DriverType { PURE_JS = 'node-firebird', NATIVE = 'node-firebird-driver-native' } /** * Interface for database driver abstraction */ export interface IFirebirdDriver { /** * Initialize driver (load dependencies) */ initialize?(): Promise<void>; /** * Connect to database */ attach(config: ConfigOptions): Promise<FirebirdDatabase>; /** * Get driver type */ getType(): DriverType; /** * Check if driver supports wire encryption */ supportsWireEncryption(): boolean; } /** * Pure JavaScript driver implementation (node-firebird) */ class PureJSDriver implements IFirebirdDriver { private Firebird: any; constructor() { // Dynamic import to avoid issues if not installed try { // Use dynamic import for ES modules this.Firebird = null; } catch (error) { throw new FirebirdError( 'node-firebird no está instalado', ErrorTypes.DATABASE_CONNECTION, { originalError: error } ); } } async initialize(): Promise<void> { if (!this.Firebird) { const module = await import('node-firebird'); this.Firebird = module.default; } } async attach(config: ConfigOptions): Promise<FirebirdDatabase> { await this.initialize(); return new Promise((resolve, reject) => { const options = { host: config.host, port: config.port, database: config.database, user: config.user, password: config.password, lowercase_keys: config.lowercase_keys ?? false, role: config.role, pageSize: config.pageSize }; logger.info('Conectando con node-firebird (Pure JavaScript)...', { host: options.host, port: options.port, database: options.database, user: options.user }); this.Firebird.attach(options, (err: Error | null, db: FirebirdDatabase) => { if (err) { logger.error('Error al conectar con node-firebird', { error: err }); reject(new FirebirdError( `Error al conectar a la base de datos: ${err.message}`, ErrorTypes.DATABASE_CONNECTION, { originalError: err } )); } else { logger.info('Conexión exitosa con node-firebird'); resolve(db); } }); }); } getType(): DriverType { return DriverType.PURE_JS; } supportsWireEncryption(): boolean { return false; } } /** * Native driver implementation (node-firebird-driver-native) */ class NativeDriver implements IFirebirdDriver { private client: any = null; private attachment: any = null; async initialize(): Promise<void> { if (!this.client) { let nativeModule; let loadMethod = 'unknown'; try { // Try to load the native driver using require (works with global modules) // We use createRequire from 'module' to enable require() in ES modules try { logger.info('Attempting to load native driver via require...'); nativeModule = require('node-firebird-driver-native'); loadMethod = 'require'; logger.info('✅ Native driver loaded via require (local or global)'); } catch (requireError) { const reqErrMsg = requireError instanceof Error ? requireError.message : String(requireError); logger.warn('Failed to load via require, trying dynamic import...', { error: reqErrMsg }); // Second try: dynamic import (for ESM compatibility) const importModule = new Function('moduleName', 'return import(moduleName)'); nativeModule = await importModule('node-firebird-driver-native'); loadMethod = 'dynamic import'; logger.info('✅ Native driver loaded via dynamic import'); } logger.info('Creating native client...', { loadMethod }); this.client = nativeModule.createNativeClient(nativeModule.getDefaultLibraryFilename()); logger.info('✅ Cliente nativo de Firebird inicializado correctamente', { loadMethod }); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); const errorStack = error instanceof Error ? error.stack : undefined; logger.error('❌ Failed to load native driver', { error: errorMessage, stack: errorStack, loadMethod }); throw new FirebirdError( 'node-firebird-driver-native no está instalado o no se pudo cargar. ' + 'Instálalo globalmente con: npm install -g node-firebird-driver-native\n' + `Error: ${errorMessage}`, ErrorTypes.DATABASE_CONNECTION, { originalError: error } ); } } } async attach(config: ConfigOptions): Promise<FirebirdDatabase> { await this.initialize(); try { logger.info('Conectando con node-firebird-driver-native (Native Client)...', { host: config.host, port: config.port, database: config.database, user: config.user, wireCrypt: config.wireCrypt || 'Enabled' }); // Build connection string for native driver // IMPORTANT: Always use TCP/IP format (host:path) even for localhost // to enable wire encryption. Direct path uses embedded mode which doesn't support wire encryption. let connectionString: string; // Always use TCP/IP format when wire encryption is enabled if (config.port && config.port !== 3050) { connectionString = `${config.host}/${config.port}:${config.database}`; } else { connectionString = `${config.host}:${config.database}`; } logger.info('Connection details', { connectionString, host: config.host, port: config.port, database: config.database, wireCrypt: config.wireCrypt }); // IMPORTANT: WireCrypt CANNOT be configured via DPB (Database Parameter Buffer) // According to Firebird documentation, WireCrypt must be configured in: // - firebird.conf (server-side) // - databases.conf (per-database override) // // The DPB only supports: username, password, role, forcedWrite // See: https://firebirdsql.org/file/documentation/release_notes/html/en/3_0/rlsnotes30.html // // If you need wire encryption, configure it in the Firebird server's firebird.conf: // WireCrypt = Required | Enabled | Disabled if (config.wireCrypt) { logger.warn('WireCrypt parameter ignored - must be configured in firebird.conf on the server'); logger.warn('See: https://firebirdsql.org/file/documentation/release_notes/html/en/3_0/rlsnotes30.html#conf-wirecrypt'); } // Use the standard connect method with connection options const connectionOptions: any = { username: config.user, password: config.password }; // Add optional parameters if (config.role) { connectionOptions.role = config.role; } logger.info('Connecting with standard connection options', connectionOptions); // Use the standard connect method this.attachment = await (this.client as any).connect( connectionString, connectionOptions ); logger.info('Conexión exitosa con node-firebird-driver-native'); // Create adapter to match node-firebird interface return this.createAdapter(this.attachment); } catch (error) { logger.error('Error al conectar con node-firebird-driver-native', { error }); throw new FirebirdError( `Error al conectar a la base de datos: ${error instanceof Error ? error.message : String(error)}`, ErrorTypes.DATABASE_CONNECTION, { originalError: error } ); } } /** * Create adapter to match node-firebird interface */ private createAdapter(attachment: any): FirebirdDatabase { return { query: async (sql: string, params: any[], callback: (err: Error | null, results?: any[]) => void) => { let transaction; try { // Start a transaction for the query transaction = await attachment.startTransaction(); // Execute query with transaction const resultSet = await attachment.executeQuery(transaction, sql, params); // Fetch all results const rows = await resultSet.fetchAsObject(); // Close result set await resultSet.close(); // Commit transaction await transaction.commit(); // Return results callback(null, rows); } catch (err) { // Rollback on error if (transaction) { try { await transaction.rollback(); } catch (rollbackErr) { logger.error('Error rolling back transaction', { error: rollbackErr }); } } callback(err as Error); } }, detach: (callback: (err: Error | null) => void) => { attachment.disconnect() .then(() => callback(null)) .catch((err: Error) => callback(err)); }, // Store original attachment for advanced usage _nativeAttachment: attachment }; } getType(): DriverType { return DriverType.NATIVE; } supportsWireEncryption(): boolean { return true; } } /** * Driver factory to create appropriate driver instance */ export class DriverFactory { private static instance: IFirebirdDriver | null = null; private static useNativeDriver: boolean = false; /** * Set whether to use native driver */ static setUseNativeDriver(useNative: boolean): void { DriverFactory.useNativeDriver = useNative; DriverFactory.instance = null; // Reset instance to force recreation if (useNative) { logger.info('Configurado para usar node-firebird-driver-native (soporta wire encryption)'); } else { logger.info('Configurado para usar node-firebird (Pure JavaScript, sin wire encryption)'); } } /** * Get driver instance (singleton) */ static async getDriver(): Promise<IFirebirdDriver> { if (!DriverFactory.instance) { if (DriverFactory.useNativeDriver) { try { DriverFactory.instance = new NativeDriver(); if (DriverFactory.instance.initialize) { await DriverFactory.instance.initialize(); } logger.info('Usando node-firebird-driver-native'); } catch (error) { logger.warn('No se pudo cargar node-firebird-driver-native, usando node-firebird', { error }); DriverFactory.instance = new PureJSDriver(); } } else { DriverFactory.instance = new PureJSDriver(); logger.info('Usando node-firebird (Pure JavaScript)'); } } return DriverFactory.instance; } /** * Check if native driver is available */ static async isNativeDriverAvailable(): Promise<boolean> { try { // Try require first (works with global modules) require('node-firebird-driver-native'); return true; } catch { try { // Fallback to dynamic import const importModule = new Function('moduleName', 'return import(moduleName)'); await importModule('node-firebird-driver-native'); return true; } catch { return false; } } } /** * Get information about available drivers */ static async getDriverInfo(): Promise<{ current: DriverType; nativeAvailable: boolean; supportsWireEncryption: boolean; }> { const driver = await DriverFactory.getDriver(); const nativeAvailable = await DriverFactory.isNativeDriverAvailable(); return { current: driver.getType(), nativeAvailable, supportsWireEncryption: driver.supportsWireEncryption() }; } }

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/PuroDelphi/mcpFirebird'

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