UNPKG

@bluspace/mssql-mcp-server

Version:

Microsoft SQL Server MCP (Model Context Protocol) Server - AI-powered database interaction tool

190 lines (189 loc) 8.13 kB
import { z } from "zod"; import sql from "mssql"; export function registerStoredProcedureTools(server, db) { // Execute stored procedure server.tool("executeStoredProcedure", "Execute a stored procedure with parameters", { procedure: z.string().describe("Stored procedure name (can include schema)"), parameters: z.record(z.any()).optional().describe("Input parameters as key-value pairs"), outputParameters: z.array(z.object({ name: z.string(), type: z.enum(["varchar", "nvarchar", "int", "bigint", "decimal", "float", "bit", "date", "datetime", "uniqueidentifier"]), size: z.number().optional() })).optional().describe("Output parameter definitions") }, async ({ procedure, parameters = {}, outputParameters = [] }) => { try { const request = new sql.Request(db); // Add input parameters for (const [name, value] of Object.entries(parameters)) { request.input(name, value); } // Add output parameters const outputParams = {}; for (const param of outputParameters) { let sqlType; switch (param.type) { case "varchar": sqlType = sql.VarChar(param.size || sql.MAX); break; case "nvarchar": sqlType = sql.NVarChar(param.size || sql.MAX); break; case "int": sqlType = sql.Int; break; case "bigint": sqlType = sql.BigInt; break; case "decimal": sqlType = sql.Decimal(18, 2); break; case "float": sqlType = sql.Float; break; case "bit": sqlType = sql.Bit; break; case "date": sqlType = sql.Date; break; case "datetime": sqlType = sql.DateTime; break; case "uniqueidentifier": sqlType = sql.UniqueIdentifier; break; } request.output(param.name, sqlType); } // Format procedure name with schema if needed const procName = procedure.includes('.') ? procedure : `dbo.${procedure}`; // Execute the stored procedure const result = await request.execute(procName); // Collect output parameters for (const param of outputParameters) { outputParams[param.name] = request.parameters[param.name].value; } return { content: [{ type: "text", text: JSON.stringify({ recordsets: result.recordsets, recordset: result.recordset, output: outputParams, returnValue: result.returnValue, rowsAffected: result.rowsAffected }) }] }; } catch (error) { console.error(`Error executing stored procedure ${procedure}:`, error); throw new Error(`Failed to execute stored procedure ${procedure}: ${error.message}`); } }); // List stored procedures server.tool("listStoredProcedures", "List available stored procedures", { schema: z.string().optional().describe("Schema name to filter by"), pattern: z.string().optional().describe("Name pattern to filter by (supports wildcards)") }, async ({ schema, pattern }) => { try { const request = new sql.Request(db); let query = ` SELECT ROUTINE_SCHEMA as schema_name, ROUTINE_NAME as procedure_name, CREATED as created_date, LAST_ALTERED as modified_date FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_TYPE = 'PROCEDURE' `; const conditions = []; let paramIndex = 1; if (schema) { conditions.push(`ROUTINE_SCHEMA = @param${paramIndex}`); request.input(`param${paramIndex}`, sql.NVarChar, schema); paramIndex++; } if (pattern) { conditions.push(`ROUTINE_NAME LIKE @param${paramIndex}`); request.input(`param${paramIndex}`, sql.NVarChar, pattern.replace('*', '%')); paramIndex++; } if (conditions.length > 0) { query += ` AND ${conditions.join(' AND ')}`; } query += ' ORDER BY ROUTINE_SCHEMA, ROUTINE_NAME'; const result = await request.query(query); return { content: [{ type: "text", text: JSON.stringify({ procedures: result.recordset }) }] }; } catch (error) { console.error("Error listing stored procedures:", error); throw new Error(`Failed to list stored procedures: ${error.message}`); } }); // Get stored procedure parameters server.tool("getStoredProcedureInfo", "Get detailed information about a stored procedure including parameters", { procedure: z.string().describe("Stored procedure name (can include schema)") }, async ({ procedure }) => { try { const request = new sql.Request(db); // Parse schema and procedure name const parts = procedure.split('.'); const procName = parts.length === 2 ? parts[1] : procedure; const schemaName = parts.length === 2 ? parts[0] : 'dbo'; request.input('schema', sql.NVarChar, schemaName); request.input('procedure', sql.NVarChar, procName); // Get procedure information const procInfo = await request.query(` SELECT ROUTINE_SCHEMA as schema_name, ROUTINE_NAME as procedure_name, ROUTINE_DEFINITION as definition, CREATED as created_date, LAST_ALTERED as modified_date FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_TYPE = 'PROCEDURE' AND ROUTINE_SCHEMA = @schema AND ROUTINE_NAME = @procedure `); // Get parameters const paramsInfo = await request.query(` SELECT PARAMETER_NAME as name, DATA_TYPE as type, PARAMETER_MODE as mode, CHARACTER_MAXIMUM_LENGTH as max_length, NUMERIC_PRECISION as precision, NUMERIC_SCALE as scale, ORDINAL_POSITION as position FROM INFORMATION_SCHEMA.PARAMETERS WHERE SPECIFIC_SCHEMA = @schema AND SPECIFIC_NAME = @procedure ORDER BY ORDINAL_POSITION `); if (procInfo.recordset.length === 0) { throw new Error(`Stored procedure ${procedure} not found`); } return { content: [{ type: "text", text: JSON.stringify({ procedure: procInfo.recordset[0], parameters: paramsInfo.recordset.filter(p => p.name !== null) // Filter out return value }) }] }; } catch (error) { console.error(`Error getting stored procedure info for ${procedure}:`, error); throw new Error(`Failed to get stored procedure info for ${procedure}: ${error.message}`); } }); }