Skip to main content
Glama

MCP SQL Server

by JubinSaniei
server.ts8.38 kB
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { z } from "zod"; import { DatabaseService, SqlConfig, QueryResult, StoredProcedureResult } from './DatabaseService.js'; // Import DatabaseService, SqlConfig, and result types import { MssqlMcpError, ErrorType, ErrorDetails } from './errors.js'; // Import ErrorDetails import { sqlConfig } from "./config.js"; import pino from "pino"; // Define mcpConfig for server name and version const mcpConfig = { name: "mssql-mcp", version: "1.0.1" }; // Initialize Pino logger const loggerOptions = { level: (sqlConfig as SqlConfig).logLevel || 'info', // Pino's default timestamp is good, serializers can be added if needed }; // pino.destination(2) creates a SonicBoom instance for stderr const logger = (pino as any)(loggerOptions, pino.destination(2)); // Create an MCP server const server = new McpServer({ name: mcpConfig.name, version: mcpConfig.version }); // Instantiate DatabaseService - will be initialized in main() let databaseService: DatabaseService; // SQL query execution tool server.tool( "execute_query", { query: z.string().describe("SQL query to execute"), database: z.string().optional().describe("Target database name") }, async (args: { query: string; database?: string }, context) => { logger.info({ tool: 'execute_query', arguments: args, context }, 'MCP execute_query tool received request'); const { query, database: rawDatabaseArg } = args; try { const result: QueryResult = await databaseService.executeQuery(query, rawDatabaseArg); // USE QueryResult type logger.info({ result }, 'Query executed successfully'); return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }] }; } catch (error: unknown) { logger.error({ err: error, query, database: rawDatabaseArg }, 'Error in execute_query tool handler'); const mcpError = MssqlMcpError.fromError(error, ErrorType.QUERY_ERROR, { tool: 'execute_query', query, database: rawDatabaseArg } as ErrorDetails); return { content: [{ type: "text" as const, text: JSON.stringify({ error: mcpError.message, errorType: mcpError.errorType, details: mcpError.details }, null, 2) }] }; } } ); // Stored procedure execution tool server.tool( "execute_StoredProcedure", { procedure: z.string().describe("Stored procedure name to execute"), parameters: z.array( z.object({ name: z.string().describe("Parameter name"), type: z.string().describe("SQL parameter type (e.g., 'VarChar', 'Int')"), value: z.any().optional().describe("Parameter value") }) ).optional().describe("Parameters for the stored procedure"), database: z.string().optional().describe("Target database name") }, async (args: { procedure: string; parameters?: Array<{ name: string; type: string; value?: any }>; database?: string }, context) => { const { procedure, parameters = [], database: rawDatabaseArg } = args; try { const result: StoredProcedureResult = await databaseService.executeStoredProcedure(procedure, parameters, rawDatabaseArg); // USE StoredProcedureResult type return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }] }; } catch (error: unknown) { logger.error({ err: error, procedure, database: rawDatabaseArg }, 'Error in execute_StoredProcedure tool handler'); const mcpError = MssqlMcpError.fromError(error, ErrorType.STORED_PROCEDURE_ERROR, { tool: 'execute_StoredProcedure', procedure, database: rawDatabaseArg } as ErrorDetails); return { content: [{ type: "text" as const, text: JSON.stringify({ error: mcpError.message, errorType: mcpError.errorType, details: mcpError.details }, null, 2) }] }; } } ); // Database schema resource server.resource( "schema", new ResourceTemplate("schema://{database}", { list: undefined }), async (uri, params: { database?: string | string[] }, context) => { const dbParam = params.database; const dbIdentifier = Array.isArray(dbParam) ? dbParam[0] || (sqlConfig as SqlConfig).database : (dbParam || (sqlConfig as SqlConfig).database); try { const tables = await databaseService.getSchema(dbIdentifier); return { contents: [{ uri: uri.href, text: JSON.stringify({ tables }, null, 2) }] }; } catch (error: unknown) { logger.error({ err: error, database: dbIdentifier }, 'Error in schema resource handler'); const mcpError = MssqlMcpError.fromError(error, ErrorType.SCHEMA_ERROR, { resource: 'schema', database: dbIdentifier } as ErrorDetails); return { contents: [{ uri: uri.href, text: JSON.stringify({ error: mcpError.message, errorType: mcpError.errorType, details: mcpError.details }, null, 2) }] }; } } ); // Create transport with debug logging const transport = new StdioServerTransport(); async function cleanup() { logger.info('Shutting down server, cleaning up resources...'); if (databaseService) { await databaseService.closePool(); } logger.info('Cleanup complete'); return true; } process.on('SIGINT', async () => { logger.info('Received SIGINT signal'); const cleanupSuccess = await cleanup(); process.exit(cleanupSuccess ? 0 : 1); }); process.on('SIGTERM', async () => { logger.info('Received SIGTERM signal'); const cleanupSuccess = await cleanup(); process.exit(cleanupSuccess ? 0 : 1); }); async function main() { logger.info('Starting MSSQL MCP server...'); const typedSqlConfig: SqlConfig = sqlConfig as SqlConfig; logger.info({ server: typedSqlConfig.server, port: typedSqlConfig.port, database: typedSqlConfig.database, logLevel: typedSqlConfig.logLevel }, 'SQL Server configuration loaded'); databaseService = new DatabaseService(typedSqlConfig, logger); process.on('uncaughtException', (error: Error) => { logger.fatal({ err: error }, 'UNCAUGHT EXCEPTION'); cleanup().finally(() => process.exit(1)); }); process.on('unhandledRejection', (reason: unknown, promise: Promise<any>) => { logger.fatal({ reason, promise }, 'UNHANDLED REJECTION'); cleanup().finally(() => process.exit(1)); }); try { logger.info('Initializing database connection pool via DatabaseService...'); const pool = await databaseService.getPool(); if (pool && pool.connected) { logger.info('Successfully established connection pool via DatabaseService.'); await pool.request().query('SELECT 1 AS initial_connection_test'); logger.info('Connection pool verified with test query via DatabaseService.'); } else { // This case should ideally not be reached if getPool throws on failure as intended. logger.fatal('Critical: Failed to establish connection pool via DatabaseService (pool not connected after getPool resolved).'); await cleanup(); process.exit(1); } } catch (error) { logger.fatal({ err: error }, 'Critical error during initial pool connection via DatabaseService'); await cleanup(); process.exit(1); } try { server.connect(transport); logger.info('MCP server ready'); logger.info('Using DatabaseService for MSSQL operations.'); logger.info({ tools: ['execute_query', 'execute_StoredProcedure'], resources: ['schema://{database}'] }, 'Available MCP tools and resources'); logger.info('MCP server is listening on stdio...'); setInterval(() => { logger.debug('MCP process kept alive by interval'); }, 60000); // Added to keep process alive } catch (error: unknown) { logger.fatal({ err: error }, 'Critical: Failed to start MCP server transport'); await cleanup(); process.exit(1); } } main().catch(async (error: unknown) => { logger.fatal({ err: error }, "Critical: Unhandled error in main function execution"); await cleanup(); process.exit(1); });

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/JubinSaniei/mcp-mssql'

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