From 7dfb73145a124b3a5f4fe59fcc1056fb21470322 Mon Sep 17 00:00:00 2001 From: nateGeorge Date: Wed, 12 Nov 2025 14:32:33 -0800 Subject: [PATCH] feat: Add configurable verbose error mode for AI debugging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Error messages were intentionally sanitized for security, making it impossible for AI agents to debug SQL command failures. Solution: Added VERBOSE_ERRORS environment variable that exposes full error details when enabled while keeping secure defaults. Changes: - New errorHandler utility with formatError() and formatSqlError() - SQL error details: number, state, class, lineNumber, serverName - Stack traces and original error context - Secure by default (VERBOSE_ERRORS not set) Files Added: - src/utils/errorHandler.ts (error formatting utilities) Files Modified: - src/index.ts (use formatError in main handler) - src/tools/ReadDataTool.ts (use formatSqlError for SQL errors) Usage: Set VERBOSE_ERRORS=true in MCP env config to enable full errors Benefits: ✅ AI agents can debug SQL syntax errors ✅ Full error context for troubleshooting ✅ Toggle on/off without code changes ✅ Secure defaults for production --- MssqlMcp/Node/src/index.ts | 5 +- MssqlMcp/Node/src/tools/ReadDataTool.ts | 18 +-- MssqlMcp/Node/src/utils/errorHandler.ts | 155 ++++++++++++++++++++++++ 3 files changed, 163 insertions(+), 15 deletions(-) create mode 100644 MssqlMcp/Node/src/utils/errorHandler.ts diff --git a/MssqlMcp/Node/src/index.ts b/MssqlMcp/Node/src/index.ts index 8dc1f30..42bdf71 100644 --- a/MssqlMcp/Node/src/index.ts +++ b/MssqlMcp/Node/src/index.ts @@ -138,8 +138,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], }; } catch (error) { + // Enhanced error handling with configurable verbosity + const { formatError } = await import("./utils/errorHandler.js"); + const errorDetails = formatError(error, `call tool '${name}'`, 'TOOL_EXECUTION_FAILED'); return { - content: [{ type: "text", text: `Error occurred: ${error}` }], + content: [{ type: "text", text: JSON.stringify(errorDetails, null, 2) }], isError: true, }; } diff --git a/MssqlMcp/Node/src/tools/ReadDataTool.ts b/MssqlMcp/Node/src/tools/ReadDataTool.ts index 49f4592..6a13558 100644 --- a/MssqlMcp/Node/src/tools/ReadDataTool.ts +++ b/MssqlMcp/Node/src/tools/ReadDataTool.ts @@ -204,8 +204,8 @@ export class ReadDataTool implements Tool { * @returns Query execution result */ async run(params: any) { + const { query } = params; // Extract query before try/catch for error handler access try { - const { query } = params; // Validate the query for security issues const validation = this.validateQuery(query); @@ -241,19 +241,9 @@ export class ReadDataTool implements Tool { }; } catch (error) { - console.error("Error executing query:", error); - - // Don't expose internal error details to prevent information leakage - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; - const safeErrorMessage = errorMessage.includes('Invalid object name') - ? errorMessage - : 'Database query execution failed'; - - return { - success: false, - message: `Failed to execute query: ${safeErrorMessage}`, - error: 'QUERY_EXECUTION_FAILED' - }; + // Use enhanced error handler with configurable verbosity + const { formatSqlError } = await import('../utils/errorHandler.js'); + return formatSqlError(error, query); } } } \ No newline at end of file diff --git a/MssqlMcp/Node/src/utils/errorHandler.ts b/MssqlMcp/Node/src/utils/errorHandler.ts new file mode 100644 index 0000000..ed15da2 --- /dev/null +++ b/MssqlMcp/Node/src/utils/errorHandler.ts @@ -0,0 +1,155 @@ +/** + * Error handling utilities for MSSQL MCP Server + * Provides configurable error verbosity for debugging + */ + +export interface DetailedError { + success: false; + message: string; + error: string; + details?: { + code?: string | number; + number?: number; + state?: any; + class?: number; + lineNumber?: number; + serverName?: string; + procName?: string; + stack?: string; + originalError?: any; + }; +} + +/** + * Check if verbose error mode is enabled + * Set environment variable: VERBOSE_ERRORS=true + */ +export function isVerboseErrorMode(): boolean { + return process.env.VERBOSE_ERRORS?.toLowerCase() === 'true'; +} + +/** + * Format error for MCP response with optional verbose details + * @param error The error object + * @param context Context message (e.g., "executing query", "connecting to database") + * @param errorCode Error code for the error type + * @returns Formatted error object + */ +export function formatError( + error: unknown, + context: string, + errorCode: string = 'ERROR' +): DetailedError { + const verbose = isVerboseErrorMode(); + + // Extract basic error message + let message = 'Unknown error occurred'; + let details: DetailedError['details'] = undefined; + + if (error instanceof Error) { + message = error.message; + + if (verbose) { + details = { + stack: error.stack + }; + + // Extract SQL-specific error properties if available + const sqlError = error as any; + if (sqlError.number !== undefined) details.number = sqlError.number; + if (sqlError.code !== undefined) details.code = sqlError.code; + if (sqlError.state !== undefined) details.state = sqlError.state; + if (sqlError.class !== undefined) details.class = sqlError.class; + if (sqlError.lineNumber !== undefined) details.lineNumber = sqlError.lineNumber; + if (sqlError.serverName !== undefined) details.serverName = sqlError.serverName; + if (sqlError.procName !== undefined) details.procName = sqlError.procName; + if (sqlError.originalError !== undefined) { + details.originalError = sqlError.originalError instanceof Error + ? sqlError.originalError.message + : String(sqlError.originalError); + } + } + } else if (typeof error === 'string') { + message = error; + } else if (error && typeof error === 'object') { + message = JSON.stringify(error); + if (verbose) { + details = { originalError: error }; + } + } + + // Build formatted error response + const result: DetailedError = { + success: false, + message: `Failed to ${context}: ${message}`, + error: errorCode + }; + + if (verbose && details && Object.keys(details).length > 0) { + result.details = details; + } + + // Log error for debugging (always logged to console) + console.error(`[${errorCode}] Error ${context}:`, error); + if (verbose && details) { + console.error('Error details:', JSON.stringify(details, null, 2)); + } + + return result; +} + +/** + * Format SQL error with enhanced debugging information + * @param error SQL error object + * @param query The SQL query that failed (optional, will be truncated) + * @returns Formatted error object + */ +export function formatSqlError( + error: unknown, + query?: string +): DetailedError { + const verbose = isVerboseErrorMode(); + const result = formatError(error, 'execute query', 'SQL_EXECUTION_FAILED'); + + // Add query context if in verbose mode + if (verbose && query && result.details) { + result.details.originalError = { + query: query.length > 500 ? query.substring(0, 500) + '...' : query, + ...(typeof result.details.originalError === 'object' ? result.details.originalError : {}) + }; + } + + return result; +} + +/** + * Safe error message for production (hides sensitive details) + * Used when VERBOSE_ERRORS is not enabled + * @param error The error object + * @param allowedPatterns Patterns that are safe to show (e.g., "Invalid object name") + * @returns Safe error message + */ +export function getSafeErrorMessage( + error: unknown, + allowedPatterns: string[] = ['Invalid object name', 'Invalid column name'] +): string { + if (isVerboseErrorMode()) { + // In verbose mode, show everything + if (error instanceof Error) { + return error.message; + } + return String(error); + } + + // In production mode, only show allowed patterns + const message = error instanceof Error ? error.message : String(error); + + for (const pattern of allowedPatterns) { + if (message.includes(pattern)) { + return message; + } + } + + // Hide error details for security + return 'Database operation failed. Enable VERBOSE_ERRORS=true for details.'; +}