Schema Libraries
Purpose and Scope
This page explains how the TypeScript SDK lets server authors use schema libraries beyond Zod when defining MCP tools, prompts, and structured outputs. In this context, a Standard Schema is a schema object that can expose enough metadata for the SDK to derive JSON Schema and validate runtime values. The guide in docs/advanced/schema-libraries.md presents ArkType, Valibot, raw JSON Schema, custom output validators, and alternate JSON Schema validators as one workflow: define the contract once, advertise it to clients, and reject invalid calls before tool code runs.
Sources: docs/advanced/schema-libraries.md
The reader problem is practical: production MCP servers often already have validation rules in a preferred library or generated JSON Schema documents from another system. The SDK does not require replacing that work with a single schema package. Instead, the public server API accepts compatible schema objects for inputSchema, uses a helper named fromJsonSchema for plain JSON Schema documents, and applies the same model to outputSchema and prompt argument schemas. That means schema choice remains local to the application while the MCP wire contract remains JSON Schema.
Sources: docs/advanced/schema-libraries.md
This guide is adjacent to, but different from, the wire-schema guide. Schema libraries are for application-level contracts such as a tool named greet accepting a name and optional times. Wire schemas are for code that already holds raw JSON-RPC or MCP protocol payloads and needs to validate the envelope, request, result, notification, or params object directly. The distinction matters for gateways and proxies: high-level McpServer and Client calls validate application data for you, while raw forwarding code imports *Schema constants from @modelcontextprotocol/core.
Sources: docs/advanced/schema-libraries.md, docs/advanced/wire-schemas.md
Relevant Source Files
docs/advanced/schema-libraries.md- Primary how-to page for Standard Schema input, ArkType, Valibot,fromJsonSchema, structured output validation, and runtime JSON Schema validator selection.docs/advanced/wire-schemas.md- Companion advanced guide that defines when to use@modelcontextprotocol/corewire schemas instead of server/client schema-library hooks.docs/_meta/CONVENTIONS.md- Documentation authoring contract for guide shape, code-first sequencing, observable results, identifier formatting, and recap expectations.docs/.vitepress/llms.ts- Build-time generator for LLM-facing markdown renditions,llms.txt, andllms-full.txt; it strips frontmatter and fence source wiring from rendered markdown copies.docs/.vitepress/theme/index.ts- VitePress v2 docs theme wrapper that extends the default theme and injects the shared banner at the top layout slot.docs/v1/.vitepress/theme/index.ts- VitePress v1 docs theme wrapper that reuses the shared custom CSS while adding the same banner layout behavior.
Core Primitives
The most important primitive on this page is inputSchema. A tool registration can pass an ArkType schema directly, as shown by the greet example using type({ name: 'string', 'times?': '1 <= number.integer <= 5' }). From that one schema, the SDK derives the JSON Schema exposed by tools/list, validates call arguments before the handler runs, and infers handler argument types. The guide makes the failure mode explicit: calling greet with times: 99 returns an error result with ArkType's own message, and the handler does not execute.
Sources: docs/advanced/schema-libraries.md
Valibot uses the same inputSchema slot, but its integration step differs because Valibot does not expose JSON Schema conversion on the schema object itself. The documented pattern wraps v.object({ name: v.string() }) with toStandardJsonSchema from @valibot/to-json-schema, then passes that wrapper to server.registerTool. After registration, tools/list advertises the derived JSON Schema for the shout tool, and Valibot parses calls before the handler receives { name }. The public contract stays the same even though the schema-library adapter changes.
Sources: docs/advanced/schema-libraries.md
Use fromJsonSchema when the source of truth is already a JSON Schema document rather than a library-specific schema object. The guide imports fromJsonSchema from @modelcontextprotocol/server and wraps an object schema with properties and required. The helper accepts a generic parameter such as { name: string } so the handler can receive typed arguments; without that type parameter, the guide states the arguments are unknown. The JSON Schema advertised to clients is the document passed to the helper, unchanged.
Sources: docs/advanced/schema-libraries.md
Register Tools with Alternate Schemas
A minimal ArkType-backed tool uses McpServer, registerTool, and an inputSchema built with ArkType's type function:
import { McpServer } from '@modelcontextprotocol/server';
import { type } from 'arktype';
const server = new McpServer({ name: 'schema-zoo', version: '1.0.0' });
server.registerTool(
'greet',
{
description: 'Greet someone by name',
inputSchema: type({ name: 'string', 'times?': '1 <= number.integer <= 5' })
},
async ({ name, times }) => ({
content: [{ type: 'text', text: Array.from({ length: times ?? 1 }, () => `Hello, ${name}`).join('\n') }]
})
);The important behavior is not only that the tool compiles. The SDK uses the schema at three points in the tool lifecycle. First, it publishes the schema so the model or host can understand valid arguments. Second, it validates each tools/call request before the handler runs. Third, TypeScript can infer the handler input shape from the schema object. For ArkType, the example narrows name to string and times to number | undefined, which keeps runtime validation and handler authoring aligned.
Sources: docs/advanced/schema-libraries.md
The Valibot variant is intentionally similar at the MCP layer. You still call server.registerTool, still provide a description, and still return MCP content from the handler. The only difference is the conversion wrapper:
import { toStandardJsonSchema } from '@valibot/to-json-schema';
import * as v from 'valibot';
server.registerTool(
'shout',
{ description: 'Greet someone, loudly', inputSchema: toStandardJsonSchema(v.object({ name: v.string() })) },
async ({ name }) => ({ content: [{ type: 'text', text: `HELLO, ${name.toUpperCase()}` }] })
);This pattern is the safest way to migrate teams that already standardize on Valibot. Keep the Valibot object as the validation source, wrap it at the boundary where the SDK needs Standard Schema behavior, and let the SDK advertise the generated JSON Schema through MCP discovery. The guide also warns v1 users that raw shapes such as inputSchema: { name: z.string() } are deprecated. In v2-style tool definitions, pass a schema object rather than an object of field validators.
Sources: docs/advanced/schema-libraries.md
Structured Output and Existing JSON Schema
outputSchema follows the same rule as inputSchema: provide a schema object and return the matching value as structuredContent next to human-readable content. This is how a tool communicates machine-readable results while still giving clients text or other content blocks for display. The schema-library guide also calls out prompt argsSchema as using the same Standard Schema rule, so the mental model is shared across tool inputs, tool outputs, and prompt arguments rather than being a special case for one API.
Sources: docs/advanced/schema-libraries.md
When your contract starts as plain JSON Schema, use fromJsonSchema instead of translating it into ArkType, Valibot, or Zod. The documented farewell tool passes an object schema with a string name property and required name list. The server advertises that document unchanged in tools/list, and the SDK validates calls against it with a real JSON Schema validator. The generic argument on fromJsonSchema<{ name: string }> is a TypeScript convenience for the handler; the runtime contract remains the JSON Schema object.
Sources: docs/advanced/schema-libraries.md
That separation is useful when schemas are generated from OpenAPI, shared with another service, or reviewed as data rather than code. The SDK does not infer TypeScript types from arbitrary JSON Schema documents by itself in the guide; the author supplies the generic when they know the expected shape. This keeps the wire contract explicit and prevents accidental trust in untyped JSON. It also makes the validator choice visible, because the guide later discusses swapping the JSON Schema validator used for these plain-schema checks.
Sources: docs/advanced/schema-libraries.md
Runtime Validators and Wire Schema Boundaries
The schema-library guide ends by focusing on the runtime JSON Schema validator. That matters because Standard Schema conversion and fromJsonSchema both produce or consume JSON Schema, but a validator still has to decide whether a specific call payload is valid. The repository declares @cfworker/json-schema in the root development dependencies, and the guide describes selecting a real JSON Schema validator for call validation. Keep that layer distinct from the schema authoring library: ArkType and Valibot describe the contract; the validator enforces JSON Schema payloads at runtime.
Sources: docs/advanced/schema-libraries.md
For raw protocol payloads, use the wire schemas instead. docs/advanced/wire-schemas.md states that @modelcontextprotocol/core exports exact Zod constants for protocol and OAuth payloads, such as CallToolResultSchema, JSONRPCMessageSchema, CallToolRequestSchema, ListToolsResultSchema, ProgressNotificationSchema, and CallToolRequestParamsSchema. These are not tool authoring schemas. They are for code that parses upstream bodies, validates undecoded JSON-RPC envelopes, routes proxy traffic by method, or checks only a params object before forwarding.
Sources: docs/advanced/wire-schemas.md
Choose the high-level path unless your code directly holds raw JSON. A server built with McpServer receives validated tool arguments in the handler, and a client built with Client receives typed results from tool calls. Gateways, proxies, test harnesses, and worker fleets sit closer to the wire, so they need the @modelcontextprotocol/core schema constants. This boundary prevents application schemas from being misused as protocol validators and prevents protocol schemas from replacing domain-specific validation on tools and prompts.
Sources: docs/advanced/schema-libraries.md, docs/advanced/wire-schemas.md
Docs and Publishing Signals
The source page is written as a how-to, and the repository's documentation conventions explain the intended reader experience. docs/_meta/CONVENTIONS.md requires guide pages to move quickly into code, use imperative second-person language, introduce key terms once, show observable results, and end with a recap. That convention matches the schema-library page: each capability is introduced by a concrete registration snippet, followed by the observable effect in tools/list, handler typing, validation behavior, or error output. The OpenWiki page preserves those task boundaries while adding source mapping.
Sources: docs/_meta/CONVENTIONS.md, docs/advanced/schema-libraries.md
The VitePress build also creates LLM-facing renditions of the docs. docs/.vitepress/llms.ts describes generated <page>.md files beside guide HTML, plus llms.txt and llms-full.txt in sidebar order. It strips frontmatter and removes source="..." attributes from code fences, which means the human docs can keep snippet wiring while generated markdown stays plain. The advanced schema guide therefore serves both browser readers and agents that fetch markdown renditions during integration work.
Sources: docs/.vitepress/llms.ts
The theme files show that both current and v1 documentation sites extend VitePress's default theme and inject a banner into the layout-top slot. The v1 theme imports shared custom CSS from the current docs tree, so the repository keeps presentation consistent across documentation generations while allowing the content to describe different SDK eras. For this page, that matters because the schema guide includes a v1 migration note: raw field-shape schemas are deprecated, and readers should follow the upgrade guide when moving to v2-style schema objects.
Sources: docs/.vitepress/theme/index.ts, docs/v1/.vitepress/theme/index.ts, docs/advanced/schema-libraries.md
Compact Reference
| Need | Public name or pattern | Source-backed behavior |
|---|---|---|
| ArkType input validation | inputSchema: type(...) | Advertises JSON Schema, validates before handler execution, and infers handler argument types. |
| Valibot input validation | toStandardJsonSchema(v.object(...)) | Wraps Valibot because conversion is not exposed on the schema object itself. |
| Existing JSON Schema | fromJsonSchema<T>(schema) | Registers a plain JSON Schema document and uses T to type handler arguments. |
| Structured tool output | outputSchema with structuredContent | Applies the same Standard Schema rule to machine-readable tool results. |
| Prompt arguments | argsSchema | Uses the same Standard Schema model as tool schemas. |
| Raw MCP payload validation | @modelcontextprotocol/core *Schema constants | Validates JSON-RPC envelopes, requests, results, notifications, and params outside high-level SDK objects. |
Next Steps
Start with inputSchema on a single tool and choose the smallest adapter that preserves your existing validation source. Use ArkType directly when its schema object can produce JSON Schema, use toStandardJsonSchema for Valibot, and use fromJsonSchema when the source of truth is already JSON Schema. Add outputSchema only when clients need structured machine-readable results, and keep content for human-readable output. If your code parses raw JSON-RPC frames, switch mental models and read the wire schema guide before building routing or proxy validation.
Sources: docs/advanced/schema-libraries.md, docs/advanced/wire-schemas.md