@@ -3,6 +3,8 @@ use std::net::AddrParseError;
33use axum:: { http:: StatusCode , response:: IntoResponse } ;
44use thiserror:: Error ;
55
6+ use crate :: mcp_http:: McpHttpError ;
7+
68#[ cfg( feature = "auth" ) ]
79use crate :: auth:: AuthenticationError ;
810
@@ -41,3 +43,262 @@ impl IntoResponse for TransportServerError {
4143 response
4244 }
4345}
46+
47+ impl From < McpHttpError > for TransportServerError {
48+ fn from ( err : McpHttpError ) -> Self {
49+ match err {
50+ McpHttpError :: SessionIdMissing => TransportServerError :: SessionIdMissing ,
51+ McpHttpError :: SessionIdInvalid ( s) => TransportServerError :: SessionIdInvalid ( s) ,
52+ McpHttpError :: StreamIoError ( s) => TransportServerError :: StreamIoError ( s) ,
53+ McpHttpError :: HttpError ( s) => TransportServerError :: HttpError ( s) ,
54+ McpHttpError :: TransportError ( s) => TransportServerError :: TransportError ( s) ,
55+ }
56+ }
57+ }
58+
59+ impl From < TransportServerError > for McpHttpError {
60+ fn from ( err : TransportServerError ) -> Self {
61+ match err {
62+ TransportServerError :: SessionIdMissing => McpHttpError :: SessionIdMissing ,
63+ TransportServerError :: SessionIdInvalid ( s) => McpHttpError :: SessionIdInvalid ( s) ,
64+ TransportServerError :: StreamIoError ( s) => McpHttpError :: StreamIoError ( s) ,
65+ TransportServerError :: HttpError ( s) => McpHttpError :: HttpError ( s) ,
66+ TransportServerError :: TransportError ( s) => McpHttpError :: TransportError ( s) ,
67+
68+ #[ cfg( feature = "auth" ) ]
69+ TransportServerError :: AuthenticationError ( e) => McpHttpError :: HttpError ( e. to_string ( ) ) ,
70+
71+ TransportServerError :: AddrParseError ( e) => McpHttpError :: HttpError ( e. to_string ( ) ) ,
72+ TransportServerError :: ServerStartError ( s) => McpHttpError :: HttpError ( s) ,
73+ TransportServerError :: InvalidServerOptions ( s) => McpHttpError :: HttpError ( s) ,
74+ TransportServerError :: SslCertError ( s) => McpHttpError :: HttpError ( s) ,
75+ }
76+ }
77+ }
78+
79+ #[ cfg( test) ]
80+ mod tests {
81+ use super :: * ;
82+ use crate :: mcp_http:: McpHttpResult ;
83+
84+ // McpHttpError to TransportServerError
85+
86+ #[ test]
87+ fn mcp_to_transport_session_id_missing ( ) {
88+ let m = McpHttpError :: SessionIdMissing ;
89+ let t: TransportServerError = m. into ( ) ;
90+ assert ! ( matches!( t, TransportServerError :: SessionIdMissing ) ) ;
91+ assert_eq ! ( format!( "{}" , t) , "'sessionId' query string is missing!" ) ;
92+ }
93+
94+ #[ test]
95+ fn mcp_to_transport_session_id_invalid ( ) {
96+ let m = McpHttpError :: SessionIdInvalid ( "s1" . into ( ) ) ;
97+ let t: TransportServerError = m. into ( ) ;
98+ assert ! ( matches!( t, TransportServerError :: SessionIdInvalid ( ref s) if s == "s1" ) ) ;
99+ assert_eq ! ( format!( "{}" , t) , "No session found for the given ID: s1." ) ;
100+ }
101+
102+ #[ test]
103+ fn mcp_to_transport_stream_io_error ( ) {
104+ let m = McpHttpError :: StreamIoError ( "io" . into ( ) ) ;
105+ let t: TransportServerError = m. into ( ) ;
106+ assert ! ( matches!( t, TransportServerError :: StreamIoError ( ref s) if s == "io" ) ) ;
107+ }
108+
109+ #[ test]
110+ fn mcp_to_transport_http_error ( ) {
111+ let m = McpHttpError :: HttpError ( "fail" . into ( ) ) ;
112+ let t: TransportServerError = m. into ( ) ;
113+ assert ! ( matches!( t, TransportServerError :: HttpError ( ref s) if s == "fail" ) ) ;
114+ }
115+
116+ #[ test]
117+ fn mcp_to_transport_transport_error ( ) {
118+ let m = McpHttpError :: TransportError ( "tcp" . into ( ) ) ;
119+ let t: TransportServerError = m. into ( ) ;
120+ assert ! ( matches!( t, TransportServerError :: TransportError ( ref s) if s == "tcp" ) ) ;
121+ }
122+
123+ // TransportServerError to McpHttpError (common variants)
124+
125+ #[ test]
126+ fn transport_to_mcp_session_id_missing ( ) {
127+ let t = TransportServerError :: SessionIdMissing ;
128+ let m: McpHttpError = t. into ( ) ;
129+ assert ! ( matches!( m, McpHttpError :: SessionIdMissing ) ) ;
130+ }
131+
132+ #[ test]
133+ fn transport_to_mcp_session_id_invalid ( ) {
134+ let t = TransportServerError :: SessionIdInvalid ( "s2" . into ( ) ) ;
135+ let m: McpHttpError = t. into ( ) ;
136+ assert ! ( matches!( m, McpHttpError :: SessionIdInvalid ( ref s) if s == "s2" ) ) ;
137+ }
138+
139+ #[ test]
140+ fn transport_to_mcp_stream_io_error ( ) {
141+ let t = TransportServerError :: StreamIoError ( "eof" . into ( ) ) ;
142+ let m: McpHttpError = t. into ( ) ;
143+ assert ! ( matches!( m, McpHttpError :: StreamIoError ( ref s) if s == "eof" ) ) ;
144+ }
145+
146+ #[ test]
147+ fn transport_to_mcp_http_error ( ) {
148+ let t = TransportServerError :: HttpError ( "gone" . into ( ) ) ;
149+ let m: McpHttpError = t. into ( ) ;
150+ assert ! ( matches!( m, McpHttpError :: HttpError ( ref s) if s == "gone" ) ) ;
151+ }
152+
153+ #[ test]
154+ fn transport_to_mcp_transport_error ( ) {
155+ let t = TransportServerError :: TransportError ( "tls" . into ( ) ) ;
156+ let m: McpHttpError = t. into ( ) ;
157+ assert ! ( matches!( m, McpHttpError :: TransportError ( ref s) if s == "tls" ) ) ;
158+ }
159+
160+ // TransportServerError to McpHttpError (lossy conversions)
161+
162+ #[ test]
163+ fn transport_to_mcp_addr_parse_lossy ( ) {
164+ use std:: net:: AddrParseError ;
165+ let parse_err: AddrParseError = ":::" . parse :: < std:: net:: IpAddr > ( ) . unwrap_err ( ) ;
166+ let t = TransportServerError :: AddrParseError ( parse_err) ;
167+ let m: McpHttpError = t. into ( ) ;
168+ assert ! ( matches!( m, McpHttpError :: HttpError ( ref s) if !s. is_empty( ) ) ) ;
169+ }
170+
171+ #[ test]
172+ fn transport_to_mcp_server_start_lossy ( ) {
173+ let t = TransportServerError :: ServerStartError ( "port in use" . into ( ) ) ;
174+ let m: McpHttpError = t. into ( ) ;
175+ assert ! ( matches!( m, McpHttpError :: HttpError ( ref s) if s == "port in use" ) ) ;
176+ }
177+
178+ #[ test]
179+ fn transport_to_mcp_invalid_options_lossy ( ) {
180+ let t = TransportServerError :: InvalidServerOptions ( "bad config" . into ( ) ) ;
181+ let m: McpHttpError = t. into ( ) ;
182+ assert ! ( matches!( m, McpHttpError :: HttpError ( ref s) if s == "bad config" ) ) ;
183+ }
184+
185+ #[ test]
186+ fn transport_to_mcp_ssl_cert_lossy ( ) {
187+ let t = TransportServerError :: SslCertError ( "cert expired" . into ( ) ) ;
188+ let m: McpHttpError = t. into ( ) ;
189+ assert ! ( matches!( m, McpHttpError :: HttpError ( ref s) if s == "cert expired" ) ) ;
190+ }
191+
192+ #[ cfg( feature = "auth" ) ]
193+ #[ test]
194+ fn transport_to_mcp_authentication_lossy ( ) {
195+ let auth_err = AuthenticationError :: InactiveToken ;
196+ let t = TransportServerError :: AuthenticationError ( auth_err) ;
197+ let m: McpHttpError = t. into ( ) ;
198+ assert ! ( matches!( m, McpHttpError :: HttpError ( ref s) if s. contains( "Inactive" ) ) ) ;
199+ }
200+
201+ // Round-trip: McpHttpError to TransportServerError to McpHttpError
202+
203+ #[ test]
204+ fn round_trip_session_id_missing ( ) {
205+ let m = McpHttpError :: SessionIdMissing ;
206+ let t: TransportServerError = m. clone ( ) . into ( ) ;
207+ let back: McpHttpError = t. into ( ) ;
208+ assert_eq ! ( format!( "{}" , m) , format!( "{}" , back) ) ;
209+ }
210+
211+ #[ test]
212+ fn round_trip_session_id_invalid ( ) {
213+ let m = McpHttpError :: SessionIdInvalid ( "round" . into ( ) ) ;
214+ let t: TransportServerError = m. clone ( ) . into ( ) ;
215+ let back: McpHttpError = t. into ( ) ;
216+ assert_eq ! ( format!( "{}" , m) , format!( "{}" , back) ) ;
217+ }
218+
219+ #[ test]
220+ fn round_trip_stream_io_error ( ) {
221+ let m = McpHttpError :: StreamIoError ( "pipe" . into ( ) ) ;
222+ let t: TransportServerError = m. clone ( ) . into ( ) ;
223+ let back: McpHttpError = t. into ( ) ;
224+ assert_eq ! ( format!( "{}" , m) , format!( "{}" , back) ) ;
225+ }
226+
227+ #[ test]
228+ fn round_trip_http_error ( ) {
229+ let m = McpHttpError :: HttpError ( "round" . into ( ) ) ;
230+ let t: TransportServerError = m. clone ( ) . into ( ) ;
231+ let back: McpHttpError = t. into ( ) ;
232+ assert_eq ! ( format!( "{}" , m) , format!( "{}" , back) ) ;
233+ }
234+
235+ #[ test]
236+ fn round_trip_transport_error ( ) {
237+ let m = McpHttpError :: TransportError ( "round" . into ( ) ) ;
238+ let t: TransportServerError = m. clone ( ) . into ( ) ;
239+ let back: McpHttpError = t. into ( ) ;
240+ assert_eq ! ( format!( "{}" , m) , format!( "{}" , back) ) ;
241+ }
242+
243+ // Round-trip: TransportServerError > McpHttpError > TransportServerError
244+
245+ #[ test]
246+ fn reverse_round_trip_session_id_missing ( ) {
247+ let t = TransportServerError :: SessionIdMissing ;
248+ let m: McpHttpError = t. clone ( ) . into ( ) ;
249+ let back: TransportServerError = m. into ( ) ;
250+ assert_eq ! ( format!( "{}" , t) , format!( "{}" , back) ) ;
251+ }
252+
253+ #[ test]
254+ fn reverse_round_trip_session_id_invalid ( ) {
255+ let t = TransportServerError :: SessionIdInvalid ( "rev" . into ( ) ) ;
256+ let m: McpHttpError = t. clone ( ) . into ( ) ;
257+ let back: TransportServerError = m. into ( ) ;
258+ assert_eq ! ( format!( "{}" , t) , format!( "{}" , back) ) ;
259+ }
260+
261+ #[ test]
262+ fn reverse_round_trip_stream_io_error ( ) {
263+ let t = TransportServerError :: StreamIoError ( "rev" . into ( ) ) ;
264+ let m: McpHttpError = t. clone ( ) . into ( ) ;
265+ let back: TransportServerError = m. into ( ) ;
266+ assert_eq ! ( format!( "{}" , t) , format!( "{}" , back) ) ;
267+ }
268+
269+ #[ test]
270+ fn reverse_round_trip_http_error ( ) {
271+ let t = TransportServerError :: HttpError ( "rev" . into ( ) ) ;
272+ let m: McpHttpError = t. clone ( ) . into ( ) ;
273+ let back: TransportServerError = m. into ( ) ;
274+ assert_eq ! ( format!( "{}" , t) , format!( "{}" , back) ) ;
275+ }
276+
277+ #[ test]
278+ fn reverse_round_trip_transport_error ( ) {
279+ let t = TransportServerError :: TransportError ( "rev" . into ( ) ) ;
280+ let m: McpHttpError = t. clone ( ) . into ( ) ;
281+ let back: TransportServerError = m. into ( ) ;
282+ assert_eq ! ( format!( "{}" , t) , format!( "{}" , back) ) ;
283+ }
284+
285+ #[ test]
286+ fn transport_result_from_mcp_http_error ( ) {
287+ let r: McpHttpResult < ( ) > = Err ( McpHttpError :: SessionIdMissing ) ;
288+ let t: TransportServerResult < ( ) > = r. map_err ( Into :: into) ;
289+ assert ! ( matches!(
290+ t. unwrap_err( ) ,
291+ TransportServerError :: SessionIdMissing
292+ ) ) ;
293+ }
294+
295+ #[ test]
296+ fn mcp_http_result_from_transport_error ( ) {
297+ let r: TransportServerResult < ( ) > = Err ( TransportServerError :: SessionIdInvalid ( "x" . into ( ) ) ) ;
298+ let m: McpHttpResult < ( ) > = r. map_err ( Into :: into) ;
299+ assert ! ( matches!(
300+ m. unwrap_err( ) ,
301+ McpHttpError :: SessionIdInvalid ( ref s) if s == "x"
302+ ) ) ;
303+ }
304+ }
0 commit comments