You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
AjvJsonSchemaValidator: allow ajv options (e.g. useDefaults) without giving up the per-dialect engine choice #3017
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:
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