diff --git a/.changeset/quiet-unknown-format-warnings.md b/.changeset/quiet-unknown-format-warnings.md new file mode 100644 index 0000000000..956565b6db --- /dev/null +++ b/.changeset/quiet-unknown-format-warnings.md @@ -0,0 +1,5 @@ +--- +'@modelcontextprotocol/sdk': patch +--- + +Silence unknown-format warnings from the default AJV validator. Servers using custom formats no longer spam one warning per subschema on listTools; other warnings and known-format validation are unchanged. diff --git a/src/validation/ajv-provider.ts b/src/validation/ajv-provider.ts index 3c2967c3a1..404475cbac 100644 --- a/src/validation/ajv-provider.ts +++ b/src/validation/ajv-provider.ts @@ -11,7 +11,18 @@ function createDefaultAjvInstance(): Ajv { strict: false, validateFormats: true, validateSchema: false, - allErrors: true + allErrors: true, + // Servers may use custom formats Ajv does not know (e.g. google-duration). + // Ajv skips validation for those with one warning per subschema, which + // spams listTools output. Drop only that noise; keep other warnings. + logger: { + log: console.log.bind(console), + error: console.error.bind(console), + warn: (message: string, ...params: unknown[]) => { + if (message.startsWith('unknown format')) return; + console.warn(message, ...params); + } + } }); const addFormats = _addFormats as unknown as typeof _addFormats.default; diff --git a/test/validation/ajv-unknown-format.test.ts b/test/validation/ajv-unknown-format.test.ts new file mode 100644 index 0000000000..85b0cdf7c5 --- /dev/null +++ b/test/validation/ajv-unknown-format.test.ts @@ -0,0 +1,39 @@ +/** + * Unknown custom formats (e.g. google-duration) should not spam the console. + * The default AJV instance skips validation for formats it does not know; + * it should do so quietly while still enforcing known formats. + */ + +import { vi } from 'vitest'; + +import { AjvJsonSchemaValidator } from '../../src/validation/ajv-provider.js'; +import type { JsonSchemaType } from '../../src/validation/types.js'; + +describe('AjvJsonSchemaValidator unknown formats', () => { + it('compiles custom formats without warning and skips their validation', () => { + const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}); + try { + const schema: JsonSchemaType = { + type: 'object', + properties: { + alignmentPeriod: { type: 'string', format: 'google-duration' } + } + }; + const validator = new AjvJsonSchemaValidator().getValidator(schema); + + expect(warn).not.toHaveBeenCalled(); + expect(validator({ alignmentPeriod: '3600s' }).valid).toBe(true); + expect(validator({ alignmentPeriod: 'bogus' }).valid).toBe(true); + } finally { + warn.mockRestore(); + } + }); + + it('still enforces known formats', () => { + const schema: JsonSchemaType = { type: 'string', format: 'email' }; + const validator = new AjvJsonSchemaValidator().getValidator(schema); + + expect(validator('user@example.com').valid).toBe(true); + expect(validator('invalid-email').valid).toBe(false); + }); +});