Skip to main content
Glama

MCP Firebird

config.ts9.18 kB
/** * Security configuration for the MCP Firebird server * Implements the security features mentioned in the README */ import { z } from 'zod'; import { createLogger } from '../utils/logger.js'; const logger = createLogger('security:config'); import * as fs from 'fs'; import * as path from 'path'; /** * Schema for data masking configuration */ export const DataMaskingSchema = z.array(z.object({ columns: z.array(z.string()).min(1), pattern: z.string().or(z.instanceof(RegExp)), replacement: z.string() })); /** * Schema for row filters configuration */ export const RowFiltersSchema = z.record(z.string(), z.string()); /** * Schema for audit configuration */ export const AuditConfigSchema = z.object({ enabled: z.boolean().default(false), destination: z.enum(['file', 'database', 'both']).default('file'), auditFile: z.string().optional(), auditTable: z.string().optional(), detailLevel: z.enum(['basic', 'medium', 'full']).default('medium'), logQueries: z.boolean().default(true), logResponses: z.boolean().default(false), logParameters: z.boolean().default(true) }); /** * Schema for resource limits configuration */ export const ResourceLimitsSchema = z.object({ maxRowsPerQuery: z.number().int().positive().default(5000), maxResponseSize: z.number().int().positive().default(5 * 1024 * 1024), // 5 MB maxQueryCpuTime: z.number().int().positive().default(10000), // 10 seconds maxQueriesPerSession: z.number().int().positive().default(100), rateLimit: z.object({ queriesPerMinute: z.number().int().positive().default(60), burstLimit: z.number().int().positive().default(20) }).optional() }); /** * Schema for authorization configuration */ export const AuthorizationSchema = z.object({ type: z.enum(['none', 'basic', 'oauth2']).default('none'), oauth2: z.object({ tokenVerifyUrl: z.string().url(), clientId: z.string(), clientSecret: z.string(), scope: z.string().optional() }).optional(), rolePermissions: z.record(z.string(), z.object({ tables: z.array(z.string()).optional(), allTablesAllowed: z.boolean().optional(), operations: z.array(z.enum(['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'CREATE', 'DROP', 'EXECUTE', 'ALTER'])).optional() })).optional() }); /** * Schema for security configuration */ export const SecurityConfigSchema = z.object({ allowedTables: z.array(z.string()).optional(), forbiddenTables: z.array(z.string()).optional(), tableNamePattern: z.string().optional(), allowedOperations: z.array(z.string()).optional(), forbiddenOperations: z.array(z.string()).optional(), maxRows: z.number().int().positive().optional(), queryTimeout: z.number().int().positive().optional(), dataMasking: DataMaskingSchema.optional(), rowFilters: RowFiltersSchema.optional(), audit: AuditConfigSchema.optional(), resourceLimits: ResourceLimitsSchema.optional(), authorization: AuthorizationSchema.optional() }); /** * Default security configuration */ export const DEFAULT_SECURITY_CONFIG = { allowedOperations: ['SELECT', 'EXECUTE'], forbiddenOperations: ['DROP', 'TRUNCATE', 'ALTER', 'GRANT', 'REVOKE'], maxRows: 1000, queryTimeout: 5000, audit: { enabled: false, destination: 'file' as 'file' | 'database' | 'both', auditFile: './logs/audit.log', detailLevel: 'medium' as 'basic' | 'medium' | 'full', logQueries: true, logResponses: false, logParameters: true }, resourceLimits: { maxRowsPerQuery: 5000, maxResponseSize: 5 * 1024 * 1024, // 5 MB maxQueryCpuTime: 10000, // 10 seconds maxQueriesPerSession: 100, rateLimit: { queriesPerMinute: 60, burstLimit: 20 } } }; /** * Security configuration */ export interface SecurityConfig { allowedTables?: string[]; forbiddenTables?: string[]; tableNamePattern?: string; allowedOperations?: string[]; forbiddenOperations?: string[]; maxRows?: number; queryTimeout?: number; dataMasking?: { columns: string[]; pattern: string | RegExp; replacement: string; }[]; rowFilters?: Record<string, string>; audit?: { enabled: boolean; destination: 'file' | 'database' | 'both'; auditFile?: string; auditTable?: string; detailLevel: 'basic' | 'medium' | 'full'; logQueries: boolean; logResponses: boolean; logParameters: boolean; }; resourceLimits?: { maxRowsPerQuery: number; maxResponseSize: number; maxQueryCpuTime: number; maxQueriesPerSession: number; rateLimit?: { queriesPerMinute: number; burstLimit: number; }; }; authorization?: { type: 'none' | 'basic' | 'oauth2'; oauth2?: { tokenVerifyUrl: string; clientId: string; clientSecret: string; scope?: string; }; rolePermissions?: Record<string, { tables?: string[]; allTablesAllowed?: boolean; operations?: ('SELECT' | 'INSERT' | 'UPDATE' | 'DELETE' | 'CREATE' | 'DROP' | 'EXECUTE' | 'ALTER')[]; }>; }; } /** * Load security configuration from a file * @param {string} configPath - Path to the configuration file * @returns {SecurityConfig} Security configuration */ export function loadSecurityConfig(configPath?: string): SecurityConfig { let config: SecurityConfig = { ...DEFAULT_SECURITY_CONFIG }; // If a config path is provided, try to load it if (configPath) { try { if (fs.existsSync(configPath)) { const configFile = require(path.resolve(configPath)); if (configFile && configFile.security) { // Validate the configuration const result = SecurityConfigSchema.safeParse(configFile.security); if (result.success) { config = { ...DEFAULT_SECURITY_CONFIG, ...result.data }; logger.info(`Loaded security configuration from ${configPath}`); } else { logger.error(`Invalid security configuration in ${configPath}: ${result.error.message}`); logger.warn('Using default security configuration'); } } else { logger.warn(`No security configuration found in ${configPath}`); logger.warn('Using default security configuration'); } } else { logger.warn(`Configuration file not found: ${configPath}`); logger.warn('Using default security configuration'); } } catch (error: any) { logger.error(`Error loading security configuration: ${error.message}`); logger.warn('Using default security configuration'); } } else { logger.info('No security configuration file provided, using default configuration'); } return config; } /** * Global security configuration instance */ export const securityConfig: SecurityConfig = { ...DEFAULT_SECURITY_CONFIG }; /** * Initialize security configuration * @param {string} configPath - Path to the configuration file */ export function initSecurityConfig(configPath?: string): void { const config = loadSecurityConfig(configPath); // Update the global security configuration Object.assign(securityConfig, config); // Initialize audit logging if enabled if (securityConfig.audit?.enabled) { initAuditLogging(securityConfig.audit); } logger.info('Security configuration initialized'); } /** * Initialize audit logging * @param {SecurityConfig['audit']} auditConfig - Audit configuration */ function initAuditLogging(auditConfig: NonNullable<SecurityConfig['audit']>): void { if (auditConfig.destination === 'file' || auditConfig.destination === 'both') { if (!auditConfig.auditFile) { auditConfig.auditFile = './logs/audit.log'; logger.warn(`No audit file specified, using default: ${auditConfig.auditFile}`); } // Ensure the audit directory exists const auditDir = path.dirname(auditConfig.auditFile); if (!fs.existsSync(auditDir)) { fs.mkdirSync(auditDir, { recursive: true }); logger.info(`Created audit log directory: ${auditDir}`); } logger.info(`Audit logging enabled to file: ${auditConfig.auditFile}`); } if (auditConfig.destination === 'database' || auditConfig.destination === 'both') { if (!auditConfig.auditTable) { auditConfig.auditTable = 'MCP_AUDIT_LOG'; logger.warn(`No audit table specified, using default: ${auditConfig.auditTable}`); } logger.info(`Audit logging enabled to database table: ${auditConfig.auditTable}`); } logger.info(`Audit logging initialized with detail level: ${auditConfig.detailLevel}`); }

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