agtrace_sdk/mcp/
error.rs

1//! MCP error types.
2
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5
6/// Structured error response for MCP tools.
7#[derive(Debug, Serialize, Deserialize, JsonSchema)]
8pub struct McpError {
9    /// Machine-readable error code
10    pub code: ErrorCode,
11    /// Human-readable error message
12    pub message: String,
13    /// Additional context (e.g., matched session IDs for ambiguous prefix)
14    #[serde(skip_serializing_if = "Option::is_none")]
15    pub details: Option<serde_json::Value>,
16    /// Whether the operation can be retried
17    pub retryable: bool,
18}
19
20#[derive(Debug, Serialize, Deserialize, JsonSchema)]
21#[serde(rename_all = "snake_case")]
22pub enum ErrorCode {
23    /// Session ID not found in index
24    SessionNotFound,
25    /// Session ID prefix matches multiple sessions
26    AmbiguousSessionPrefix,
27    /// Invalid session ID format
28    InvalidSessionId,
29    /// Event index out of bounds for session
30    InvalidEventIndex,
31    /// Pagination cursor is invalid or expired
32    InvalidCursor,
33    /// Parameter validation failed
34    InvalidParameter,
35    /// Search operation timed out
36    SearchTimeout,
37    /// Internal server error
38    InternalError,
39}
40
41impl McpError {
42    #[allow(dead_code)]
43    pub fn session_not_found(session_id: &str) -> Self {
44        Self {
45            code: ErrorCode::SessionNotFound,
46            message: format!("Session not found: {}", session_id),
47            details: Some(serde_json::json!({ "session_id": session_id })),
48            retryable: false,
49        }
50    }
51
52    #[allow(dead_code)]
53    pub fn ambiguous_prefix(prefix: &str, matches: Vec<String>) -> Self {
54        Self {
55            code: ErrorCode::AmbiguousSessionPrefix,
56            message: format!(
57                "Session ID prefix '{}' matches {} sessions. Provide more characters.",
58                prefix,
59                matches.len()
60            ),
61            details: Some(serde_json::json!({
62                "prefix": prefix,
63                "matches": matches,
64            })),
65            retryable: false,
66        }
67    }
68
69    pub fn invalid_event_index(session_id: &str, index: usize, max: usize) -> Self {
70        Self {
71            code: ErrorCode::InvalidEventIndex,
72            message: format!(
73                "Event index {} out of bounds for session {} (max: {})",
74                index, session_id, max
75            ),
76            details: Some(serde_json::json!({
77                "session_id": session_id,
78                "requested_index": index,
79                "max_index": max,
80            })),
81            retryable: false,
82        }
83    }
84
85    #[allow(dead_code)]
86    pub fn invalid_cursor(cursor: &str) -> Self {
87        Self {
88            code: ErrorCode::InvalidCursor,
89            message: "Invalid or expired pagination cursor".to_string(),
90            details: Some(serde_json::json!({ "cursor": cursor })),
91            retryable: false,
92        }
93    }
94
95    #[allow(dead_code)]
96    pub fn invalid_parameter(param_name: &str, reason: &str) -> Self {
97        Self {
98            code: ErrorCode::InvalidParameter,
99            message: format!("Invalid parameter '{}': {}", param_name, reason),
100            details: Some(serde_json::json!({
101                "parameter": param_name,
102                "reason": reason,
103            })),
104            retryable: false,
105        }
106    }
107
108    pub fn internal_error(message: String) -> Self {
109        Self {
110            code: ErrorCode::InternalError,
111            message,
112            details: None,
113            retryable: true,
114        }
115    }
116}
117
118impl From<crate::Error> for McpError {
119    fn from(err: crate::Error) -> Self {
120        Self::internal_error(err.to_string())
121    }
122}
123
124impl std::fmt::Display for McpError {
125    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
126        write!(f, "{}", self.message)
127    }
128}
129
130impl std::error::Error for McpError {}