Skip to content

Commit a30ef44

Browse files
feat: Add Zod 4 support while maintaining Zod 3 compatibility
- Move zod to peerDependencies supporting ^3.23.8 || ^4.0.0 - Create zodJsonSchema wrapper with auto-detection - Use native z.toJSONSchema() for Zod 4 - Fall back to zod-to-json-schema for Zod 3 - Add ZOD_4_MIGRATION.md documentation - Non-breaking change, fully backward compatible Closes #555 Amp-Thread-ID: https://ampcode.com/threads/T-6db05869-7946-4144-922f-b7adac392de5 Co-authored-by: Amp <[email protected]>
1 parent 29cb080 commit a30ef44

File tree

5 files changed

+177
-11
lines changed

5 files changed

+177
-11
lines changed

ZOD_4_MIGRATION.md

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# Zod 4 Support
2+
3+
This SDK now supports both Zod 3 and Zod 4 seamlessly.
4+
5+
## Installation
6+
7+
### With Zod 3 (Current Default)
8+
9+
```bash
10+
npm install @modelcontextprotocol/sdk zod@^3.23.8 zod-to-json-schema@^3.24.1
11+
```
12+
13+
### With Zod 4
14+
15+
```bash
16+
npm install @modelcontextprotocol/sdk zod@^4.0.0
17+
```
18+
19+
Note: `zod-to-json-schema` is **not needed** with Zod 4, as Zod 4 has native JSON Schema support via `z.toJSONSchema()`.
20+
21+
## How It Works
22+
23+
The SDK automatically detects which version of Zod you're using:
24+
25+
- **Zod 4**: Uses the native `z.toJSONSchema()` function for optimal performance
26+
- **Zod 3**: Falls back to the `zod-to-json-schema` library (must be installed)
27+
28+
This is handled transparently by the SDK - you don't need to change your code when upgrading from Zod 3 to Zod 4.
29+
30+
## Migration from Zod 3 to Zod 4
31+
32+
If you're currently using Zod 3 and want to upgrade to Zod 4:
33+
34+
1. **Update your dependencies:**
35+
36+
```bash
37+
npm install zod@^4.0.0
38+
npm uninstall zod-to-json-schema # Optional, no longer needed
39+
```
40+
41+
2. **Review Zod 4 breaking changes** that may affect your application code (not the MCP SDK itself):
42+
- See [Zod 4 Migration Guide](https://zod.dev/v4)
43+
- Most common changes:
44+
- `z.string().email()``z.email()` (top-level function)
45+
- `.default()` behavior changed (use `.prefault()` for old behavior)
46+
- Error customization API changed (`message``error`)
47+
48+
3. **Test your application** to ensure schema definitions work as expected
49+
50+
## Compatibility Notes
51+
52+
- The SDK maintains **full backwards compatibility** with Zod 3
53+
- You can upgrade to Zod 4 at your own pace
54+
- Both versions are fully supported and tested
55+
56+
## Examples
57+
58+
Your existing code works with both versions:
59+
60+
```typescript
61+
import { McpServer } from '@modelcontextprotocol/sdk/server';
62+
import { z } from 'zod';
63+
64+
const server = new McpServer({
65+
name: 'example-server',
66+
version: '1.0.0'
67+
});
68+
69+
// This works with both Zod 3 and Zod 4
70+
server.tool(
71+
'greet',
72+
'Greets a person',
73+
{
74+
name: z.string(),
75+
age: z.number().optional()
76+
},
77+
async ({ name, age }) => ({
78+
content: [
79+
{
80+
type: 'text',
81+
text: `Hello ${name}${age ? `, you are ${age} years old` : ''}!`
82+
}
83+
]
84+
})
85+
);
86+
```
87+
88+
## Troubleshooting
89+
90+
### "zod-to-json-schema is required but not installed"
91+
92+
If you see this error while using Zod 3, install the missing dependency:
93+
94+
```bash
95+
npm install zod-to-json-schema@^3.24.1
96+
```
97+
98+
This dependency is only needed for Zod 3. Zod 4 does not require it.
99+
100+
## Version Support
101+
102+
- **Zod 3**: `^3.23.8` (requires `zod-to-json-schema`)
103+
- **Zod 4**: `^4.0.0` (native JSON Schema support)

package-lock.json

Lines changed: 10 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,14 @@
8787
"express": "^5.0.1",
8888
"express-rate-limit": "^7.5.0",
8989
"pkce-challenge": "^5.0.0",
90-
"raw-body": "^3.0.0",
91-
"zod": "^3.23.8",
92-
"zod-to-json-schema": "^3.24.1"
90+
"raw-body": "^3.0.0"
9391
},
9492
"peerDependencies": {
95-
"@cfworker/json-schema": "^4.1.1"
93+
"@cfworker/json-schema": "^4.1.1",
94+
"zod": "^3.23.8 || ^4.0.0"
95+
},
96+
"optionalDependencies": {
97+
"zod-to-json-schema": "^3.24.1"
9698
},
9799
"peerDependenciesMeta": {
98100
"@cfworker/json-schema": {
@@ -123,7 +125,8 @@
123125
"tsx": "^4.16.5",
124126
"typescript": "^5.5.4",
125127
"typescript-eslint": "^8.0.0",
126-
"ws": "^8.18.0"
128+
"ws": "^8.18.0",
129+
"zod": "^3.23.8"
127130
},
128131
"resolutions": {
129132
"strip-ansi": "6.0.1"

src/server/mcp.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Server, ServerOptions } from './index.js';
2-
import { zodToJsonSchema } from 'zod-to-json-schema';
2+
import { zodToJsonSchema } from './zodJsonSchema.js';
33
import { z, ZodRawShape, ZodObject, ZodString, ZodTypeAny, ZodType, ZodTypeDef, ZodOptional } from 'zod';
44
import {
55
Implementation,

src/server/zodJsonSchema.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/**
2+
* Compatibility wrapper for converting Zod schemas to JSON Schema.
3+
* Supports both Zod 3 (via zod-to-json-schema) and Zod 4 (via native z.toJSONSchema).
4+
*/
5+
6+
import { ZodType } from 'zod';
7+
8+
// Store the imported function to avoid repeated dynamic imports
9+
let zodToJsonSchemaFn: ((schema: ZodType, options?: { strictUnions?: boolean; pipeStrategy?: 'input' | 'output' }) => unknown) | null =
10+
null;
11+
let importAttempted = false;
12+
13+
/**
14+
* Converts a Zod schema to JSON Schema, supporting both Zod 3 and Zod 4.
15+
*/
16+
export function zodToJsonSchema(schema: ZodType, options?: { strictUnions?: boolean; pipeStrategy?: 'input' | 'output' }): unknown {
17+
// Try Zod 4's native toJSONSchema first
18+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
19+
const z = schema.constructor as any;
20+
if (z.toJSONSchema && typeof z.toJSONSchema === 'function') {
21+
// Zod 4 native support
22+
try {
23+
return z.toJSONSchema(schema);
24+
} catch {
25+
// Fall through to zod-to-json-schema
26+
}
27+
}
28+
29+
// Fall back to zod-to-json-schema for Zod 3
30+
if (!importAttempted) {
31+
importAttempted = true;
32+
try {
33+
// Dynamic import for optional dependency - works in both ESM and CJS
34+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
35+
const zodToJsonSchemaModule = eval('require')('zod-to-json-schema');
36+
zodToJsonSchemaFn =
37+
zodToJsonSchemaModule.zodToJsonSchema || zodToJsonSchemaModule.default?.zodToJsonSchema || zodToJsonSchemaModule.default;
38+
} catch (e: unknown) {
39+
const error = e as { code?: string; message?: string };
40+
if (error?.code === 'MODULE_NOT_FOUND' || error?.message?.includes('Cannot find module')) {
41+
throw new Error(
42+
'zod-to-json-schema is required for Zod 3 support but is not installed. ' +
43+
'Please install it: npm install zod-to-json-schema'
44+
);
45+
}
46+
throw e;
47+
}
48+
}
49+
50+
if (!zodToJsonSchemaFn) {
51+
throw new Error('zod-to-json-schema module found but zodToJsonSchema function not available');
52+
}
53+
54+
return zodToJsonSchemaFn(schema, options);
55+
}

0 commit comments

Comments
 (0)