Skip to main content
Glama

MCP SQL Server

by ryudg
SchemaRepositoryImpl.ts8.44 kB
import { DatabaseSchema, TableInfo, ColumnInfo } from '../../domain/schema/entities/Schema.js'; import { DatabaseConnectionManager } from '../../database/connection/connection.manager.js'; import { Logger } from '../../core/logger.js'; import { SchemaRepository } from '../../domain/schema/services/SchemaDomainService.js'; import { SchemaStatistics } from '../../application/use-cases/GetSchemaStatisticsUseCase.js'; export class SchemaRepositoryImpl implements SchemaRepository { private schemaCache: Map<string, DatabaseSchema> = new Map(); constructor( private readonly dbManager: DatabaseConnectionManager, private readonly logger: Logger ) {} async getSchema(databaseName: string): Promise<DatabaseSchema> { try { // Get current database name if not provided if (!databaseName || databaseName === 'default') { const connection = this.dbManager.getCurrentConnection(); const dbResult = await connection.executeQuery('SELECT DB_NAME() as dbName'); databaseName = dbResult.recordset?.[0]?.dbName || 'WMS'; } // Check cache first const cacheKey = databaseName; const cachedSchema = this.schemaCache.get(cacheKey); if (cachedSchema) { this.logger.info('Returning cached schema', { databaseName }); return cachedSchema; } // If not in cache, refresh and return return await this.refreshSchema(databaseName); } catch (error) { this.logger.error('Failed to get schema', { error }); throw error; } } async getTables(pattern?: string): Promise<any[]> { try { const connection = this.dbManager.getCurrentConnection(); let query = ` SELECT TABLE_NAME as name, TABLE_SCHEMA as tableSchema, TABLE_TYPE as type FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' `; if (pattern) { query += ` AND TABLE_NAME LIKE '%${pattern}%'`; } const result = await connection.executeQuery(query); return result.recordset || []; } catch (error) { this.logger.error('Failed to get tables', { error }); throw error; } } async getTableInfo(tableName: string): Promise<any> { try { this.logger.info('Getting table info', { tableName }); const adapter = this.dbManager.getCurrentConnection(); const tableInfo = await adapter.getTableInfo(tableName); this.logger.info('Retrieved table info', { tableName, columnCount: tableInfo.columns?.length || 0, primaryKeyCount: tableInfo.primaryKeys?.length || 0, }); return tableInfo; } catch (error) { this.logger.error('Failed to get table info', { tableName, error }); throw error; } } async getSchemaStatistics(includeSystemTables: boolean = false): Promise<SchemaStatistics> { try { const connection = this.dbManager.getCurrentConnection(); // Get table count let tableQuery = ` SELECT COUNT(*) as count FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' `; if (!includeSystemTables) { tableQuery += ` AND TABLE_SCHEMA != 'sys'`; } const tableResult = await connection.executeQuery(tableQuery); const totalTables = tableResult.recordset?.[0]?.count || 0; // Get view count const viewResult = await connection.executeQuery(` SELECT COUNT(*) as count FROM INFORMATION_SCHEMA.VIEWS `); const totalViews = viewResult.recordset?.[0]?.count || 0; // Get procedure count const procResult = await connection.executeQuery(` SELECT COUNT(*) as count FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_TYPE = 'PROCEDURE' `); const totalProcedures = procResult.recordset?.[0]?.count || 0; // Get function count const funcResult = await connection.executeQuery(` SELECT COUNT(*) as count FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_TYPE = 'FUNCTION' `); const totalFunctions = funcResult.recordset?.[0]?.count || 0; // Get column count const columnResult = await connection.executeQuery(` SELECT COUNT(*) as count FROM INFORMATION_SCHEMA.COLUMNS `); const totalColumns = columnResult.recordset?.[0]?.count || 0; // Get index count (approximate) const indexResult = await connection.executeQuery(` SELECT COUNT(*) as count FROM sys.indexes WHERE type > 0 `); const totalIndexes = indexResult.recordset?.[0]?.count || 0; // Calculate average columns per table const averageColumnsPerTable = totalTables > 0 ? totalColumns / totalTables : 0; // Get largest table by column count const largestTableResult = await connection.executeQuery(` SELECT TOP 1 t.TABLE_NAME as name, t.TABLE_TYPE as type, COUNT(c.COLUMN_NAME) as columnCount FROM INFORMATION_SCHEMA.TABLES t LEFT JOIN INFORMATION_SCHEMA.COLUMNS c ON t.TABLE_NAME = c.TABLE_NAME WHERE t.TABLE_TYPE = 'BASE TABLE' GROUP BY t.TABLE_NAME, t.TABLE_TYPE ORDER BY COUNT(c.COLUMN_NAME) DESC `); const largestTable = largestTableResult.recordset?.[0] ? { name: largestTableResult.recordset[0].name, type: largestTableResult.recordset[0].type, columns: [], // 실제 컬럼 정보는 별도 조회 필요 } : undefined; return { totalTables, totalViews, totalProcedures, totalFunctions, totalColumns, totalIndexes, averageColumnsPerTable: Math.round(averageColumnsPerTable * 100) / 100, largestTable, }; } catch (error) { this.logger.error('Failed to get schema statistics', { error }); throw error; } } async refreshSchema(databaseName: string): Promise<DatabaseSchema> { this.logger.info('Refreshing schema from database', { databaseName }); try { const adapter = this.dbManager.getCurrentConnection(); const tables = await adapter.getTables(); const schema = new DatabaseSchema(databaseName); // Add tables to schema if (tables && tables.length > 0) { this.logger.info('Processing tables', { tableCount: tables.length }); for (const table of tables) { try { const tableInfo: TableInfo = { name: table.name, schema: table.schema, type: table.type === 'system_table' ? 'table' : table.type, }; schema.addTable(tableInfo); // Get columns using adapter's getTableInfo method try { const detailedTableInfo = await adapter.getTableInfo(table.name); if (detailedTableInfo.columns) { const columns: ColumnInfo[] = detailedTableInfo.columns.map((col: any) => ({ name: col.name, type: col.dataType || col.type, nullable: col.isNullable, defaultValue: col.defaultValue, isPrimaryKey: col.isPrimaryKey || false, isForeignKey: col.isForeignKey || false, })); schema.addColumns(table.name, columns); } } catch (columnError) { this.logger.warn('Failed to get columns for table', { tableName: table.name, error: columnError, }); } } catch (tableError) { this.logger.error('Failed to process table', { table, error: tableError }); } } } else { this.logger.warn('No tables found from adapter', { tableCount: tables.length, }); } // Cache the schema this.schemaCache.set(databaseName, schema); this.logger.info('Schema refreshed successfully', { databaseName, tableCount: schema.getTableCount(), }); return schema; } catch (error) { this.logger.error('Failed to refresh schema', { databaseName, error: error instanceof Error ? error.message : String(error), }); throw error; } } clearCache(): void { this.schemaCache.clear(); this.logger.info('Schema cache cleared'); } }

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/ryudg/mcp-sql'

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