evalstate's picture
download
raw
6.02 kB
import type { Tool } from '@modelcontextprotocol/sdk/types.js';
import type {
JsonSchema,
JsonSchemaProperty,
SchemaComplexityResult,
ParameterInfo,
} from '../types.js';
import { isFileDataProperty } from '../types.js';
/**
* Analyzes a tool schema to determine if it's simple enough for the dynamic space tool
*
* Supported types:
* - string, number, boolean
* - enum (with predefined values)
* - array of primitives
* - shallow objects (one level deep with primitive properties)
* - FileData (as string URLs)
*
* Rejected types:
* - Deeply nested objects (2+ levels)
* - Arrays of objects
* - Union types
* - Recursive schemas
*/
export function analyzeSchemaComplexity(tool: Tool): SchemaComplexityResult {
const result: SchemaComplexityResult = {
isSimple: true,
parameters: [],
toolName: tool.name,
toolDescription: tool.description,
};
const inputSchema = tool.inputSchema as JsonSchema;
if (!inputSchema || !inputSchema.properties) {
return result;
}
const properties = inputSchema.properties;
const required = inputSchema.required || [];
for (const [paramName, prop] of Object.entries(properties)) {
const paramInfo = analyzeProperty(paramName, prop, required.includes(paramName));
// Check if this parameter is too complex
if (paramInfo.complexType) {
result.isSimple = false;
result.reason = `Parameter "${paramName}" has complex type: ${paramInfo.complexType}`;
return result;
}
result.parameters.push(paramInfo);
}
return result;
}
/**
* Analyzes a single property to extract parameter information
*/
function analyzeProperty(name: string, prop: JsonSchemaProperty, isRequired: boolean): ParameterInfo {
const paramInfo: ParameterInfo = {
name,
type: prop.type || 'unknown',
description: prop.description,
required: isRequired,
default: prop.default,
enum: prop.enum,
};
// Check for FileData types - treat as string URLs
if (isFileDataProperty(prop)) {
paramInfo.type = 'string (file URL)';
paramInfo.isFileData = true;
return paramInfo;
}
// Check for enum types
if (prop.enum && Array.isArray(prop.enum) && prop.enum.length > 0) {
paramInfo.type = `enum (${prop.enum.length} values)`;
return paramInfo;
}
// Check for complex types
if (prop.type === 'object') {
// Check if it's a shallow object
if (prop.properties) {
const nestedProps = Object.values(prop.properties);
const hasComplexNested = nestedProps.some(
(nested) => nested.type === 'object' || nested.type === 'array'
);
if (hasComplexNested) {
paramInfo.complexType = 'deeply nested object (2+ levels)';
} else {
paramInfo.type = 'object (shallow)';
}
} else {
// Object without properties definition is too vague
paramInfo.complexType = 'object without defined properties';
}
return paramInfo;
}
// Check for array types
if (prop.type === 'array') {
if (prop.items) {
const itemType = prop.items.type;
if (itemType === 'object') {
paramInfo.complexType = 'array of objects';
} else if (itemType === 'array') {
paramInfo.complexType = 'nested arrays';
} else {
paramInfo.type = `array<${itemType || 'unknown'}>`;
}
} else {
paramInfo.type = 'array<any>';
}
return paramInfo;
}
// Simple types are fine
if (['string', 'number', 'integer', 'boolean'].includes(prop.type || '')) {
return paramInfo;
}
// Unknown or unsupported type
if (!prop.type) {
paramInfo.complexType = 'unknown type';
}
return paramInfo;
}
/**
* Validates parameter values against the schema
*/
export function validateParameters(
parameters: Record<string, unknown>,
schemaResult: SchemaComplexityResult
): { valid: boolean; errors: string[] } {
const errors: string[] = [];
// Check for missing required parameters
const requiredParams = schemaResult.parameters.filter((p) => p.required);
for (const param of requiredParams) {
if (!(param.name in parameters) || parameters[param.name] === undefined) {
errors.push(`Missing required parameter: "${param.name}"`);
}
}
// Check types (basic validation)
for (const [key, value] of Object.entries(parameters)) {
const paramInfo = schemaResult.parameters.find((p) => p.name === key);
if (!paramInfo) {
// Unknown parameter - warning but not error (permissive inputs)
continue;
}
// Type checking
if (value !== null && value !== undefined) {
if (!validateType(value, paramInfo)) {
errors.push(
`Parameter "${key}" should be type ${paramInfo.type}, got ${typeof value}`
);
}
}
}
return {
valid: errors.length === 0,
errors,
};
}
/**
* Validates a value against a parameter type
*/
function validateType(value: unknown, paramInfo: ParameterInfo): boolean {
// FileData as string URL
if (paramInfo.isFileData) {
return typeof value === 'string';
}
// Enum validation
if (paramInfo.enum && Array.isArray(paramInfo.enum)) {
return paramInfo.enum.includes(value);
}
// Basic type validation
const baseType = paramInfo.type.split(' ')[0]?.split('<')[0]; // Extract base type
switch (baseType) {
case 'string':
return typeof value === 'string';
case 'number':
case 'integer':
return typeof value === 'number';
case 'boolean':
return typeof value === 'boolean';
case 'array':
return Array.isArray(value);
case 'object':
return typeof value === 'object' && value !== null && !Array.isArray(value);
case 'enum':
// Already checked above
return true;
default:
// Unknown type, allow it (permissive)
return true;
}
}
/**
* Applies default values to parameters
*/
export function applyDefaults(
parameters: Record<string, unknown>,
schemaResult: SchemaComplexityResult
): Record<string, unknown> {
const result = { ...parameters };
for (const param of schemaResult.parameters) {
// Only apply defaults for optional parameters that are missing
if (!param.required && !(param.name in result) && param.default !== undefined) {
result[param.name] = param.default;
}
}
return result;
}

Xet Storage Details

Size:
6.02 kB
·
Xet hash:
f91f16d94216fece7c5acbdf8e844170cc6dc65a0623e93ddc9dbcea74eb79d4

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.