feat: support Standard Schema for tool/prompt schemas (#1689)

Co-authored-by: Matt Carey <mcarey@cloudflare.com>
This commit is contained in:
Felix Weinberger 2026-03-23 14:44:38 +00:00 • committed by GitHub
commit 0784be1a67
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 1327 additions and 212 deletions

View file

@ -209,7 +209,7 @@ if (error instanceof OAuthError && error.code === OAuthErrorCode.InvalidClient)
The variadic `.tool()`, `.prompt()`, `.resource()` methods are removed. Use the `register*` methods with a config object.
**IMPORTANT**: v2 requires full Zod schemas — raw shapes like `{ name: z.string() }` are no longer supported. You must wrap with `z.object()`. This applies to `inputSchema`, `outputSchema`, and `argsSchema`.
**IMPORTANT**: v2 requires schema objects implementing [Standard Schema](https://standardschema.dev/) — raw shapes like `{ name: z.string() }` are no longer supported. Wrap with `z.object()` (Zod v4), or use ArkType's `type({...})`, or Valibot. For raw JSON Schema, wrap with `fromJsonSchema(schema, validator)` from `@modelcontextprotocol/core`. Applies to `inputSchema`, `outputSchema`, and `argsSchema`.
### Tools
@ -279,13 +279,22 @@ Note: the third argument (`metadata`) is required — pass `{}` if no metadata.
### Schema Migration Quick Reference
| v1 (raw shape) | v2 (Zod schema) |
| v1 (raw shape) | v2 (Standard Schema object) |
|----------------|-----------------|
| `{ name: z.string() }` | `z.object({ name: z.string() })` |
| `{ count: z.number().optional() }` | `z.object({ count: z.number().optional() })` |
| `{}` (empty) | `z.object({})` |
| `undefined` (no schema) | `undefined` or omit the field |
### Removed core exports
| Removed from `@modelcontextprotocol/core` | Replacement |
|---|---|
| `schemaToJson(schema)` | `standardSchemaToJsonSchema(schema)` |
| `parseSchemaAsync(schema, data)` | `validateStandardSchema(schema, data)` |
| `SchemaInput<T>` | `StandardSchemaWithJSON.InferInput<T>` |
| `getSchemaShape`, `getSchemaDescription`, `isOptionalSchema`, `unwrapOptionalSchema` | none (internal Zod introspection helpers) |
## 7. Headers API
Transport constructors and `RequestInfo.headers` now use the Web Standard `Headers` object instead of plain objects.

View file

@ -200,17 +200,17 @@ import * as z from 'zod/v4';
const server = new McpServer({ name: 'demo', version: '1.0.0' });
// Tool with schema
server.registerTool('greet', { inputSchema: { name: z.string() } }, async ({ name }) => {
server.registerTool('greet', { inputSchema: z.object({ name: z.string() }) }, async ({ name }) => {
return { content: [{ type: 'text', text: `Hello, ${name}!` }] };
});
// Tool with description
server.registerTool('greet', { description: 'Greet a user', inputSchema: { name: z.string() } }, async ({ name }) => {
server.registerTool('greet', { description: 'Greet a user', inputSchema: z.object({ name: z.string() }) }, async ({ name }) => {
return { content: [{ type: 'text', text: `Hello, ${name}!` }] };
});
// Prompt
server.registerPrompt('summarize', { argsSchema: { text: z.string() } }, async ({ text }) => {
server.registerPrompt('summarize', { argsSchema: z.object({ text: z.string() }) }, async ({ text }) => {
return { messages: [{ role: 'user', content: { type: 'text', text: `Summarize: ${text}` } }] };
});
@ -220,9 +220,9 @@ server.registerResource('config', 'config://app', {}, async uri => {
});
```
### Zod schemas required (raw shapes no longer supported)
### Standard Schema objects required (raw shapes no longer supported)
v2 requires full Zod schemas for `inputSchema` and `argsSchema`. Raw object shapes are no longer accepted.
v2 requires schema objects implementing the [Standard Schema spec](https://standardschema.dev/) for `inputSchema`, `outputSchema`, and `argsSchema`. Raw object shapes are no longer accepted. Zod v4, ArkType, and Valibot all implement the spec.
**Before (v1):**
@ -240,11 +240,23 @@ server.registerTool('greet', {
```typescript
import * as z from 'zod/v4';
// Must wrap with z.object()
// Wrap with z.object() (or use any Standard Schema library)
server.registerTool('greet', {
inputSchema: z.object({ name: z.string() }) // full Zod schema
inputSchema: z.object({ name: z.string() })
}, async ({ name }) => { ... });
// ArkType works too
import { type } from 'arktype';
server.registerTool('greet', {
inputSchema: type({ name: 'string' })
}, async ({ name }) => { ... });
// Raw JSON Schema via fromJsonSchema
import { fromJsonSchema, AjvJsonSchemaValidator } from '@modelcontextprotocol/core';
server.registerTool('greet', {
inputSchema: fromJsonSchema({ type: 'object', properties: { name: { type: 'string' } } }, new AjvJsonSchemaValidator())
}, handler);
// For tools with no parameters, use z.object({})
server.registerTool('ping', {
inputSchema: z.object({})
@ -256,6 +268,15 @@ This applies to:
- `outputSchema` in `registerTool()`
- `argsSchema` in `registerPrompt()`
**Removed Zod-specific helpers** from `@modelcontextprotocol/core` (use Standard Schema equivalents):
| Removed | Replacement |
|---|---|
| `schemaToJson(schema)` | `standardSchemaToJsonSchema(schema)` |
| `parseSchemaAsync(schema, data)` | `validateStandardSchema(schema, data)` |
| `SchemaInput<T>` | `StandardSchemaWithJSON.InferInput<T>` |
| `getSchemaShape`, `getSchemaDescription`, `isOptionalSchema`, `unwrapOptionalSchema` | No replacement — these are now internal Zod introspection helpers |
### Host header validation moved
Express-specific middleware (`hostHeaderValidation()`, `localhostHostValidation()`) moved from the server package to `@modelcontextprotocol/express`. The server package now exports framework-agnostic functions instead: `validateHostHeader()`, `localhostAllowedHostnames()`,