Skip to main content
Glama
constants.ts10.7 kB
/** * Constants for MCP Tool Schema Validation */ import { ValidationRule, ValidationSeverity, ValidationError } from './types'; /** * MCP Protocol version being validated against */ export const MCP_SPECIFICATION_VERSION = '2024-11-05'; /** * Supported JSON Schema draft version */ export const SUPPORTED_JSON_SCHEMA_VERSION = 'http://json-schema.org/draft-07/schema#'; /** * Maximum allowed schema nesting depth */ export const MAX_SCHEMA_DEPTH = 10; /** * Reserved tool names that cannot be used */ export const RESERVED_TOOL_NAMES = [ 'initialize', 'ping', 'list', 'call', 'cancel', 'notifications/initialized', 'notifications/cancelled', 'notifications/progress' ]; /** * Required JSON Schema properties for MCP tools */ export const REQUIRED_SCHEMA_PROPERTIES = [ 'type', 'properties' ]; /** * Valid JSON Schema types */ export const VALID_JSON_SCHEMA_TYPES = [ 'null', 'boolean', 'object', 'array', 'number', 'string', 'integer' ]; /** * MCP-specific validation rules */ export const VALIDATION_RULES: ValidationRule[] = [ { id: 'tool-name-required', description: 'Tool name is required and must be non-empty', severity: ValidationSeverity.ERROR, validate: (tool) => { const errors: ValidationError[] = []; if (!tool.name || typeof tool.name !== 'string' || tool.name.trim().length === 0) { errors.push({ code: 'TOOL_NAME_REQUIRED', message: 'Tool name is required and must be a non-empty string', severity: ValidationSeverity.ERROR, path: 'name', expected: 'non-empty string', actual: tool.name, specReference: 'MCP Specification - Tool Definition' }); } return errors; } }, { id: 'tool-name-format', description: 'Tool name must follow valid naming conventions', severity: ValidationSeverity.ERROR, validate: (tool) => { const errors: ValidationError[] = []; if (tool.name) { // Check for valid characters (alphanumeric, underscore, hyphen) if (!/^[a-zA-Z0-9_-]+$/.test(tool.name)) { errors.push({ code: 'TOOL_NAME_INVALID_FORMAT', message: 'Tool name must contain only alphanumeric characters, underscores, and hyphens', severity: ValidationSeverity.ERROR, path: 'name', expected: 'alphanumeric characters, _, or -', actual: tool.name, specReference: 'MCP Specification - Tool Naming' }); } // Check for reserved names if (RESERVED_TOOL_NAMES.includes(tool.name)) { errors.push({ code: 'TOOL_NAME_RESERVED', message: `Tool name '${tool.name}' is reserved and cannot be used`, severity: ValidationSeverity.ERROR, path: 'name', expected: 'non-reserved name', actual: tool.name, context: { reservedNames: RESERVED_TOOL_NAMES }, specReference: 'MCP Specification - Tool Naming' }); } } return errors; } }, { id: 'tool-description-required', description: 'Tool description is required and must be meaningful', severity: ValidationSeverity.ERROR, validate: (tool) => { const errors: ValidationError[] = []; if (!tool.description || typeof tool.description !== 'string' || tool.description.trim().length === 0) { errors.push({ code: 'TOOL_DESCRIPTION_REQUIRED', message: 'Tool description is required and must be a non-empty string', severity: ValidationSeverity.ERROR, path: 'description', expected: 'non-empty string', actual: tool.description, specReference: 'MCP Specification - Tool Definition' }); } else if (tool.description.trim().length < 10) { errors.push({ code: 'TOOL_DESCRIPTION_TOO_SHORT', message: 'Tool description should be at least 10 characters long for clarity', severity: ValidationSeverity.WARNING, path: 'description', expected: 'string with at least 10 characters', actual: tool.description, specReference: 'Best Practices - Tool Documentation' }); } return errors; } }, { id: 'input-schema-required', description: 'Input schema is required and must be a valid object', severity: ValidationSeverity.ERROR, validate: (tool) => { const errors: ValidationError[] = []; if (!tool.inputSchema) { errors.push({ code: 'INPUT_SCHEMA_REQUIRED', message: 'Input schema is required', severity: ValidationSeverity.ERROR, path: 'inputSchema', expected: 'object', actual: tool.inputSchema, specReference: 'MCP Specification - Tool Schema' }); } else if (typeof tool.inputSchema !== 'object' || tool.inputSchema === null || Array.isArray(tool.inputSchema)) { errors.push({ code: 'INPUT_SCHEMA_INVALID_TYPE', message: 'Input schema must be a valid object', severity: ValidationSeverity.ERROR, path: 'inputSchema', expected: 'object', actual: typeof tool.inputSchema, specReference: 'MCP Specification - Tool Schema' }); } return errors; } }, { id: 'json-schema-structure', description: 'Input schema must follow JSON Schema structure', severity: ValidationSeverity.ERROR, validate: (tool) => { const errors: ValidationError[] = []; if (tool.inputSchema && typeof tool.inputSchema === 'object') { // Check for required JSON Schema properties if (!tool.inputSchema.type) { errors.push({ code: 'JSON_SCHEMA_TYPE_MISSING', message: 'Input schema must specify a type property', severity: ValidationSeverity.ERROR, path: 'inputSchema.type', expected: 'string indicating schema type', actual: tool.inputSchema.type, specReference: 'JSON Schema Specification' }); } else if (!VALID_JSON_SCHEMA_TYPES.includes(tool.inputSchema.type)) { errors.push({ code: 'JSON_SCHEMA_TYPE_INVALID', message: `Invalid JSON Schema type: ${tool.inputSchema.type}`, severity: ValidationSeverity.ERROR, path: 'inputSchema.type', expected: VALID_JSON_SCHEMA_TYPES.join(', '), actual: tool.inputSchema.type, specReference: 'JSON Schema Specification' }); } // For object types, properties should be defined if (tool.inputSchema.type === 'object' && !tool.inputSchema.properties) { errors.push({ code: 'JSON_SCHEMA_PROPERTIES_MISSING', message: 'Object type schemas should define properties', severity: ValidationSeverity.WARNING, path: 'inputSchema.properties', expected: 'object defining schema properties', actual: tool.inputSchema.properties, specReference: 'JSON Schema Specification' }); } } return errors; } }, { id: 'schema-depth-check', description: 'Schema nesting should not exceed maximum depth', severity: ValidationSeverity.WARNING, validate: (tool) => { const errors: ValidationError[] = []; function checkDepth(obj: any, currentDepth = 0, path = 'inputSchema'): number { if (currentDepth > MAX_SCHEMA_DEPTH) { errors.push({ code: 'SCHEMA_DEPTH_EXCEEDED', message: `Schema nesting depth exceeds maximum of ${MAX_SCHEMA_DEPTH}`, severity: ValidationSeverity.WARNING, path, expected: `depth <= ${MAX_SCHEMA_DEPTH}`, actual: `depth > ${MAX_SCHEMA_DEPTH}`, specReference: 'Best Practices - Schema Complexity' }); return currentDepth; } if (typeof obj !== 'object' || obj === null) { return currentDepth; } let maxDepth = currentDepth; for (const [key, value] of Object.entries(obj)) { const newPath = `${path}.${key}`; const depth = checkDepth(value, currentDepth + 1, newPath); maxDepth = Math.max(maxDepth, depth); } return maxDepth; } if (tool.inputSchema) { checkDepth(tool.inputSchema); } return errors; } }, { id: 'required-fields-format', description: 'Required fields must be properly formatted', severity: ValidationSeverity.ERROR, validate: (tool) => { const errors: ValidationError[] = []; if (tool.inputSchema && tool.inputSchema.required) { if (!Array.isArray(tool.inputSchema.required)) { errors.push({ code: 'REQUIRED_FIELDS_INVALID_FORMAT', message: 'Required fields must be an array of strings', severity: ValidationSeverity.ERROR, path: 'inputSchema.required', expected: 'array of strings', actual: typeof tool.inputSchema.required, specReference: 'JSON Schema Specification' }); } else { // Check each required field is a string and exists in properties tool.inputSchema.required.forEach((field: any, index: number) => { if (typeof field !== 'string') { errors.push({ code: 'REQUIRED_FIELD_INVALID_TYPE', message: `Required field at index ${index} must be a string`, severity: ValidationSeverity.ERROR, path: `inputSchema.required[${index}]`, expected: 'string', actual: typeof field, specReference: 'JSON Schema Specification' }); } else if (tool.inputSchema.properties && !tool.inputSchema.properties[field]) { errors.push({ code: 'REQUIRED_FIELD_NOT_DEFINED', message: `Required field '${field}' is not defined in properties`, severity: ValidationSeverity.ERROR, path: `inputSchema.required[${index}]`, expected: 'field defined in properties', actual: field, context: { availableProperties: Object.keys(tool.inputSchema.properties) }, specReference: 'JSON Schema Specification' }); } }); } } return errors; } } ];

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/learnwithcc/tally-mcp'

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