Skip to main content
Glama

1MCP Server

validationHelpers.ts8.68 kB
import fs from 'fs'; import os from 'os'; import path from 'path'; /** * Validation utilities for app configuration consolidation. * * Provides comprehensive validation for configuration files, * server connectivity, and permission handling. */ export interface ValidationResult { valid: boolean; errors: string[]; warnings: string[]; fixable: boolean; } export interface OperationPreview { app: string; configPath: string; serversToImport: string[]; replacementUrl: string; backupPath: string; risks: string[]; } /** * Validate application configuration file */ export function validateAppConfig(configPath: string, content: any): ValidationResult { const result: ValidationResult = { valid: true, errors: [], warnings: [], fixable: false, }; // JSON structure validation if (!content || typeof content !== 'object') { result.valid = false; result.errors.push('Invalid JSON structure'); return result; } // Check for MCP servers section if (!content.mcpServers && !content.servers) { result.warnings.push('No MCP servers section found'); } // Server configuration validation const servers = content.mcpServers || content.servers || {}; Object.entries(servers).forEach(([name, config]: [string, any]) => { if (!config || typeof config !== 'object') { result.errors.push(`Server "${name}" has invalid configuration`); return; } if (!config.command && !config.url) { result.errors.push(`Server "${name}" missing command or url`); } if (name === '1mcp') { result.warnings.push('Existing 1mcp server will be replaced'); } // Validate URL format if present if (config.url) { try { new URL(config.url); } catch { result.errors.push(`Server "${name}" has invalid URL format`); } } // Validate command format if present if (config.command && typeof config.command !== 'string') { result.errors.push(`Server "${name}" command must be a string`); } }); result.valid = result.errors.length === 0; result.fixable = result.errors.every( (error) => error.includes('missing command') || error.includes('invalid format') || error.includes('invalid URL'), ); return result; } /** * Validate 1mcp server connectivity */ export async function validateServer1mcpConnection(url: string): Promise<ValidationResult> { try { // Test basic connectivity using OAuth endpoint (which always exists) const baseUrl = url.replace('/mcp', ''); const oauthResponse = await fetch(`${baseUrl}/oauth/`, { signal: AbortSignal.timeout(5000), }); if (!oauthResponse.ok) { return { valid: false, errors: [`1mcp server not responding (HTTP ${oauthResponse.status})`], warnings: [], fixable: true, }; } // For MCP endpoint, we just verify OAuth is working since MCP requires POST with specific payload // The OAuth endpoint responding successfully indicates the server is properly running return { valid: true, errors: [], warnings: [], fixable: false, }; } catch (error: any) { return { valid: false, errors: [`Cannot connect to 1mcp server: ${error.message}`], warnings: ['Make sure 1mcp server is running'], fixable: true, }; } } /** * Handle file permission errors with specific diagnostics */ export function handlePermissionError(filePath: string, error: any): string { const dirPath = path.dirname(filePath); try { const stats = fs.statSync(dirPath); const currentUser = os.userInfo(); // Specific permission diagnostics if (error.code === 'EACCES') { if (stats.uid !== currentUser.uid) { return `Configuration directory owned by different user. Fix: chown -R ${currentUser.username} "${dirPath}" Alternative: Copy config to your user directory`; } if (!(stats.mode & 0o200)) { return `Configuration directory is read-only. Fix: chmod u+w "${dirPath}"`; } return `Permission denied. Check directory permissions: Current: ${stats.mode.toString(8)} Required: Read/write access Fix: chmod u+rw "${dirPath}"`; } if (error.code === 'ENOENT') { return `Configuration directory does not exist. The application may not be installed or configured yet. Fix: Create directory "mkdir -p ${dirPath}" and run application first`; } if (error.code === 'EBUSY' || error.code === 'ETXTBSY') { return `Configuration file is currently in use. Fix: Close the application and try again`; } } catch (_statError) { return `Cannot access configuration directory: ${error.message} Try: Check if the path exists and you have proper permissions`; } return `Unexpected permission error: ${error.message} Last resort: Run with elevated permissions (not recommended)`; } /** * Validate file system permissions for configuration operations */ export function validateFilePermissions(configPath: string): ValidationResult { const result: ValidationResult = { valid: true, errors: [], warnings: [], fixable: true, }; const dirPath = path.dirname(configPath); try { // Check if directory exists if (!fs.existsSync(dirPath)) { result.valid = false; result.errors.push(`Configuration directory does not exist: ${dirPath}`); return result; } // Check directory permissions try { fs.accessSync(dirPath, fs.constants.R_OK | fs.constants.W_OK); } catch (error: any) { result.valid = false; result.errors.push(handlePermissionError(configPath, error)); return result; } // Check file permissions if file exists if (fs.existsSync(configPath)) { try { fs.accessSync(configPath, fs.constants.R_OK | fs.constants.W_OK); } catch (error: any) { result.valid = false; result.errors.push(handlePermissionError(configPath, error)); return result; } } } catch (error: any) { result.valid = false; result.errors.push(`File system error: ${error.message}`); } return result; } /** * Generate operation preview for user confirmation */ export function generateOperationPreview( app: string, configPath: string, serversToImport: string[], replacementUrl: string, backupPath: string, ): OperationPreview { const risks: string[] = []; // Check for potential issues if (serversToImport.length === 0) { risks.push('No MCP servers found to import'); } if (serversToImport.length > 10) { risks.push(`Large number of servers (${serversToImport.length}) - consolidation may take time`); } if (!fs.existsSync(configPath)) { risks.push('Configuration file does not exist - will be created'); } return { app, configPath, serversToImport, replacementUrl, backupPath, risks, }; } /** * Validate operation before execution */ export async function validateOperation( configPath: string, content: any, serverUrl: string, ): Promise<{ configValidation: ValidationResult; permissionValidation: ValidationResult; connectivityValidation: ValidationResult; canProceed: boolean; }> { const configValidation = validateAppConfig(configPath, content); const permissionValidation = validateFilePermissions(configPath); const connectivityValidation = await validateServer1mcpConnection(serverUrl); const canProceed = configValidation.valid && permissionValidation.valid && connectivityValidation.valid; return { configValidation, permissionValidation, connectivityValidation, canProceed, }; } /** * Validate that a path is safe (prevent path traversal attacks) */ export function validateSafePath(filePath: string): boolean { const resolved = path.resolve(filePath); const normalized = path.normalize(filePath); // Check for path traversal attempts if (normalized.includes('..')) { return false; } // Check for dangerous paths (adjust based on your security requirements) const dangerousPaths = ['/etc/', '/usr/', '/bin/', '/sbin/', '/var/']; if (process.platform !== 'win32') { for (const dangerous of dangerousPaths) { if (resolved.startsWith(dangerous)) { return false; } } } return true; } /** * Validate JSON content safely */ export function validateJsonContent(content: string): { valid: boolean; parsed?: any; error?: string } { try { const parsed = JSON.parse(content); return { valid: true, parsed }; } catch (error: any) { return { valid: false, error: `Invalid JSON: ${error.message}`, }; } }

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/1mcp-app/agent'

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