feat: zod v4 with backwards compatibility for v3.25+ (#1040)
This commit is contained in:
parent
7fca1f2b15
commit
418b7f9b85
3 changed files with 14723 additions and 1002 deletions
|
|
@ -2,7 +2,7 @@
|
|||
/* eslint-disable no-constant-binary-expression */
|
||||
/* eslint-disable @typescript-eslint/no-unused-expressions */
|
||||
import { Client, getSupportedElicitationModes } from './index.js';
|
||||
import { z } from 'zod';
|
||||
import * as z from 'zod/v4';
|
||||
import {
|
||||
RequestSchema,
|
||||
NotificationSchema,
|
||||
|
|
|
|||
|
|
@ -42,7 +42,15 @@ import {
|
|||
} from '../types.js';
|
||||
import { AjvJsonSchemaValidator } from '../validation/ajv-provider.js';
|
||||
import type { JsonSchemaType, JsonSchemaValidator, jsonSchemaValidator } from '../validation/types.js';
|
||||
import { ZodLiteral, ZodObject, z } from 'zod';
|
||||
import {
|
||||
AnyObjectSchema,
|
||||
SchemaOutput,
|
||||
getObjectShape,
|
||||
isZ4Schema,
|
||||
safeParse,
|
||||
type ZodV3Internal,
|
||||
type ZodV4Internal
|
||||
} from '../server/zod-compat.js';
|
||||
import type { RequestHandlerExtra } from '../shared/protocol.js';
|
||||
|
||||
/**
|
||||
|
|
@ -216,26 +224,46 @@ export class Client<
|
|||
/**
|
||||
* Override request handler registration to enforce client-side validation for elicitation.
|
||||
*/
|
||||
public override setRequestHandler<
|
||||
T extends ZodObject<{
|
||||
method: ZodLiteral<string>;
|
||||
}>
|
||||
>(
|
||||
public override setRequestHandler<T extends AnyObjectSchema>(
|
||||
requestSchema: T,
|
||||
handler: (
|
||||
request: z.infer<T>,
|
||||
request: SchemaOutput<T>,
|
||||
extra: RequestHandlerExtra<ClientRequest | RequestT, ClientNotification | NotificationT>
|
||||
) => ClientResult | ResultT | Promise<ClientResult | ResultT>
|
||||
): void {
|
||||
const method = requestSchema.shape.method.value;
|
||||
const shape = getObjectShape(requestSchema);
|
||||
const methodSchema = shape?.method;
|
||||
if (!methodSchema) {
|
||||
throw new Error('Schema is missing a method literal');
|
||||
}
|
||||
|
||||
// Extract literal value using type-safe property access
|
||||
let methodValue: unknown;
|
||||
if (isZ4Schema(methodSchema)) {
|
||||
const v4Schema = methodSchema as unknown as ZodV4Internal;
|
||||
const v4Def = v4Schema._zod?.def;
|
||||
methodValue = v4Def?.value ?? v4Schema.value;
|
||||
} else {
|
||||
const v3Schema = methodSchema as unknown as ZodV3Internal;
|
||||
const legacyDef = v3Schema._def;
|
||||
methodValue = legacyDef?.value ?? v3Schema.value;
|
||||
}
|
||||
|
||||
if (typeof methodValue !== 'string') {
|
||||
throw new Error('Schema method literal must be a string');
|
||||
}
|
||||
const method = methodValue;
|
||||
if (method === 'elicitation/create') {
|
||||
const wrappedHandler = async (
|
||||
request: z.infer<T>,
|
||||
request: SchemaOutput<T>,
|
||||
extra: RequestHandlerExtra<ClientRequest | RequestT, ClientNotification | NotificationT>
|
||||
): Promise<ClientResult | ResultT> => {
|
||||
const validatedRequest = ElicitRequestSchema.safeParse(request);
|
||||
const validatedRequest = safeParse(ElicitRequestSchema, request);
|
||||
if (!validatedRequest.success) {
|
||||
throw new McpError(ErrorCode.InvalidParams, `Invalid elicitation request: ${validatedRequest.error.message}`);
|
||||
// Type guard: if success is false, error is guaranteed to exist
|
||||
const errorMessage =
|
||||
validatedRequest.error instanceof Error ? validatedRequest.error.message : String(validatedRequest.error);
|
||||
throw new McpError(ErrorCode.InvalidParams, `Invalid elicitation request: ${errorMessage}`);
|
||||
}
|
||||
|
||||
const { params } = validatedRequest.data;
|
||||
|
|
@ -251,9 +279,12 @@ export class Client<
|
|||
|
||||
const result = await Promise.resolve(handler(request, extra));
|
||||
|
||||
const validationResult = ElicitResultSchema.safeParse(result);
|
||||
const validationResult = safeParse(ElicitResultSchema, result);
|
||||
if (!validationResult.success) {
|
||||
throw new McpError(ErrorCode.InvalidParams, `Invalid elicitation result: ${validationResult.error.message}`);
|
||||
// Type guard: if success is false, error is guaranteed to exist
|
||||
const errorMessage =
|
||||
validationResult.error instanceof Error ? validationResult.error.message : String(validationResult.error);
|
||||
throw new McpError(ErrorCode.InvalidParams, `Invalid elicitation result: ${errorMessage}`);
|
||||
}
|
||||
|
||||
const validatedResult = validationResult.data;
|
||||
|
|
|
|||
1591
src/client/v3/index.v3.test.ts
Normal file
1591
src/client/v3/index.v3.test.ts
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue