Skip to content

AjvJsonSchemaValidator: allow ajv options (e.g. useDefaults) without giving up the per-dialect engine choice #3017

Open

Description

@marcusschiesser

AjvJsonSchemaValidator (@modelcontextprotocol/server/validators/ajv, 2.3.1) picks an ajv engine by each schema's $schema: Ajv2020 for 2020-12 or none, Ajv2019 for 2019-09, and the classic Ajv for draft-07 and draft-06. It creates each engine lazily, with strict: false, validateFormats: true, validateSchema: false, allErrors: true and ajv-formats. That is exactly what a server needs when it registers tools from raw JSON Schema with fromJsonSchema.

The only way to change an engine's options is to pass a whole ajv instance (new AjvJsonSchemaValidator(ajv)). As documented, that instance is then used for every schema and the $schema dispatch is skipped. So a server that needs one more option loses the dialect handling and has to rebuild it: one instance per draft, with formats registered on each.

Use case. We serve tools whose input schemas were captured from other MCP servers, some of them not written in TypeScript, through registerTool(name, { inputSchema: fromJsonSchema(schema, validator) }, cb). A real server applies the defaults its schema states before its handler runs. With the SDK's validator a call with {} reaches the callback as {}. To match the real server we need ajv's useDefaults: true. Today that means either keeping our own copy of the dispatch, or filling defaults after validation ourselves (top-level only).

Proposal. Let the constructor take ajv options that are applied to every engine it creates, keeping the dispatch:

new AjvJsonSchemaValidator({ options: { useDefaults: true } })

The existing new AjvJsonSchemaValidator(ajv) form would stay as it is. An alternative of the same size: an engine factory, new AjvJsonSchemaValidator({ engine: (dialect) => … }), called once per dialect.

Notes

  • useDefaults changes the data ajv validates. The validator already returns data and the SDK passes it to the callback, so defaults would reach handlers with no other change. We tested this by passing an instance with useDefaults: nested defaults arrived filled too.
  • We're happy to send a PR if this direction is acceptable.

Activity

  1. added
    v2Ideas, requests and plans for v2 of the SDK which will incorporate major changes and fixes
    on Oct 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementRequest for a new feature that's not currently supportedv2Ideas, requests and plans for v2 of the SDK which will incorporate major changes and fixes

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions