Microsoft SQL Server MCP Server (MSSQL)

by dperussina
Verified
// lib/errors.js - Error handling utilities import { logger } from './logger.mjs'; /** * JSON-RPC 2.0 error codes * @see https://www.jsonrpc.org/specification#error_object */ export const JsonRpcErrorCodes = { PARSE_ERROR: -32700, // Invalid JSON was received INVALID_REQUEST: -32600, // The JSON sent is not a valid Request object METHOD_NOT_FOUND: -32601, // The method does not exist / is not available INVALID_PARAMS: -32602, // Invalid method parameter(s) INTERNAL_ERROR: -32603, // Internal JSON-RPC error // Server error codes (reserved from -32000 to -32099) SERVER_ERROR_START: -32099, SERVER_ERROR_END: -32000, // Custom error codes (below -32100) AUTHENTICATION_ERROR: -32100, AUTHORIZATION_ERROR: -32101, RATE_LIMIT_EXCEEDED: -32102, RESOURCE_NOT_FOUND: -32103, TOOL_EXECUTION_ERROR: -32104, DATABASE_ERROR: -32105, VALIDATION_ERROR: -32106 }; /** * Create a JSON-RPC 2.0 error object * @param {number} code - Error code * @param {string} message - Error message * @param {*} data - Additional data (optional) * @returns {object} - JSON-RPC error object */ export function createJsonRpcError(code, message, data = undefined) { const error = { code, message }; if (data !== undefined) { error.data = data; } return error; } /** * Create a standard error response for HTTP endpoints * @param {number} statusCode - HTTP status code * @param {string} message - Error message * @param {*} details - Additional details (optional) * @returns {object} - Error response object */ export function createErrorResponse(statusCode, message, details = undefined) { const response = { error: { status: statusCode, message } }; if (details !== undefined) { response.error.details = details; } return response; } /** * Get a readable error message from any error * @param {Error} error - Error object * @returns {string} - Human-readable error message */ export function getReadableErrorMessage(error) { if (!error) { return 'Unknown error occurred'; } // Handle specific types of errors if (error.code === 'ECONNREFUSED') { return 'Unable to connect to the database. Please check your database configuration.'; } if (error.code === 'ETIMEDOUT') { return 'Connection to the database timed out. Please try again later.'; } if (error.name === 'ValidationError') { return `Validation error: ${error.message}`; } if (error.name === 'SyntaxError' && error.message.includes('JSON')) { return 'Invalid JSON format in the request'; } return error.message || 'An unknown error occurred'; } /** * Custom error class for MCP server errors */ export class McpError extends Error { /** * Create a new MCP error * @param {string} message - Error message * @param {number} code - Error code * @param {*} data - Additional data */ constructor(message, code = JsonRpcErrorCodes.INTERNAL_ERROR, data = undefined) { super(message); this.name = 'McpError'; this.code = code; this.data = data; } /** * Convert to JSON-RPC error object * @returns {object} - JSON-RPC error object */ toJsonRpcError() { return createJsonRpcError(this.code, this.message, this.data); } } /** * Custom error for validation failures */ export class ValidationError extends McpError { /** * Create a new validation error * @param {string} message - Error message * @param {*} validationDetails - Validation details */ constructor(message, validationDetails = undefined) { super(message, JsonRpcErrorCodes.VALIDATION_ERROR, validationDetails); this.name = 'ValidationError'; } } /** * Custom error for resource not found */ export class ResourceNotFoundError extends McpError { /** * Create a new resource not found error * @param {string} resourceType - Type of resource * @param {string} resourceId - ID of the resource */ constructor(resourceType, resourceId) { super( `Resource not found: ${resourceType} with ID ${resourceId}`, JsonRpcErrorCodes.RESOURCE_NOT_FOUND, { resourceType, resourceId } ); this.name = 'ResourceNotFoundError'; } } /** * Custom error for tool execution failures */ export class ToolExecutionError extends McpError { /** * Create a new tool execution error * @param {string} toolName - Name of the tool * @param {string} message - Error message * @param {*} details - Additional details */ constructor(toolName, message, details = undefined) { super( `Tool execution failed: ${toolName} - ${message}`, JsonRpcErrorCodes.TOOL_EXECUTION_ERROR, { toolName, details } ); this.name = 'ToolExecutionError'; } } /** * Global error handler middleware for Express * @param {Error} err - Error object * @param {object} req - Express request * @param {object} res - Express response * @param {function} next - Express next function */ export function errorHandler(err, req, res, next) { // Log the error logger.error(`Error processing request: ${err.message}`, { stack: err.stack, path: req.path, method: req.method, requestId: req.id }); // Determine if this is a JSON-RPC request const isJsonRpc = req.headers['content-type']?.includes('application/json') && (req.body?.jsonrpc === '2.0' || req.body?.id); if (isJsonRpc) { // Handle as JSON-RPC error const jsonRpcId = req.body?.id || null; // Determine error code and message let errorCode = JsonRpcErrorCodes.INTERNAL_ERROR; let errorMessage = getReadableErrorMessage(err); let errorData = undefined; // Handle specific error types if (err instanceof McpError) { errorCode = err.code; errorData = err.data; } else if (err.name === 'SyntaxError' && err.message.includes('JSON')) { errorCode = JsonRpcErrorCodes.PARSE_ERROR; } else if (err.name === 'ValidationError') { errorCode = JsonRpcErrorCodes.INVALID_PARAMS; } // Send JSON-RPC error response res.status(200).json({ jsonrpc: '2.0', id: jsonRpcId, error: createJsonRpcError(errorCode, errorMessage, errorData) }); } else { // Handle as regular HTTP error const statusCode = err.statusCode || 500; // Send HTTP error response res.status(statusCode).json( createErrorResponse(statusCode, getReadableErrorMessage(err)) ); } }