Skip to main content
Glama
pixxelboy
by pixxelboy
validation.ts3.94 kB
/** * Input Validation Utility */ import { MCPError } from '../types/mcp-tools.js'; import { formatError } from './errors.js'; /** * Supported audio MIME types */ export const SUPPORTED_MIME_TYPES = [ 'audio/mpeg', // MP3 'audio/wav', // WAV 'audio/x-wav', // WAV alt 'audio/flac', // FLAC 'audio/ogg', // OGG 'audio/mp4', // M4A 'audio/x-m4a', // M4A alt ] as const; /** * Supported audio file extensions */ export const SUPPORTED_EXTENSIONS = ['.mp3', '.wav', '.flac', '.ogg', '.m4a'] as const; /** * Maximum file size in bytes (100MB) */ export const MAX_FILE_SIZE_BYTES = 100 * 1024 * 1024; /** * Maximum file size in MB (for display) */ export const MAX_FILE_SIZE_MB = 100; /** * URL validation regex (HTTP/HTTPS only) */ const URL_REGEX = /^https?:\/\/.+/i; /** * Validation result */ export interface ValidationResult { valid: boolean; error?: MCPError; } /** * Validate audio URL format and extension */ export function validateAudioUrl(url: string): ValidationResult { // Check if URL is provided if (!url || url.trim() === '') { return { valid: false, error: formatError('INVALID_URL', 'Audio URL is required'), }; } const trimmedUrl = url.trim(); // Check URL format if (!URL_REGEX.test(trimmedUrl)) { return { valid: false, error: formatError('INVALID_URL', 'URL must start with http:// or https://'), }; } // Try to parse URL let parsedUrl: URL; try { parsedUrl = new URL(trimmedUrl); } catch { return { valid: false, error: formatError('INVALID_URL', 'Invalid URL format'), }; } // Check file extension const pathname = parsedUrl.pathname.toLowerCase(); const hasValidExtension = SUPPORTED_EXTENSIONS.some((ext) => pathname.endsWith(ext)); if (!hasValidExtension) { // Allow URLs without extension (might be served with proper Content-Type) // but warn if no extension and no query params (likely a page, not a file) const hasQueryParams = parsedUrl.search.length > 0; const hasExtension = pathname.includes('.'); if (!hasExtension && !hasQueryParams) { return { valid: false, error: formatError( 'UNSUPPORTED_FORMAT', `URL does not appear to point to an audio file. Accepted formats: ${SUPPORTED_EXTENSIONS.join(', ')}` ), }; } } return { valid: true }; } /** * Validate job ID format */ export function validateJobId(jobId: string): ValidationResult { // Check if job ID is provided if (!jobId || jobId.trim() === '') { return { valid: false, error: formatError('JOB_NOT_FOUND', 'Job ID is required'), }; } const trimmedJobId = jobId.trim(); // Basic format validation (alphanumeric with dashes/underscores) if (!/^[a-zA-Z0-9_-]+$/.test(trimmedJobId)) { return { valid: false, error: formatError('JOB_NOT_FOUND', 'Invalid job ID format'), }; } return { valid: true }; } /** * Check if file extension is supported */ export function isSupportedExtension(filename: string): boolean { const lower = filename.toLowerCase(); return SUPPORTED_EXTENSIONS.some((ext) => lower.endsWith(ext)); } /** * Get file extension from URL or filename */ export function getFileExtension(urlOrFilename: string): string | null { try { const url = new URL(urlOrFilename); const pathname = url.pathname; const lastDot = pathname.lastIndexOf('.'); if (lastDot === -1) return null; return pathname.slice(lastDot).toLowerCase(); } catch { // Not a valid URL, treat as filename const lastDot = urlOrFilename.lastIndexOf('.'); if (lastDot === -1) return null; return urlOrFilename.slice(lastDot).toLowerCase(); } } /** * Format supported formats for error messages */ export function formatSupportedFormats(): string { return SUPPORTED_EXTENSIONS.map((ext) => ext.slice(1).toUpperCase()).join(', '); }

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/pixxelboy/amplify-mcp'

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