Skip to main content
Glama

MCP Firebird

/** * Database connection module for MCP Firebird * Provides functionality for connecting to Firebird databases */ import Firebird from 'node-firebird'; import { createLogger } from '../utils/logger.js'; import { FirebirdError, ErrorTypes } from '../utils/errors.js'; const logger = createLogger('db:connection'); /** * Tipo para el objeto de conexión de Firebird */ export interface FirebirdDatabase { query: (sql: string, params: any[], callback: (err: Error | null, results?: any[]) => void) => any; detach: (callback: (err: Error | null) => void) => void; [key: string]: any; } /** * Interfaz que define las opciones de configuración para la conexión a Firebird */ export interface ConfigOptions { host: string; port: number; database: string; user: string; password: string; lowercase_keys?: boolean; role?: string; pageSize?: number; } import * as path from 'path'; // Normalize database path for Windows export function normalizeDatabasePath(dbPath: string | undefined): string { if (!dbPath) return ''; return path.normalize(dbPath); } // Get configuration from global variable export const getGlobalConfig = (): ConfigOptions | null => { try { if ((global as any).MCP_FIREBIRD_CONFIG) { const config = (global as any).MCP_FIREBIRD_CONFIG; console.error('Using global database configuration'); console.error(`Global config: database=${config.database}, host=${config.host}, port=${config.port}, user=${config.user}`); return config; } else { console.error('No global database configuration found'); } } catch (error) { console.error(`Error getting global configuration: ${error instanceof Error ? error.message : String(error)}`); } return null; }; // Default configuration for the connection export const getDefaultConfig = (): ConfigOptions => { // First try to load from global configuration const globalConfig = getGlobalConfig(); if (globalConfig && globalConfig.database) { console.error('Using global configuration'); return globalConfig; } // Debug: Log all environment variables related to database connection console.error('Environment variables for database connection:'); console.error(`FIREBIRD_DATABASE: ${process.env.FIREBIRD_DATABASE || 'not set'}`); console.error(`FB_DATABASE: ${process.env.FB_DATABASE || 'not set'}`); console.error(`FIREBIRD_HOST: ${process.env.FIREBIRD_HOST || 'not set'}`); console.error(`FB_HOST: ${process.env.FB_HOST || 'not set'}`); console.error(`FIREBIRD_PORT: ${process.env.FIREBIRD_PORT || 'not set'}`); console.error(`FB_PORT: ${process.env.FB_PORT || 'not set'}`); console.error(`FIREBIRD_USER: ${process.env.FIREBIRD_USER || 'not set'}`); console.error(`FB_USER: ${process.env.FB_USER || 'not set'}`); // Don't log passwords console.error(`FIREBIRD_ROLE: ${process.env.FIREBIRD_ROLE || 'not set'}`); console.error(`FB_ROLE: ${process.env.FB_ROLE || 'not set'}`); // Check for global configuration first (set by CLI) const globalConfigFromEnv = (global as any).MCP_FIREBIRD_CONFIG; const config = { host: globalConfigFromEnv?.host || process.env.FIREBIRD_HOST || process.env.FB_HOST || '127.0.0.1', // Use 127.0.0.1 instead of 'localhost' port: parseInt(String(globalConfigFromEnv?.port || process.env.FIREBIRD_PORT || process.env.FB_PORT || '3050'), 10), database: normalizeDatabasePath(globalConfigFromEnv?.database || process.env.FIREBIRD_DATABASE || process.env.FB_DATABASE), user: globalConfigFromEnv?.user || process.env.FIREBIRD_USER || process.env.FB_USER || 'SYSDBA', password: globalConfigFromEnv?.password || process.env.FIREBIRD_PASSWORD || process.env.FB_PASSWORD || 'masterkey', role: globalConfigFromEnv?.role || process.env.FIREBIRD_ROLE || process.env.FB_ROLE || undefined, pageSize: globalConfigFromEnv?.pageSize || 4096 }; // Debug: Log the final configuration (without password) console.error('Final database configuration:'); console.error(`host: ${config.host}`); console.error(`port: ${config.port}`); console.error(`database: ${config.database}`); console.error(`user: ${config.user}`); // Don't log password console.error(`role: ${config.role || 'not set'}`); console.error(`pageSize: ${config.pageSize}`); return config; }; // For backward compatibility export const DEFAULT_CONFIG: ConfigOptions = { host: '127.0.0.1', // Use 127.0.0.1 instead of 'localhost' port: 3050, database: '', user: 'SYSDBA', password: 'masterkey', role: undefined, pageSize: 4096 }; // FirebirdError is now imported from '../utils/errors.js' /** * Establece conexión con la base de datos * @param config - Configuración de conexión a la base de datos * @returns Objeto de conexión a la base de datos * @throws {FirebirdError} Error categorizado si la conexión falla */ export const connectToDatabase = (config = getDefaultConfig()): Promise<FirebirdDatabase> => { return new Promise((resolve, reject) => { logger.info(`Connecting to ${config.host}:${config.port}/${config.database}`); // Verify minimum parameters if (!config.database) { // Si no hay base de datos configurada, usar una ruta predeterminada para pruebas console.error('No database specified in config, using hardcoded default path'); config.database = 'F:/Proyectos/SAI/EMPLOYEE.FDB'; console.error(`Using default database path: ${config.database}`); } // Log connection attempt with full details console.error('Attempting to connect with the following configuration:'); console.error(`- Host: ${config.host}`); console.error(`- Port: ${config.port}`); console.error(`- Database: ${config.database}`); console.error(`- User: ${config.user}`); // Don't log password console.error(`- Role: ${config.role || 'Not specified'}`); Firebird.attach(config, (err: Error | null, db: any) => { if (err) { // Categorize the error for better handling let errorType = ErrorTypes.DATABASE_CONNECTION; let errorMsg = `Error connecting to database: ${err.message}`; if (err.message.includes('service is not defined')) { errorType = ErrorTypes.DATABASE_CONNECTION; errorMsg = 'Firebird service is not available. Verify that the Firebird server is running.'; } else if (err.message.includes('ECONNREFUSED')) { errorType = ErrorTypes.DATABASE_CONNECTION; errorMsg = `Connection refused at ${config.host}:${config.port}. Verify that the Firebird server is running and accessible at this address.`; } else if (err.message.includes('ENOENT')) { errorType = ErrorTypes.DATABASE_CONNECTION; errorMsg = `Database not found: ${config.database}. Verify the path and permissions.`; } else if (err.message.includes('password') || err.message.includes('user')) { errorType = ErrorTypes.SECURITY_AUTHENTICATION; errorMsg = 'Authentication error. Verify username and password.'; } else if (err.message.includes('ETIMEDOUT')) { errorType = ErrorTypes.DATABASE_CONNECTION; errorMsg = `Connection timed out at ${config.host}:${config.port}. Verify that the Firebird server is accessible and not blocked by a firewall.`; } else if (err.message.includes('EHOSTUNREACH')) { errorType = ErrorTypes.DATABASE_CONNECTION; errorMsg = `Host unreachable at ${config.host}:${config.port}. Verify network connectivity and that the host exists.`; } logger.error(errorMsg, { originalError: err }); reject(new FirebirdError(errorMsg, errorType, err)); return; } logger.info('Connection established successfully'); resolve(db); }); }); }; /** * Ejecuta una consulta en la base de datos * @param {FirebirdDatabase} db - Objeto de conexión a la base de datos * @param {string} sql - Consulta SQL a ejecutar * @param {Array} params - Parámetros para la consulta * @returns {Promise<any[]>} Resultado de la consulta * @throws {FirebirdError} Error categorizado si la consulta falla */ export const queryDatabase = (db: FirebirdDatabase, sql: string, params: any[] = []): Promise<any[]> => { return new Promise((resolve, reject) => { logger.info(`Ejecutando consulta: ${sql.substring(0, 100)}${sql.length > 100 ? '...' : ''}`); db.query(sql, params, (err: Error | null, result: any) => { if (err) { // Categorizar el error para mejor manejo let errorType = 'QUERY_ERROR'; // Intentar categorizar el error según su contenido if (err.message.includes('syntax error')) { errorType = 'SYNTAX_ERROR'; } else if (err.message.includes('not defined')) { errorType = 'OBJECT_NOT_FOUND'; } else if (err.message.includes('permission')) { errorType = 'PERMISSION_ERROR'; } else if (err.message.includes('deadlock')) { errorType = 'DEADLOCK_ERROR'; } else if (err.message.includes('timeout')) { errorType = 'TIMEOUT_ERROR'; } // Crear un error más informativo const error = new FirebirdError( `Error al ejecutar consulta: ${err.message}`, errorType, err ); logger.error(`${error.message} [${errorType}]`); reject(error); return; } // Si no hay resultados, devolver un array vacío if (!result) { result = []; } logger.info(`Consulta ejecutada exitosamente, ${result.length} filas obtenidas`); resolve(result); }); }); }; /** * Prueba la conexión a la base de datos usando la configuración proporcionada. * Intenta conectar y desconectar inmediatamente. * @param {ConfigOptions} [config=DEFAULT_CONFIG] - Configuración a usar para la prueba. * @returns {Promise<void>} Resuelve si la conexión es exitosa, rechaza si falla. * @throws {FirebirdError} Error categorizado si la conexión falla. */ export const testConnection = async (config = getDefaultConfig()): Promise<void> => { logger.info('Probando conexión a la base de datos...'); let db: FirebirdDatabase | null = null; try { db = await connectToDatabase(config); logger.info('Prueba de conexión exitosa.'); } catch (error) { logger.error('Prueba de conexión fallida.'); // El error ya debería ser un FirebirdError de connectToDatabase throw error; } finally { if (db) { await new Promise<void>((resolve, reject) => { db?.detach((detachErr: Error | null) => { if (detachErr) { logger.warn(`Error al cerrar conexión de prueba: ${detachErr.message}`); // No rechazamos la promesa principal por un error de detach, // pero sí lo registramos. } resolve(); }); }); } } }; // Se ha eliminado la función executeQuery ya que está duplicada // La implementación mejorada se mantiene en queries.ts con validación de SQL // y manejo de errores más robusto

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