Something went wrong. Try again.
source dump of claude code forked from oppi.li/claude-code
Something went wrong. Try again.
9.4 kB · 336 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337import type { EnumSchema, MultiSelectEnumSchema, PrimitiveSchemaDefinition, StringSchema,} from '@modelcontextprotocol/sdk/types.js'import { z } from 'zod/v4'import { jsonStringify } from '../slowOperations.js'import { plural } from '../stringUtils.js'import { looksLikeISO8601, parseNaturalLanguageDateTime,} from './dateTimeParser.js'
export type ValidationResult = { value?: string | number | boolean isValid: boolean error?: string}
const STRING_FORMATS = { email: { description: 'email address', example: 'user@example.com', }, uri: { description: 'URI', example: 'https://example.com', }, date: { description: 'date', example: '2024-03-15', }, 'date-time': { description: 'date-time', example: '2024-03-15T14:30:00Z', },}
/** * Check if schema is a single-select enum (either legacy `enum` format or new `oneOf` format) */export const isEnumSchema = ( schema: PrimitiveSchemaDefinition,): schema is EnumSchema => { return schema.type === 'string' && ('enum' in schema || 'oneOf' in schema)}
/** * Check if schema is a multi-select enum (`type: "array"` with `items.enum` or `items.anyOf`) */export function isMultiSelectEnumSchema( schema: PrimitiveSchemaDefinition,): schema is MultiSelectEnumSchema { return ( schema.type === 'array' && 'items' in schema && typeof schema.items === 'object' && schema.items !== null && ('enum' in schema.items || 'anyOf' in schema.items) )}
/** * Get values from a multi-select enum schema */export function getMultiSelectValues(schema: MultiSelectEnumSchema): string[] { if ('anyOf' in schema.items) { return schema.items.anyOf.map(item => item.const) } if ('enum' in schema.items) { return schema.items.enum } return []}
/** * Get display labels from a multi-select enum schema */export function getMultiSelectLabels(schema: MultiSelectEnumSchema): string[] { if ('anyOf' in schema.items) { return schema.items.anyOf.map(item => item.title) } if ('enum' in schema.items) { return schema.items.enum } return []}
/** * Get label for a specific value in a multi-select enum */export function getMultiSelectLabel( schema: MultiSelectEnumSchema, value: string,): string { const index = getMultiSelectValues(schema).indexOf(value) return index >= 0 ? (getMultiSelectLabels(schema)[index] ?? value) : value}
/** * Get enum values from EnumSchema (handles both legacy `enum` and new `oneOf` formats) */export function getEnumValues(schema: EnumSchema): string[] { if ('oneOf' in schema) { return schema.oneOf.map(item => item.const) } if ('enum' in schema) { return schema.enum } return []}
/** * Get enum display labels from EnumSchema */export function getEnumLabels(schema: EnumSchema): string[] { if ('oneOf' in schema) { return schema.oneOf.map(item => item.title) } if ('enum' in schema) { return ('enumNames' in schema ? schema.enumNames : undefined) ?? schema.enum } return []}
/** * Get label for a specific enum value */export function getEnumLabel(schema: EnumSchema, value: string): string { const index = getEnumValues(schema).indexOf(value) return index >= 0 ? (getEnumLabels(schema)[index] ?? value) : value}
function getZodSchema(schema: PrimitiveSchemaDefinition): z.ZodTypeAny { if (isEnumSchema(schema)) { const [first, ...rest] = getEnumValues(schema) if (!first) { return z.never() } return z.enum([first, ...rest]) } if (schema.type === 'string') { let stringSchema = z.string() if (schema.minLength !== undefined) { stringSchema = stringSchema.min(schema.minLength, { message: `Must be at least ${schema.minLength} ${plural(schema.minLength, 'character')}`, }) } if (schema.maxLength !== undefined) { stringSchema = stringSchema.max(schema.maxLength, { message: `Must be at most ${schema.maxLength} ${plural(schema.maxLength, 'character')}`, }) } switch (schema.format) { case 'email': stringSchema = stringSchema.email({ message: 'Must be a valid email address, e.g. user@example.com', }) break case 'uri': stringSchema = stringSchema.url({ message: 'Must be a valid URI, e.g. https://example.com', }) break case 'date': stringSchema = stringSchema.date( 'Must be a valid date, e.g. 2024-03-15, today, next Monday', ) break case 'date-time': stringSchema = stringSchema.datetime({ offset: true, message: 'Must be a valid date-time, e.g. 2024-03-15T14:30:00Z, tomorrow at 3pm', }) break default: // No specific format validation break } return stringSchema } if (schema.type === 'number' || schema.type === 'integer') { const typeLabel = schema.type === 'integer' ? 'an integer' : 'a number' const isInteger = schema.type === 'integer' const formatNum = (n: number) => Number.isInteger(n) && !isInteger ? `${n}.0` : String(n)
// Build a single descriptive error message for range violations const rangeMsg = schema.minimum !== undefined && schema.maximum !== undefined ? `Must be ${typeLabel} between ${formatNum(schema.minimum)} and ${formatNum(schema.maximum)}` : schema.minimum !== undefined ? `Must be ${typeLabel} >= ${formatNum(schema.minimum)}` : schema.maximum !== undefined ? `Must be ${typeLabel} <= ${formatNum(schema.maximum)}` : `Must be ${typeLabel}`
let numberSchema = z.coerce.number({ error: rangeMsg, }) if (schema.type === 'integer') { numberSchema = numberSchema.int({ message: rangeMsg }) } if (schema.minimum !== undefined) { numberSchema = numberSchema.min(schema.minimum, { message: rangeMsg, }) } if (schema.maximum !== undefined) { numberSchema = numberSchema.max(schema.maximum, { message: rangeMsg, }) } return numberSchema } if (schema.type === 'boolean') { return z.coerce.boolean() }
throw new Error(`Unsupported schema: ${jsonStringify(schema)}`)}
export function validateElicitationInput( stringValue: string, schema: PrimitiveSchemaDefinition,): ValidationResult { const zodSchema = getZodSchema(schema) const parseResult = zodSchema.safeParse(stringValue)
if (parseResult.success) { // zodSchema always produces primitive types for elicitation return { value: parseResult.data as string | number | boolean, isValid: true, } } return { isValid: false, error: parseResult.error.issues.map(e => e.message).join('; '), }}
const hasStringFormat = ( schema: PrimitiveSchemaDefinition,): schema is StringSchema & { format: string } => { return ( schema.type === 'string' && 'format' in schema && typeof schema.format === 'string' )}
/** * Returns a helpful placeholder/hint for a given format */export function getFormatHint( schema: PrimitiveSchemaDefinition,): string | undefined { if (schema.type === 'string') { if (!hasStringFormat(schema)) { return undefined }
const { description, example } = STRING_FORMATS[schema.format] || {} return `${description}, e.g. ${example}` }
if (schema.type === 'number' || schema.type === 'integer') { const isInteger = schema.type === 'integer' const formatNum = (n: number) => Number.isInteger(n) && !isInteger ? `${n}.0` : String(n)
if (schema.minimum !== undefined && schema.maximum !== undefined) { return `(${schema.type} between ${formatNum(schema.minimum!)} and ${formatNum(schema.maximum!)})` } else if (schema.minimum !== undefined) { return `(${schema.type} >= ${formatNum(schema.minimum!)})` } else if (schema.maximum !== undefined) { return `(${schema.type} <= ${formatNum(schema.maximum!)})` } else { const example = schema.type === 'integer' ? '42' : '3.14' return `(${schema.type}, e.g. ${example})` } }
return undefined}
/** * Check if a schema is a date or date-time format that supports NL parsing */export function isDateTimeSchema( schema: PrimitiveSchemaDefinition,): schema is StringSchema & { format: 'date' | 'date-time' } { return ( schema.type === 'string' && 'format' in schema && (schema.format === 'date' || schema.format === 'date-time') )}
/** * Async validation that attempts NL date/time parsing via Haiku * when the input doesn't look like ISO 8601. */export async function validateElicitationInputAsync( stringValue: string, schema: PrimitiveSchemaDefinition, signal: AbortSignal,): Promise<ValidationResult> { const syncResult = validateElicitationInput(stringValue, schema) if (syncResult.isValid) { return syncResult }
if (isDateTimeSchema(schema) && !looksLikeISO8601(stringValue)) { const parseResult = await parseNaturalLanguageDateTime( stringValue, schema.format, signal, )
if (parseResult.success) { const validatedParsed = validateElicitationInput( parseResult.value, schema, ) if (validatedParsed.isValid) { return validatedParsed } } }
return syncResult}