-
Notifications
You must be signed in to change notification settings - Fork 1.9k
feat(client,server): bundle default validators, expose customisation via subpaths #2088
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
ba61c07
Bundle automatic validator defaults in client and server shims
mattzcarey 1436a5a
docs(core): align validator examples with bundled-shim surface
mattzcarey 44e7b26
fix: address PR #2088 review feedback
mattzcarey 698944e
test(integration): drop pre-installed @cfworker/json-schema from CF W…
mattzcarey f94ceb6
feat(client,server): re-expose validator classes via explicit subpaths
mattzcarey 99cf12f
feat(codemod): rewrite v1 validator paths to the v2 subpaths
mattzcarey File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| --- | ||
| '@modelcontextprotocol/core': minor | ||
| '@modelcontextprotocol/client': patch | ||
| '@modelcontextprotocol/server': patch | ||
| --- | ||
|
|
||
| Bundle automatic JSON Schema validator defaults in `@modelcontextprotocol/client` and `@modelcontextprotocol/server` runtime shims. | ||
|
|
||
| Client and server pick the right validator automatically based on the runtime: the Node shim uses AJV, the browser/workerd shim uses `@cfworker/json-schema`. Both backends are bundled into the shim chunks that select them, so the default code path needs no extra installs — `import { McpServer } from '@modelcontextprotocol/server'` does not pull `ajv` or `@cfworker/json-schema` into the root entry chunk. | ||
|
|
||
| The named validator classes remain part of the public surface for consumers who want to customize the built-in backend (pre-register schemas by `$id`, register custom AJV formats, switch dialects, change `@cfworker/json-schema` draft). They are exposed through explicit subpaths so they do not bloat the root index chunk: | ||
|
|
||
| - `import { AjvJsonSchemaValidator } from '@modelcontextprotocol/{client,server}/validators/ajv'` | ||
| - `import { CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/{client,server}/validators/cf-worker'` | ||
|
|
||
| Importing from one of these subpaths means the corresponding peer dep (`ajv` + `ajv-formats`, or `@cfworker/json-schema`) must be in your `package.json`. The shim keeps its own vendored copy for the default path, so a project can use the subpath in some files and rely on the default in others. | ||
|
|
||
| The `jsonSchemaValidator` interface remains the public extension point for replacing validation entirely with a custom implementation. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| /** | ||
| * Customisation entry point for the AJV validator. Re-exports `Ajv` + `addFormats` from the | ||
| * SDK's bundled copy, so customising the validator needs no extra installs. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * import { Ajv, addFormats, AjvJsonSchemaValidator } from '@modelcontextprotocol/client/validators/ajv'; | ||
| * | ||
| * const ajv = new Ajv({ strict: true, allErrors: true }); | ||
| * addFormats(ajv); | ||
| * const validator = new AjvJsonSchemaValidator(ajv); | ||
| * ``` | ||
| */ | ||
| export { addFormats, Ajv, AjvJsonSchemaValidator } from '@modelcontextprotocol/core/validators/ajv'; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,3 @@ | ||
| /** | ||
| * Cloudflare Workers JSON Schema validator, available as a sub-path export. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * import { CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/client/validators/cf-worker'; | ||
| * ``` | ||
| */ | ||
| /** Customisation entry point for the `@cfworker/json-schema` validator. */ | ||
| export type { CfWorkerSchemaDraft } from '@modelcontextprotocol/core/validators/cfWorker'; | ||
| export { CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/core/validators/cfWorker'; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
110 changes: 110 additions & 0 deletions
110
packages/client/test/client/jsonSchemaValidatorOverride.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| import type { JSONRPCMessage, JsonSchemaType, JsonSchemaValidatorResult, jsonSchemaValidator } from '@modelcontextprotocol/core'; | ||
| import { InMemoryTransport, LATEST_PROTOCOL_VERSION } from '@modelcontextprotocol/core'; | ||
| import { Client } from '../../src/client/client.js'; | ||
| import { fromJsonSchema } from '../../src/fromJsonSchema.js'; | ||
|
|
||
| class RecordingValidator implements jsonSchemaValidator { | ||
| schemas: JsonSchemaType[] = []; | ||
| values: unknown[] = []; | ||
|
|
||
| getValidator<T>(schema: JsonSchemaType) { | ||
| this.schemas.push(schema); | ||
| return (value: unknown): JsonSchemaValidatorResult<T> => { | ||
| this.values.push(value); | ||
| return { valid: true, data: value as T, errorMessage: undefined }; | ||
| }; | ||
| } | ||
| } | ||
|
|
||
| async function connectInitializedClient(client: Client) { | ||
| const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); | ||
| serverTransport.onmessage = async message => { | ||
| if ('method' in message && 'id' in message && message.method === 'initialize') { | ||
| await serverTransport.send({ | ||
| jsonrpc: '2.0', | ||
| id: message.id, | ||
| result: { | ||
| protocolVersion: LATEST_PROTOCOL_VERSION, | ||
| capabilities: { tools: {} }, | ||
| serverInfo: { name: 'test-server', version: '1.0.0' } | ||
| } | ||
| }); | ||
| } else if ('method' in message && 'id' in message && message.method === 'tools/list') { | ||
| await serverTransport.send({ | ||
| jsonrpc: '2.0', | ||
| id: message.id, | ||
| result: { | ||
| tools: [ | ||
| { | ||
| name: 'structured-tool', | ||
| description: 'A tool with structured output', | ||
| inputSchema: { type: 'object' }, | ||
| outputSchema: { | ||
| type: 'object', | ||
| properties: { count: { type: 'number' } }, | ||
| required: ['count'] | ||
| } | ||
| } | ||
| ] | ||
| } | ||
| } satisfies JSONRPCMessage); | ||
| } | ||
| }; | ||
|
|
||
| await Promise.all([client.connect(clientTransport), serverTransport.start()]); | ||
| return { clientTransport, serverTransport }; | ||
| } | ||
|
|
||
| describe('client JSON Schema validator overrides', () => { | ||
| test('Client constructor uses a custom validator for tool output schema caching', async () => { | ||
| const validator = new RecordingValidator(); | ||
| const client = new Client( | ||
| { name: 'test-client', version: '1.0.0' }, | ||
| { | ||
| capabilities: {}, | ||
| jsonSchemaValidator: validator | ||
| } | ||
| ); | ||
| const { clientTransport, serverTransport } = await connectInitializedClient(client); | ||
|
|
||
| await expect(client.listTools()).resolves.toMatchObject({ | ||
| tools: [ | ||
| { | ||
| name: 'structured-tool', | ||
| outputSchema: { | ||
| type: 'object', | ||
| properties: { count: { type: 'number' } }, | ||
| required: ['count'] | ||
| } | ||
| } | ||
| ] | ||
| }); | ||
|
|
||
| expect(validator.schemas).toEqual([ | ||
| { | ||
| type: 'object', | ||
| properties: { count: { type: 'number' } }, | ||
| required: ['count'] | ||
| } | ||
| ]); | ||
|
|
||
| await client.close(); | ||
| await clientTransport.close(); | ||
| await serverTransport.close(); | ||
| }); | ||
|
|
||
| test('fromJsonSchema uses an explicitly supplied custom validator', async () => { | ||
| const validator = new RecordingValidator(); | ||
| const schema: JsonSchemaType = { | ||
| type: 'object', | ||
| properties: { name: { type: 'string' } }, | ||
| required: ['name'] | ||
| }; | ||
|
|
||
| const standardSchema = fromJsonSchema<{ name: string }>(schema, validator); | ||
| expect(standardSchema['~standard'].validate({ name: 123 })).toEqual({ value: { name: 123 } }); | ||
|
|
||
| expect(validator.schemas).toEqual([schema]); | ||
| expect(validator.values).toEqual([{ name: 123 }]); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.