Skip to main content
Glama

Nexus MCP Server

zod-error-parser.ts8.11 kB
import { ZodError, ZodIssue } from 'zod'; import { SUPPORTED_MODELS } from '../schemas/search.js'; /** * Parse a Zod error and convert to user-friendly message */ export function parseZodError(error: ZodError): { message: string; details: Array<{ field: string; issue: string; received?: unknown; expected?: string | string[]; suggestion?: string; }>; } { const details = error.issues.map(issue => parseZodIssue(issue)); // Create a comprehensive error message const primaryIssue = details[0]; let message: string; if (details.length === 1) { message = primaryIssue.suggestion || primaryIssue.issue; } else { const issueCount = details.length; message = `Found ${issueCount} validation errors. Primary issue: ${primaryIssue.suggestion || primaryIssue.issue}`; } return { message, details, }; } /** * Parse individual Zod issue into user-friendly details */ function parseZodIssue(issue: ZodIssue): { field: string; issue: string; received?: unknown; expected?: string | string[]; suggestion?: string; } { const field = issue.path.join('.'); switch (issue.code) { case 'invalid_type': return { field, issue: `Invalid type for ${field}`, received: issue.received, expected: issue.expected, suggestion: `Parameter '${field}' must be of type ${issue.expected}, but received ${issue.received}`, }; case 'invalid_enum_value': return handleInvalidEnum(field, issue); case 'too_small': return handleTooSmall(field, issue); case 'too_big': return handleTooBig(field, issue); case 'invalid_string': return { field, issue: `Invalid string format for ${field}`, suggestion: `Parameter '${field}' has invalid string format: ${issue.validation}`, }; case 'custom': return { field, issue: issue.message || `Custom validation failed for ${field}`, suggestion: issue.message, }; default: return { field, issue: issue.message || `Validation failed for ${field}`, suggestion: issue.message, }; } } /** * Handle invalid enum value with model-specific suggestions */ function handleInvalidEnum( field: string, issue: ZodIssue & { code: 'invalid_enum_value' } ): { field: string; issue: string; received?: unknown; expected?: string | string[]; suggestion?: string; } { const { received, options } = issue; // Special handling for model field if (field === 'model') { const suggestion = suggestModelName(String(received)); return { field, issue: `Invalid model name: ${received}`, received, expected: SUPPORTED_MODELS as string[], suggestion: suggestion ? `Invalid model '${received}'. Did you mean '${suggestion}'? Supported models: ${SUPPORTED_MODELS.join(', ')}` : `Invalid model '${received}'. Supported models: ${SUPPORTED_MODELS.join(', ')}`, }; } // Generic enum handling return { field, issue: `Invalid value for ${field}`, received, expected: options.map(String), suggestion: `Parameter '${field}' must be one of: ${options.join(', ')}. Received: ${received}`, }; } /** * Handle "too small" validation errors */ function handleTooSmall( field: string, issue: ZodIssue & { code: 'too_small' } ): { field: string; issue: string; received?: unknown; expected?: string; suggestion?: string; } { const { minimum, type, inclusive } = issue; if (type === 'string') { return { field, issue: `String too short for ${field}`, expected: `minimum ${minimum} characters`, suggestion: `Parameter '${field}' must be at least ${minimum} character${minimum === 1 ? '' : 's'} long`, }; } if (type === 'number') { const operator = inclusive ? '>=' : '>'; return { field, issue: `Number too small for ${field}`, expected: `${operator} ${minimum}`, suggestion: `Parameter '${field}' must be ${operator} ${minimum}`, }; } if (type === 'array') { return { field, issue: `Array too small for ${field}`, expected: `minimum ${minimum} items`, suggestion: `Parameter '${field}' must contain at least ${minimum} item${minimum === 1 ? '' : 's'}`, }; } return { field, issue: `Value too small for ${field}`, expected: `minimum ${minimum}`, suggestion: `Parameter '${field}' must be at least ${minimum}`, }; } /** * Handle "too big" validation errors */ function handleTooBig( field: string, issue: ZodIssue & { code: 'too_big' } ): { field: string; issue: string; received?: unknown; expected?: string; suggestion?: string; } { const { maximum, type, inclusive } = issue; if (type === 'string') { return { field, issue: `String too long for ${field}`, expected: `maximum ${maximum} characters`, suggestion: `Parameter '${field}' must be at most ${maximum} character${maximum === 1 ? '' : 's'} long`, }; } if (type === 'number') { const operator = inclusive ? '<=' : '<'; return { field, issue: `Number too large for ${field}`, expected: `${operator} ${maximum}`, suggestion: `Parameter '${field}' must be ${operator} ${maximum}`, }; } if (type === 'array') { return { field, issue: `Array too large for ${field}`, expected: `maximum ${maximum} items`, suggestion: `Parameter '${field}' must contain at most ${maximum} item${maximum === 1 ? '' : 's'}`, }; } return { field, issue: `Value too large for ${field}`, expected: `maximum ${maximum}`, suggestion: `Parameter '${field}' must be at most ${maximum}`, }; } /** * Suggest similar model names using simple string similarity */ function suggestModelName(input: string): string | null { if (!input || typeof input !== 'string') { return null; } const inputLower = input.toLowerCase(); let bestMatch: string | null = null; let bestScore = 0; for (const model of SUPPORTED_MODELS) { const score = calculateStringSimilarity(inputLower, model.toLowerCase()); if (score > bestScore && score > 0.4) { // Minimum threshold for suggestions bestScore = score; bestMatch = model; } } return bestMatch; } /** * Calculate string similarity using simple algorithm * Returns a score between 0 and 1, where 1 is identical */ function calculateStringSimilarity(str1: string, str2: string): number { if (str1 === str2) return 1; if (str1.length === 0 || str2.length === 0) return 0; // Simple similarity based on common substrings and character overlap const longer = str1.length > str2.length ? str1 : str2; const shorter = str1.length > str2.length ? str2 : str1; // Check if shorter string is contained in longer string if (longer.includes(shorter)) { return shorter.length / longer.length; } // Count common characters const chars1 = new Set(str1); const chars2 = new Set(str2); const intersection = new Set([...chars1].filter(x => chars2.has(x))); const union = new Set([...chars1, ...chars2]); return intersection.size / union.size; } /** * Check if an error is a Zod validation error */ export function isZodError(error: unknown): error is ZodError { return error instanceof ZodError; } /** * Create a user-friendly error message from any error */ export function createUserFriendlyMessage(error: unknown): { message: string; isValidationError: boolean; details?: Array<{ field: string; issue: string; received?: unknown; expected?: string | string[]; suggestion?: string; }>; } { if (isZodError(error)) { const parsed = parseZodError(error); return { message: parsed.message, isValidationError: true, details: parsed.details, }; } // Handle other error types const message = error instanceof Error ? error.message : 'An unexpected error occurred'; return { message, isValidationError: false, }; }

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/adawalli/nexus'

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