Skip to content

Commit eeb81c0

Browse files
ochafikclaude
andauthored
fix(schemas): import from zod/v4 to match SDK's zod API (modelcontextprotocol#548)
The generated schemas compose with schemas imported from @modelcontextprotocol/sdk/types.js (e.g. z.array(ContentBlockSchema)). The SDK constructs its schemas via `import * as z from 'zod/v4'`, but our generated schema.ts used `import { z } from "zod"` — which returns the v3 API when the consumer has zod 3.25.x installed. Mixing v3-created and v4-created schema instances throws at parse time with errors like `keyValidator._parse is not a function` because zod v4 dropped the `_parse` / `_parseSync` internals that v3 wrappers call. This was masked before modelcontextprotocol#534 because zod was bundled — all schema creation shared the same inlined zod copy. Externalizing zod exposed the mismatch on consumers pinned to zod 3.x (e.g. anthropics/apps). The `zod/v4` subpath is exported by both zod 3.25+ and zod 4.x, so the existing peerDependency range `^3.25.0 || ^4.0.0` is preserved. https://claude.ai/code/session_013xQUegrvVGct5xpqeSL5av Co-authored-by: Claude <noreply@anthropic.com>
1 parent 1aa3c9c commit eeb81c0

File tree

3 files changed

+20
-8
lines changed

3 files changed

+20
-8
lines changed

scripts/generate-schemas.ts

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,15 @@
88
*
99
* ts-to-zod is a powerful tool but has limitations that require post-processing:
1010
*
11-
* ### 1. Zod Import Path (keep standard `"zod"` for version agnosticism)
11+
* ### 1. Zod Import Path (`"zod"` → `"zod/v4"`)
1212
*
13-
* ts-to-zod generates `import { z } from "zod"` which works with both
14-
* Zod v3.25+ and v4. We keep this standard import to support both versions.
13+
* ts-to-zod generates `import { z } from "zod"`. We rewrite this to
14+
* `import { z } from "zod/v4"` because the generated schemas compose with
15+
* schemas imported from `@modelcontextprotocol/sdk/types.js`, which the SDK
16+
* constructs via `zod/v4`. Mixing schemas from the v3 and v4 APIs at runtime
17+
* fails with errors like `keyValidator._parse is not a function` (v3 internals
18+
* calling into v4 objects, or vice versa). The `zod/v4` subpath is exported by
19+
* both zod 3.25+ and zod 4.x, so the peerDependency range is preserved.
1520
*
1621
* ### 2. External Type References (`z.any()` → actual schemas)
1722
*
@@ -179,11 +184,13 @@ async function generateJsonSchema() {
179184
* Post-process generated schemas for project compatibility.
180185
*/
181186
function postProcess(content: string): string {
182-
// 1. Add MCP SDK schema imports (keep standard zod import for v3/v4 compatibility)
187+
// 1. Rewrite to zod/v4 and add MCP SDK schema imports.
188+
// zod/v4 aligns with the SDK's own zod import — composing v3 and v4
189+
// schema instances throws at parse time. See header comment for details.
183190
const mcpImports = EXTERNAL_TYPE_SCHEMAS.join(",\n ");
184191
content = content.replace(
185192
'import { z } from "zod";',
186-
`import { z } from "zod";
193+
`import { z } from "zod/v4";
187194
import {
188195
${mcpImports},
189196
} from "@modelcontextprotocol/sdk/types.js";`,
@@ -257,7 +264,12 @@ function replaceRecordAndWithPassthrough(content: string): string {
257264
* Post-process generated integration tests.
258265
*/
259266
function postProcessTests(content: string): string {
260-
// Keep standard zod import for v3/v4 compatibility
267+
// Rewrite to zod/v4 so z.infer matches the v4 schemas in schema.ts.
268+
content = content.replace(
269+
'import { z } from "zod";',
270+
'import { z } from "zod/v4";',
271+
);
272+
261273
content = content.replace(
262274
"// Generated by ts-to-zod",
263275
`// Generated by ts-to-zod

src/generated/schema.test.ts

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/generated/schema.ts

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)