Skip to main content

haematite_cli/mcp/
stdio_error.rs

1use super::{USAGE, adapter, rpc};
2use crate::client;
3use std::{fmt, io};
4
5#[derive(Debug)]
6pub enum RelayError {
7    Input(io::Error),
8    ReceiptOutput {
9        cause: io::Error,
10        response: Vec<u8>,
11    },
12    ResultEncoding {
13        refusal: rpc::Refusal,
14        result: adapter::ToolResult,
15    },
16    Encoding(serde_json::Error),
17    Adapter(adapter::EncodeError),
18    Arguments(&'static str),
19    ConfigRead(io::Error),
20    ConfigDecode(serde_json::Error),
21    Runtime(crate::serve::runtime::Refusal),
22    SocketCap(client::Ended),
23    CapNotRepresentable,
24}
25
26impl fmt::Display for RelayError {
27    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
28        // Diagnostics name the stage without exposing request or record text.
29        let code = match self {
30            Self::Input(_) => "mcp_input_read_failed",
31            Self::ReceiptOutput { .. } => "mcp_output_write_failed",
32            Self::ResultEncoding { .. } | Self::Encoding(_) => "mcp_response_encoding_failed",
33            Self::Adapter(_) => "mcp_result_encoding_failed",
34            Self::Arguments(why) => {
35                return write!(formatter, "mcp_arguments_invalid: {why}; {USAGE}");
36            }
37            Self::ConfigRead(_) => "mcp_config_read_failed",
38            Self::ConfigDecode(_) => "mcp_config_invalid",
39            Self::Runtime(error) => return fmt::Display::fmt(error, formatter),
40            Self::SocketCap(_) => "mcp_socket_cap_unavailable",
41            Self::CapNotRepresentable => "mcp_frame_cap_unrepresentable",
42        };
43        formatter.write_str(code)
44    }
45}
46
47impl std::error::Error for RelayError {
48    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
49        match self {
50            Self::Input(error) | Self::ConfigRead(error) => Some(error),
51            Self::Encoding(error) | Self::ConfigDecode(error) => Some(error),
52            Self::Adapter(error) => Some(error),
53            Self::Runtime(error) => Some(error),
54            Self::ReceiptOutput { cause, .. } => Some(cause),
55            Self::ResultEncoding { .. }
56            | Self::Arguments(_)
57            | Self::SocketCap(_)
58            | Self::CapNotRepresentable => None,
59        }
60    }
61}