export enum ErrorCode {
INVALID_ARGUMENT = 'INVALID_ARGUMENT',
UNAUTHENTICATED = 'UNAUTHENTICATED',
FORBIDDEN = 'FORBIDDEN',
NOT_FOUND = 'NOT_FOUND',
CONFLICT = 'CONFLICT',
RATE_LIMITED = 'RATE_LIMITED',
INTERNAL = 'INTERNAL'
}
export interface McpError {
code: ErrorCode;
message: string;
provider_code?: string | undefined;
retry_after_ms?: number | undefined;
}
export interface McpResponse<T = unknown> {
ok: boolean;
data?: T;
error?: McpError;
}
export const GOOGLE_ERROR_MAP: Record<string, { code: ErrorCode; retry_after_ms?: number }> = {
'rateLimitExceeded': { code: ErrorCode.RATE_LIMITED, retry_after_ms: 60000 },
'quotaExceeded': { code: ErrorCode.RATE_LIMITED, retry_after_ms: 300000 },
'conflict': { code: ErrorCode.CONFLICT },
'forbidden': { code: ErrorCode.FORBIDDEN },
'notFound': { code: ErrorCode.NOT_FOUND },
'invalidCredentials': { code: ErrorCode.UNAUTHENTICATED },
'invalidArgument': { code: ErrorCode.INVALID_ARGUMENT },
'failedPrecondition': { code: ErrorCode.INVALID_ARGUMENT },
'insufficientAuthentication': { code: ErrorCode.UNAUTHENTICATED },
'invalidGrant': { code: ErrorCode.UNAUTHENTICATED },
'accessDenied': { code: ErrorCode.FORBIDDEN },
'userRateLimitExceeded': { code: ErrorCode.RATE_LIMITED, retry_after_ms: 60000 },
'dailyLimitExceeded': { code: ErrorCode.RATE_LIMITED, retry_after_ms: 86400000 },
'400': { code: ErrorCode.INVALID_ARGUMENT },
'401': { code: ErrorCode.UNAUTHENTICATED },
'403': { code: ErrorCode.FORBIDDEN },
'404': { code: ErrorCode.NOT_FOUND },
'409': { code: ErrorCode.CONFLICT },
'429': { code: ErrorCode.RATE_LIMITED, retry_after_ms: 60000 },
'500': { code: ErrorCode.INTERNAL },
'502': { code: ErrorCode.INTERNAL },
'503': { code: ErrorCode.INTERNAL },
'504': { code: ErrorCode.INTERNAL }
};
export function normalizeGoogleError(error: any, provider: 'calendar' | 'gmail' = 'calendar'): McpError {
const status = error?.status || error?.code || error?.statusCode;
const message = error?.message || error?.error_description || 'Unknown error';
const providerCode = error?.error || error?.code;
if (providerCode && GOOGLE_ERROR_MAP[providerCode]) {
const mapping = GOOGLE_ERROR_MAP[providerCode];
return {
code: mapping.code,
message,
provider_code: providerCode,
retry_after_ms: mapping.retry_after_ms
};
}
if (status && GOOGLE_ERROR_MAP[status.toString()]) {
const mapping = GOOGLE_ERROR_MAP[status.toString()];
if (mapping) {
return {
code: mapping.code,
message,
provider_code: status.toString(),
retry_after_ms: mapping.retry_after_ms
};
}
}
return {
code: ErrorCode.INTERNAL,
message: `Unexpected error from ${provider} API: ${message}`,
provider_code: providerCode || status?.toString()
};
}
export function createErrorResponse<T = unknown>(
code: ErrorCode,
message: string,
provider_code?: string,
retry_after_ms?: number
): McpResponse<T> {
return {
ok: false,
error: {
code,
message,
provider_code: provider_code || undefined,
retry_after_ms: retry_after_ms || undefined
}
};
}
export function createSuccessResponse<T>(data: T): McpResponse<T> {
return {
ok: true,
data
};
}
export function isRetryableError(error: McpError): boolean {
return error.code === ErrorCode.RATE_LIMITED ||
error.code === ErrorCode.INTERNAL ||
(error.retry_after_ms !== undefined && error.retry_after_ms > 0);
}
export function getRetryDelay(error: McpError): number {
return error.retry_after_ms || 1000;
}