Skip to main content
Glama
IBM
by IBM
connectionPool.ts6.31 kB
/** * @fileoverview IBM i connection pool management using mapepire-js * This module provides a singleton connection pool for IBM i DB2 database operations. * Credentials are provided by the MCP client via environment variables. * * @module src/services/mapepire/connectionPool */ import { BindingValue, QueryResult } from "@ibm/mapepire-js"; import { config } from "@/config/index.js"; import { logger } from "@/utils/internal/logger.js"; import { ErrorHandler } from "@/utils/internal/errorHandler.js"; import { requestContextService, RequestContext, } from "@/utils/internal/requestContext.js"; import { JsonRpcErrorCode, McpError } from "@/types-global/errors.js"; import { BaseConnectionPool, PoolConnectionConfig, } from "./baseConnectionPool.js"; // Singleton identifier for the IBM i connection pool const IBM_I_POOL_ID = Symbol("ibmi-singleton-pool"); /** * IBM i connection pool manager with lazy initialization * Credentials are provided by MCP client via environment variables */ export class IBMiConnectionPool extends BaseConnectionPool< typeof IBM_I_POOL_ID > { private static instance: IBMiConnectionPool | undefined; /** * Get the singleton instance */ private static getInstance(): IBMiConnectionPool { if (!this.instance) { this.instance = new IBMiConnectionPool(); } return this.instance; } /** * Initialize the connection pool using credentials from config * Called automatically on first query if not already initialized */ private static async ensureInitialized(): Promise<void> { const instance = this.getInstance(); const poolState = instance.pools.get(IBM_I_POOL_ID); if (poolState && poolState.isInitialized) { return; } const context = requestContextService.createRequestContext({ operation: "InitializeIBMiConnectionPool", }); try { // Check if Db2i configuration is available if (!config.db2i) { throw new McpError( JsonRpcErrorCode.ConfigurationError, "Db2i configuration not found. Please ensure DB2i_HOST, DB2i_USER, and DB2i_PASS environment variables are set.", { configSection: "db2i" }, ); } const { host, user, password, ignoreUnauthorized } = config.db2i; logger.info( { ...context, host, user: user.substring(0, 3) + "***", // Mask username for security ignoreUnauthorized, }, "Initializing IBM i connection pool", ); // Convert config to pool connection config const poolConfig: PoolConnectionConfig = { host, user, password, ignoreUnauthorized, }; // Initialize the pool using base class await instance.initializePool(IBM_I_POOL_ID, poolConfig, context); logger.info(context, "IBM i connection pool initialized successfully"); } catch (error) { const handledError = ErrorHandler.handleError(error, { operation: "InitializeIBMiConnectionPool", context, errorCode: JsonRpcErrorCode.InitializationFailed, critical: true, }); throw handledError; } } /** * Execute a SQL query with automatic pagination to fetch all results * Uses the query/execute/fetchMore pattern for large result sets */ static async executeQueryWithPagination( query: string, params?: BindingValue[], context?: RequestContext, fetchSize: number = 300, ): Promise<{ data: unknown[]; success: boolean; sql_rc?: unknown; execution_time?: number; }> { const operationContext = context || requestContextService.createRequestContext({ operation: "ExecuteQueryWithPagination", }); await this.ensureInitialized(); const instance = this.getInstance(); return instance.executeQueryWithPagination( IBM_I_POOL_ID, query, params, operationContext, fetchSize, ); } /** * Execute a SQL query against the IBM i database * Automatically initializes the pool if not already done */ static async executeQuery( query: string, params?: BindingValue[], context?: RequestContext, ): Promise<QueryResult<unknown>> { const operationContext = context || requestContextService.createRequestContext({ operation: "ExecuteQuery", }); await this.ensureInitialized(); const instance = this.getInstance(); return instance.executeQuery( IBM_I_POOL_ID, query, params, operationContext, ); } /** * Check the health of the connection pool */ static async healthCheck(): Promise<boolean> { const context = requestContextService.createRequestContext({ operation: "ConnectionHealthCheck", }); try { await this.ensureInitialized(); const instance = this.getInstance(); const health = await instance.checkPoolHealth(IBM_I_POOL_ID, context); logger.debug(context, "Connection health check passed"); return health.status === "healthy"; } catch (error) { logger.error( { ...context, error: error instanceof Error ? error.message : String(error), }, "Connection health check failed", ); return false; } } /** * Close the connection pool gracefully */ static async close(): Promise<void> { const context = requestContextService.createRequestContext({ operation: "CloseConnectionPool", }); const instance = this.getInstance(); const poolState = instance.pools.get(IBM_I_POOL_ID); if (poolState && poolState.pool) { logger.info(context, "Closing IBM i connection pool"); await instance.closePool(IBM_I_POOL_ID, context); logger.info(context, "IBM i connection pool closed successfully"); } } /** * Get connection pool status for monitoring */ static getStatus(): { initialized: boolean; connecting: boolean; poolExists: boolean; } { const instance = this.getInstance(); const poolStatus = instance.getPoolStatus(IBM_I_POOL_ID); return { initialized: poolStatus?.initialized ?? false, connecting: poolStatus?.connecting ?? false, poolExists: poolStatus !== null, }; } }

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/IBM/ibmi-mcp'

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