@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
JavaScript
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}`);
}
});
}