Skip to main content
Glama
errors.ts6.85 kB
/** * Custom Error Classes for Domain Search MCP. * * These errors are designed to be: * 1. User-friendly (clear messages for non-developers) * 2. Actionable (suggest what to do next) * 3. Informative (include context for debugging) */ /** * Base error class for all domain search errors. */ export class DomainSearchError extends Error { /** Machine-readable error code */ readonly code: string; /** User-friendly message */ readonly userMessage: string; /** Can this operation be retried? */ readonly retryable: boolean; /** Suggested action for the user */ readonly suggestedAction?: string; constructor( code: string, message: string, userMessage: string, options?: { retryable?: boolean; suggestedAction?: string; cause?: Error; }, ) { super(message); this.name = 'DomainSearchError'; this.code = code; this.userMessage = userMessage; this.retryable = options?.retryable ?? false; this.suggestedAction = options?.suggestedAction; if (options?.cause) { this.cause = options.cause; } } /** * Convert to a plain object for JSON responses. */ toJSON(): object { return { code: this.code, message: this.userMessage, retryable: this.retryable, suggestedAction: this.suggestedAction, }; } } /** * Error when a domain name is invalid. */ export class InvalidDomainError extends DomainSearchError { constructor(domain: string, reason: string) { super( 'INVALID_DOMAIN', `Invalid domain: ${domain} - ${reason}`, `The domain "${domain}" is not valid: ${reason}`, { retryable: false, suggestedAction: 'Check the domain name for typos or invalid characters.', }, ); this.name = 'InvalidDomainError'; } } /** * Error when a TLD is not supported. */ export class UnsupportedTldError extends DomainSearchError { constructor(tld: string, availableTlds: string[]) { const suggestion = availableTlds.length > 0 ? `Try one of these: ${availableTlds.slice(0, 5).join(', ')}` : 'Contact support for TLD availability.'; super( 'UNSUPPORTED_TLD', `TLD not supported: .${tld}`, `The TLD ".${tld}" is not supported for searching.`, { retryable: false, suggestedAction: suggestion, }, ); this.name = 'UnsupportedTldError'; } } /** * Error when an API rate limit is hit. */ export class RateLimitError extends DomainSearchError { /** When to retry (Unix timestamp) */ readonly retryAfter?: number; constructor(registrar: string, retryAfterSeconds?: number) { super( 'RATE_LIMIT', `Rate limit hit for ${registrar}`, `Too many requests to ${registrar}. Please slow down.`, { retryable: true, suggestedAction: retryAfterSeconds ? `Wait ${retryAfterSeconds} seconds before trying again.` : 'Wait a moment and try again, or check fewer domains at once.', }, ); this.name = 'RateLimitError'; if (retryAfterSeconds) { this.retryAfter = Date.now() + retryAfterSeconds * 1000; } } } /** * Error when a registrar API fails. */ export class RegistrarApiError extends DomainSearchError { /** HTTP status code if available */ readonly statusCode?: number; constructor( registrar: string, message: string, statusCode?: number, cause?: Error, ) { const isServerError = statusCode !== undefined && statusCode >= 500; super( 'REGISTRAR_API_ERROR', `${registrar} API error: ${message}`, isServerError ? `${registrar} is experiencing issues. We'll try another source.` : `Could not check with ${registrar}: ${message}`, { retryable: isServerError, suggestedAction: isServerError ? 'The system will automatically try alternative sources.' : `Check your ${registrar} API configuration.`, cause, }, ); this.name = 'RegistrarApiError'; this.statusCode = statusCode; } } /** * Error when API credentials are missing or invalid. */ export class AuthenticationError extends DomainSearchError { constructor(registrar: string, reason?: string) { super( 'AUTH_ERROR', `Authentication failed for ${registrar}: ${reason || 'Invalid credentials'}`, `Could not authenticate with ${registrar}.`, { retryable: false, suggestedAction: `Check your ${registrar.toUpperCase()}_API_KEY and ${registrar.toUpperCase()}_API_SECRET in your .env file.`, }, ); this.name = 'AuthenticationError'; } } /** * Error when no data source is available. */ export class NoSourceAvailableError extends DomainSearchError { constructor(domain: string, triedSources: string[]) { super( 'NO_SOURCE_AVAILABLE', `No source available for ${domain}. Tried: ${triedSources.join(', ')}`, `Could not check availability for "${domain}". All sources failed.`, { retryable: true, suggestedAction: 'Try again in a few minutes, or check the domain manually at a registrar website.', }, ); this.name = 'NoSourceAvailableError'; } } /** * Error when a network request times out. */ export class TimeoutError extends DomainSearchError { constructor(operation: string, timeoutMs: number) { super( 'TIMEOUT', `Operation timed out: ${operation} (${timeoutMs}ms)`, `The request took too long to complete.`, { retryable: true, suggestedAction: 'Try again - this might be a temporary network issue.', }, ); this.name = 'TimeoutError'; } } /** * Error when a required configuration is missing. */ export class ConfigurationError extends DomainSearchError { constructor(missing: string, howToFix: string) { super( 'CONFIG_ERROR', `Missing configuration: ${missing}`, `Server configuration is incomplete.`, { retryable: false, suggestedAction: howToFix, }, ); this.name = 'ConfigurationError'; } } /** * Convert any error to a DomainSearchError. */ export function wrapError(error: unknown): DomainSearchError { if (error instanceof DomainSearchError) { return error; } if (error instanceof Error) { return new DomainSearchError( 'UNKNOWN_ERROR', error.message, 'An unexpected error occurred.', { retryable: true, suggestedAction: 'Try again or contact support if the issue persists.', cause: error, }, ); } return new DomainSearchError( 'UNKNOWN_ERROR', String(error), 'An unexpected error occurred.', { retryable: true, suggestedAction: 'Try again or contact support if the issue persists.', }, ); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/dorukardahan/domain-search-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server