Skip to main content
Glama
validation.tsβ€’5.82 kB
import { Request, Response, NextFunction } from 'express'; import { body, param, query, validationResult } from 'express-validator'; import { errorResponse, ERROR_CODES } from '../utils/response.js'; import { sanitizeInput, isValidSessionId } from '../utils/security.js'; /** * Middleware to check validation results and return error if validation failed */ export function handleValidationErrors(req: Request, res: Response, next: NextFunction): void { const errors = validationResult(req); if (!errors.isEmpty()) { errorResponse( res, ERROR_CODES.VALIDATION_ERROR, 'Validation failed', 400, errors.array(), req.requestId ); return; } next(); } /** * Sanitize text input middleware */ export function sanitizeTextInput(fields: string[]) { return (req: Request, res: Response, next: NextFunction): void => { for (const field of fields) { if (req.body[field] && typeof req.body[field] === 'string') { req.body[field] = sanitizeInput(req.body[field]); } } next(); }; } // Validation schemas for different endpoints /** * Message processing validation */ export const validateMessageRequest = [ body('message') .isString() .isLength({ min: 1, max: 50000 }) .withMessage('Message must be a string between 1 and 50000 characters'), body('sessionId') .optional() .custom(value => { if (value && !isValidSessionId(value)) { throw new Error('Invalid session ID format'); } return true; }), body('images').optional().isArray().withMessage('Images must be an array'), body('images.*').optional().isString().withMessage('Each image must be a base64 string'), sanitizeTextInput(['message']), handleValidationErrors, ]; /** * Session ID parameter validation */ export const validateSessionId = [ param('sessionId').custom(value => { if (!isValidSessionId(value)) { throw new Error('Invalid session ID format'); } return true; }), handleValidationErrors, ]; /** * Session creation validation */ export const validateCreateSession = [ body('sessionId') .optional() .custom(value => { if (value && !isValidSessionId(value)) { throw new Error('Invalid session ID format'); } return true; }), body('config').optional().isObject().withMessage('Config must be an object'), handleValidationErrors, ]; /** * MCP server configuration validation */ export const validateMcpServerConfig = [ body('name') .isString() .isLength({ min: 1, max: 100 }) .withMessage('Server name must be between 1 and 100 characters'), body('command').optional().isString().withMessage('Command must be a string'), body('args').optional().isArray().withMessage('Args must be an array'), body('env').optional().isObject().withMessage('Environment must be an object'), body('transport') .optional() .isIn(['stdio', 'sse', 'streamable-http']) .withMessage('Transport must be stdio, sse, or streamable-http'), body('connectionMode') .optional() .isIn(['strict', 'lenient']) .withMessage('Connection mode must be strict or lenient'), sanitizeTextInput(['name', 'command']), handleValidationErrors, ]; /** * MCP server ID validation */ export const validateMcpServerId = [ param('serverId') .isString() .isLength({ min: 1, max: 100 }) .withMessage('Server ID must be between 1 and 100 characters'), handleValidationErrors, ]; /** * Tool execution validation */ export const validateToolExecution = [ param('serverId') .isString() .isLength({ min: 1, max: 100 }) .withMessage('Server ID must be between 1 and 100 characters'), param('toolName') .isString() .isLength({ min: 1, max: 100 }) .withMessage('Tool name must be between 1 and 100 characters'), body('arguments').optional().isObject().withMessage('Arguments must be an object'), handleValidationErrors, ]; /** * LLM configuration validation */ export const validateLlmConfig = [ body('provider') .isString() .isIn(['openai', 'anthropic', 'openrouter', 'ollama', 'qwen', 'aws', 'azure']) .withMessage( 'Provider must be one of: openai, anthropic, openrouter, ollama, qwen, aws, azure' ), body('model') .isString() .isLength({ min: 1, max: 100 }) .withMessage('Model must be between 1 and 100 characters'), body('config').optional().isObject().withMessage('Config must be an object'), // AWS-specific validations body('config.aws.region').optional().isString().withMessage('AWS region must be a string'), body('config.aws.accessKeyId') .optional() .isString() .withMessage('AWS access key ID must be a string'), body('config.aws.secretAccessKey') .optional() .isString() .withMessage('AWS secret access key must be a string'), body('config.aws.sessionToken') .optional() .isString() .withMessage('AWS session token must be a string'), // Azure-specific validations body('config.azure.endpoint') .optional() .isURL() .withMessage('Azure endpoint must be a valid URL'), body('config.azure.deploymentName') .optional() .isString() .withMessage('Azure deployment name must be a string'), // Conditional validation - Azure requires endpoint body().custom(value => { if (value.provider === 'azure' && !value.config?.azure?.endpoint) { throw new Error('Azure provider requires config.azure.endpoint to be provided'); } if (value.provider === 'aws' && !value.config?.aws) { throw new Error('AWS provider requires config.aws object to be provided'); } return true; }), sanitizeTextInput(['provider', 'model']), handleValidationErrors, ]; /** * Query parameter validation */ export const validateListParams = [ query('limit') .optional() .isInt({ min: 1, max: 100 }) .withMessage('Limit must be between 1 and 100'), query('offset').optional().isInt({ min: 0 }).withMessage('Offset must be a non-negative integer'), handleValidationErrors, ];

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/campfirein/cipher'

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