/**
* 통합 오류 처리 시스템
* MCP 서버의 모든 오류를 일관되게 처리하고 분류
*/
/**
* 데이터베이스 오류 타입
*/
export enum DatabaseErrorType {
INVALID_PATH = 'INVALID_PATH',
PERMISSION_DENIED = 'PERMISSION_DENIED',
DISK_FULL = 'DISK_FULL',
CORRUPTED_DATABASE = 'CORRUPTED_DATABASE',
CONNECTION_FAILED = 'CONNECTION_FAILED'
}
/**
* SQL 오류 타입
*/
export enum SQLErrorType {
SYNTAX_ERROR = 'SYNTAX_ERROR',
TABLE_NOT_EXISTS = 'TABLE_NOT_EXISTS',
COLUMN_NOT_EXISTS = 'COLUMN_NOT_EXISTS',
CONSTRAINT_VIOLATION = 'CONSTRAINT_VIOLATION',
TYPE_MISMATCH = 'TYPE_MISMATCH'
}
/**
* MCP 프로토콜 오류 타입
*/
export enum MCPErrorType {
INVALID_REQUEST = 'INVALID_REQUEST',
TOOL_NOT_FOUND = 'TOOL_NOT_FOUND',
INVALID_PARAMETERS = 'INVALID_PARAMETERS',
INTERNAL_ERROR = 'INTERNAL_ERROR'
}
/**
* 데이터베이스 오류 인터페이스
*/
export interface DatabaseError {
type: DatabaseErrorType;
message: string;
path: string;
originalError?: Error;
}
/**
* SQL 오류 인터페이스
*/
export interface SQLError {
type: SQLErrorType;
message: string;
query: string;
position?: number;
}
/**
* MCP 프로토콜 오류 인터페이스
*/
export interface MCPError {
type: MCPErrorType;
message: string;
code: number;
data?: any;
}
/**
* 통합 오류 처리 클래스
*/
export class ErrorHandler {
/**
* 데이터베이스 오류 분류 및 처리
*/
static handleDatabaseError(error: Error, path: string): DatabaseError {
const message = error.message.toLowerCase();
if (message.includes('no such file') || message.includes('cannot open')) {
return {
type: DatabaseErrorType.INVALID_PATH,
message: `데이터베이스 파일을 찾을 수 없습니다: ${path}`,
path,
originalError: error
};
}
if (message.includes('permission denied') || message.includes('access denied')) {
return {
type: DatabaseErrorType.PERMISSION_DENIED,
message: `데이터베이스 파일에 대한 접근 권한이 없습니다: ${path}`,
path,
originalError: error
};
}
if (message.includes('disk full') || message.includes('no space')) {
return {
type: DatabaseErrorType.DISK_FULL,
message: `디스크 공간이 부족합니다: ${path}`,
path,
originalError: error
};
}
if (message.includes('corrupt') || message.includes('malformed')) {
return {
type: DatabaseErrorType.CORRUPTED_DATABASE,
message: `데이터베이스 파일이 손상되었습니다: ${path}`,
path,
originalError: error
};
}
return {
type: DatabaseErrorType.CONNECTION_FAILED,
message: `데이터베이스 연결에 실패했습니다: ${error.message}`,
path,
originalError: error
};
}
/**
* SQL 오류 분류 및 처리
*/
static handleSQLError(error: Error, query: string): SQLError {
const message = error.message.toLowerCase();
if (message.includes('syntax error') || message.includes('near')) {
return {
type: SQLErrorType.SYNTAX_ERROR,
message: `SQL 구문 오류: ${error.message}`,
query
};
}
if (message.includes('no such table')) {
return {
type: SQLErrorType.TABLE_NOT_EXISTS,
message: `테이블이 존재하지 않습니다: ${error.message}`,
query
};
}
if (message.includes('no such column')) {
return {
type: SQLErrorType.COLUMN_NOT_EXISTS,
message: `컬럼이 존재하지 않습니다: ${error.message}`,
query
};
}
if (message.includes('constraint') || message.includes('unique') || message.includes('foreign key')) {
return {
type: SQLErrorType.CONSTRAINT_VIOLATION,
message: `제약 조건 위반: ${error.message}`,
query
};
}
if (message.includes('type') || message.includes('affinity')) {
return {
type: SQLErrorType.TYPE_MISMATCH,
message: `데이터 타입 불일치: ${error.message}`,
query
};
}
return {
type: SQLErrorType.SYNTAX_ERROR,
message: `SQL 실행 오류: ${error.message}`,
query
};
}
/**
* MCP 프로토콜 오류 분류 및 처리
*/
static handleMCPError(error: Error, context?: any): MCPError {
const message = error.message.toLowerCase();
if (message.includes('tool not found') || message.includes('unknown tool')) {
return {
type: MCPErrorType.TOOL_NOT_FOUND,
message: `도구를 찾을 수 없습니다: ${error.message}`,
code: -32601,
data: context
};
}
if (message.includes('invalid parameters') || message.includes('validation')) {
return {
type: MCPErrorType.INVALID_PARAMETERS,
message: `잘못된 매개변수: ${error.message}`,
code: -32602,
data: context
};
}
if (message.includes('invalid request') || message.includes('malformed')) {
return {
type: MCPErrorType.INVALID_REQUEST,
message: `잘못된 요청: ${error.message}`,
code: -32600,
data: context
};
}
return {
type: MCPErrorType.INTERNAL_ERROR,
message: `내부 서버 오류: ${error.message}`,
code: -32603,
data: context
};
}
/**
* 오류를 MCP 응답 형식으로 변환
*/
static formatErrorResponse(error: DatabaseError | SQLError | MCPError): any {
return {
content: [
{
type: 'text' as const,
text: JSON.stringify({
success: false,
error: error.message,
errorType: error.type,
...(('code' in error) && { code: error.code }),
...(('path' in error) && { path: error.path }),
...(('query' in error) && { query: error.query })
}, null, 2)
}
],
isError: true
};
}
/**
* 일반 오류를 처리하고 적절한 형식으로 변환
*/
static handleGenericError(error: unknown, context?: { path?: string; query?: string; tool?: string }): any {
if (!(error instanceof Error)) {
return this.formatErrorResponse({
type: MCPErrorType.INTERNAL_ERROR,
message: `알 수 없는 오류: ${String(error)}`,
code: -32603
});
}
// 컨텍스트에 따라 적절한 오류 처리기 선택
if (context?.path && (error.message.includes('database') || error.message.includes('sqlite'))) {
const dbError = this.handleDatabaseError(error, context.path);
return this.formatErrorResponse(dbError);
}
if (context?.query) {
const sqlError = this.handleSQLError(error, context.query);
return this.formatErrorResponse(sqlError);
}
if (context?.tool) {
const mcpError = this.handleMCPError(error, context);
return this.formatErrorResponse(mcpError);
}
// 기본 MCP 오류로 처리
const mcpError = this.handleMCPError(error);
return this.formatErrorResponse(mcpError);
}
/**
* 오류 로깅
*/
static logError(error: DatabaseError | SQLError | MCPError, context?: any): void {
const timestamp = new Date().toISOString();
const logEntry = {
timestamp,
errorType: error.type,
message: error.message,
context,
...(('originalError' in error) && error.originalError && {
stack: error.originalError.stack
})
};
console.error('SQLite MCP Server Error:', JSON.stringify(logEntry, null, 2));
}
}