fix(validators): honor declared draft-07/06 JSON Schema dialects instead of rejecting them (#2534)
This commit is contained in:
parent
6dac272880
commit
f130e1ac5f
1 changed files with 851 additions and 109 deletions
|
|
@ -707,10 +707,9 @@ the host side and register the result with `fromJsonSchema()`: zod-4 input via z
|
|||
own `z.toJSONSchema(z.object(shape), { io: 'input', target: 'draft-2020-12' })` (the
|
||||
conversion is runtime-structural, so a zod ≥4.2 in the host handles schemas built by a
|
||||
different zod-4 copy), zod-3 input via the
|
||||
[`zod-to-json-schema`](https://www.npmjs.com/package/zod-to-json-schema) package. Strip
|
||||
the `$schema` member from the converted output before passing it to `fromJsonSchema()`
|
||||
— `zod-to-json-schema` stamps a draft-07 `$schema` by default, and the default
|
||||
validator [accepts 2020-12 only](#json-schema-2020-12-posture-sep-1613-sep-2106).
|
||||
[`zod-to-json-schema`](https://www.npmjs.com/package/zod-to-json-schema) package. Its
|
||||
default draft-07 `$schema` stamp is fine as-is — the default validator
|
||||
[honors declared draft-07/06 dialects](#json-schema-2020-12-posture-sep-1613-sep-2106).
|
||||
|
||||
How a too-old zod surfaces depends on which entry point your code imports. With
|
||||
main-entry `import { z } from 'zod'` on a zod-3 range, the project **typechecks cleanly
|
||||
|
|
@ -1455,9 +1454,19 @@ classes — import it from one package consistently within a process.
|
|||
|
||||
#### JSON Schema 2020-12 posture (SEP-1613, SEP-2106)
|
||||
|
||||
The default validator supports **JSON Schema 2020-12 only**. On Node it is now `Ajv2020`
|
||||
instead of draft-07 `Ajv`; the Cloudflare Workers default was already 2020-12. Schemas
|
||||
declaring a different `$schema` are rejected with `Error("…unsupported dialect…")`.
|
||||
The default validator dispatches on the schema's declared `$schema`: absent or 2020-12
|
||||
validates as **JSON Schema 2020-12** — on Node via `Ajv2020` instead of v1's draft-07
|
||||
`Ajv` (the Cloudflare Workers default was already 2020-12) — a declared 2019-09
|
||||
`$schema` validates with 2019-09 semantics (`Ajv2019`), and a declared draft-07 or
|
||||
draft-06 `$schema` validates with draft-07 semantics. Schemas declaring any other
|
||||
`$schema` are rejected with `Error("…unsupported dialect…")`. Two known draft-07 engine
|
||||
differences: the Node engine (classic Ajv, same as v1's default) evaluates keywords
|
||||
adjacent to `$ref`, stricter than draft-07's ignore-siblings rule, while the
|
||||
browser/Workers engine ignores them per spec; and the browser/Workers engine does not
|
||||
resolve a `$ref` inside a `dependencies` entry whose key collides with a JSON Schema
|
||||
keyword (`type`, `default`, `format`, …) — validation throws `Unresolved $ref` when
|
||||
that dependency triggers (surfaced as the SDK's typed validation error), while the
|
||||
Node engine handles the same schema correctly.
|
||||
|
||||
`CallToolResult.structuredContent` is widened from `{ [k: string]: unknown }` to
|
||||
`unknown` (SEP-2106 lifts the `type:"object"` root restriction). The presence check is
|
||||
|
|
@ -1465,13 +1474,13 @@ declaring a different `$schema` are rejected with `Error("…unsupported dialect
|
|||
`$ref` is not dereferenced (unchanged from v1; Ajv throws `MissingRefError` at compile,
|
||||
surfaced per-tool on `callTool`).
|
||||
|
||||
| v1 pattern | Mechanical fix |
|
||||
| ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `result.structuredContent.<key>` / `result.structuredContent?.<k>` | narrow first: `const sc = result.structuredContent; if (typeof sc === 'object' && sc !== null && '<k>' in sc) { sc.<k> }` |
|
||||
| `if (!result.structuredContent)` | `if (result.structuredContent === undefined)` |
|
||||
| relying on default `Ajv` being draft-07 | `new AjvJsonSchemaValidator(new Ajv({ strict: false, validateFormats: true, validateSchema: false, allErrors: true }))` (import `Ajv`, `addFormats`, `AjvJsonSchemaValidator` from `…/validators/ajv`) |
|
||||
| draft-07 idioms via `fromJsonSchema(schema)` | `fromJsonSchema(schema, new AjvJsonSchemaValidator(ajv))` — the `McpServer`/`Client` `jsonSchemaValidator` option does **not** reach `fromJsonSchema`-authored schemas |
|
||||
| `outputSchema` / `inputSchema` with absolute-URI `$ref` | inline under `$defs` and reference with `#/$defs/Name` |
|
||||
| v1 pattern | Mechanical fix |
|
||||
| ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `result.structuredContent.<key>` / `result.structuredContent?.<k>` | narrow first: `const sc = result.structuredContent; if (typeof sc === 'object' && sc !== null && '<k>' in sc) { sc.<k> }` |
|
||||
| `if (!result.structuredContent)` | `if (result.structuredContent === undefined)` |
|
||||
| draft-07 idioms **without** a declared `$schema` (a declared draft-07/06 `$schema` dispatches automatically) | `new AjvJsonSchemaValidator(new Ajv({ strict: false, validateFormats: true, validateSchema: false, allErrors: true }))` (import `Ajv`, `addFormats`, `AjvJsonSchemaValidator` from `…/validators/ajv`) |
|
||||
| undeclared draft-07 idioms via `fromJsonSchema(schema)` | `fromJsonSchema(schema, new AjvJsonSchemaValidator(ajv))` — the `McpServer`/`Client` `jsonSchemaValidator` option does **not** reach `fromJsonSchema`-authored schemas |
|
||||
| `outputSchema` / `inputSchema` with absolute-URI `$ref` | inline under `$defs` and reference with `#/$defs/Name` |
|
||||
|
||||
A tool may now register an `outputSchema` whose root is `type:"array"`, `type:"string"`,
|
||||
etc.; toward 2025-era clients the codec wraps it in a `{result:…}` envelope, and toward
|
||||
|
|
|
|||
Loading…
Reference in a new issue