use thiserror::Error;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Error)]
pub enum Error {
#[error("Transport error: {0}")]
Transport(#[from] TransportError),
#[error("Protocol error: {0}")]
Protocol(#[from] ProtocolError),
#[error("Serialization error: {0}")]
Serialization(#[from] serde_json::Error),
#[error("Invalid request: {0}")]
InvalidRequest(String),
#[error("Method not found: {0}")]
MethodNotFound(String),
#[error("Invalid parameters: {0}")]
InvalidParams(String),
#[error("Internal error: {0}")]
Internal(String),
#[error("Request cancelled")]
Cancelled,
#[error("Parse error: {0}")]
Parse(String),
#[error("Server error: {0}")]
Server(String),
#[error("Client error: {0}")]
Client(String),
#[error("Timeout")]
Timeout,
#[error("Connection closed")]
ConnectionClosed,
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("URL parse error: {0}")]
UrlParse(#[from] url::ParseError),
#[error("Security error: {0}")]
Security(String),
#[error("Configuration error: {0}")]
Configuration(String),
#[error("Resource access error: {0}")]
ResourceAccess(String),
#[error("Validation error: {0}")]
Validation(String),
#[error("Other error: {0}")]
Other(#[from] anyhow::Error),
}
#[derive(Debug, Error)]
pub enum TransportError {
#[error("Connection failed: {0}")]
ConnectionFailed(String),
#[error("Send failed: {0}")]
SendFailed(String),
#[error("Receive failed: {0}")]
ReceiveFailed(String),
#[error("Invalid message format")]
InvalidMessageFormat,
#[error("Transport not ready")]
NotReady,
#[error("Transport closed")]
Closed,
#[error("Authentication failed: {0}")]
AuthenticationFailed(String),
#[error("Invalid transport configuration: {0}")]
InvalidConfiguration(String),
#[error("Network error: {0}")]
NetworkError(String),
}
#[derive(Debug, Error)]
pub enum ProtocolError {
#[error("Capability not supported: {0}")]
CapabilityNotSupported(String),
#[error("Invalid capability negotiation")]
InvalidCapabilityNegotiation,
#[error("Unexpected message type")]
UnexpectedMessageType,
#[error("Invalid message sequence")]
InvalidMessageSequence,
#[error("Resource not found: {0}")]
ResourceNotFound(String),
#[error("Tool not found: {0}")]
ToolNotFound(String),
#[error("Prompt not found: {0}")]
PromptNotFound(String),
#[error("Permission denied")]
PermissionDenied,
#[error("Rate limit exceeded")]
RateLimitExceeded,
#[error("Unsupported protocol version: {0}")]
UnsupportedProtocolVersion(String),
#[error("Initialization failed: {0}")]
InitializationFailed(String),
#[error("Invalid session state: {0}")]
InvalidSessionState(String),
#[error("Missing required parameter: {0}")]
MissingParameter(String),
#[error("Invalid parameter value: {0}")]
InvalidParameterValue(String),
}
impl Error {
pub fn internal(msg: impl Into<String>) -> Self {
Self::Internal(msg.into())
}
pub fn transport(err: TransportError) -> Self {
Self::Transport(err)
}
pub fn protocol(err: ProtocolError) -> Self {
Self::Protocol(err)
}
pub fn security(msg: impl Into<String>) -> Self {
Self::Security(msg.into())
}
pub fn validation(msg: impl Into<String>) -> Self {
Self::Validation(msg.into())
}
pub fn resource_access(msg: impl Into<String>) -> Self {
Self::ResourceAccess(msg.into())
}
pub fn method_not_found(method: impl Into<String>) -> Self {
Self::MethodNotFound(method.into())
}
pub fn invalid_params(msg: impl Into<String>) -> Self {
Self::InvalidParams(msg.into())
}
pub fn not_found(msg: impl Into<String>) -> Self {
Self::ResourceAccess(msg.into())
}
pub fn operation_failed(msg: impl Into<String>) -> Self {
Self::Internal(msg.into())
}
pub fn resource_error(msg: impl Into<String>) -> Self {
Self::ResourceAccess(msg.into())
}
pub fn is_recoverable(&self) -> bool {
match self {
Self::Transport(TransportError::NotReady) => true,
Self::Transport(TransportError::Closed) => false,
Self::Transport(TransportError::NetworkError(_)) => true,
Self::ConnectionClosed => false,
Self::Cancelled => false,
Self::Timeout => true,
Self::Security(_) => false,
Self::Configuration(_) => false,
Self::Protocol(ProtocolError::RateLimitExceeded) => true,
_ => true,
}
}
pub fn json_rpc_code(&self) -> i32 {
match self {
Self::Parse(_) => -32700,
Self::InvalidRequest(_) => -32600,
Self::MethodNotFound(_) => -32601,
Self::InvalidParams(_) | Self::Validation(_) => -32602,
Self::Internal(_) => -32603,
Self::Protocol(ProtocolError::ToolNotFound(_)) => -32601,
Self::Protocol(ProtocolError::ResourceNotFound(_)) => -32601,
Self::Protocol(ProtocolError::PromptNotFound(_)) => -32601,
Self::Security(_) | Self::Protocol(ProtocolError::PermissionDenied) => -32000,
Self::Protocol(ProtocolError::RateLimitExceeded) => -32001,
Self::Timeout => -32002,
Self::ConnectionClosed => -32003,
_ => -32000, }
}
pub fn is_client_error(&self) -> bool {
matches!(
self,
Self::InvalidRequest(_)
| Self::MethodNotFound(_)
| Self::InvalidParams(_)
| Self::Parse(_)
| Self::Client(_)
| Self::Validation(_)
| Self::UrlParse(_)
| Self::Protocol(ProtocolError::UnsupportedProtocolVersion(_))
| Self::Protocol(ProtocolError::MissingParameter(_))
| Self::Protocol(ProtocolError::InvalidParameterValue(_))
)
}
}