Skip to main content
Glama
errors.ts6.45 kB
/** * Error codes for the MCP server */ export const ErrorCode = { BROWSER_NOT_LAUNCHED: 'E001', BROWSER_CRASHED: 'E002', TAB_NOT_FOUND: 'E003', NO_ACTIVE_TAB: 'E004', SELECTOR_NOT_FOUND: 'E101', NAVIGATION_TIMEOUT: 'E102', NAVIGATION_FAILED: 'E103', EVALUATION_ERROR: 'E201', OPERATION_TIMEOUT: 'E301', VALIDATION_ERROR: 'E401', UNKNOWN_ERROR: 'E999', } as const; /** * MCP error structure */ export interface McpError { /** Error code */ code: string; /** Human-readable error message */ message: string; /** Whether the error is recoverable */ recoverable: boolean; } /** * Success result */ export interface Success<T> { success: true; data: T; } /** * Failure result */ export interface Failure<E = McpError> { success: false; error: E; } /** * Result type for operations that can fail */ export type Result<T, E = McpError> = Success<T> | Failure<E>; /** * Create a success result */ export function ok<T>(data: T): Success<T> { return { success: true, data }; } /** * Create a failure result */ export function err<E = McpError>(error: E): Failure<E> { return { success: false, error }; } /** * Create a browser not launched error */ export function browserNotLaunched(): McpError { return { code: ErrorCode.BROWSER_NOT_LAUNCHED, message: 'Browser has not been launched', recoverable: true, }; } /** * Create a browser crashed error */ export function browserCrashed(reason?: string): McpError { return { code: ErrorCode.BROWSER_CRASHED, message: reason ? `Browser crashed: ${reason}` : 'Browser crashed unexpectedly', recoverable: true, }; } /** * Create a tab not found error */ export function tabNotFound(tabId: string): McpError { return { code: ErrorCode.TAB_NOT_FOUND, message: `Tab not found: ${tabId}`, recoverable: false, }; } /** * Create a no active tab error */ export function noActiveTab(): McpError { return { code: ErrorCode.NO_ACTIVE_TAB, message: 'No active tab available', recoverable: true, }; } /** * Create a selector not found error */ export function selectorNotFound(selector: string): McpError { return { code: ErrorCode.SELECTOR_NOT_FOUND, message: `Element not found for selector: ${selector}`, recoverable: false, }; } /** * Create a navigation timeout error */ export function navigationTimeout(url: string, timeoutMs?: number): McpError { const timeoutInfo = timeoutMs ? ` after ${timeoutMs}ms` : ''; return { code: ErrorCode.NAVIGATION_TIMEOUT, message: `Navigation to ${url} timed out${timeoutInfo}`, recoverable: true, }; } /** * Create a navigation failed error */ export function navigationFailed(url: string, reason: string): McpError { return { code: ErrorCode.NAVIGATION_FAILED, message: `Navigation to ${url} failed: ${reason}`, recoverable: false, }; } /** * Create an evaluation error */ export function evaluationError(message: string): McpError { return { code: ErrorCode.EVALUATION_ERROR, message: `JavaScript evaluation failed: ${message}`, recoverable: false, }; } /** * Create an operation timeout error */ export function operationTimeout(operation: string, timeoutMs?: number): McpError { const timeoutInfo = timeoutMs ? ` after ${timeoutMs}ms` : ''; return { code: ErrorCode.OPERATION_TIMEOUT, message: `Operation "${operation}" timed out${timeoutInfo}`, recoverable: true, }; } /** * Create a validation error */ export function validationError(message: string): McpError { return { code: ErrorCode.VALIDATION_ERROR, message: `Validation error: ${message}`, recoverable: false, }; } /** * Create an unknown error */ export function unknownError(message: string): McpError { return { code: ErrorCode.UNKNOWN_ERROR, message: message, recoverable: false, }; } /** * Convert an unknown error to McpError */ export function normalizeError(error: unknown): McpError { if (isError(error)) { const message = error.message.toLowerCase(); // Check for specific Puppeteer errors if (message.includes('navigation timeout') || message.includes('timeout') && message.includes('navigation')) { return navigationTimeout(extractUrl(error.message) ?? 'unknown'); } if (message.includes('waiting for selector') || message.includes('failed to find element') || message.includes('no element found')) { return selectorNotFound(extractSelector(error.message) ?? 'unknown'); } if (message.includes('timeout') && (message.includes('selector') || message.includes('element'))) { return operationTimeout('wait_for_selector'); } if (message.includes('target closed') || message.includes('session closed')) { return browserCrashed('Target closed'); } if (message.includes('protocol error')) { return browserCrashed(error.message); } return unknownError(error.message); } return unknownError(String(error)); } /** * Type guard for Error */ function isError(error: unknown): error is Error { return error instanceof Error; } /** * Extract URL from error message */ function extractUrl(message: string): string | undefined { const match = message.match(/https?:\/\/[^\s]+/); return match?.[0]; } /** * Extract selector from error message */ function extractSelector(message: string): string | undefined { const match = message.match(/selector ['"`]([^'"`]+)['"`]/); return match?.[1]; } /** * Format an MCP error for tool response */ export function formatErrorResponse(error: McpError): { isError: true; content: Array<{ type: 'text'; text: string }> } { return { isError: true, content: [{ type: 'text', text: JSON.stringify({ error: error.code, message: error.message, recoverable: error.recoverable, }), }], }; } /** * Format a success response for tool */ export function formatSuccessResponse<T>(data: T): { content: Array<{ type: 'text'; text: string }> } { return { content: [{ type: 'text', text: JSON.stringify(data), }], }; } /** * Handle a result and format it for tool response */ export function handleResult<T>(result: Result<T>): { isError?: boolean; content: Array<{ type: 'text'; text: string }> } { if (result.success) { return formatSuccessResponse(result.data); } return formatErrorResponse(result.error); }

Implementation Reference

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/andytango/puppeteer-mcp'

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