Skip to main content
Glama

MCP Firebird

/** * Database management functions for Firebird * Provides functionality for backup, restore, and validation of databases */ import { spawn } from 'child_process'; import * as fs from 'fs'; import * as path from 'path'; import { createLogger } from '../utils/logger.js'; import { checkFirebirdTools, findFirebirdBinPath } from '../utils/firebird-tools.js'; import { FirebirdError } from '../utils/errors.js'; const logger = createLogger('db:management'); import { ConfigOptions, DEFAULT_CONFIG } from './connection.js'; /** * Interface for backup options */ export interface BackupOptions { format?: 'gbak' | 'nbackup'; compress?: boolean; metadata_only?: boolean; verbose?: boolean; } /** * Interface for restore options */ export interface RestoreOptions { replace?: boolean; pageSize?: number; verbose?: boolean; } /** * Interface for validation options */ export interface ValidateOptions { checkData?: boolean; checkIndexes?: boolean; fixErrors?: boolean; verbose?: boolean; } /** * Interface for backup result */ export interface BackupResult { success: boolean; backupPath: string; size: number; duration: number; error?: string; details?: string; } /** * Interface for restore result */ export interface RestoreResult { success: boolean; targetPath: string; duration: number; error?: string; details?: string; } /** * Interface for validation result */ export interface ValidationResult { success: boolean; valid: boolean; issues: string[]; details: string; error?: string; } /** * Creates a backup of a Firebird database * @param {string} backupPath - Path where the backup file will be saved * @param {BackupOptions} options - Backup options * @param {ConfigOptions} config - Database connection configuration * @returns {Promise<BackupResult>} Result of the backup operation */ export const backupDatabase = async ( backupPath: string, options: BackupOptions = {}, config = DEFAULT_CONFIG ): Promise<BackupResult> => { const startTime = Date.now(); try { // Check if Firebird tools are installed const toolsCheck = await checkFirebirdTools(); if (!toolsCheck.installed) { throw new FirebirdError( `Firebird client tools are not installed. ${toolsCheck.installInstructions}`, 'MISSING_FIREBIRD_TOOLS' ); } // Ensure the backup directory exists const backupDir = path.dirname(backupPath); if (!fs.existsSync(backupDir)) { fs.mkdirSync(backupDir, { recursive: true }); } // Determine which backup tool to use const format = options.format || 'gbak'; let command: string; let args: string[] = []; // Try to find Firebird bin directory const firebirdBinPath = await findFirebirdBinPath(); if (firebirdBinPath) { logger.info(`Found Firebird bin directory: ${firebirdBinPath}`); // Use full path to command if bin directory was found command = path.join(firebirdBinPath, format); } else { // Use command name only, relying on system PATH command = format; } if (format === 'gbak') { args = [ '-b', // Backup '-v', // Verbose output '-user', config.user || 'SYSDBA', '-password', config.password || 'masterkey' ]; if (options.metadata_only) { args.push('-m'); // Metadata only } if (options.compress) { args.push('-z'); // Compress } // Add connection string and backup path const connectionString = `${config.host}/${config.port}:${config.database}`; args.push(connectionString, backupPath); } else if (format === 'nbackup') { args = [ '-B', '0', // Level 0 backup (full) '-user', config.user || 'SYSDBA', '-password', config.password || 'masterkey' ]; // Add database path and backup path args.push(config.database, backupPath); } else { throw new FirebirdError( `Invalid backup format: ${format}`, 'CONFIGURATION_ERROR' ); } logger.info(`Starting database backup to ${backupPath} using ${format}`); if (options.verbose) { logger.debug(`Backup command: ${command} ${args.join(' ')}`); } // Execute the backup command const result = await executeCommand(command, args, options.verbose); // Get the size of the backup file const stats = fs.statSync(backupPath); const duration = Date.now() - startTime; logger.info(`Backup completed successfully in ${duration}ms, size: ${stats.size} bytes`); return { success: true, backupPath, size: stats.size, duration, details: result }; } catch (error: any) { const duration = Date.now() - startTime; const errorMessage = `Error creating database backup: ${error.message || error}`; logger.error(errorMessage); return { success: false, backupPath, size: 0, duration, error: errorMessage, details: error.details || '' }; } }; /** * Restores a Firebird database from a backup * @param {string} backupPath - Path to the backup file * @param {string} targetPath - Path where the database will be restored * @param {RestoreOptions} options - Restore options * @param {ConfigOptions} config - Database connection configuration * @returns {Promise<RestoreResult>} Result of the restore operation */ export const restoreDatabase = async ( backupPath: string, targetPath: string, options: RestoreOptions = {}, config = DEFAULT_CONFIG ): Promise<RestoreResult> => { const startTime = Date.now(); try { // Check if Firebird tools are installed const toolsCheck = await checkFirebirdTools(); if (!toolsCheck.installed) { throw new FirebirdError( `Firebird client tools are not installed. ${toolsCheck.installInstructions}`, 'MISSING_FIREBIRD_TOOLS' ); } // Check if the backup file exists if (!fs.existsSync(backupPath)) { throw new FirebirdError( `Backup file not found: ${backupPath}`, 'FILE_NOT_FOUND' ); } // Check if the target database already exists if (fs.existsSync(targetPath) && !options.replace) { throw new FirebirdError( `Target database already exists: ${targetPath}. Use 'replace: true' to overwrite.`, 'FILE_EXISTS' ); } // Ensure the target directory exists const targetDir = path.dirname(targetPath); if (!fs.existsSync(targetDir)) { fs.mkdirSync(targetDir, { recursive: true }); } // Determine which restore tool to use based on the backup file extension const ext = path.extname(backupPath).toLowerCase(); let command: string; let args: string[] = []; // Try to find Firebird bin directory const firebirdBinPath = await findFirebirdBinPath(); if (ext === '.fbk' || ext === '.gbk') { // GBAK restore command = firebirdBinPath ? path.join(firebirdBinPath, 'gbak') : 'gbak'; args = [ '-c', // Create (restore) '-v', // Verbose output '-user', config.user || 'SYSDBA', '-password', config.password || 'masterkey' ]; // Set page size if specified if (options.pageSize) { args.push('-page_size', options.pageSize.toString()); } // Add backup path and target database path args.push(backupPath, targetPath); } else if (ext === '.nbk') { // NBACKUP restore command = firebirdBinPath ? path.join(firebirdBinPath, 'nbackup') : 'nbackup'; args = [ '-R', // Restore '-user', config.user || 'SYSDBA', '-password', config.password || 'masterkey' ]; // Add target database path and backup path args.push(targetPath, backupPath); } else { throw new FirebirdError( `Unknown backup file format: ${ext}`, 'CONFIGURATION_ERROR' ); } logger.info(`Starting database restore from ${backupPath} to ${targetPath}`); if (options.verbose) { logger.debug(`Restore command: ${command} ${args.join(' ')}`); } // Execute the restore command const result = await executeCommand(command, args, options.verbose); const duration = Date.now() - startTime; logger.info(`Restore completed successfully in ${duration}ms`); return { success: true, targetPath, duration, details: result }; } catch (error: any) { const duration = Date.now() - startTime; const errorMessage = `Error restoring database: ${error.message || error}`; logger.error(errorMessage); return { success: false, targetPath, duration, error: errorMessage, details: error.details || '' }; } }; /** * Validates a Firebird database * @param {ValidateOptions} options - Validation options * @param {ConfigOptions} config - Database connection configuration * @returns {Promise<ValidationResult>} Result of the validation */ export const validateDatabase = async ( options: ValidateOptions = {}, config = DEFAULT_CONFIG ): Promise<ValidationResult> => { const startTime = Date.now(); try { // Check if Firebird tools are installed const toolsCheck = await checkFirebirdTools(); if (!toolsCheck.installed) { throw new FirebirdError( `Firebird client tools are not installed. ${toolsCheck.installInstructions}`, 'MISSING_FIREBIRD_TOOLS' ); } // Try to find Firebird bin directory const firebirdBinPath = await findFirebirdBinPath(); // Use GFIX for validation const command = firebirdBinPath ? path.join(firebirdBinPath, 'gfix') : 'gfix'; let args: string[] = [ '-user', config.user || 'SYSDBA', '-password', config.password || 'masterkey' ]; // Add validation options if (options.checkData) { args.push('-v'); // Validate database } if (options.checkIndexes) { args.push('-i'); // Validate indexes } if (options.fixErrors) { args.push('-mend'); // Fix errors } // Add database path args.push(config.database); logger.info(`Starting database validation for ${config.database}`); if (options.verbose) { logger.debug(`Validation command: ${command} ${args.join(' ')}`); } // Execute the validation command const result = await executeCommand(command, args, options.verbose); // Parse the result to determine if the database is valid const issues: string[] = []; const lines = result.split('\n'); for (const line of lines) { if (line.includes('error') || line.includes('corrupt') || line.includes('invalid')) { issues.push(line.trim()); } } const valid = issues.length === 0; const duration = Date.now() - startTime; logger.info(`Validation completed in ${duration}ms, valid: ${valid}, issues: ${issues.length}`); return { success: true, valid, issues, details: result }; } catch (error: any) { const duration = Date.now() - startTime; const errorMessage = `Error validating database: ${error.message || error}`; logger.error(errorMessage); return { success: false, valid: false, issues: [errorMessage], details: error.details || '', error: errorMessage }; } }; /** * Helper function to execute a command and return its output * @param {string} command - Command to execute * @param {string[]} args - Command arguments * @param {boolean} verbose - Whether to log verbose output * @returns {Promise<string>} Command output */ async function executeCommand(command: string, args: string[], verbose: boolean = false): Promise<string> { return new Promise((resolve, reject) => { const process = spawn(command, args); let stdout = ''; let stderr = ''; process.stdout.on('data', (data) => { const output = data.toString(); stdout += output; if (verbose) { logger.debug(`[${command}] ${output.trim()}`); } }); process.stderr.on('data', (data) => { const output = data.toString(); stderr += output; if (verbose) { logger.error(`[${command}] ${output.trim()}`); } }); process.on('close', (code) => { if (code === 0) { resolve(stdout); } else { const error = new FirebirdError( `Command failed with code ${code}: ${stderr || 'No error message'}`, 'COMMAND_EXECUTION_ERROR' ); (error as any).details = stderr; reject(error); } }); process.on('error', (error) => { const fbError = new FirebirdError( `Failed to execute command: ${error.message}`, 'COMMAND_EXECUTION_ERROR' ); (fbError as any).details = error.message; reject(fbError); }); }); }

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