pulseengine_mcp_protocol/
error.rs

1//! Error types for the MCP protocol
2
3use serde::{Deserialize, Serialize};
4use std::fmt;
5
6/// Result type alias for MCP operations
7pub type Result<T> = std::result::Result<T, Error>;
8
9/// Core MCP error type
10#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, thiserror::Error)]
11pub struct Error {
12    /// Error code following MCP specification
13    pub code: ErrorCode,
14    /// Human-readable error message
15    pub message: String,
16    /// Optional additional error data
17    #[serde(skip_serializing_if = "Option::is_none")]
18    pub data: Option<serde_json::Value>,
19}
20
21impl fmt::Display for Error {
22    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23        write!(f, "{}: {}", self.code, self.message)
24    }
25}
26
27impl Error {
28    /// Create a new error with the given code and message
29    pub fn new(code: ErrorCode, message: impl Into<String>) -> Self {
30        Self {
31            code,
32            message: message.into(),
33            data: None,
34        }
35    }
36
37    /// Create an error with additional data
38    pub fn with_data(code: ErrorCode, message: impl Into<String>, data: serde_json::Value) -> Self {
39        Self {
40            code,
41            message: message.into(),
42            data: Some(data),
43        }
44    }
45
46    /// Create a parse error
47    pub fn parse_error(message: impl Into<String>) -> Self {
48        Self::new(ErrorCode::ParseError, message)
49    }
50
51    /// Create an invalid request error
52    pub fn invalid_request(message: impl Into<String>) -> Self {
53        Self::new(ErrorCode::InvalidRequest, message)
54    }
55
56    /// Create a method not found error
57    pub fn method_not_found(method: impl Into<String>) -> Self {
58        Self::new(
59            ErrorCode::MethodNotFound,
60            format!("Method not found: {}", method.into()),
61        )
62    }
63
64    /// Create an invalid params error
65    pub fn invalid_params(message: impl Into<String>) -> Self {
66        Self::new(ErrorCode::InvalidParams, message)
67    }
68
69    /// Create an internal error
70    pub fn internal_error(message: impl Into<String>) -> Self {
71        Self::new(ErrorCode::InternalError, message)
72    }
73
74    /// Create a protocol version mismatch error
75    pub fn protocol_version_mismatch(client_version: &str, server_version: &str) -> Self {
76        Self::with_data(
77            ErrorCode::InvalidRequest,
78            format!("Protocol version mismatch: client={client_version}, server={server_version}"),
79            serde_json::json!({
80                "client_version": client_version,
81                "server_version": server_version
82            }),
83        )
84    }
85
86    /// Create an authorization error
87    pub fn unauthorized(message: impl Into<String>) -> Self {
88        Self::new(ErrorCode::Unauthorized, message)
89    }
90
91    /// Create a forbidden error
92    pub fn forbidden(message: impl Into<String>) -> Self {
93        Self::new(ErrorCode::Forbidden, message)
94    }
95
96    /// Create a resource not found error
97    pub fn resource_not_found(resource: impl Into<String>) -> Self {
98        Self::new(
99            ErrorCode::ResourceNotFound,
100            format!("Resource not found: {}", resource.into()),
101        )
102    }
103
104    /// Create a tool not found error
105    pub fn tool_not_found(tool: impl Into<String>) -> Self {
106        Self::new(
107            ErrorCode::ToolNotFound,
108            format!("Tool not found: {}", tool.into()),
109        )
110    }
111
112    /// Create a validation error
113    pub fn validation_error(message: impl Into<String>) -> Self {
114        Self::new(ErrorCode::ValidationError, message)
115    }
116
117    /// Create a rate limit exceeded error
118    pub fn rate_limit_exceeded(message: impl Into<String>) -> Self {
119        Self::new(ErrorCode::RateLimitExceeded, message)
120    }
121}
122
123/// MCP error codes following JSON-RPC 2.0 specification
124#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
125pub enum ErrorCode {
126    // Standard JSON-RPC 2.0 errors
127    #[serde(rename = "-32700")]
128    ParseError = -32700,
129    #[serde(rename = "-32600")]
130    InvalidRequest = -32600,
131    #[serde(rename = "-32601")]
132    MethodNotFound = -32601,
133    #[serde(rename = "-32602")]
134    InvalidParams = -32602,
135    #[serde(rename = "-32603")]
136    InternalError = -32603,
137
138    // MCP-specific errors
139    #[serde(rename = "-32000")]
140    Unauthorized = -32000,
141    #[serde(rename = "-32001")]
142    Forbidden = -32001,
143    #[serde(rename = "-32002")]
144    ResourceNotFound = -32002,
145    #[serde(rename = "-32003")]
146    ToolNotFound = -32003,
147    #[serde(rename = "-32004")]
148    ValidationError = -32004,
149    #[serde(rename = "-32005")]
150    RateLimitExceeded = -32005,
151}
152
153impl fmt::Display for ErrorCode {
154    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
155        let name = match self {
156            ErrorCode::ParseError => "ParseError",
157            ErrorCode::InvalidRequest => "InvalidRequest",
158            ErrorCode::MethodNotFound => "MethodNotFound",
159            ErrorCode::InvalidParams => "InvalidParams",
160            ErrorCode::InternalError => "InternalError",
161            ErrorCode::Unauthorized => "Unauthorized",
162            ErrorCode::Forbidden => "Forbidden",
163            ErrorCode::ResourceNotFound => "ResourceNotFound",
164            ErrorCode::ToolNotFound => "ToolNotFound",
165            ErrorCode::ValidationError => "ValidationError",
166            ErrorCode::RateLimitExceeded => "RateLimitExceeded",
167        };
168        write!(f, "{name}")
169    }
170}
171
172// Implement conversion from common error types
173impl From<serde_json::Error> for Error {
174    fn from(err: serde_json::Error) -> Self {
175        Error::parse_error(err.to_string())
176    }
177}
178
179impl From<uuid::Error> for Error {
180    fn from(err: uuid::Error) -> Self {
181        Error::validation_error(format!("Invalid UUID: {err}"))
182    }
183}
184
185impl From<validator::ValidationErrors> for Error {
186    fn from(err: validator::ValidationErrors) -> Self {
187        Error::validation_error(err.to_string())
188    }
189}