Skip to main content
Glama
errors.ts8.85 kB
/** * Custom error classes for the mcp-docsrs-rustdoc MCP server * All errors are fully typed with detailed context information */ /** * Base error class for all mcp-docsrs errors */ export abstract class MCPDocsRsError extends Error { readonly timestamp: Date readonly context?: Record<string, unknown> constructor(message: string, context?: Record<string, unknown>) { super(message) this.name = this.constructor.name this.timestamp = new Date() this.context = context Error.captureStackTrace(this, this.constructor) } toJSON() { return { name: this.name, message: this.message, timestamp: this.timestamp, context: this.context, stack: this.stack } } } /** * Error thrown when JSON parsing fails */ export class JSONParseError extends MCPDocsRsError { readonly rawData: string readonly parseError: Error constructor(rawData: string, parseError: Error, url?: string) { const preview = rawData.length > 200 ? `${rawData.substring(0, 200)}...` : rawData const message = `Failed to parse JSON: ${parseError.message}` super(message, { url, dataLength: rawData.length, dataPreview: preview, contentType: typeof rawData, parseErrorName: parseError.name }) this.rawData = rawData this.parseError = parseError } } /** * Error thrown when network requests fail */ export class NetworkError extends MCPDocsRsError { readonly statusCode?: number readonly statusText?: string readonly url: string constructor(url: string, statusCode?: number, statusText?: string, details?: string) { const message = statusCode ? `Network request failed: HTTP ${statusCode} ${statusText || ""}${details ? ` - ${details}` : ""}` : `Network request failed: ${details || "Unknown error"}` super(message, { url, statusCode, statusText, details }) this.url = url this.statusCode = statusCode this.statusText = statusText } } /** * Error thrown when a crate is not found */ export class CrateNotFoundError extends MCPDocsRsError { readonly crateName: string readonly version?: string constructor(crateName: string, version?: string, details?: string) { const versionStr = version ? ` version ${version}` : "" const message = `Crate '${crateName}'${versionStr} not found. ${details || "Note: docs.rs started building rustdoc JSON on 2023-05-23, so older releases may not have JSON available yet."}` super(message, { crateName, version, details }) this.crateName = crateName this.version = version } } /** * Error thrown when rustdoc JSON is not available */ export class RustdocNotAvailableError extends MCPDocsRsError { readonly crateName: string readonly version?: string readonly reason?: string constructor(crateName: string, version?: string, reason?: string) { const versionStr = version ? ` version ${version}` : "" const message = `Rustdoc JSON not available for crate '${crateName}'${versionStr}. ${reason || "The crate may not have been built with rustdoc JSON support."}` super(message, { crateName, version, reason }) this.crateName = crateName this.version = version this.reason = reason } } /** * Error thrown when request times out */ export class TimeoutError extends MCPDocsRsError { readonly url: string readonly timeoutMs: number constructor(url: string, timeoutMs: number) { const message = `Request timeout after ${timeoutMs}ms` super(message, { url, timeoutMs }) this.url = url this.timeoutMs = timeoutMs } } /** * Error thrown when decompression fails */ export class DecompressionError extends MCPDocsRsError { readonly encoding: string readonly url: string constructor(url: string, encoding: string, details?: string) { const message = `Failed to decompress ${encoding} content: ${details || "Unknown error"}` super(message, { url, encoding, details }) this.url = url this.encoding = encoding } } /** * Error thrown when cache operations fail */ export class CacheError extends MCPDocsRsError { readonly operation: "get" | "set" | "delete" | "clear" | "close" | "query" | "stats" | "list" constructor( operation: "get" | "set" | "delete" | "clear" | "close" | "query" | "stats" | "list", details?: string ) { const message = `Cache operation '${operation}' failed: ${details || "Unknown error"}` super(message, { operation, details }) this.operation = operation } } /** * Error thrown when parsing rustdoc data structures fails */ export class RustdocParseError extends MCPDocsRsError { readonly itemPath?: string readonly expectedType?: string constructor(message: string, itemPath?: string, expectedType?: string) { super(message, { itemPath, expectedType }) this.itemPath = itemPath this.expectedType = expectedType } } /** * Error thrown when an item is not found in rustdoc */ export class ItemNotFoundError extends MCPDocsRsError { readonly crateName: string readonly itemPath: string constructor(crateName: string, itemPath: string) { const message = `Item '${itemPath}' not found in crate '${crateName}'` super(message, { crateName, itemPath }) this.crateName = crateName this.itemPath = itemPath } } /** * Error thrown when an operation is aborted */ export class AbortError extends MCPDocsRsError { constructor(operation: string, reason?: string) { const message = `Operation aborted: ${operation}${reason ? ` - ${reason}` : ""}` super(message, { operation, reason }) this.name = "AbortError" // Ensure the name matches what fetch throws } } /** * Error logger utility functions */ const formatError = (error: Error): string => { if (error instanceof MCPDocsRsError) { const lines = [`[${error.timestamp.toISOString()}] ${error.name}: ${error.message}`] if (error.context && Object.keys(error.context).length > 0) { lines.push("Context:") for (const [key, value] of Object.entries(error.context)) { lines.push(` ${key}: ${JSON.stringify(value)}`) } } if (error.stack) { lines.push("Stack trace:") lines.push(error.stack) } return lines.join("\n") } return `[${new Date().toISOString()}] ${error.name || "Error"}: ${error.message}\n${error.stack || ""}` } export const ErrorLogger = { log(error: Error): void { // During tests, check if this is an expected error if (process.env.NODE_ENV === "test" || process.env.BUN_ENV === "test") { // In test environment, use a different format for expected errors if ( error instanceof CrateNotFoundError || error instanceof TimeoutError || error instanceof RustdocParseError || error instanceof AbortError || error instanceof NetworkError || error.name === "AbortError" || // For native AbortError error.message === "Test interception" // For URL validation tests ) { // Show a brief indicator that the expected error was caught if (process.env.LOG_EXPECTED_ERRORS === "true") { // Full logging if explicitly requested console.log(`\x1b[32m[EXPECTED ERROR] ${error.name}: ${error.message}\x1b[0m`) } else { // Brief indicator in green to show test is working correctly console.log(`\x1b[32m✓ Expected ${error.name} thrown\x1b[0m`) } return } } console.error(formatError(error)) }, logWarning(message: string, context?: Record<string, unknown>): void { const timestamp = new Date().toISOString() const contextStr = context ? ` - Context: ${JSON.stringify(context)}` : "" console.warn(`[${timestamp}] WARNING: ${message}${contextStr}`) }, logInfo(message: string, context?: Record<string, unknown>): void { // Skip info logging during tests or when silent mode is enabled if (process.env.SILENT_LOGS === "true" || process.env.MCP_TEST === "true") { return } const timestamp = new Date().toISOString() const contextStr = context ? ` - Context: ${JSON.stringify(context)}` : "" console.info(`[${timestamp}] INFO: ${message}${contextStr}`) } } /** * Type guard to check if an error is an MCPDocsRsError */ export function isMCPDocsRsError(error: unknown): error is MCPDocsRsError { return error instanceof MCPDocsRsError } /** * Type guard for specific error types */ export function isNetworkError(error: unknown): error is NetworkError { return error instanceof NetworkError } export function isJSONParseError(error: unknown): error is JSONParseError { return error instanceof JSONParseError } export function isCrateNotFoundError(error: unknown): error is CrateNotFoundError { return error instanceof CrateNotFoundError } export function isTimeoutError(error: unknown): error is TimeoutError { return error instanceof TimeoutError } export function isAbortError(error: unknown): error is AbortError { return error instanceof AbortError || (error instanceof Error && error.name === "AbortError") }

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/vexxvakan/mcp-docsrs'

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