use crate::error::TurboPropError;
use crate::mcp::protocol::{JsonRpcError, JsonRpcRequest, JsonRpcResponse};
use anyhow::Result;
use serde_json::Value;
use thiserror::Error;
pub type McpResult<T> = Result<T, McpError>;
#[derive(Error, Debug)]
pub enum McpError {
#[error("JSON-RPC protocol error: {message}")]
ProtocolError { message: String },
#[error("MCP server initialization failed: {reason}")]
ServerInitializationError { reason: String },
#[error("MCP transport error: {message}")]
TransportError { message: String },
#[error("MCP tool execution failed for '{tool_name}': {reason}")]
ToolExecutionError { tool_name: String, reason: String },
#[error("MCP configuration error: {message}")]
ConfigurationError { message: String },
#[error("MCP client capability error: {capability} not supported")]
UnsupportedCapability { capability: String },
#[error("Security validation failed: invalid path")]
InvalidPath,
#[error("Security validation failed: path traversal detected")]
PathTraversal,
#[error("Security validation failed: symbolic link attack detected")]
SymlinkAttack,
#[error("Security validation failed: query too long (max {max} characters)")]
QueryTooLong { max: usize },
#[error("Security validation failed: suspicious query pattern detected")]
SuspiciousQuery,
}
impl McpError {
pub fn protocol(message: impl Into<String>) -> Self {
Self::ProtocolError {
message: message.into(),
}
}
pub fn server_initialization(reason: impl Into<String>) -> Self {
Self::ServerInitializationError {
reason: reason.into(),
}
}
pub fn transport(message: impl Into<String>) -> Self {
Self::TransportError {
message: message.into(),
}
}
pub fn tool_execution(tool_name: impl Into<String>, reason: impl Into<String>) -> Self {
Self::ToolExecutionError {
tool_name: tool_name.into(),
reason: reason.into(),
}
}
pub fn configuration(message: impl Into<String>) -> Self {
Self::ConfigurationError {
message: message.into(),
}
}
pub fn unsupported_capability(capability: impl Into<String>) -> Self {
Self::UnsupportedCapability {
capability: capability.into(),
}
}
pub fn invalid_path() -> Self {
Self::InvalidPath
}
pub fn path_traversal() -> Self {
Self::PathTraversal
}
pub fn symlink_attack() -> Self {
Self::SymlinkAttack
}
pub fn query_too_long(max: usize) -> Self {
Self::QueryTooLong { max }
}
pub fn suspicious_query() -> Self {
Self::SuspiciousQuery
}
}
impl From<anyhow::Error> for McpError {
fn from(error: anyhow::Error) -> Self {
Self::ToolExecutionError {
tool_name: "unknown".to_string(),
reason: error.to_string(),
}
}
}
impl From<McpError> for TurboPropError {
fn from(error: McpError) -> Self {
TurboPropError::other(error.to_string())
}
}
impl From<McpError> for JsonRpcError {
fn from(error: McpError) -> Self {
match error {
McpError::ProtocolError { message } => JsonRpcError::invalid_request(message),
McpError::ServerInitializationError { reason } => JsonRpcError::internal_error(reason),
McpError::TransportError { message } => JsonRpcError::internal_error(message),
McpError::ToolExecutionError {
tool_name: _,
reason,
} => JsonRpcError::application_error(-32001, reason),
McpError::ConfigurationError { message } => JsonRpcError::internal_error(message),
McpError::UnsupportedCapability { capability } => {
JsonRpcError::method_not_found(capability)
}
McpError::InvalidPath => JsonRpcError::invalid_params("Invalid path".to_string()),
McpError::PathTraversal => {
JsonRpcError::invalid_params("Path traversal detected".to_string())
}
McpError::SymlinkAttack => {
JsonRpcError::invalid_params("Symbolic link attack detected".to_string())
}
McpError::QueryTooLong { max } => {
JsonRpcError::invalid_params(format!("Query too long (max {} characters)", max))
}
McpError::SuspiciousQuery => {
JsonRpcError::invalid_params("Suspicious query pattern detected".to_string())
}
}
}
}
pub struct ErrorHandler;
impl ErrorHandler {
pub fn handle_internal_result(
request: &JsonRpcRequest,
result: Result<Value>,
) -> JsonRpcResponse {
match result {
Ok(value) => request.create_success_response(value),
Err(e) => {
let error = JsonRpcError::internal_error(format!("Internal error: {}", e));
request.create_error_response(error)
}
}
}
pub fn handle_mcp_result(
request: &JsonRpcRequest,
result: McpResult<Value>,
) -> JsonRpcResponse {
match result {
Ok(value) => request.create_success_response(value),
Err(mcp_error) => {
let json_rpc_error = JsonRpcError::from(mcp_error);
request.create_error_response(json_rpc_error)
}
}
}
pub fn handle_tool_execution_error(
request: &JsonRpcRequest,
tool_name: &str,
error: anyhow::Error,
) -> JsonRpcResponse {
let mcp_error = McpError::tool_execution(tool_name, error.to_string());
let json_rpc_error = JsonRpcError::from(mcp_error);
request.create_error_response(json_rpc_error)
}
pub fn handle_configuration_error(
request: &JsonRpcRequest,
message: String,
) -> JsonRpcResponse {
let mcp_error = McpError::configuration(message);
let json_rpc_error = JsonRpcError::from(mcp_error);
request.create_error_response(json_rpc_error)
}
pub fn handle_security_error(
request: &JsonRpcRequest,
security_error: McpError,
) -> JsonRpcResponse {
let json_rpc_error = JsonRpcError::from(security_error);
request.create_error_response(json_rpc_error)
}
pub fn handle_internal_result_with_context(
request: &JsonRpcRequest,
result: Result<Value>,
context: &str,
) -> JsonRpcResponse {
match result {
Ok(value) => request.create_success_response(value),
Err(e) => {
let error = JsonRpcError::internal_error(format!("{}: {}", context, e));
request.create_error_response(error)
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mcp_error_constructors() {
let protocol_error = McpError::protocol("Invalid JSON-RPC format");
matches!(protocol_error, McpError::ProtocolError { .. });
let tool_error = McpError::tool_execution("search", "Parameter validation failed");
matches!(tool_error, McpError::ToolExecutionError { .. });
}
#[test]
fn test_mcp_to_turboprop_error_conversion() {
let mcp_error = McpError::protocol("Test error");
let turboprop_error: TurboPropError = mcp_error.into();
matches!(turboprop_error, TurboPropError::Other { .. });
}
}