import { ZodTypeAny, ZodTypeDef, ZodType, ParseInput, ParseReturnType, RawCreateParams, ZodErrorMap, ProcessedCreateParams, } from "zod"; export enum McpZodTypeKind { Completable = "McpCompletable", } export type CompleteCallback = ( value: T["_input"] ) => T["_input"][] | Promise; export interface CompletableDef extends ZodTypeDef { type: T; complete: CompleteCallback; typeName: McpZodTypeKind.Completable; } export class Completable extends ZodType< T["_output"], CompletableDef, T["_input"] > { _parse(input: ParseInput): ParseReturnType { const { ctx } = this._processInputParams(input); const data = ctx.data; return this._def.type._parse({ data, path: ctx.path, parent: ctx, }); } unwrap() { return this._def.type; } static create = ( type: T, params: RawCreateParams & { complete: CompleteCallback; } ): Completable => { return new Completable({ type, typeName: McpZodTypeKind.Completable, complete: params.complete, ...processCreateParams(params), }); }; } /** * Wraps a Zod type to provide autocompletion capabilities. Useful for, e.g., prompt arguments in MCP. */ export function completable( schema: T, complete: CompleteCallback ): Completable { return Completable.create(schema, { ...schema._def, complete }); } // Not sure why this isn't exported from Zod: // https://github.com/colinhacks/zod/blob/f7ad26147ba291cb3fb257545972a8e00e767470/src/types.ts#L130 function processCreateParams(params: RawCreateParams): ProcessedCreateParams { if (!params) return {}; const { errorMap, invalid_type_error, required_error, description } = params; if (errorMap && (invalid_type_error || required_error)) { throw new Error( `Can't use "invalid_type_error" or "required_error" in conjunction with custom error map.` ); } if (errorMap) return { errorMap: errorMap, description }; const customMap: ZodErrorMap = (iss, ctx) => { const { message } = params; if (iss.code === "invalid_enum_value") { return { message: message ?? ctx.defaultError }; } if (typeof ctx.data === "undefined") { return { message: message ?? required_error ?? ctx.defaultError }; } if (iss.code !== "invalid_type") return { message: ctx.defaultError }; return { message: message ?? invalid_type_error ?? ctx.defaultError }; }; return { errorMap: customMap, description }; }