@@ -7,7 +7,7 @@ use uuid::Uuid;
77/// This module defines a hierarchy of error types that provide
88/// rich context for debugging and monitoring while maintaining
99/// security by avoiding sensitive data exposure.
10- #[ derive( Error , Debug ) ]
10+ #[ derive( Error , Debug , Clone , PartialEq ) ]
1111pub enum McpError {
1212 /// Client-side errors (invalid input, malformed requests)
1313 #[ error( "Client error: {message}" ) ]
@@ -23,7 +23,7 @@ pub enum McpError {
2323 message : String ,
2424 request_id : Option < Uuid > ,
2525 method : Option < String > ,
26- source : Option < Box < dyn std :: error:: Error + Send + Sync > > ,
26+ source_message : Option < String > , // Store source error as string for Clone/PartialEq
2727 } ,
2828
2929 /// RPC-specific errors (Solana client failures)
@@ -33,7 +33,7 @@ pub enum McpError {
3333 request_id : Option < Uuid > ,
3434 method : Option < String > ,
3535 rpc_url : Option < String > ,
36- source : Option < Box < dyn std :: error:: Error + Send + Sync > > ,
36+ source_message : Option < String > , // Store source error as string for Clone/PartialEq
3737 } ,
3838
3939 /// Validation errors (invalid parameters, security checks)
@@ -79,7 +79,7 @@ impl McpError {
7979 message : message. into ( ) ,
8080 request_id : None ,
8181 method : None ,
82- source : None ,
82+ source_message : None ,
8383 }
8484 }
8585
@@ -90,7 +90,7 @@ impl McpError {
9090 request_id : None ,
9191 method : None ,
9292 rpc_url : None ,
93- source : None ,
93+ source_message : None ,
9494 }
9595 }
9696
@@ -176,9 +176,10 @@ impl McpError {
176176
177177 /// Adds source error context
178178 pub fn with_source ( mut self , source : Box < dyn std:: error:: Error + Send + Sync > ) -> Self {
179+ let source_message = source. to_string ( ) ;
179180 match & mut self {
180- McpError :: Server { source : ref mut s, .. } => * s = Some ( source ) ,
181- McpError :: Rpc { source : ref mut s, .. } => * s = Some ( source ) ,
181+ McpError :: Server { source_message : ref mut s, .. } => * s = Some ( source_message ) ,
182+ McpError :: Rpc { source_message : ref mut s, .. } => * s = Some ( source_message ) ,
182183 _ => { } , // Other error types don't have source fields
183184 }
184185 self
@@ -253,19 +254,27 @@ impl McpError {
253254 log_data. insert ( "parameter" . to_string ( ) , Value :: String ( param. clone ( ) ) ) ;
254255 }
255256 } ,
256- McpError :: Rpc { rpc_url, .. } => {
257+ McpError :: Rpc { rpc_url, source_message , .. } => {
257258 if let Some ( url) = rpc_url {
258259 // Sanitize URL for logging
259260 let sanitized = crate :: validation:: sanitize_for_logging ( url) ;
260261 log_data. insert ( "rpc_url" . to_string ( ) , Value :: String ( sanitized) ) ;
261262 }
263+ if let Some ( source_msg) = source_message {
264+ log_data. insert ( "source_error" . to_string ( ) , Value :: String ( source_msg. clone ( ) ) ) ;
265+ }
262266 } ,
263267 McpError :: Network { endpoint, .. } => {
264268 if let Some ( ep) = endpoint {
265269 let sanitized = crate :: validation:: sanitize_for_logging ( ep) ;
266270 log_data. insert ( "endpoint" . to_string ( ) , Value :: String ( sanitized) ) ;
267271 }
268272 } ,
273+ McpError :: Server { source_message, .. } => {
274+ if let Some ( source_msg) = source_message {
275+ log_data. insert ( "source_error" . to_string ( ) , Value :: String ( source_msg. clone ( ) ) ) ;
276+ }
277+ } ,
269278 _ => { }
270279 }
271280
@@ -352,4 +361,26 @@ mod tests {
352361 assert ! ( log_value. get( "method" ) . is_some( ) ) ;
353362 assert ! ( log_value. get( "rpc_url" ) . is_some( ) ) ;
354363 }
364+
365+ #[ test]
366+ fn test_derived_traits ( ) {
367+ let request_id = Uuid :: new_v4 ( ) ;
368+ let error1 = McpError :: validation ( "Invalid pubkey format" )
369+ . with_request_id ( request_id)
370+ . with_method ( "getBalance" )
371+ . with_parameter ( "pubkey" ) ;
372+
373+ // Test Clone
374+ let error2 = error1. clone ( ) ;
375+ assert_eq ! ( error1. request_id( ) , error2. request_id( ) ) ;
376+ assert_eq ! ( error1. method( ) , error2. method( ) ) ;
377+ assert_eq ! ( error1. error_type( ) , error2. error_type( ) ) ;
378+
379+ // Test PartialEq
380+ assert_eq ! ( error1, error2) ;
381+
382+ // Test that different errors are not equal
383+ let error3 = McpError :: client ( "Different error" ) ;
384+ assert_ne ! ( error1, error3) ;
385+ }
355386}
0 commit comments