-
Couldn't load subscription status.
- Fork 1.4k
feat: zod v4 #869
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat: zod v4 #869
Changes from 6 commits
6db0e68
08513d3
3b28aac
1cc8c7b
9b178b7
9298723
aca892b
e2d351c
2010203
8531d8e
1461fa2
9cf8b2e
5a761b5
30fb023
3cd0546
5a64ed1
ab26df8
aa5e35b
374a673
79f6c04
a55f3aa
467c0c2
3440675
e8e91ed
b6b7d96
5813fc3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,7 +2,7 @@ | |
| /* eslint-disable no-constant-binary-expression */ | ||
| /* eslint-disable @typescript-eslint/no-unused-expressions */ | ||
| import { Client } from "./index.js"; | ||
| import { z } from "zod"; | ||
| import { z } from "zod/v4"; | ||
|
||
| import { | ||
| RequestSchema, | ||
| NotificationSchema, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,13 +1,4 @@ | ||
| import { | ||
| ZodTypeAny, | ||
| ZodTypeDef, | ||
| ZodType, | ||
| ParseInput, | ||
| ParseReturnType, | ||
| RawCreateParams, | ||
| ZodErrorMap, | ||
| ProcessedCreateParams, | ||
| } from "zod"; | ||
| import { ZodTypeAny } from "zod/v4"; | ||
|
|
||
| export enum McpZodTypeKind { | ||
| Completable = "McpCompletable", | ||
|
|
@@ -17,82 +8,38 @@ export type CompleteCallback<T extends ZodTypeAny = ZodTypeAny> = ( | |
| value: T["_input"], | ||
| context?: { | ||
| arguments?: Record<string, string>; | ||
| }, | ||
| } | ||
| ) => T["_input"][] | Promise<T["_input"][]>; | ||
|
|
||
| export interface CompletableDef<T extends ZodTypeAny = ZodTypeAny> | ||
| extends ZodTypeDef { | ||
| export interface CompletableDef<T extends ZodTypeAny = ZodTypeAny> { | ||
| type: T; | ||
| complete: CompleteCallback<T>; | ||
| typeName: McpZodTypeKind.Completable; | ||
| } | ||
|
|
||
| export class Completable<T extends ZodTypeAny> extends ZodType< | ||
| T["_output"], | ||
| CompletableDef<T>, | ||
| T["_input"] | ||
| > { | ||
| _parse(input: ParseInput): ParseReturnType<this["_output"]> { | ||
| 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 = <T extends ZodTypeAny>( | ||
| type: T, | ||
| params: RawCreateParams & { | ||
| complete: CompleteCallback<T>; | ||
| }, | ||
| ): Completable<T> => { | ||
| 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<T extends ZodTypeAny>( | ||
| schema: T, | ||
| complete: CompleteCallback<T>, | ||
| ): Completable<T> { | ||
| 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.`, | ||
| ); | ||
| complete: CompleteCallback<T> | ||
| ): T & { | ||
| _def: (T extends { _def: infer D } ? D : unknown) & CompletableDef<T>; | ||
|
||
| } { | ||
| const target = schema as unknown as { _def?: Record<string, unknown> }; | ||
| const originalDef = (target._def ?? {}) as Record<string, unknown>; | ||
| // Only mutate the existing _def object to respect read-only property semantics | ||
| if ( | ||
| (originalDef as { typeName?: unknown }).typeName !== | ||
| McpZodTypeKind.Completable | ||
| ) { | ||
| (originalDef as { typeName?: McpZodTypeKind; type?: ZodTypeAny }).typeName = | ||
| McpZodTypeKind.Completable; | ||
| (originalDef as { typeName?: McpZodTypeKind; type?: ZodTypeAny }).type = | ||
| schema; | ||
| } | ||
| 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 }; | ||
| (originalDef as { complete?: CompleteCallback<T> }).complete = complete; | ||
| return schema as unknown as T & { | ||
| _def: (T extends { _def: infer D } ? D : unknown) & CompletableDef<T>; | ||
| }; | ||
| return { errorMap: customMap, description }; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need this? This seems like an unintentional change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left this in as I was seeing issues running the auth test suite. Adding this prevent the mocks from duplicating and was able to get my tests back running again. We can remove if perhaps it's just my local setup.