Uh oh!
There was an error while loading. Please reload this page.
feat: support Standard JSON Schema (StandardJSONSchemaV1) for tool/prompt schemas - #1473
feat: support Standard JSON Schema (StandardJSONSchemaV1) for tool/prompt schemas#1473mattzcarey wants to merge 12 commits into
Conversation
|
@modelcontextprotocol/client@modelcontextprotocol/server@modelcontextprotocol/express@modelcontextprotocol/hono@modelcontextprotocol/nodecommit: |
35f4667 to
edc3098Compare- Replace z.safeParse with parseSchema utility in client.ts and protocol.ts - Replace z.core.$ZodType with AnySchema alias - Replace z.core.$ZodObject with AnyObjectSchema alias - Replace z.output<T> with SchemaOutput<T> alias - Remove direct zod import from protocol.ts (now uses schema utilities)
- Add arktypeExample.ts demonstrating ArkType schema validation - Add valibotExample.ts demonstrating Valibot schema validation - Fix type guards to accept function-based schemas (ArkType uses functions) - Fix validation result handling to check issues array first (Valibot compatibility) - Add arktype, valibot, and @valibot/to-json-schema dependencies Both examples demonstrate: - Tool registration with inputSchema and outputSchema - Native validation via ~standard.validate() - JSON Schema conversion via ~standard.jsonSchema.input()
Tests cover: - ArkType tool registration (input/output schemas) - Valibot tool registration (with descriptions) - Tool validation with valid/invalid input for both libraries - Mixed schema libraries in the same server (Zod + ArkType + Valibot) - Prompt completions with Zod completable - Error message quality from all three libraries - Type inference verification All libraries produce clear, user-friendly error messages: - ArkType: "age must be a number (was a string)" - Valibot: "Invalid type: Expected string but received 123" - Zod: "Invalid input: expected string, received number"
- Add eslint-disable for namespace rule in standardSchema.ts (matches Standard Schema spec design) - Remove unused imports in standardSchema.test.ts - Fix switch case braces in example files - Apply prettier formatting
- Add AnySchema import to experimental/tasks/server.ts - Add parseSchema import to server/server.ts
edc3098 to
2983603Compare- Simplify ArkType and Valibot examples to minimal single-tool demos - Remove verbose docstrings and section dividers from standardSchema.ts
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Add arktype, valibot, @valibot/to-json-schema to pnpm catalog - Replace z.safeParse with parseSchema in server.ts - Replace z.output/z.core.$ZodType with SchemaOutput/AnySchema aliases - Remove unused zod imports
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Update comments in extractCompleters to explicitly document that the completable feature only supports Zod schemas due to reliance on Zod-specific .shape property introspection.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
…le lookup Revert the completable handling back to the original approach where completable fields are looked up at request time rather than pre-extracted at registration time. This removes: - extractCompleters() function and completers Map optimization - CompleteCallback type import (unused) - completers field from RegisteredPrompt The original approach directly inspects the schema at completion request time using getSchemaShape/isCompletable/getCompleter.
| ): PromptHandler { | ||
| if (argsSchema) { | ||
| const typedCallback = callback as (args: SchemaOutput<AnySchema>, ctx: ServerContext) => GetPromptResult | Promise<GetPromptResult>; | ||
| const typedCallback = callback as (args: unknown, ctx: ServerContext) => GetPromptResult | Promise<GetPromptResult>; |
There was a problem hiding this comment.
why the change to unknown?
felixweinberger
commented
Feb 3, 2026
One thought for a potential follow-up: now that the SDK is moving to schema-library-agnostic types, the Instead of embedding completion callbacks inside schema objects (which requires Zod-specific introspection to extract), completers could be passed explicitly in the prompt config: server.registerPrompt('greeting',{argsSchema: type({name: 'string',language: 'string'}),// any librarycompleters: {name: (value)=>['Alice','Bob'].filter(n=>n.startsWith(value)),language: (value)=>['en','fr'].filter(l=>l.startsWith(value))}},callback);The type simplifies too, since completion values are always strings over the wire: typeCompleteCallback=(value: string,context?: {arguments?: Record<string,string>})=>string[]|Promise<string[]>;This PR already does most of the internal plumbing — Not suggesting this as a blocker for this PR — just flagging as a natural follow-up that would complete the schema-agnostic story. |
felixweinberger
commented
Mar 16, 2026
Superseded by #1689 |
Summary
Replace the Zod-specific
AnySchematype withStandardJSONSchemaV1from the Standard Schema spec for user-provided tool and prompt schemas. This enables any schema library that implements the spec (Zod v4, Valibot, ArkType, etc.) to be used for tool/prompt schemas.Motivation and Context
Currently, the MCP SDK is tightly coupled to Zod for user-provided schemas. This PR makes the SDK library-agnostic by using the Standard Schema spec interfaces:
StandardJSONSchemaV1: Primary interface - uses~standard.jsonSchema.input/output()for JSON Schema conversion (wire protocol)StandardSchemaV1: Optional interface - uses~standard.validate()for native validation when availableLibraries that implement these interfaces:
@valibot/to-json-schema(native validation + JSON Schema)Changes
Core (
packages/core)src/util/standardSchema.ts- Standard Schema interfaces and utilitiesisStandardJSONSchema(),isStandardSchema(),isStandardSchemaWithJSON()standardSchemaToJsonSchema()validateStandardSchema()(uses native validation if available, otherwise JSON Schema)promptArgumentsFromStandardSchema()src/util/schema.ts- Internal Zod utilities for protocol handling (unchanged API)src/shared/protocol.ts- Use schema utility aliases instead of direct Zod importsServer (
packages/server)src/server/mcp.tsRegisteredTool.inputSchema/outputSchemanow acceptStandardJSONSchemaV1RegisteredPrompt.argsSchemanow acceptsStandardJSONSchemaV1StandardJSONSchemaV1.InferOutput<T>for type inferencevalidateStandardSchema()for native validation when availableClient (
packages/client)src/client/client.ts- UseparseSchemautility instead of directz.safeParseExamples
examples/server/src/arktypeExample.ts- ArkType schema exampleexamples/server/src/valibotExample.ts- Valibot schema exampleTests
test/integration/test/standardSchema.test.ts- Comprehensive integration testsHow Has This Been Tested?
Error Message Quality
All libraries produce clear, user-friendly error messages:
age must be a number (was a string)Invalid type: Expected string but received 123Invalid input: expected string, received numberBreaking Changes
inputSchema,outputSchema, andargsSchemanow expectStandardJSONSchemaV1instead of Zod'sAnySchemaStandardJSONSchemaV1completable()function for prompt argument completion remains Zod-specificAPI Examples
Zod (existing, unchanged)
ArkType (new)
Valibot (new)
Types of changes
Checklist