Skip to main content

brainwires_mcp_server/
error.rs

1use brainwires_mcp::JsonRpcError;
2
3/// Errors that can occur in the agent network layer.
4#[derive(Debug, thiserror::Error)]
5pub enum AgentNetworkError {
6    /// JSON-RPC parse error.
7    #[error("Parse error: {0}")]
8    ParseError(String),
9    /// Requested method does not exist.
10    #[error("Method not found: {0}")]
11    MethodNotFound(String),
12    /// Invalid parameters supplied.
13    #[error("Invalid params: {0}")]
14    InvalidParams(String),
15    /// Internal server error.
16    #[error("Internal error: {0}")]
17    Internal(#[from] anyhow::Error),
18    /// Transport-level error.
19    #[error("Transport error: {0}")]
20    Transport(String),
21    /// Requested tool does not exist.
22    #[error("Tool not found: {0}")]
23    ToolNotFound(String),
24    /// Request was rate-limited.
25    #[error("Rate limited")]
26    RateLimited,
27    /// Request was not authorized.
28    #[error("Unauthorized")]
29    Unauthorized,
30}
31
32impl AgentNetworkError {
33    /// Convert to a JSON-RPC error with the appropriate code.
34    pub fn to_json_rpc_error(&self) -> JsonRpcError {
35        match self {
36            AgentNetworkError::ParseError(msg) => JsonRpcError {
37                code: -32700,
38                message: msg.clone(),
39                data: None,
40            },
41            AgentNetworkError::MethodNotFound(method) => JsonRpcError {
42                code: -32601,
43                message: format!("Method not found: {method}"),
44                data: None,
45            },
46            AgentNetworkError::InvalidParams(msg) => JsonRpcError {
47                code: -32602,
48                message: msg.clone(),
49                data: None,
50            },
51            AgentNetworkError::Internal(err) => JsonRpcError {
52                code: -32603,
53                message: err.to_string(),
54                data: None,
55            },
56            AgentNetworkError::Transport(msg) => JsonRpcError {
57                code: -32000,
58                message: format!("Transport error: {msg}"),
59                data: None,
60            },
61            AgentNetworkError::ToolNotFound(name) => JsonRpcError {
62                code: -32001,
63                message: format!("Tool not found: {name}"),
64                data: None,
65            },
66            AgentNetworkError::RateLimited => JsonRpcError {
67                code: -32002,
68                message: "Rate limited".to_string(),
69                data: None,
70            },
71            AgentNetworkError::Unauthorized => JsonRpcError {
72                code: -32003,
73                message: "Unauthorized".to_string(),
74                data: None,
75            },
76        }
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use super::*;
83
84    #[test]
85    fn parse_error_code_is_minus_32700() {
86        let err = AgentNetworkError::ParseError("bad JSON".to_string());
87        assert_eq!(err.to_json_rpc_error().code, -32700);
88    }
89
90    #[test]
91    fn method_not_found_code_is_minus_32601() {
92        let err = AgentNetworkError::MethodNotFound("unknownMethod".to_string());
93        let rpc = err.to_json_rpc_error();
94        assert_eq!(rpc.code, -32601);
95        assert!(rpc.message.contains("unknownMethod"));
96    }
97
98    #[test]
99    fn invalid_params_code_is_minus_32602() {
100        let err = AgentNetworkError::InvalidParams("missing field".to_string());
101        assert_eq!(err.to_json_rpc_error().code, -32602);
102    }
103
104    #[test]
105    fn internal_error_code_is_minus_32603() {
106        let err = AgentNetworkError::Internal(anyhow::anyhow!("database down"));
107        let rpc = err.to_json_rpc_error();
108        assert_eq!(rpc.code, -32603);
109        assert!(rpc.message.contains("database down"));
110    }
111
112    #[test]
113    fn transport_error_code_is_minus_32000() {
114        let err = AgentNetworkError::Transport("connection reset".to_string());
115        let rpc = err.to_json_rpc_error();
116        assert_eq!(rpc.code, -32000);
117        assert!(rpc.message.contains("connection reset"));
118    }
119
120    #[test]
121    fn tool_not_found_code_is_minus_32001() {
122        let err = AgentNetworkError::ToolNotFound("my_tool".to_string());
123        let rpc = err.to_json_rpc_error();
124        assert_eq!(rpc.code, -32001);
125        assert!(rpc.message.contains("my_tool"));
126    }
127
128    #[test]
129    fn rate_limited_code_is_minus_32002() {
130        let err = AgentNetworkError::RateLimited;
131        assert_eq!(err.to_json_rpc_error().code, -32002);
132    }
133
134    #[test]
135    fn unauthorized_code_is_minus_32003() {
136        let err = AgentNetworkError::Unauthorized;
137        assert_eq!(err.to_json_rpc_error().code, -32003);
138    }
139
140    #[test]
141    fn display_messages_are_non_empty() {
142        let errors: Vec<AgentNetworkError> = vec![
143            AgentNetworkError::ParseError("x".to_string()),
144            AgentNetworkError::MethodNotFound("m".to_string()),
145            AgentNetworkError::InvalidParams("p".to_string()),
146            AgentNetworkError::Transport("t".to_string()),
147            AgentNetworkError::ToolNotFound("tool".to_string()),
148            AgentNetworkError::RateLimited,
149            AgentNetworkError::Unauthorized,
150        ];
151        for e in errors {
152            assert!(!e.to_string().is_empty());
153        }
154    }
155}