use crate::errors::GmailApiError;
use base64;
use log::{debug, error};
use mcp_attr::{jsoncall::ErrorCode, Error as McpError};
use serde_json;
pub mod error_codes {
pub const GENERAL_ERROR: u32 = 1000;
pub const CONFIG_ERROR: u32 = 1001;
pub const AUTH_ERROR: u32 = 1002;
pub const API_ERROR: u32 = 1003;
pub const MESSAGE_FORMAT_ERROR: u32 = 1005;
pub fn get_error_description(code: u32) -> &'static str {
match code {
CONFIG_ERROR => "Configuration Error: Missing or invalid environment variables required for Gmail authentication",
AUTH_ERROR => "Authentication Error: Failed to authenticate with Gmail API using the provided credentials",
API_ERROR => "Gmail API Error: The request to the Gmail API failed",
MESSAGE_FORMAT_ERROR => "Message Format Error: The response from Gmail API has missing or invalid fields",
GENERAL_ERROR => "General Error: An unspecified error occurred in the Gmail MCP server",
_ => "Unknown Error: An unclassified error occurred",
}
}
pub fn get_troubleshooting_steps(code: u32) -> &'static str {
match code {
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.",
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.",
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.",
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.",
GENERAL_ERROR => "Review server logs for more details about what went wrong. Check for any recent changes to your code or environment.",
_ => "Check the server logs for more specific error information. Ensure all dependencies are up to date.",
}
}
}
pub fn parse_max_results(value: Option<serde_json::Value>, default: u32) -> u32 {
match value {
Some(val) => {
match val {
serde_json::Value::Number(num) => {
if let Some(n) = num.as_u64() {
if n <= u32::MAX as u64 {
n as u32
} else {
debug!("Number too large for u32, using default {}", default);
default
}
} else {
debug!("Number not convertible to u32, using default {}", default);
default
}
}
serde_json::Value::String(s) => {
match s.parse::<u32>() {
Ok(n) => n,
Err(_) => {
debug!(
"String \"{}\" not convertible to u32, using default {}",
s, default
);
default
}
}
}
_ => {
debug!("Invalid type for max_results, using default {}", default);
default
}
}
}
None => default,
}
}
pub fn decode_base64(data: &str) -> Result<String, String> {
let bytes = base64::decode(data).map_err(|e| format!("Error decoding base64: {}", e))?;
String::from_utf8(bytes).map_err(|e| format!("Error converting base64 to string: {}", e))
}
pub fn encode_base64_url_safe(data: &[u8]) -> String {
base64::encode_config(data, base64::URL_SAFE)
}
pub fn to_mcp_error(message: &str, code: u32) -> McpError {
use error_codes::{get_error_description, get_troubleshooting_steps};
let description = get_error_description(code);
let steps = get_troubleshooting_steps(code);
let detailed_error =
format!(
"ERROR CODE {}: {}\n\nDETAILS: {}\n\nTROUBLESHOOTING: {}\n\nSERVER MESSAGE: {}",
code, description, message, steps,
"If the problem persists, contact the server administrator and reference this error code."
);
error!(
"Creating MCP error: {} (code: {})\n{}",
message, code, detailed_error
);
McpError::new(ErrorCode(code as i64)).with_message(detailed_error, true)
}
pub fn map_gmail_error(err: GmailApiError) -> McpError {
match err {
GmailApiError::ApiError(e) => {
let (code, detailed_msg) = if e.contains("quota")
|| e.contains("rate")
|| e.contains("limit")
{
(
error_codes::API_ERROR,
format!(
"Gmail API rate limit exceeded: {}. The server has made too many requests to the Gmail API. \
This typically happens when many requests are made in quick succession. \
Please try again in a few minutes.",
e
)
)
} else if e.contains("network") || e.contains("connection") || e.contains("timeout") {
(
error_codes::API_ERROR,
format!(
"Network error while connecting to Gmail API: {}. The server couldn't establish a \
connection to the Gmail API. This may be due to network issues or the Gmail API \
might be experiencing downtime.",
e
)
)
} else if e.contains("authentication") || e.contains("auth") || e.contains("token") {
(
error_codes::AUTH_ERROR,
format!(
"Gmail API authentication failed: {}. The OAuth token used to authenticate with \
Gmail may have expired or been revoked. Please check your credentials and try \
regenerating your refresh token.",
e
)
)
} else if e.contains("format") || e.contains("missing field") || e.contains("parse") {
(
error_codes::MESSAGE_FORMAT_ERROR,
format!(
"Gmail API response format error: {}. The API returned data in an unexpected format. \
This might be due to changes in the Gmail API or issues with specific messages.",
e
)
)
} else if e.contains("not found") || e.contains("404") {
(
error_codes::API_ERROR,
format!(
"Gmail API resource not found: {}. The requested message or resource doesn't exist \
or you don't have permission to access it. Please check the message ID and ensure \
it exists in your Gmail account.",
e
)
)
} else {
(
error_codes::API_ERROR,
format!(
"Unspecified Gmail API error: {}. An unexpected error occurred when communicating \
with the Gmail API. Please check the server logs for more details.",
e
)
)
};
to_mcp_error(&detailed_msg, code)
}
GmailApiError::AuthError(e) => {
let detailed_msg = format!(
"Gmail authentication error: {}. Failed to authenticate with the Gmail API using the provided \
credentials. Please verify your client ID, client secret, and refresh token.",
e
);
to_mcp_error(&detailed_msg, error_codes::AUTH_ERROR)
}
GmailApiError::MessageRetrievalError(e) => {
let detailed_msg = format!(
"Message retrieval error: {}. Failed to retrieve the requested message from Gmail. \
This may be due to the message being deleted, access permissions, or temporary Gmail API issues.",
e
);
to_mcp_error(&detailed_msg, error_codes::API_ERROR)
}
GmailApiError::MessageFormatError(e) => {
let detailed_msg = format!(
"Message format error: {}. The Gmail API returned a malformed message or one with missing required fields.",
e
);
to_mcp_error(&detailed_msg, error_codes::MESSAGE_FORMAT_ERROR)
}
GmailApiError::NetworkError(e) => {
let detailed_msg = format!(
"Network error: {}. The server couldn't establish a connection to the Gmail API. \
This might be due to network configuration issues, outages, or firewall restrictions. \
Please check your internet connection and server network configuration.",
e
);
to_mcp_error(&detailed_msg, error_codes::API_ERROR)
}
GmailApiError::RateLimitError(e) => {
let detailed_msg = format!(
"Rate limit error: {}. The Gmail API has rate-limited the server's requests. \
This happens when too many requests are made in a short period of time. \
The server will automatically retry after a cooldown period, but you may need to wait \
or reduce the frequency of requests.",
e
);
to_mcp_error(&detailed_msg, error_codes::API_ERROR)
}
GmailApiError::CacheError(e) => {
let detailed_msg = format!(
"Token cache error: {}. The server encountered an error with the token cache. \
This is an internal error and should not affect functionality. \
The application will continue with in-memory token handling.",
e
);
to_mcp_error(&detailed_msg, error_codes::GENERAL_ERROR)
}
}
}