gglib_core/ports/
mcp_error.rs

1//! MCP service error types.
2//!
3//! This module defines service-level errors for MCP operations.
4
5use thiserror::Error;
6
7use super::McpRepositoryError;
8
9/// Domain-specific errors for MCP service operations.
10///
11/// This error type wraps repository errors and adds service-level failure modes
12/// without leaking infrastructure details (OS process errors, SQL errors, etc.).
13#[derive(Debug, Error)]
14pub enum McpServiceError {
15    /// Repository operation failed.
16    #[error(transparent)]
17    Repository(#[from] McpRepositoryError),
18
19    /// Server process failed to start.
20    #[error("Failed to start MCP server: {0}")]
21    StartFailed(String),
22
23    /// Server process failed to stop.
24    #[error("Failed to stop MCP server: {0}")]
25    StopFailed(String),
26
27    /// Server is not running (e.g., when trying to stop).
28    #[error("MCP server not running: {0}")]
29    NotRunning(String),
30
31    /// Protocol error (JSON-RPC communication failure).
32    #[error("MCP protocol error: {0}")]
33    Protocol(String),
34
35    /// Tool invocation failed.
36    #[error("MCP tool error: {0}")]
37    ToolError(String),
38
39    /// Configuration validation error.
40    #[error("Invalid MCP configuration: {0}")]
41    InvalidConfig(String),
42
43    /// Internal service error.
44    #[error("Internal MCP error: {0}")]
45    Internal(String),
46}
47
48/// User-safe error information for MCP events.
49///
50/// This type is used in `AppEvent::McpServerError` to provide error details
51/// that are safe to display to users (no raw process/SQL errors).
52#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
53#[serde(rename_all = "camelCase")]
54pub struct McpErrorInfo {
55    /// ID of the MCP server (if known).
56    #[serde(skip_serializing_if = "Option::is_none")]
57    pub server_id: Option<i64>,
58
59    /// Name of the MCP server.
60    pub server_name: String,
61
62    /// User-friendly error message.
63    pub message: String,
64
65    /// Error category for UI handling.
66    pub category: McpErrorCategory,
67}
68
69/// Categories of MCP errors for UI handling.
70#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
71#[serde(rename_all = "snake_case")]
72pub enum McpErrorCategory {
73    /// Server process lifecycle error.
74    Process,
75    /// Protocol communication error.
76    Protocol,
77    /// Tool invocation error.
78    Tool,
79    /// Configuration error.
80    Configuration,
81    /// Unknown/internal error.
82    Unknown,
83}
84
85impl McpErrorInfo {
86    /// Create error info for a process error.
87    pub fn process(
88        server_id: Option<i64>,
89        server_name: impl Into<String>,
90        message: impl Into<String>,
91    ) -> Self {
92        Self {
93            server_id,
94            server_name: server_name.into(),
95            message: message.into(),
96            category: McpErrorCategory::Process,
97        }
98    }
99
100    /// Create error info for a protocol error.
101    pub fn protocol(
102        server_id: Option<i64>,
103        server_name: impl Into<String>,
104        message: impl Into<String>,
105    ) -> Self {
106        Self {
107            server_id,
108            server_name: server_name.into(),
109            message: message.into(),
110            category: McpErrorCategory::Protocol,
111        }
112    }
113
114    /// Create error info for a tool error.
115    pub fn tool(
116        server_id: Option<i64>,
117        server_name: impl Into<String>,
118        message: impl Into<String>,
119    ) -> Self {
120        Self {
121            server_id,
122            server_name: server_name.into(),
123            message: message.into(),
124            category: McpErrorCategory::Tool,
125        }
126    }
127}
128
129impl From<&McpServiceError> for McpErrorCategory {
130    fn from(error: &McpServiceError) -> Self {
131        match error {
132            McpServiceError::Repository(_) | McpServiceError::Internal(_) => Self::Unknown,
133            McpServiceError::StartFailed(_)
134            | McpServiceError::StopFailed(_)
135            | McpServiceError::NotRunning(_) => Self::Process,
136            McpServiceError::Protocol(_) => Self::Protocol,
137            McpServiceError::ToolError(_) => Self::Tool,
138            McpServiceError::InvalidConfig(_) => Self::Configuration,
139        }
140    }
141}