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    /// A network store's trust roots, endpoint or TLS refused, named by its
25    /// cause (HAEMNET-003 R2).
26    Network(String),
27}
28
29impl fmt::Display for RelayError {
30    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
31        // Diagnostics name the stage without exposing request or record text.
32        let code = match self {
33            Self::Input(_) => "mcp_input_read_failed",
34            Self::ReceiptOutput { .. } => "mcp_output_write_failed",
35            Self::ResultEncoding { .. } | Self::Encoding(_) => "mcp_response_encoding_failed",
36            Self::Adapter(_) => "mcp_result_encoding_failed",
37            Self::Arguments(why) => {
38                return write!(formatter, "mcp_arguments_invalid: {why}; {USAGE}");
39            }
40            Self::ConfigRead(_) => "mcp_config_read_failed",
41            Self::ConfigDecode(_) => "mcp_config_invalid",
42            Self::Runtime(error) => return fmt::Display::fmt(error, formatter),
43            Self::SocketCap(_) => "mcp_socket_cap_unavailable",
44            Self::CapNotRepresentable => "mcp_frame_cap_unrepresentable",
45            Self::Network(why) => return write!(formatter, "mcp_store_unreached: {why}"),
46        };
47        formatter.write_str(code)
48    }
49}
50
51impl std::error::Error for RelayError {
52    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
53        match self {
54            Self::Input(error) | Self::ConfigRead(error) => Some(error),
55            Self::Encoding(error) | Self::ConfigDecode(error) => Some(error),
56            Self::Adapter(error) => Some(error),
57            Self::Runtime(error) => Some(error),
58            Self::ReceiptOutput { cause, .. } => Some(cause),
59            Self::ResultEncoding { .. }
60            | Self::Arguments(_)
61            | Self::SocketCap(_)
62            | Self::CapNotRepresentable
63            | Self::Network(_) => None,
64        }
65    }
66}