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 intomainfrom
Conversation
|
@modelcontextprotocol/client
@modelcontextprotocol/server
@modelcontextprotocol/express
@modelcontextprotocol/hono
@modelcontextprotocol/node
commit: |
35f4667 to
edc3098
Compare
- 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
2983603
Compare
- Simplify ArkType and Valibot examples to minimal single-tool demos - Remove verbose docstrings and section dividers from standardSchema.ts
|
|
||
| /** | ||
| * The base Standard interface for typed schemas. | ||
| * @see https://standardschema.dev |
There was a problem hiding this comment.
note: copy/paste from https://github.com/standard-schema/standard-schema as recommended - this file basically is the spec.
examples/server/package.json
Outdated
| "@modelcontextprotocol/hono": "workspace:^", | ||
| "@modelcontextprotocol/node": "workspace:^", | ||
| "@modelcontextprotocol/server": "workspace:^", | ||
| "@valibot/to-json-schema": "^1.5.0", |
| @@ -0,0 +1,179 @@ | |||
| /** | |||
There was a problem hiding this comment.
- 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
Update comments in extractCompleters to explicitly document that the completable feature only supports Zod schemas due to reliance on Zod-specific .shape property introspection.
packages/server/src/server/mcp.ts
Outdated
| /** | ||
| * @internal Extracts completable callbacks from a Zod schema at registration time. | ||
| * NOTE: This only works with Zod schemas. ArkType and Valibot are not supported. | ||
| */ | ||
| function extractCompleters(schema: StandardJSONSchemaV1 | undefined): Map<string, CompleteCallback<StandardJSONSchemaV1>> | undefined { |
There was a problem hiding this comment.
isn't it weird to annotate StandardJSONSchemaV1 but it actually requires a zod type as input?
There was a problem hiding this comment.
yes, cant be helped
packages/server/src/server/mcp.ts
Outdated
| if (!schema) return undefined; | ||
|
|
||
| const shape = getZodSchemaShape(schema); | ||
| if (!shape) return undefined; |
There was a problem hiding this comment.
This is the guard for passing in a non-zod schema in here
…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?
|
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 library
completers: {
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: type CompleteCallback = (
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. |
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