Skip to main content

lemma/mcp/
error.rs

1use crate::error::{EngineError, Error, ErrorKind, RequestErrorKind};
2
3/// Expected MCP tool failure. Unexpected engine invariants still panic at the call site.
4#[derive(Debug, Clone, PartialEq, Eq)]
5pub enum ToolError {
6    InvalidArguments(String),
7    NotFound(String),
8    Diagnostics(String),
9}
10
11impl ToolError {
12    pub fn invalid_arguments(message: impl Into<String>) -> Self {
13        Self::InvalidArguments(message.into())
14    }
15
16    pub fn not_found(message: impl Into<String>) -> Self {
17        Self::NotFound(message.into())
18    }
19
20    pub fn diagnostics(errors: &[Error]) -> Self {
21        let diagnostics: Vec<EngineError> = errors.iter().map(EngineError::from).collect();
22        let text = serde_json::to_string_pretty(&diagnostics)
23            .expect("BUG: EngineError diagnostics must serialize");
24        Self::Diagnostics(text)
25    }
26}
27
28impl std::fmt::Display for ToolError {
29    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30        match self {
31            Self::InvalidArguments(message)
32            | Self::NotFound(message)
33            | Self::Diagnostics(message) => f.write_str(message),
34        }
35    }
36}
37
38/// Unknown or malformed resource URI.
39#[derive(Debug, Clone, PartialEq, Eq)]
40pub enum ResourceError {
41    UnknownUri(String),
42}
43
44impl std::fmt::Display for ResourceError {
45    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46        match self {
47            Self::UnknownUri(message) => f.write_str(message),
48        }
49    }
50}
51
52pub fn map_engine_error(error: Error) -> ToolError {
53    match error.request_kind() {
54        Some(RequestErrorKind::SpecNotFound) => ToolError::not_found(error.message()),
55        Some(RequestErrorKind::RuleNotFound) | Some(RequestErrorKind::InvalidRequest) => {
56            ToolError::invalid_arguments(error.message())
57        }
58        None => match error.kind() {
59            ErrorKind::MissingRepository => ToolError::not_found(error.message()),
60            ErrorKind::Request => ToolError::not_found(error.message()),
61            ErrorKind::Parsing
62            | ErrorKind::Validation
63            | ErrorKind::Inversion
64            | ErrorKind::Registry
65            | ErrorKind::ResourceLimit => ToolError::invalid_arguments(error.message()),
66        },
67    }
68}