use serde::{Deserialize, Serialize};
use thiserror::Error;
pub type McpResult<T> = Result<T, McpError>;
#[derive(Error, Debug, Serialize, Deserialize)]
#[serde(tag = "error_type")]
pub enum McpError {
#[error("Parse error: {message}")]
ParseError {
message: String,
#[serde(skip_serializing_if = "Option::is_none")]
location: Option<Location>,
},
#[error("Analysis error: {message}")]
AnalysisError {
message: String,
#[serde(skip_serializing_if = "Option::is_none")]
location: Option<Location>,
},
#[error("Validation error in field '{field}': {message}")]
ValidationError { field: String, message: String },
#[error("Tool not found: {tool_name}")]
ToolNotFound { tool_name: String },
#[error("File not found: {path}")]
FileNotFound { path: String },
#[error("Symbol not found: {symbol}")]
SymbolNotFound { symbol: String },
#[error("Operation timed out after {duration_ms}ms")]
Timeout { duration_ms: u64 },
#[error("Internal error: {message}")]
InternalError { message: String },
#[error("JSON-RPC error: {message}")]
JsonRpcError { message: String },
#[error("IO error: {message}")]
IoError { message: String },
#[error("Authentication error: {message}")]
AuthenticationError { message: String },
#[error("Authorization error: {message}")]
AuthorizationError { message: String },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Location {
pub file: Option<String>,
pub line: usize,
pub column: usize,
}
impl Location {
pub fn new(file: Option<String>, line: usize, column: usize) -> Self {
Self { file, line, column }
}
}
impl From<anyhow::Error> for McpError {
fn from(err: anyhow::Error) -> Self {
McpError::InternalError {
message: err.to_string(),
}
}
}
impl From<std::io::Error> for McpError {
fn from(err: std::io::Error) -> Self {
McpError::IoError {
message: err.to_string(),
}
}
}
impl From<serde_json::Error> for McpError {
fn from(err: serde_json::Error) -> Self {
McpError::JsonRpcError {
message: err.to_string(),
}
}
}