jezweb-mcp-core
Version:
Jezweb Model Context Protocol (MCP) Core - A universal server for providing AI tools and resources, designed for seamless integration with various AI models and clients. Features adaptable multi-provider support, comprehensive tool and resource management
397 lines • 17.6 kB
JavaScript
/**
* Shared Validation Library - Comprehensive validation utilities for OpenAI Assistants MCP Server
*
* This module consolidates all validation logic used across both Cloudflare Workers
* and NPM package deployments. Following MCP best practices for error messages
* and parameter validation.
*
* Consolidates 562 lines of duplicate validation code.
*/
import { MCPError, ErrorCodes } from '../types/index.js';
// Supported OpenAI models
export const SUPPORTED_MODELS = [
'gpt-4',
'gpt-4o',
'gpt-4-turbo',
'gpt-4-turbo-preview',
'gpt-4-0125-preview',
'gpt-4-1106-preview',
'gpt-4-vision-preview',
'gpt-3.5-turbo',
'gpt-3.5-turbo-0125',
'gpt-3.5-turbo-1106',
'gpt-3.5-turbo-16k'
];
// OpenAI ID format patterns
export const ID_PATTERNS = {
assistant: /^asst_[a-zA-Z0-9]{24}$/,
thread: /^thread_[a-zA-Z0-9]{24}$/,
message: /^msg_[a-zA-Z0-9]{24}$/,
run: /^run_[a-zA-Z0-9]{24}$/,
step: /^step_[a-zA-Z0-9]{24}$/,
file: /^file-[a-zA-Z0-9]{24}$/,
tool_call: /^call_[a-zA-Z0-9]{24}$/
};
/**
* Validate OpenAI ID format
*/
export function validateOpenAIId(id, type, paramName) {
if (!id) {
return {
isValid: false,
error: new MCPError(ErrorCodes.INVALID_PARAMS, `Required parameter '${paramName}' is missing. Provide a valid ${type} ID in format '${type}_' followed by 24 characters (e.g., '${type}_abc123def456ghi789jkl012'). See docs://openai-assistants-api for more information.`)
};
}
if (typeof id !== 'string') {
return {
isValid: false,
error: new MCPError(ErrorCodes.INVALID_PARAMS, `Parameter '${paramName}' must be a string. Expected ${type} ID format: '${type}_' followed by 24 characters (e.g., '${type}_abc123def456ghi789jkl012').`)
};
}
const pattern = ID_PATTERNS[type];
if (!pattern.test(id)) {
return {
isValid: false,
error: new MCPError(ErrorCodes.INVALID_PARAMS, `Invalid ${type} ID format for parameter '${paramName}'. Expected '${type}_' followed by 24 characters (e.g., '${type}_abc123def456ghi789jkl012'), but received: '${id}'. See docs://openai-assistants-api for ID format specifications.`)
};
}
return { isValid: true };
}
/**
* Validate OpenAI model name
*/
export function validateModel(model, paramName = 'model') {
if (!model) {
return {
isValid: false,
error: new MCPError(ErrorCodes.INVALID_PARAMS, `Required parameter '${paramName}' is missing. Specify a supported model like 'gpt-4', 'gpt-4o', 'gpt-4-turbo', or 'gpt-3.5-turbo'. See docs://openai-assistants-api for the complete list of supported models.`)
};
}
if (typeof model !== 'string') {
return {
isValid: false,
error: new MCPError(ErrorCodes.INVALID_PARAMS, `Parameter '${paramName}' must be a string. Supported models include: ${SUPPORTED_MODELS.join(', ')}. See docs://openai-assistants-api for model specifications.`)
};
}
if (!SUPPORTED_MODELS.includes(model)) {
return {
isValid: false,
error: new MCPError(ErrorCodes.INVALID_PARAMS, `Invalid model '${model}' for parameter '${paramName}'. Supported models include: ${SUPPORTED_MODELS.join(', ')}. See assistant://templates for configuration examples.`)
};
}
return { isValid: true };
}
/**
* Validate numeric range
*/
export function validateNumericRange(value, paramName, min, max, defaultValue) {
if (value === undefined || value === null) {
if (defaultValue !== undefined) {
return { isValid: true };
}
return {
isValid: false,
error: new MCPError(ErrorCodes.INVALID_PARAMS, `Required parameter '${paramName}' is missing. Provide a number between ${min} and ${max} (inclusive). See docs://openai-assistants-api for parameter specifications.`)
};
}
if (typeof value !== 'number' || isNaN(value)) {
return {
isValid: false,
error: new MCPError(ErrorCodes.INVALID_PARAMS, `Parameter '${paramName}' must be a valid number between ${min} and ${max} (inclusive), but received: ${value}. Example: ${paramName}: ${Math.min(max, Math.max(min, defaultValue || min))}.`)
};
}
if (value < min || value > max) {
return {
isValid: false,
error: new MCPError(ErrorCodes.INVALID_PARAMS, `Parameter '${paramName}' must be between ${min} and ${max} (inclusive), but received: ${value}. Adjust the value to be within the valid range. See docs://openai-assistants-api for parameter limits.`)
};
}
return { isValid: true };
}
/**
* Validate required string parameter
*/
export function validateRequiredString(value, paramName, examples) {
if (!value) {
const exampleText = examples ? ` Examples: ${examples.join(', ')}.` : '';
return {
isValid: false,
error: new MCPError(ErrorCodes.INVALID_PARAMS, `Required parameter '${paramName}' is missing. Provide a non-empty string value.${exampleText} See docs://openai-assistants-api for parameter specifications.`)
};
}
if (typeof value !== 'string') {
const exampleText = examples ? ` Examples: ${examples.join(', ')}.` : '';
return {
isValid: false,
error: new MCPError(ErrorCodes.INVALID_PARAMS, `Parameter '${paramName}' must be a string, but received: ${typeof value}.${exampleText}`)
};
}
if (value.trim().length === 0) {
const exampleText = examples ? ` Examples: ${examples.join(', ')}.` : '';
return {
isValid: false,
error: new MCPError(ErrorCodes.INVALID_PARAMS, `Parameter '${paramName}' cannot be empty. Provide a non-empty string value.${exampleText}`)
};
}
return { isValid: true };
}
/**
* Validate enum value
*/
export function validateEnum(value, paramName, allowedValues, defaultValue) {
if (value === undefined || value === null) {
if (defaultValue !== undefined) {
return { isValid: true };
}
return {
isValid: false,
error: new MCPError(ErrorCodes.INVALID_PARAMS, `Required parameter '${paramName}' is missing. Allowed values: ${allowedValues.join(', ')}. See docs://openai-assistants-api for parameter specifications.`)
};
}
if (!allowedValues.includes(value)) {
return {
isValid: false,
error: new MCPError(ErrorCodes.INVALID_PARAMS, `Invalid value '${value}' for parameter '${paramName}'. Allowed values: ${allowedValues.join(', ')}. Example: ${paramName}: "${allowedValues[0]}".`)
};
}
return { isValid: true };
}
/**
* Validate array parameter
*/
export function validateArray(value, paramName, required = false) {
if (value === undefined || value === null) {
if (required) {
return {
isValid: false,
error: new MCPError(ErrorCodes.INVALID_PARAMS, `Required parameter '${paramName}' is missing. Provide an array value. Example: ${paramName}: []. See docs://openai-assistants-api for parameter specifications.`)
};
}
return { isValid: true };
}
if (!Array.isArray(value)) {
return {
isValid: false,
error: new MCPError(ErrorCodes.INVALID_PARAMS, `Parameter '${paramName}' must be an array, but received: ${typeof value}. Example: ${paramName}: [].`)
};
}
return { isValid: true };
}
/**
* Validate metadata object
*/
export function validateMetadata(metadata, paramName = 'metadata') {
if (metadata === undefined || metadata === null) {
return { isValid: true };
}
if (typeof metadata !== 'object' || Array.isArray(metadata)) {
return {
isValid: false,
error: new MCPError(ErrorCodes.INVALID_PARAMS, `Parameter '${paramName}' must be an object with key-value pairs, but received: ${typeof metadata}. Example: ${paramName}: {"key": "value", "category": "support"}.`)
};
}
// Check metadata size (OpenAI has a 16KB limit)
const metadataString = JSON.stringify(metadata);
if (metadataString.length > 16384) {
return {
isValid: false,
error: new MCPError(ErrorCodes.INVALID_PARAMS, `Parameter '${paramName}' exceeds the 16KB size limit. Current size: ${metadataString.length} bytes. Reduce the amount of metadata or use shorter keys/values. See docs://openai-assistants-api for metadata limitations.`)
};
}
return { isValid: true };
}
/**
* Validate tool configuration
*/
export function validateTools(tools, paramName = 'tools') {
if (tools === undefined || tools === null) {
return { isValid: true };
}
const arrayValidation = validateArray(tools, paramName);
if (!arrayValidation.isValid) {
return arrayValidation;
}
const allowedToolTypes = ['code_interpreter', 'file_search', 'function'];
for (let i = 0; i < tools.length; i++) {
const tool = tools[i];
if (!tool || typeof tool !== 'object') {
return {
isValid: false,
error: new MCPError(ErrorCodes.INVALID_PARAMS, `Tool at index ${i} in '${paramName}' must be an object. Example: {"type": "code_interpreter"} or {"type": "function", "function": {...}}. See docs://best-practices for tool configuration.`)
};
}
if (!tool.type || !allowedToolTypes.includes(tool.type)) {
return {
isValid: false,
error: new MCPError(ErrorCodes.INVALID_PARAMS, `Tool at index ${i} has invalid type '${tool.type}'. Allowed types: ${allowedToolTypes.join(', ')}. Example: {"type": "code_interpreter"}. See assistant://templates for tool examples.`)
};
}
// Validate function tool
if (tool.type === 'function') {
if (!tool.function) {
return {
isValid: false,
error: new MCPError(ErrorCodes.INVALID_PARAMS, `Function tool at index ${i} is missing 'function' property. Example: {"type": "function", "function": {"name": "my_function", "description": "Function description"}}. See docs://best-practices for function tool configuration.`)
};
}
if (!tool.function.name || typeof tool.function.name !== 'string') {
return {
isValid: false,
error: new MCPError(ErrorCodes.INVALID_PARAMS, `Function tool at index ${i} is missing or has invalid 'name' property. Provide a string name for the function. Example: "name": "calculate_sum".`)
};
}
}
}
return { isValid: true };
}
/**
* Validate tool resources configuration
*/
export function validateToolResources(toolResources, tools, paramName = 'tool_resources') {
if (toolResources === undefined || toolResources === null) {
return { isValid: true };
}
if (typeof toolResources !== 'object' || Array.isArray(toolResources)) {
return {
isValid: false,
error: new MCPError(ErrorCodes.INVALID_PARAMS, `Parameter '${paramName}' must be an object, but received: ${typeof toolResources}. Example: {"file_search": {"vector_store_ids": ["vs_123"]}}. See docs://best-practices for tool resource configuration.`)
};
}
// Check if file_search tool_resources is provided without file_search tool
if (toolResources.file_search && !tools.some(tool => tool.type === 'file_search')) {
return {
isValid: false,
error: new MCPError(ErrorCodes.INVALID_PARAMS, `Cannot specify 'file_search' in '${paramName}' without including file_search tool in tools array. Add {"type": "file_search"} to tools or remove file_search from tool_resources. See docs://best-practices for configuration guidance.`)
};
}
// Check if code_interpreter tool_resources is provided without code_interpreter tool
if (toolResources.code_interpreter && !tools.some(tool => tool.type === 'code_interpreter')) {
return {
isValid: false,
error: new MCPError(ErrorCodes.INVALID_PARAMS, `Cannot specify 'code_interpreter' in '${paramName}' without including code_interpreter tool in tools array. Add {"type": "code_interpreter"} to tools or remove code_interpreter from tool_resources. See docs://best-practices for configuration guidance.`)
};
}
return { isValid: true };
}
/**
* Validate message role
*/
export function validateMessageRole(role, paramName = 'role') {
return validateEnum(role, paramName, ['user', 'assistant']);
}
/**
* Validate pagination parameters
*/
export function validatePaginationParams(params) {
// Validate limit
if (params.limit !== undefined) {
const limitValidation = validateNumericRange(params.limit, 'limit', 1, 100, 20);
if (!limitValidation.isValid) {
return limitValidation;
}
}
// Validate order
if (params.order !== undefined) {
const orderValidation = validateEnum(params.order, 'order', ['asc', 'desc'], 'desc');
if (!orderValidation.isValid) {
return orderValidation;
}
}
// Validate after/before cursor format (if provided)
if (params.after !== undefined && params.after !== null) {
if (typeof params.after !== 'string' || params.after.trim().length === 0) {
return {
isValid: false,
error: new MCPError(ErrorCodes.INVALID_PARAMS, `Parameter 'after' must be a non-empty string cursor ID (e.g., 'asst_abc123def456ghi789jkl012'). Use the ID from the last item of the previous page.`)
};
}
}
if (params.before !== undefined && params.before !== null) {
if (typeof params.before !== 'string' || params.before.trim().length === 0) {
return {
isValid: false,
error: new MCPError(ErrorCodes.INVALID_PARAMS, `Parameter 'before' must be a non-empty string cursor ID (e.g., 'asst_abc123def456ghi789jkl012'). Use the ID from the first item of the next page.`)
};
}
}
// Cannot specify both after and before
if (params.after && params.before) {
return {
isValid: false,
error: new MCPError(ErrorCodes.INVALID_PARAMS, `Cannot specify both 'after' and 'before' parameters simultaneously. Use 'after' for forward pagination or 'before' for backward pagination, but not both.`)
};
}
return { isValid: true };
}
/**
* Comprehensive validation for assistant creation
*/
export function validateCreateAssistantParams(params) {
// Validate required model
const modelValidation = validateModel(params.model);
if (!modelValidation.isValid) {
return modelValidation;
}
// Validate optional name
if (params.name !== undefined) {
const nameValidation = validateRequiredString(params.name, 'name', ['Customer Support Bot', 'Code Review Assistant']);
if (!nameValidation.isValid) {
return nameValidation;
}
}
// Validate optional description
if (params.description !== undefined && params.description !== null) {
if (typeof params.description !== 'string') {
return {
isValid: false,
error: new MCPError(ErrorCodes.INVALID_PARAMS, `Parameter 'description' must be a string, but received: ${typeof params.description}. Example: "Helps customers with product questions and troubleshooting".`)
};
}
}
// Validate optional instructions
if (params.instructions !== undefined && params.instructions !== null) {
if (typeof params.instructions !== 'string') {
return {
isValid: false,
error: new MCPError(ErrorCodes.INVALID_PARAMS, `Parameter 'instructions' must be a string, but received: ${typeof params.instructions}. Example: "You are a helpful customer support assistant. Be polite and provide clear answers."`)
};
}
}
// Validate tools
if (params.tools !== undefined) {
const toolsValidation = validateTools(params.tools);
if (!toolsValidation.isValid) {
return toolsValidation;
}
// Validate tool_resources if tools are provided
if (params.tool_resources !== undefined) {
const toolResourcesValidation = validateToolResources(params.tool_resources, params.tools);
if (!toolResourcesValidation.isValid) {
return toolResourcesValidation;
}
}
}
// Validate metadata
if (params.metadata !== undefined) {
const metadataValidation = validateMetadata(params.metadata);
if (!metadataValidation.isValid) {
return metadataValidation;
}
}
// Validate temperature
if (params.temperature !== undefined) {
const tempValidation = validateNumericRange(params.temperature, 'temperature', 0, 2);
if (!tempValidation.isValid) {
return tempValidation;
}
}
// Validate top_p
if (params.top_p !== undefined) {
const topPValidation = validateNumericRange(params.top_p, 'top_p', 0, 1);
if (!topPValidation.isValid) {
return topPValidation;
}
}
return { isValid: true };
}
//# sourceMappingURL=validation.js.map