Skip to content

Commit 7dfb731

Browse files
committed
feat: Add configurable verbose error mode for AI debugging
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
1 parent f723313 commit 7dfb731

File tree

3 files changed

+163
-15
lines changed

3 files changed

+163
-15
lines changed

MssqlMcp/Node/src/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
138138
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
139139
};
140140
} catch (error) {
141+
// Enhanced error handling with configurable verbosity
142+
const { formatError } = await import("./utils/errorHandler.js");
143+
const errorDetails = formatError(error, `call tool '${name}'`, 'TOOL_EXECUTION_FAILED');
141144
return {
142-
content: [{ type: "text", text: `Error occurred: ${error}` }],
145+
content: [{ type: "text", text: JSON.stringify(errorDetails, null, 2) }],
143146
isError: true,
144147
};
145148
}

MssqlMcp/Node/src/tools/ReadDataTool.ts

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -204,8 +204,8 @@ export class ReadDataTool implements Tool {
204204
* @returns Query execution result
205205
*/
206206
async run(params: any) {
207+
const { query } = params; // Extract query before try/catch for error handler access
207208
try {
208-
const { query } = params;
209209

210210
// Validate the query for security issues
211211
const validation = this.validateQuery(query);
@@ -241,19 +241,9 @@ export class ReadDataTool implements Tool {
241241
};
242242

243243
} catch (error) {
244-
console.error("Error executing query:", error);
245-
246-
// Don't expose internal error details to prevent information leakage
247-
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
248-
const safeErrorMessage = errorMessage.includes('Invalid object name')
249-
? errorMessage
250-
: 'Database query execution failed';
251-
252-
return {
253-
success: false,
254-
message: `Failed to execute query: ${safeErrorMessage}`,
255-
error: 'QUERY_EXECUTION_FAILED'
256-
};
244+
// Use enhanced error handler with configurable verbosity
245+
const { formatSqlError } = await import('../utils/errorHandler.js');
246+
return formatSqlError(error, query);
257247
}
258248
}
259249
}
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
/**
2+
* Error handling utilities for MSSQL MCP Server
3+
* Provides configurable error verbosity for debugging
4+
*/
5+
6+
export interface DetailedError {
7+
success: false;
8+
message: string;
9+
error: string;
10+
details?: {
11+
code?: string | number;
12+
number?: number;
13+
state?: any;
14+
class?: number;
15+
lineNumber?: number;
16+
serverName?: string;
17+
procName?: string;
18+
stack?: string;
19+
originalError?: any;
20+
};
21+
}
22+
23+
/**
24+
* Check if verbose error mode is enabled
25+
* Set environment variable: VERBOSE_ERRORS=true
26+
*/
27+
export function isVerboseErrorMode(): boolean {
28+
return process.env.VERBOSE_ERRORS?.toLowerCase() === 'true';
29+
}
30+
31+
/**
32+
* Format error for MCP response with optional verbose details
33+
* @param error The error object
34+
* @param context Context message (e.g., "executing query", "connecting to database")
35+
* @param errorCode Error code for the error type
36+
* @returns Formatted error object
37+
*/
38+
export function formatError(
39+
error: unknown,
40+
context: string,
41+
errorCode: string = 'ERROR'
42+
): DetailedError {
43+
const verbose = isVerboseErrorMode();
44+
45+
// Extract basic error message
46+
let message = 'Unknown error occurred';
47+
let details: DetailedError['details'] = undefined;
48+
49+
if (error instanceof Error) {
50+
message = error.message;
51+
52+
if (verbose) {
53+
details = {
54+
stack: error.stack
55+
};
56+
57+
// Extract SQL-specific error properties if available
58+
const sqlError = error as any;
59+
if (sqlError.number !== undefined) details.number = sqlError.number;
60+
if (sqlError.code !== undefined) details.code = sqlError.code;
61+
if (sqlError.state !== undefined) details.state = sqlError.state;
62+
if (sqlError.class !== undefined) details.class = sqlError.class;
63+
if (sqlError.lineNumber !== undefined) details.lineNumber = sqlError.lineNumber;
64+
if (sqlError.serverName !== undefined) details.serverName = sqlError.serverName;
65+
if (sqlError.procName !== undefined) details.procName = sqlError.procName;
66+
if (sqlError.originalError !== undefined) {
67+
details.originalError = sqlError.originalError instanceof Error
68+
? sqlError.originalError.message
69+
: String(sqlError.originalError);
70+
}
71+
}
72+
} else if (typeof error === 'string') {
73+
message = error;
74+
} else if (error && typeof error === 'object') {
75+
message = JSON.stringify(error);
76+
if (verbose) {
77+
details = { originalError: error };
78+
}
79+
}
80+
81+
// Build formatted error response
82+
const result: DetailedError = {
83+
success: false,
84+
message: `Failed to ${context}: ${message}`,
85+
error: errorCode
86+
};
87+
88+
if (verbose && details && Object.keys(details).length > 0) {
89+
result.details = details;
90+
}
91+
92+
// Log error for debugging (always logged to console)
93+
console.error(`[${errorCode}] Error ${context}:`, error);
94+
if (verbose && details) {
95+
console.error('Error details:', JSON.stringify(details, null, 2));
96+
}
97+
98+
return result;
99+
}
100+
101+
/**
102+
* Format SQL error with enhanced debugging information
103+
* @param error SQL error object
104+
* @param query The SQL query that failed (optional, will be truncated)
105+
* @returns Formatted error object
106+
*/
107+
export function formatSqlError(
108+
error: unknown,
109+
query?: string
110+
): DetailedError {
111+
const verbose = isVerboseErrorMode();
112+
const result = formatError(error, 'execute query', 'SQL_EXECUTION_FAILED');
113+
114+
// Add query context if in verbose mode
115+
if (verbose && query && result.details) {
116+
result.details.originalError = {
117+
query: query.length > 500 ? query.substring(0, 500) + '...' : query,
118+
...(typeof result.details.originalError === 'object' ? result.details.originalError : {})
119+
};
120+
}
121+
122+
return result;
123+
}
124+
125+
/**
126+
* Safe error message for production (hides sensitive details)
127+
* Used when VERBOSE_ERRORS is not enabled
128+
* @param error The error object
129+
* @param allowedPatterns Patterns that are safe to show (e.g., "Invalid object name")
130+
* @returns Safe error message
131+
*/
132+
export function getSafeErrorMessage(
133+
error: unknown,
134+
allowedPatterns: string[] = ['Invalid object name', 'Invalid column name']
135+
): string {
136+
if (isVerboseErrorMode()) {
137+
// In verbose mode, show everything
138+
if (error instanceof Error) {
139+
return error.message;
140+
}
141+
return String(error);
142+
}
143+
144+
// In production mode, only show allowed patterns
145+
const message = error instanceof Error ? error.message : String(error);
146+
147+
for (const pattern of allowedPatterns) {
148+
if (message.includes(pattern)) {
149+
return message;
150+
}
151+
}
152+
153+
// Hide error details for security
154+
return 'Database operation failed. Enable VERBOSE_ERRORS=true for details.';
155+
}

0 commit comments

Comments
 (0)