JSON Schema vs Zod for Agent Tools
Compare source-of-truth choices through conversion loss, runtime validation, provider portability, and useful errors.
JSON Schema vs Zod is not a syntax preference when an agent tool crosses provider, runtime, and language boundaries. The source of truth determines which constraints survive conversion, where validation runs, and what evidence a failed call produces.
This comparison sends one customer-search tool through both directions, then records lost refinements, portable structure, runtime errors, and maintenance cost.
JSON Schema vs Zod is an authority decision
The fixture is a searchCustomers tool accepting query, optional status, page size, and an application rule that either query or status must be present. Its wire contract must travel to an agent provider, its server must validate untrusted arguments, and its TypeScript handler needs useful types. JSON Schema vs Zod asks which representation authors edit directly and which representations are generated, because two hand-maintained copies will drift.
Authority should match the broadest promise. If the schema is a public, cross-language artifact consumed by several providers and validators, portable JSON Schema is a strong canonical form. If one TypeScript application owns the tool and relies heavily on transforms or refinements, Zod may be the authoring form. Neither choice removes the need to inspect the generated boundary and test semantic behavior.
List the teams that edit, generate, validate, and debug agent tool schemas before selecting syntax. JSON Schema vs Zod is settled by ownership and boundary count more reliably than by which authoring API feels pleasant.
Separate structural portability from application semantics
JSON Schema excels at declarative structure: object properties, required fields, enums, numeric limits, string patterns, composition, references, and annotations within a chosen draft and supported subset. Zod adds executable TypeScript-side behavior such as transforms and refinements. Those functions are not inherently portable data. A converter may approximate them, omit them, or reject them, which is a contract fact rather than a library defect.
For the fixture, query length and page-size bounds belong in the portable schema. The “query or status” rule can be expressed declaratively, but a database-aware check such as “status is enabled for this tenant” belongs in application validation. JSON Schema vs Zod becomes clearer when every rule is classified as wire structure, cross-field semantic, environment policy, or normalization transform before implementation begins.
Structural portability covers keys, types, required fields, and many constraints; application semantics cover permissions, cross-field rules, and business lookups. Treating both as one validator makes conversion failures difficult to locate.
Pin the specifications and supported subsets
The JSON Schema specification index distinguishes drafts and vocabularies; record which dialect and metaschema your canonical artifact targets. Zod's JSON Schema conversion documentation describes conversion behavior and options, while OpenAI's function calling guide defines the current tool interface and structured argument expectations. A provider may support a subset rather than every keyword in a draft.
Create a provider profile that names accepted keywords, limits, strictness mode, and known rewrites. Validate the canonical schema against its metaschema, then validate the lowered provider artifact against that profile. Do not silently delete unsupported constraints. Emit a loss report with source path, lost rule, reason, and the server-side control that still enforces it.
Pin the JSON Schema dialect, Zod version, conversion library, and provider subset in the corpus header. A passing conversion without those coordinates cannot be reproduced when defaults or unsupported keywords change. Include the generated artifact digest so reviewers can distinguish source drift from compiler drift.
Try JSON Schema as the canonical artifact
With JSON Schema authority, the repository stores a versioned schema and generates TypeScript types or runtime validator code through a pinned toolchain. The same document can feed provider adapters in other languages. Diffs are data diffs, references remain addressable, and documentation can render from annotations. This path favors interoperability and makes the external tool contract independent of one TypeScript library's release cycle.
The cost is that runtime ergonomics depend on the selected validator and generated types. Application-only normalization needs a separate named layer, and sophisticated TypeScript inference may be less direct. Keep the handler pipeline explicit: parse JSON, validate portable schema, apply policy validation, normalize into an internal command, then execute. Generated types are a convenience derived from validated data; they are not proof that an inbound value passed at runtime.
With JSON Schema canonical, generate TypeScript types and runtime adapters as disposable outputs. Review the schema diff first, then prove that each consumer reports the same accepted and rejected golden instances.
| Question | JSON Schema authority | Zod authority |
|---|---|---|
| Languages | Many | TypeScript-first |
| Refinements | Separate policy | Native functions |
| Provider artifact | Lower subset | Generated export |
| Main risk | Runtime ergonomics | Conversion loss |
Try Zod as the canonical authoring model
With Zod authority, TypeScript authors compose one executable schema close to the handler and infer the input type directly. Refinements can produce domain-specific errors, and transformations can yield a convenient internal command. Export a JSON Schema representation for provider registration, then inspect its loss report and store the generated artifact so reviewers see what the model receives. JSON Schema vs Zod tilts toward this option for a TypeScript-only product with application-rich validation.
Avoid conflating input and output types when transforms run. The provider supplies pre-transform data, while the handler may consume a normalized value. Name both. Keep refinements deterministic and side-effect free; tenant and database policy should remain a later service step. A schema that makes network calls during parse is hard to cache, replay, and explain after a failed agent invocation.
With Zod canonical, preserve refinements in the server validator and test Zod JSON Schema conversion explicitly. Every dropped transform, default, branded type, or custom issue should appear in a machine-readable loss report.
Compare errors at three audiences
A model-facing error should identify invalid fields and a repairable constraint without exposing private policy. A developer error should include schema version, instance path, keyword or refinement ID, and the rejected-value class. A user error should explain the product consequence in ordinary language. One raw validator message cannot serve all three audiences safely, so map structured issues through explicit adapters.
Run malformed JSON, unknown property, empty object, invalid enum, oversized page, contradictory filters, and tenant-forbidden status through both paths. Compare acceptance and normalized outputs, not prose punctuation. If Zod rejects something the exported schema accepts, record it as conversion loss and ensure the server catches it. If the provider rejects a keyword during registration, lower it deliberately rather than changing canonical semantics by accident.
Runtime validation errors serve callers, operators, and end users differently. Keep a stable machine path and code, add an operator-safe cause, then translate only the actionable portion into user-facing recovery text.
Measure conversion loss with a golden corpus
Build a corpus of valid, invalid, and ambiguous instances, plus expected normalized commands and issue IDs. Evaluate canonical validation, generated validation, provider-lowered validation, and handler policy. A parity table reveals where the representations diverge. Include recursive references, unions, optional versus nullable fields, defaults, formats, unknown keys, transforms, and cross-field rules because happy scalar objects conceal the consequential edges.
The failure mode is round-tripping a generated schema back into the authoring library and assuming semantic identity. Its consequence is constraint erosion hidden by successful parsing. Treat conversion as one-way compilation unless a fixture proves otherwise. JSON Schema vs Zod should be settled by the actual corpus and consumers, not by how pleasant the first five lines look in an editor.
The golden corpus should include boundary numbers, unknown properties, Unicode, nested unions, conflicting constraints, and examples that fail policy after structure passes. Portable function calling depends on consistent failure as much as acceptance.
Version tools as public behavioral contracts
Additive optional fields may still change model behavior, while enum removal or meaning changes can break saved prompts and replays. Give every tool schema a stable name, semantic version or immutable digest, compatibility note, and migration fixture. Keep old validators available while old runs or clients can call them. The handler should know exactly which version produced an argument and never reinterpret historical payloads under a new schema silently.
Use agent tool schema evolution for migration mechanics, constrained decoding for syntax boundaries, the agent-ready API specification for operation semantics, and idempotency lifecycle contracts for effect policy. These layers are more important than the authoring library because they preserve meaning across providers and releases.
Version agent tool schemas with their normalized command semantics, not just their JSON shape. A renamed enum that triggers the same effect differs from an unchanged field whose handler now performs a new side effect. Test both directions explicitly.
- 1Classify
Sort rules by portability and policy.
- 2Compile
Generate every downstream artifact.
- 3Probe
Run one golden instance corpus.
- 4Decide
Choose authority and store the loss report.
Choose from the ownership matrix
Choose JSON Schema authority when several languages, providers, or independent validators consume the contract and portable review is the dominant requirement. Choose Zod authority when one TypeScript service owns the tool, executable refinements materially improve maintenance, and a tested export profile covers every external consumer. In either case, generate one direction, store the output, fail CI on unexplained loss, and run both positive and negative corpora.
JSON Schema vs Zod has no universal winner because the useful boundary differs. The wrong choice is dual authorship without parity evidence. The right choice names one editable source, one compiler path, one loss ledger, one runtime policy layer, and error views for model, developer, and user. That system remains portable even when the canonical syntax is application-specific.
Runnable artifact: The portability probe deliberately strips an application-only refinement while proving that the portable email format and object boundary remain. Save it as schema-portability-probe.test.mjs and run node schema-portability-probe.test.mjs. Expected final line: PASS: conversion loss exposed.
import assert from "node:assert/strict";
const author={type:"object",properties:{email:{type:"string",format:"email"}},required:["email"],additionalProperties:false,refinement:"company-domain"};
const portable=(({refinement,...schema})=>schema)(author);
assert.equal(portable.refinement,undefined);assert.equal(portable.properties.email.format,"email");
assert.equal(author.refinement,"company-domain");
console.log("PASS: conversion loss exposed");
The team can now choose authority without ideology. JSON Schema vs Zod resolves to portable canonical data when independent languages and providers dominate, or executable Zod authoring when one TypeScript service owns meaningful refinements and every export loss is tested. Both choices require the same discipline: one editable source, stored generated artifacts, a provider subset profile, layered policy validation, structured error adapters, and a golden instance corpus.
Put the conversion-loss report in code review beside the schema diff. A new field is incomplete until reviewers see its provider representation, runtime acceptance, normalized command, error view, and compatibility note. Reject any pipeline that silently deletes a constraint, even if the server still happens to catch today's examples. Re-run parity after schema dialect, Zod, converter, provider strictness, handler policy, or tool version changes; the source of truth is only useful while all compiled boundaries remain observable.