/**
* Validation utilities for MCP tool inputs
*/
export function validateAndNormalizeArgs(args: any, schema: any): any {
const normalized: any = {};
// Handle required fields
if (schema.required) {
for (const field of schema.required) {
if (args[field] === undefined || args[field] === null) {
throw new Error(`Missing required field: ${field}`);
}
}
}
// Normalize and validate each property
for (const [key, value] of Object.entries(args)) {
if (value === undefined || value === null) {
continue;
}
const propSchema = schema.properties?.[key];
if (!propSchema) {
// Skip unknown properties but don't error
continue;
}
normalized[key] = normalizeValue(value, propSchema, key);
}
return normalized;
}
function normalizeValue(value: any, schema: any, fieldName: string): any {
switch (schema.type) {
case 'string':
if (typeof value !== 'string') {
return String(value);
}
return value;
case 'number':
if (typeof value === 'string') {
const parsed = parseFloat(value);
if (isNaN(parsed)) {
throw new Error(`Field ${fieldName} must be a valid number`);
}
return parsed;
}
if (typeof value !== 'number') {
throw new Error(`Field ${fieldName} must be a number`);
}
return value;
case 'boolean':
if (typeof value === 'string') {
return value.toLowerCase() === 'true';
}
return Boolean(value);
case 'array':
if (!Array.isArray(value)) {
throw new Error(`Field ${fieldName} must be an array`);
}
return value.map((item, index) =>
schema.items ? normalizeValue(item, schema.items, `${fieldName}[${index}]`) : item
);
default:
return value;
}
}
export function safeStringify(obj: any): string {
try {
return JSON.stringify(obj, null, 2);
} catch (error) {
return `[Object could not be serialized: ${error}]`;
}
}