1use crate::errors::GmailApiError;
2use base64;
3use log::{debug, error};
4use mcp_attr::{jsoncall::ErrorCode, Error as McpError};
5use serde_json;
6
7pub mod error_codes {
9 pub const GENERAL_ERROR: u32 = 1000;
11
12 pub const CONFIG_ERROR: u32 = 1001;
14
15 pub const AUTH_ERROR: u32 = 1002;
17
18 pub const API_ERROR: u32 = 1003;
20
21 pub const MESSAGE_FORMAT_ERROR: u32 = 1005;
23
24 pub fn get_error_description(code: u32) -> &'static str {
26 match code {
27 CONFIG_ERROR => "Configuration Error: Missing or invalid environment variables required for Gmail authentication",
28 AUTH_ERROR => "Authentication Error: Failed to authenticate with Gmail API using the provided credentials",
29 API_ERROR => "Gmail API Error: The request to the Gmail API failed",
30 MESSAGE_FORMAT_ERROR => "Message Format Error: The response from Gmail API has missing or invalid fields",
31 GENERAL_ERROR => "General Error: An unspecified error occurred in the Gmail MCP server",
32 _ => "Unknown Error: An unclassified error occurred",
33 }
34 }
35
36 pub fn get_troubleshooting_steps(code: u32) -> &'static str {
38 match code {
39 CONFIG_ERROR => "Check that you have correctly set the following environment variables: GMAIL_CLIENT_ID, GMAIL_CLIENT_SECRET, and GMAIL_REFRESH_TOKEN. These should be in your .env file or exported in your shell.",
40 AUTH_ERROR => "Verify your OAuth credentials. Your refresh token may have expired or been revoked. Try generating new OAuth credentials and updating your environment variables.",
41 API_ERROR => "The Gmail API request failed. This could be due to API rate limits, network issues, or an invalid request. Check your internet connection and review the specific error details.",
42 MESSAGE_FORMAT_ERROR => "The Gmail API returned data in an unexpected format. This may be due to changes in the API or issues with specific messages. Try with a different message ID or update the server code.",
43 GENERAL_ERROR => "Review server logs for more details about what went wrong. Check for any recent changes to your code or environment.",
44 _ => "Check the server logs for more specific error information. Ensure all dependencies are up to date.",
45 }
46 }
47}
48
49pub fn parse_max_results(value: Option<serde_json::Value>, default: u32) -> u32 {
51 match value {
52 Some(val) => {
53 match val {
54 serde_json::Value::Number(num) => {
55 if let Some(n) = num.as_u64() {
57 if n <= u32::MAX as u64 {
59 n as u32
60 } else {
61 debug!("Number too large for u32, using default {}", default);
62 default
63 }
64 } else {
65 debug!("Number not convertible to u32, using default {}", default);
66 default
67 }
68 }
69 serde_json::Value::String(s) => {
70 match s.parse::<u32>() {
72 Ok(n) => n,
73 Err(_) => {
74 debug!(
75 "String \"{}\" not convertible to u32, using default {}",
76 s, default
77 );
78 default
79 }
80 }
81 }
82 _ => {
83 debug!("Invalid type for max_results, using default {}", default);
84 default
85 }
86 }
87 }
88 None => default,
89 }
90}
91
92pub fn decode_base64(data: &str) -> Result<String, String> {
94 let bytes = base64::decode(data).map_err(|e| format!("Error decoding base64: {}", e))?;
95
96 String::from_utf8(bytes).map_err(|e| format!("Error converting base64 to string: {}", e))
97}
98
99pub fn encode_base64_url_safe(data: &[u8]) -> String {
101 base64::encode_config(data, base64::URL_SAFE)
102}
103
104pub fn to_mcp_error(message: &str, code: u32) -> McpError {
106 use error_codes::{get_error_description, get_troubleshooting_steps};
107
108 let description = get_error_description(code);
110
111 let steps = get_troubleshooting_steps(code);
113
114 let detailed_error =
116 format!(
117 "ERROR CODE {}: {}\n\nDETAILS: {}\n\nTROUBLESHOOTING: {}\n\nSERVER MESSAGE: {}",
118 code, description, message, steps,
119 "If the problem persists, contact the server administrator and reference this error code."
120 );
121
122 error!(
124 "Creating MCP error: {} (code: {})\n{}",
125 message, code, detailed_error
126 );
127
128 McpError::new(ErrorCode(code as i64)).with_message(detailed_error, true)
130}
131
132pub fn map_gmail_error(err: GmailApiError) -> McpError {
134 match err {
135 GmailApiError::ApiError(e) => {
136 let (code, detailed_msg) = if e.contains("quota")
138 || e.contains("rate")
139 || e.contains("limit")
140 {
141 (
142 error_codes::API_ERROR,
143 format!(
144 "Gmail API rate limit exceeded: {}. The server has made too many requests to the Gmail API. \
145 This typically happens when many requests are made in quick succession. \
146 Please try again in a few minutes.",
147 e
148 )
149 )
150 } else if e.contains("network") || e.contains("connection") || e.contains("timeout") {
151 (
152 error_codes::API_ERROR,
153 format!(
154 "Network error while connecting to Gmail API: {}. The server couldn't establish a \
155 connection to the Gmail API. This may be due to network issues or the Gmail API \
156 might be experiencing downtime.",
157 e
158 )
159 )
160 } else if e.contains("authentication") || e.contains("auth") || e.contains("token") {
161 (
162 error_codes::AUTH_ERROR,
163 format!(
164 "Gmail API authentication failed: {}. The OAuth token used to authenticate with \
165 Gmail may have expired or been revoked. Please check your credentials and try \
166 regenerating your refresh token.",
167 e
168 )
169 )
170 } else if e.contains("format") || e.contains("missing field") || e.contains("parse") {
171 (
172 error_codes::MESSAGE_FORMAT_ERROR,
173 format!(
174 "Gmail API response format error: {}. The API returned data in an unexpected format. \
175 This might be due to changes in the Gmail API or issues with specific messages.",
176 e
177 )
178 )
179 } else if e.contains("not found") || e.contains("404") {
180 (
181 error_codes::API_ERROR,
182 format!(
183 "Gmail API resource not found: {}. The requested message or resource doesn't exist \
184 or you don't have permission to access it. Please check the message ID and ensure \
185 it exists in your Gmail account.",
186 e
187 )
188 )
189 } else {
190 (
191 error_codes::API_ERROR,
192 format!(
193 "Unspecified Gmail API error: {}. An unexpected error occurred when communicating \
194 with the Gmail API. Please check the server logs for more details.",
195 e
196 )
197 )
198 };
199
200 to_mcp_error(&detailed_msg, code)
201 }
202 GmailApiError::AuthError(e) => {
203 let detailed_msg = format!(
204 "Gmail authentication error: {}. Failed to authenticate with the Gmail API using the provided \
205 credentials. Please verify your client ID, client secret, and refresh token.",
206 e
207 );
208 to_mcp_error(&detailed_msg, error_codes::AUTH_ERROR)
209 }
210 GmailApiError::MessageRetrievalError(e) => {
211 let detailed_msg = format!(
212 "Message retrieval error: {}. Failed to retrieve the requested message from Gmail. \
213 This may be due to the message being deleted, access permissions, or temporary Gmail API issues.",
214 e
215 );
216 to_mcp_error(&detailed_msg, error_codes::API_ERROR)
217 }
218 GmailApiError::MessageFormatError(e) => {
219 let detailed_msg = format!(
220 "Message format error: {}. The Gmail API returned a malformed message or one with missing required fields.",
221 e
222 );
223 to_mcp_error(&detailed_msg, error_codes::MESSAGE_FORMAT_ERROR)
224 }
225 GmailApiError::NetworkError(e) => {
226 let detailed_msg = format!(
227 "Network error: {}. The server couldn't establish a connection to the Gmail API. \
228 This might be due to network configuration issues, outages, or firewall restrictions. \
229 Please check your internet connection and server network configuration.",
230 e
231 );
232 to_mcp_error(&detailed_msg, error_codes::API_ERROR)
233 }
234 GmailApiError::RateLimitError(e) => {
235 let detailed_msg = format!(
236 "Rate limit error: {}. The Gmail API has rate-limited the server's requests. \
237 This happens when too many requests are made in a short period of time. \
238 The server will automatically retry after a cooldown period, but you may need to wait \
239 or reduce the frequency of requests.",
240 e
241 );
242 to_mcp_error(&detailed_msg, error_codes::API_ERROR)
243 }
244 GmailApiError::CacheError(e) => {
245 let detailed_msg = format!(
246 "Token cache error: {}. The server encountered an error with the token cache. \
247 This is an internal error and should not affect functionality. \
248 The application will continue with in-memory token handling.",
249 e
250 );
251 to_mcp_error(&detailed_msg, error_codes::GENERAL_ERROR)
252 }
253 }
254}