diff --git a/.changeset/cfworker-out-of-barrel.md b/.changeset/cfworker-out-of-barrel.md index 9a35b845dc..c2265736bb 100644 --- a/.changeset/cfworker-out-of-barrel.md +++ b/.changeset/cfworker-out-of-barrel.md @@ -3,4 +3,4 @@ '@modelcontextprotocol/client': patch --- -Stop bundling `@cfworker/json-schema` into the main package barrel. Previously `CfWorkerJsonSchemaValidator` was re-exported from the core internal barrel, so tsdown inlined the `@cfworker/json-schema` dev dependency into every consumer's bundle even when it was never used. The validator is now reachable only via the `_shims` conditional (workerd/browser) and the explicit `@modelcontextprotocol/{server,client}/validators/cf-worker` subpath, so consumers that don't opt into it no longer ship that code. No public API change. +Stop bundling `@cfworker/json-schema` into the main package barrel. Previously `CfWorkerJsonSchemaValidator` was re-exported from the core internal barrel, so tsdown inlined the `@cfworker/json-schema` dev dependency into every consumer's bundle even when it was never used. The validator is now reachable only via the `_shims` conditional (workerd/browser), so consumers that don't opt into it no longer ship that code. The interim `@modelcontextprotocol/{server,client}/validators/cf-worker` subpath this introduced has been removed in a follow-up — the runtime shim is now the only entry point. diff --git a/.changeset/support-standard-json-schema.md b/.changeset/support-standard-json-schema.md index 1ceff35844..792e15f1f7 100644 --- a/.changeset/support-standard-json-schema.md +++ b/.changeset/support-standard-json-schema.md @@ -21,10 +21,10 @@ server.registerTool('greet', { For raw JSON Schema (e.g. TypeBox output), use the new `fromJsonSchema` adapter: ```typescript -import { fromJsonSchema, AjvJsonSchemaValidator } from '@modelcontextprotocol/core'; +import { fromJsonSchema } from '@modelcontextprotocol/server'; server.registerTool('greet', { - inputSchema: fromJsonSchema({ type: 'object', properties: { name: { type: 'string' } } }, new AjvJsonSchemaValidator()) + inputSchema: fromJsonSchema({ type: 'object', properties: { name: { type: 'string' } } }) }, handler); ``` diff --git a/.changeset/workerd-shim-vendors-cfworker.md b/.changeset/workerd-shim-vendors-cfworker.md new file mode 100644 index 0000000000..5ebd80f3a0 --- /dev/null +++ b/.changeset/workerd-shim-vendors-cfworker.md @@ -0,0 +1,11 @@ +--- +'@modelcontextprotocol/core': minor +'@modelcontextprotocol/client': patch +'@modelcontextprotocol/server': patch +--- + +Bundle automatic JSON Schema validator defaults in `@modelcontextprotocol/client` and `@modelcontextprotocol/server` runtime shims. + +Client/server select defaults automatically based on the runtime: Node shims use AJV, while browser/workerd shims use `@cfworker/json-schema`. Those backends are bundled into the shim chunks that select them, so consumers do not need to install validator packages or import explicit validators for default behavior. Advanced users can still pass their own `jsonSchemaValidator` interface implementation. + +The `@modelcontextprotocol/{client,server}/validators/cf-worker` subpath export has been removed — there is no longer any public entry point for the SDK's built-in validator classes. `AjvJsonSchemaValidator` and `CfWorkerJsonSchemaValidator` are now `@internal` and no longer exported from `@modelcontextprotocol/client` or `@modelcontextprotocol/server` (not even as types). The `jsonSchemaValidator` interface remains the public extension point for custom validators, and example JSDoc snippets no longer demonstrate direct validator instantiation. diff --git a/docs/migration-SKILL.md b/docs/migration-SKILL.md index dbe6a4e9f7..3d1e133017 100644 --- a/docs/migration-SKILL.md +++ b/docs/migration-SKILL.md @@ -500,8 +500,8 @@ Type changes in handler context: The SDK now auto-selects the appropriate JSON Schema validator based on runtime: -- Node.js → `AjvJsonSchemaValidator` (no change from v1) -- Cloudflare Workers (workerd) → `CfWorkerJsonSchemaValidator` (previously required manual config) +- Node.js → AJV (no change from v1) +- Cloudflare Workers (workerd) → `@cfworker/json-schema` (previously required manual config) **No action required** for most users. Cloudflare Workers users can remove explicit `jsonSchemaValidator` configuration: @@ -518,11 +518,12 @@ new McpServer( new McpServer({ name: 'server', version: '1.0.0' }, {}); ``` -Access validators explicitly: +Validator behavior: -- Runtime-aware default: `import { DefaultJsonSchemaValidator } from '@modelcontextprotocol/server/_shims';` -- AJV (Node.js): `import { AjvJsonSchemaValidator } from '@modelcontextprotocol/server';` -- CF Worker: `import { CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/server/validators/cf-worker';` +- Do not add validator imports for normal migrations. +- Do not install `ajv`, `ajv-formats`, or `@cfworker/json-schema`; client/server bundle the runtime-selected defaults. +- The SDK's built-in validator classes (`AjvJsonSchemaValidator`, `CfWorkerJsonSchemaValidator`) are not exported from `@modelcontextprotocol/{client,server}` (not even as types). The `@modelcontextprotocol/{client,server}/validators/cf-worker` subpath that existed in v2 pre-releases has been removed. +- Advanced users may pass `jsonSchemaValidator: myCustomValidator` with their own implementation of the `jsonSchemaValidator` interface. ## 15. Migration Steps (apply in this order) diff --git a/docs/migration.md b/docs/migration.md index cd3da6dcda..727e9d9454 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -900,8 +900,8 @@ server.setRequestHandler('tools/call', async (request, ctx) => { The SDK now automatically selects the appropriate JSON Schema validator based on your runtime environment: -- **Node.js**: Uses `AjvJsonSchemaValidator` (same as v1 default) -- **Cloudflare Workers**: Uses `CfWorkerJsonSchemaValidator` (previously required manual configuration) +- **Node.js**: Uses AJV (same as v1 default) +- **Cloudflare Workers**: Uses `@cfworker/json-schema` (previously required manual configuration) This means Cloudflare Workers users no longer need to explicitly pass the validator: @@ -932,17 +932,22 @@ const server = new McpServer( ); ``` -You can still explicitly override the validator if needed: +You do not need to install or import validator packages for the default behavior. The client and server packages bundle the validator backend selected by the runtime shim. -```typescript -// Runtime-aware default (auto-selects AjvJsonSchemaValidator or CfWorkerJsonSchemaValidator) -import { DefaultJsonSchemaValidator } from '@modelcontextprotocol/server/_shims'; +Advanced users can still override validation by passing an object that implements the SDK's JSON Schema validator interface: -// Specific validators -import { AjvJsonSchemaValidator } from '@modelcontextprotocol/server'; -import { CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/server/validators/cf-worker'; +```typescript +const server = new McpServer( + { name: 'my-server', version: '1.0.0' }, + { + capabilities: { tools: {} }, + jsonSchemaValidator: myCustomValidator + } +); ``` +The SDK's built-in validator classes (`AjvJsonSchemaValidator`, `CfWorkerJsonSchemaValidator`) are not part of the public surface — `@modelcontextprotocol/{client,server}` no longer export them as runtime values or types, and the `@modelcontextprotocol/{client,server}/validators/cf-worker` subpath that briefly existed in v2 pre-releases has been removed. To customise validation, implement the `jsonSchemaValidator` interface yourself and pass your implementation through the option above. + ## Unchanged APIs The following APIs are unchanged between v1 and v2 (only the import paths changed): diff --git a/packages/client/package.json b/packages/client/package.json index 537804b732..d436c04493 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -28,10 +28,6 @@ "types": "./dist/stdio.d.mts", "import": "./dist/stdio.mjs" }, - "./validators/cf-worker": { - "types": "./dist/validators/cfWorker.d.mts", - "import": "./dist/validators/cfWorker.mjs" - }, "./_shims": { "workerd": { "types": "./dist/shimsWorkerd.d.mts", @@ -54,9 +50,6 @@ "types": "./dist/index.d.mts", "typesVersions": { "*": { - "validators/cf-worker": [ - "dist/validators/cfWorker.d.mts" - ], "stdio": [ "dist/stdio.d.mts" ] @@ -93,6 +86,8 @@ "@modelcontextprotocol/eslint-config": "workspace:^", "@modelcontextprotocol/test-helpers": "workspace:^", "@cfworker/json-schema": "catalog:runtimeShared", + "ajv": "catalog:runtimeShared", + "ajv-formats": "catalog:runtimeShared", "@types/content-type": "catalog:devTools", "@types/cross-spawn": "catalog:devTools", "@types/eventsource": "catalog:devTools", diff --git a/packages/client/src/client/client.ts b/packages/client/src/client/client.ts index 5fa2e14d94..92a25cea09 100644 --- a/packages/client/src/client/client.ts +++ b/packages/client/src/client/client.ts @@ -161,7 +161,7 @@ export type ClientOptions = ProtocolOptions & { * The validator is used to validate structured content returned by tools * against their declared output schemas. * - * @default {@linkcode DefaultJsonSchemaValidator} ({@linkcode index.AjvJsonSchemaValidator | AjvJsonSchemaValidator} on Node.js, `CfWorkerJsonSchemaValidator` on Cloudflare Workers) + * @default Runtime-selected validator (AJV-backed on Node.js, `@cfworker/json-schema`-backed on browser/workerd runtimes) */ jsonSchemaValidator?: jsonSchemaValidator; diff --git a/packages/client/src/shimsNode.ts b/packages/client/src/shimsNode.ts index 00b80abe05..de48ea2de6 100644 --- a/packages/client/src/shimsNode.ts +++ b/packages/client/src/shimsNode.ts @@ -3,7 +3,7 @@ * * This file is selected via package.json export conditions when running in Node.js. */ -export { AjvJsonSchemaValidator as DefaultJsonSchemaValidator } from '@modelcontextprotocol/core'; +export { AjvJsonSchemaValidator as DefaultJsonSchemaValidator } from '@modelcontextprotocol/core/validators/ajv'; /** * Whether `fetch()` may throw `TypeError` due to CORS. CORS is a browser-only concept — diff --git a/packages/client/src/validators/cfWorker.ts b/packages/client/src/validators/cfWorker.ts deleted file mode 100644 index 8d66770e0d..0000000000 --- a/packages/client/src/validators/cfWorker.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Cloudflare Workers JSON Schema validator, available as a sub-path export. - * - * @example - * ```ts - * import { CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/client/validators/cf-worker'; - * ``` - */ -export type { CfWorkerSchemaDraft } from '@modelcontextprotocol/core/validators/cfWorker'; -export { CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/core/validators/cfWorker'; diff --git a/packages/client/test/client/barrelClean.test.ts b/packages/client/test/client/barrelClean.test.ts index 6a7dc02b7f..b8dd65bbb0 100644 --- a/packages/client/test/client/barrelClean.test.ts +++ b/packages/client/test/client/barrelClean.test.ts @@ -8,6 +8,7 @@ import { beforeAll, describe, expect, test } from 'vitest'; const pkgDir = join(dirname(fileURLToPath(import.meta.url)), '../..'); const distDir = join(pkgDir, 'dist'); const NODE_ONLY = /\b(child_process|cross-spawn|node:stream|node:child_process)\b/; +const VALIDATOR_BACKEND_IMPORT = /from\s+["'](?:ajv|ajv-formats|@cfworker\/json-schema)["']/; function chunkImportsOf(entryPath: string): string[] { const visited = new Set(); @@ -52,4 +53,17 @@ describe('@modelcontextprotocol/client root entry is browser-safe', () => { expect(stdio).toMatch(/\bgetDefaultEnvironment\b/); expect(stdio).toMatch(/\bDEFAULT_INHERITED_ENV_VARS\b/); }); + + test('runtime shims vendor default validator backends instead of requiring consumers to install them', () => { + for (const shim of ['shimsNode.mjs', 'shimsWorkerd.mjs', 'shimsBrowser.mjs']) { + const entry = join(distDir, shim); + expect(readFileSync(entry, 'utf8')).not.toMatch(VALIDATOR_BACKEND_IMPORT); + + for (const chunk of chunkImportsOf(entry)) { + expect({ chunk, content: readFileSync(chunk, 'utf8') }).not.toEqual( + expect.objectContaining({ content: expect.stringMatching(VALIDATOR_BACKEND_IMPORT) }) + ); + } + } + }); }); diff --git a/packages/client/test/client/jsonSchemaValidatorOverride.test.ts b/packages/client/test/client/jsonSchemaValidatorOverride.test.ts new file mode 100644 index 0000000000..2e38f618c5 --- /dev/null +++ b/packages/client/test/client/jsonSchemaValidatorOverride.test.ts @@ -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(schema: JsonSchemaType) { + this.schemas.push(schema); + return (value: unknown): JsonSchemaValidatorResult => { + 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 }]); + }); +}); diff --git a/packages/client/tsconfig.json b/packages/client/tsconfig.json index a40ee9fd5e..5f47efeceb 100644 --- a/packages/client/tsconfig.json +++ b/packages/client/tsconfig.json @@ -7,6 +7,7 @@ "*": ["./*"], "@modelcontextprotocol/core": ["./node_modules/@modelcontextprotocol/core/src/index.ts"], "@modelcontextprotocol/core/public": ["./node_modules/@modelcontextprotocol/core/src/exports/public/index.ts"], + "@modelcontextprotocol/core/validators/ajv": ["./node_modules/@modelcontextprotocol/core/src/validators/ajvProvider.ts"], "@modelcontextprotocol/core/validators/cfWorker": [ "./node_modules/@modelcontextprotocol/core/src/validators/cfWorkerProvider.ts" ], diff --git a/packages/client/tsdown.config.ts b/packages/client/tsdown.config.ts index c547e6ec9a..83b69f4fca 100644 --- a/packages/client/tsdown.config.ts +++ b/packages/client/tsdown.config.ts @@ -4,7 +4,7 @@ export default defineConfig({ failOnWarn: 'ci-only', // 1. Entry Points // Directly matches package.json include/exclude globs - entry: ['src/index.ts', 'src/stdio.ts', 'src/shimsNode.ts', 'src/shimsWorkerd.ts', 'src/shimsBrowser.ts', 'src/validators/cfWorker.ts'], + entry: ['src/index.ts', 'src/stdio.ts', 'src/shimsNode.ts', 'src/shimsWorkerd.ts', 'src/shimsBrowser.ts'], // 2. Output Configuration format: ['esm'], @@ -27,13 +27,19 @@ export default defineConfig({ paths: { '@modelcontextprotocol/core': ['../core/src/index.ts'], '@modelcontextprotocol/core/public': ['../core/src/exports/public/index.ts'], + '@modelcontextprotocol/core/validators/ajv': ['../core/src/validators/ajvProvider.ts'], '@modelcontextprotocol/core/validators/cfWorker': ['../core/src/validators/cfWorkerProvider.ts'] } } }, - // 5. Vendoring Strategy - Bundle the code for this specific package into the output, - // but treat all other dependencies as external (require/import). - noExternal: ['@modelcontextprotocol/core'], + // 5. Vendoring Strategy - Bundle this package's core implementation into the output, + // but treat most dependencies as external (require/import). + // + // The runtime `_shims` entries choose default JSON Schema validators: AJV on Node and + // @cfworker/json-schema on workerd/browser. Client users should not have to install a + // validator backend just to use the runtime default, so bundle the default backends into + // the shim chunks that select them. + noExternal: ['@modelcontextprotocol/core', 'ajv', 'ajv-formats', '@cfworker/json-schema'], // 6. External packages - keep self-reference imports external for runtime resolution external: ['@modelcontextprotocol/client/_shims'] diff --git a/packages/core/package.json b/packages/core/package.json index 201773736f..beb46ccb88 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -33,6 +33,10 @@ "types": "./src/exports/public/index.ts", "import": "./src/exports/public/index.ts" }, + "./validators/ajv": { + "types": "./src/validators/ajvProvider.ts", + "import": "./src/validators/ajvProvider.ts" + }, "./validators/cfWorker": { "types": "./src/validators/cfWorkerProvider.ts", "import": "./src/validators/cfWorkerProvider.ts" @@ -49,19 +53,25 @@ "client": "tsx scripts/cli.ts client" }, "dependencies": { - "ajv": "catalog:runtimeShared", - "ajv-formats": "catalog:runtimeShared", "json-schema-typed": "catalog:runtimeShared", "zod": "catalog:runtimeShared" }, "peerDependencies": { "@cfworker/json-schema": "catalog:runtimeShared", + "ajv": "catalog:runtimeShared", + "ajv-formats": "catalog:runtimeShared", "zod": "catalog:runtimeShared" }, "peerDependenciesMeta": { "@cfworker/json-schema": { "optional": true }, + "ajv": { + "optional": true + }, + "ajv-formats": { + "optional": true + }, "zod": { "optional": false } @@ -71,6 +81,8 @@ "@modelcontextprotocol/vitest-config": "workspace:^", "@modelcontextprotocol/eslint-config": "workspace:^", "@cfworker/json-schema": "catalog:runtimeShared", + "ajv": "catalog:runtimeShared", + "ajv-formats": "catalog:runtimeShared", "@eslint/js": "catalog:devTools", "@types/content-type": "catalog:devTools", "@types/cors": "catalog:devTools", diff --git a/packages/core/src/exports/public/index.ts b/packages/core/src/exports/public/index.ts index e305f32a44..334f45b090 100644 --- a/packages/core/src/exports/public/index.ts +++ b/packages/core/src/exports/public/index.ts @@ -141,8 +141,9 @@ export { InMemoryTaskMessageQueue, InMemoryTaskStore } from '../../experimental/ export type { SpecTypeName, SpecTypes } from '../../types/specTypeSchema.js'; export { isSpecType, specTypeSchemas } from '../../types/specTypeSchema.js'; export type { StandardSchemaV1, StandardSchemaV1Sync, StandardSchemaWithJSON } from '../../util/standardSchema.js'; -export { AjvJsonSchemaValidator } from '../../validators/ajvProvider.js'; -export type { CfWorkerSchemaDraft } from '../../validators/cfWorkerProvider.js'; +// Concrete validator providers (AjvJsonSchemaValidator, CfWorkerJsonSchemaValidator) are +// intentionally NOT exported: client/server bundle them via the runtime shim and end users +// cannot reach them. To override validation, implement the `jsonSchemaValidator` interface. // fromJsonSchema is intentionally NOT exported here — the server and client packages // provide runtime-aware wrappers that default to the appropriate validator via _shims. export type { JsonSchemaType, JsonSchemaValidator, jsonSchemaValidator, JsonSchemaValidatorResult } from '../../validators/types.js'; diff --git a/packages/core/src/index.examples.ts b/packages/core/src/index.examples.ts deleted file mode 100644 index 531f512113..0000000000 --- a/packages/core/src/index.examples.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Type-checked examples for `index.ts`. - * - * These examples are synced into JSDoc comments via the sync-snippets script. - * Each function's region markers define the code snippet that appears in the docs. - * - * @module - */ - -import { AjvJsonSchemaValidator } from './validators/ajvProvider.js'; -import { CfWorkerJsonSchemaValidator } from './validators/cfWorkerProvider.js'; - -/** - * Example: AJV validator for Node.js. - */ -function validation_ajv() { - //#region validation_ajv - const validator = new AjvJsonSchemaValidator(); - //#endregion validation_ajv - return validator; -} - -/** - * Example: CfWorker validator for edge runtimes. - */ -function validation_cfWorker() { - //#region validation_cfWorker - const validator = new CfWorkerJsonSchemaValidator(); - //#endregion validation_cfWorker - return validator; -} diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 8bcc9c9591..b752250fc5 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -19,35 +19,24 @@ export * from './util/zodCompat.js'; // experimental exports export * from './experimental/index.js'; -export * from './validators/ajvProvider.js'; -// cfWorkerProvider is intentionally NOT re-exported here: it statically imports -// `@cfworker/json-schema` (an optional peer), and bundling it into the main barrel -// would force that import on all Node consumers. Import via `@modelcontextprotocol/core/validators/cfWorker` -// (used by the workerd/browser `_shims` and the public `/validators/cf-worker` subpaths). -export type { CfWorkerSchemaDraft } from './validators/cfWorkerProvider.js'; +export type { AjvJsonSchemaValidator } from './validators/ajvProvider.js'; +// Validator providers are intentionally NOT re-exported as runtime values here: AJV +// and @cfworker/json-schema are optional peers, and importing either provider from +// the root barrel would force that backend on all consumers. Internal runtime shims +// import concrete defaults via explicit core validator subpaths. +export type { CfWorkerJsonSchemaValidator, CfWorkerSchemaDraft } from './validators/cfWorkerProvider.js'; export * from './validators/fromJsonSchema.js'; /** * JSON Schema validation * - * This module provides configurable JSON Schema validation for the MCP SDK. - * Choose a validator based on your runtime environment: + * Client and server packages automatically select a JSON Schema validator backend based on the + * runtime: AJV-backed on Node.js, `@cfworker/json-schema`-backed on browser/workerd. Both backends + * are bundled into the corresponding shim, so consumers do not need to install or import validator + * packages for the default behaviour. * - * - {@linkcode AjvJsonSchemaValidator}: Best for Node.js (default, fastest) - * Bundled — no additional dependencies required. - * - * - `CfWorkerJsonSchemaValidator`: Best for edge runtimes - * Import from: `@modelcontextprotocol/server/validators/cf-worker` or `@modelcontextprotocol/client/validators/cf-worker` - * Bundled — no additional dependencies required. - * - * @example For Node.js with AJV - * ```ts source="./index.examples.ts#validation_ajv" - * const validator = new AjvJsonSchemaValidator(); - * ``` - * - * @example For Cloudflare Workers - * ```ts source="./index.examples.ts#validation_cfWorker" - * const validator = new CfWorkerJsonSchemaValidator(); - * ``` + * To override validation, pass an object implementing the {@link jsonSchemaValidator} interface as + * `jsonSchemaValidator` on the client/server options. See `validators/types.ts` for the contract + * and a sample implementation. * * @module validation */ diff --git a/packages/core/src/validators/ajvProvider.examples.ts b/packages/core/src/validators/ajvProvider.examples.ts deleted file mode 100644 index eea45bf15c..0000000000 --- a/packages/core/src/validators/ajvProvider.examples.ts +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Type-checked examples for `ajvProvider.ts`. - * - * These examples are synced into JSDoc comments via the sync-snippets script. - * Each function's region markers define the code snippet that appears in the docs. - * - * @module - */ - -import { Ajv } from 'ajv'; -import _addFormats from 'ajv-formats'; - -import { AjvJsonSchemaValidator } from './ajvProvider.js'; - -const addFormats = _addFormats as unknown as typeof _addFormats.default; - -/** - * Example: Default AJV instance. - */ -function AjvJsonSchemaValidator_default() { - //#region AjvJsonSchemaValidator_default - const validator = new AjvJsonSchemaValidator(); - //#endregion AjvJsonSchemaValidator_default - return validator; -} - -/** - * Example: Custom AJV instance. - */ -function AjvJsonSchemaValidator_customInstance() { - //#region AjvJsonSchemaValidator_customInstance - const ajv = new Ajv({ strict: true, allErrors: true }); - const validator = new AjvJsonSchemaValidator(ajv); - //#endregion AjvJsonSchemaValidator_customInstance - return validator; -} - -/** - * Example: Constructor with advanced AJV configuration including formats. - */ -function AjvJsonSchemaValidator_constructor_withFormats() { - //#region AjvJsonSchemaValidator_constructor_withFormats - const ajv = new Ajv({ validateFormats: true }); - addFormats(ajv); - const validator = new AjvJsonSchemaValidator(ajv); - //#endregion AjvJsonSchemaValidator_constructor_withFormats - return validator; -} diff --git a/packages/core/src/validators/ajvProvider.ts b/packages/core/src/validators/ajvProvider.ts index 820a3d6618..9ab6bd003f 100644 --- a/packages/core/src/validators/ajvProvider.ts +++ b/packages/core/src/validators/ajvProvider.ts @@ -22,18 +22,13 @@ function createDefaultAjvInstance(): Ajv { } /** - * @example Use with default AJV instance (recommended) - * ```ts source="./ajvProvider.examples.ts#AjvJsonSchemaValidator_default" - * const validator = new AjvJsonSchemaValidator(); - * ``` + * AJV-backed validator used as the default in Node shims. * - * @example Use with custom AJV instance - * ```ts source="./ajvProvider.examples.ts#AjvJsonSchemaValidator_customInstance" - * const ajv = new Ajv({ strict: true, allErrors: true }); - * const validator = new AjvJsonSchemaValidator(ajv); - * ``` + * Not part of the public surface: end users get this automatically via the runtime shim and + * cannot import it from `@modelcontextprotocol/client` or `@modelcontextprotocol/server`. + * To override validation, implement the {@link jsonSchemaValidator} interface. * - * @see `CfWorkerJsonSchemaValidator` for an edge-runtime-compatible alternative (import from `@modelcontextprotocol/server/validators/cf-worker` or `@modelcontextprotocol/client/validators/cf-worker`) + * @internal */ export class AjvJsonSchemaValidator implements jsonSchemaValidator { private _ajv: Ajv; @@ -42,18 +37,6 @@ export class AjvJsonSchemaValidator implements jsonSchemaValidator { * Create an AJV validator * * @param ajv - Optional pre-configured AJV instance. If not provided, a default instance will be created. - * - * @example Use default configuration (recommended for most cases) - * ```ts source="./ajvProvider.examples.ts#AjvJsonSchemaValidator_default" - * const validator = new AjvJsonSchemaValidator(); - * ``` - * - * @example Provide custom AJV instance for advanced configuration - * ```ts source="./ajvProvider.examples.ts#AjvJsonSchemaValidator_constructor_withFormats" - * const ajv = new Ajv({ validateFormats: true }); - * addFormats(ajv); - * const validator = new AjvJsonSchemaValidator(ajv); - * ``` */ constructor(ajv?: Ajv) { this._ajv = ajv ?? createDefaultAjvInstance(); diff --git a/packages/core/src/validators/cfWorkerProvider.examples.ts b/packages/core/src/validators/cfWorkerProvider.examples.ts deleted file mode 100644 index a347f9b7cf..0000000000 --- a/packages/core/src/validators/cfWorkerProvider.examples.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Type-checked examples for `cfWorkerProvider.ts`. - * - * These examples are synced into JSDoc comments via the sync-snippets script. - * Each function's region markers define the code snippet that appears in the docs. - * - * @module - */ - -import { CfWorkerJsonSchemaValidator } from './cfWorkerProvider.js'; - -/** - * Example: Default configuration. - */ -function CfWorkerJsonSchemaValidator_default() { - //#region CfWorkerJsonSchemaValidator_default - const validator = new CfWorkerJsonSchemaValidator(); - //#endregion CfWorkerJsonSchemaValidator_default - return validator; -} - -/** - * Example: Custom configuration with all errors reported. - */ -function CfWorkerJsonSchemaValidator_customConfig() { - //#region CfWorkerJsonSchemaValidator_customConfig - const validator = new CfWorkerJsonSchemaValidator({ - draft: '2020-12', - shortcircuit: false // Report all errors - }); - //#endregion CfWorkerJsonSchemaValidator_customConfig - return validator; -} diff --git a/packages/core/src/validators/cfWorkerProvider.ts b/packages/core/src/validators/cfWorkerProvider.ts index f2cce37e8b..d3cedbb4cf 100644 --- a/packages/core/src/validators/cfWorkerProvider.ts +++ b/packages/core/src/validators/cfWorkerProvider.ts @@ -14,23 +14,19 @@ import type { JsonSchemaType, JsonSchemaValidator, jsonSchemaValidator, JsonSche /** * JSON Schema draft version supported by @cfworker/json-schema + * + * @internal */ export type CfWorkerSchemaDraft = '4' | '7' | '2019-09' | '2020-12'; /** + * `@cfworker/json-schema`-backed validator used as the default in browser/workerd shims. * - * @example Use with default configuration (2020-12, shortcircuit) - * ```ts source="./cfWorkerProvider.examples.ts#CfWorkerJsonSchemaValidator_default" - * const validator = new CfWorkerJsonSchemaValidator(); - * ``` + * Not part of the public surface: end users get this automatically via the runtime shim and + * cannot import it from `@modelcontextprotocol/client` or `@modelcontextprotocol/server`. + * To override validation, implement the {@link jsonSchemaValidator} interface. * - * @example Use with custom configuration - * ```ts source="./cfWorkerProvider.examples.ts#CfWorkerJsonSchemaValidator_customConfig" - * const validator = new CfWorkerJsonSchemaValidator({ - * draft: '2020-12', - * shortcircuit: false // Report all errors - * }); - * ``` + * @internal */ export class CfWorkerJsonSchemaValidator implements jsonSchemaValidator { private shortcircuit: boolean; diff --git a/packages/core/src/validators/fromJsonSchema.examples.ts b/packages/core/src/validators/fromJsonSchema.examples.ts index 22ff4a9d60..af72b5b036 100644 --- a/packages/core/src/validators/fromJsonSchema.examples.ts +++ b/packages/core/src/validators/fromJsonSchema.examples.ts @@ -6,17 +6,23 @@ * @module */ -import { AjvJsonSchemaValidator } from './ajvProvider.js'; import { fromJsonSchema } from './fromJsonSchema.js'; +import type { jsonSchemaValidator } from './types.js'; + +declare const validator: jsonSchemaValidator; /** * Example: wrap a raw JSON Schema object for use with registerTool. + * + * Consumers importing `fromJsonSchema` from `@modelcontextprotocol/server` or + * `@modelcontextprotocol/client` omit the second argument — the runtime shim + * supplies the appropriate default validator. */ function fromJsonSchema_basicUsage() { //#region fromJsonSchema_basicUsage const inputSchema = fromJsonSchema<{ name: string }>( { type: 'object', properties: { name: { type: 'string' } }, required: ['name'] }, - new AjvJsonSchemaValidator() + validator ); // Use with server.registerTool('greet', { inputSchema }, handler) //#endregion fromJsonSchema_basicUsage diff --git a/packages/core/src/validators/fromJsonSchema.ts b/packages/core/src/validators/fromJsonSchema.ts index 73db24e8cc..4b7a6f11a3 100644 --- a/packages/core/src/validators/fromJsonSchema.ts +++ b/packages/core/src/validators/fromJsonSchema.ts @@ -19,7 +19,7 @@ import type { JsonSchemaType, jsonSchemaValidator } from './types.js'; * ```ts source="./fromJsonSchema.examples.ts#fromJsonSchema_basicUsage" * const inputSchema = fromJsonSchema<{ name: string }>( * { type: 'object', properties: { name: { type: 'string' } }, required: ['name'] }, - * new AjvJsonSchemaValidator() + * validator * ); * // Use with server.registerTool('greet', { inputSchema }, handler) * ``` diff --git a/packages/server/package.json b/packages/server/package.json index 20195e7101..98e58f498b 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -28,10 +28,6 @@ "types": "./dist/stdio.d.mts", "import": "./dist/stdio.mjs" }, - "./validators/cf-worker": { - "types": "./dist/validators/cfWorker.d.mts", - "import": "./dist/validators/cfWorker.mjs" - }, "./_shims": { "workerd": { "types": "./dist/shimsWorkerd.d.mts", @@ -54,9 +50,6 @@ "types": "./dist/index.d.mts", "typesVersions": { "*": { - "validators/cf-worker": [ - "dist/validators/cfWorker.d.mts" - ], "zod-schemas": [ "dist/zodSchemas.d.mts" ], @@ -86,6 +79,8 @@ }, "devDependencies": { "@cfworker/json-schema": "catalog:runtimeShared", + "ajv": "catalog:runtimeShared", + "ajv-formats": "catalog:runtimeShared", "@eslint/js": "catalog:devTools", "@modelcontextprotocol/core": "workspace:^", "@modelcontextprotocol/eslint-config": "workspace:^", diff --git a/packages/server/src/server/server.ts b/packages/server/src/server/server.ts index f6a34f02da..70e7cba486 100644 --- a/packages/server/src/server/server.ts +++ b/packages/server/src/server/server.ts @@ -83,7 +83,7 @@ export type ServerOptions = ProtocolOptions & { * The validator is used to validate user input returned from elicitation * requests against the requested schema. * - * @default {@linkcode DefaultJsonSchemaValidator} ({@linkcode index.AjvJsonSchemaValidator | AjvJsonSchemaValidator} on Node.js, `CfWorkerJsonSchemaValidator` on Cloudflare Workers) + * @default Runtime-selected validator (AJV-backed on Node.js, `@cfworker/json-schema`-backed on browser/workerd runtimes) */ jsonSchemaValidator?: jsonSchemaValidator; }; diff --git a/packages/server/src/shimsNode.ts b/packages/server/src/shimsNode.ts index 09283a40de..9354850b6e 100644 --- a/packages/server/src/shimsNode.ts +++ b/packages/server/src/shimsNode.ts @@ -3,5 +3,5 @@ * * This file is selected via package.json export conditions when running in Node.js. */ -export { AjvJsonSchemaValidator as DefaultJsonSchemaValidator } from '@modelcontextprotocol/core'; +export { AjvJsonSchemaValidator as DefaultJsonSchemaValidator } from '@modelcontextprotocol/core/validators/ajv'; export { default as process } from 'node:process'; diff --git a/packages/server/src/validators/cfWorker.ts b/packages/server/src/validators/cfWorker.ts deleted file mode 100644 index f804b768eb..0000000000 --- a/packages/server/src/validators/cfWorker.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Cloudflare Workers JSON Schema validator, available as a sub-path export. - * - * @example - * ```ts - * import { CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/server/validators/cf-worker'; - * ``` - */ -export type { CfWorkerSchemaDraft } from '@modelcontextprotocol/core/validators/cfWorker'; -export { CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/core/validators/cfWorker'; diff --git a/packages/server/test/server/barrelClean.test.ts b/packages/server/test/server/barrelClean.test.ts index e7f3e33c50..df4c940903 100644 --- a/packages/server/test/server/barrelClean.test.ts +++ b/packages/server/test/server/barrelClean.test.ts @@ -8,6 +8,7 @@ import { beforeAll, describe, expect, test } from 'vitest'; const pkgDir = join(dirname(fileURLToPath(import.meta.url)), '../..'); const distDir = join(pkgDir, 'dist'); const NODE_ONLY = /\b(child_process|cross-spawn|node:stream|node:child_process)\b/; +const VALIDATOR_BACKEND_IMPORT = /from\s+["'](?:ajv|ajv-formats|@cfworker\/json-schema)["']/; function chunkImportsOf(entryPath: string): string[] { const visited = new Set(); @@ -53,4 +54,17 @@ describe('@modelcontextprotocol/server root entry is browser-safe', () => { const stdio = readFileSync(join(distDir, 'stdio.mjs'), 'utf8'); expect(stdio).toMatch(/\bStdioServerTransport\b/); }); + + test('runtime shims vendor default validator backends instead of requiring consumers to install them', () => { + for (const shim of ['shimsNode.mjs', 'shimsWorkerd.mjs']) { + const entry = join(distDir, shim); + expect(readFileSync(entry, 'utf8')).not.toMatch(VALIDATOR_BACKEND_IMPORT); + + for (const chunk of chunkImportsOf(entry)) { + expect({ chunk, content: readFileSync(chunk, 'utf8') }).not.toEqual( + expect.objectContaining({ content: expect.stringMatching(VALIDATOR_BACKEND_IMPORT) }) + ); + } + } + }); }); diff --git a/packages/server/test/server/jsonSchemaValidatorOverride.test.ts b/packages/server/test/server/jsonSchemaValidatorOverride.test.ts new file mode 100644 index 0000000000..729111d9a8 --- /dev/null +++ b/packages/server/test/server/jsonSchemaValidatorOverride.test.ts @@ -0,0 +1,97 @@ +import type { JsonSchemaType, JsonSchemaValidatorResult, jsonSchemaValidator } from '@modelcontextprotocol/core'; +import { InMemoryTransport, LATEST_PROTOCOL_VERSION } from '@modelcontextprotocol/core'; +import { fromJsonSchema } from '../../src/fromJsonSchema.js'; +import { Server } from '../../src/server/server.js'; + +class RecordingValidator implements jsonSchemaValidator { + schemas: JsonSchemaType[] = []; + values: unknown[] = []; + + getValidator(schema: JsonSchemaType) { + this.schemas.push(schema); + return (value: unknown): JsonSchemaValidatorResult => { + this.values.push(value); + return { valid: true, data: value as T, errorMessage: undefined }; + }; + } +} + +describe('server JSON Schema validator overrides', () => { + test('Server constructor uses a custom validator for elicitation response validation', async () => { + const validator = new RecordingValidator(); + const server = new Server( + { name: 'test-server', version: '1.0.0' }, + { + capabilities: {}, + jsonSchemaValidator: validator + } + ); + + const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); + await server.connect(serverTransport); + await clientTransport.start(); + + const initializeResponse = new Promise(resolve => { + clientTransport.onmessage = message => resolve(message); + }); + await clientTransport.send({ + jsonrpc: '2.0', + id: 1, + method: 'initialize', + params: { + protocolVersion: LATEST_PROTOCOL_VERSION, + capabilities: { elicitation: { form: {} } }, + clientInfo: { name: 'test-client', version: '1.0.0' } + } + }); + await initializeResponse; + + clientTransport.onmessage = async message => { + if ('method' in message && 'id' in message && message.method === 'elicitation/create') { + await clientTransport.send({ + jsonrpc: '2.0', + id: message.id, + result: { action: 'accept', content: { name: 123 } } + }); + } + }; + + await expect( + server.elicitInput({ + message: 'What is your name?', + requestedSchema: { + type: 'object', + properties: { name: { type: 'string' } }, + required: ['name'] + } + }) + ).resolves.toEqual({ action: 'accept', content: { name: 123 } }); + + expect(validator.schemas).toEqual([ + { + type: 'object', + properties: { name: { type: 'string' } }, + required: ['name'] + } + ]); + expect(validator.values).toEqual([{ name: 123 }]); + + await server.close(); + await clientTransport.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 }]); + }); +}); diff --git a/packages/server/tsconfig.json b/packages/server/tsconfig.json index 7ab6d79a56..24da6e426d 100644 --- a/packages/server/tsconfig.json +++ b/packages/server/tsconfig.json @@ -7,6 +7,7 @@ "*": ["./*"], "@modelcontextprotocol/core": ["./node_modules/@modelcontextprotocol/core/src/index.ts"], "@modelcontextprotocol/core/public": ["./node_modules/@modelcontextprotocol/core/src/exports/public/index.ts"], + "@modelcontextprotocol/core/validators/ajv": ["./node_modules/@modelcontextprotocol/core/src/validators/ajvProvider.ts"], "@modelcontextprotocol/core/validators/cfWorker": [ "./node_modules/@modelcontextprotocol/core/src/validators/cfWorkerProvider.ts" ], diff --git a/packages/server/tsdown.config.ts b/packages/server/tsdown.config.ts index 25a65f4e16..995357d4d7 100644 --- a/packages/server/tsdown.config.ts +++ b/packages/server/tsdown.config.ts @@ -4,7 +4,7 @@ export default defineConfig({ failOnWarn: 'ci-only', // 1. Entry Points // Directly matches package.json include/exclude globs - entry: ['src/index.ts', 'src/stdio.ts', 'src/shimsNode.ts', 'src/shimsWorkerd.ts', 'src/validators/cfWorker.ts'], + entry: ['src/index.ts', 'src/stdio.ts', 'src/shimsNode.ts', 'src/shimsWorkerd.ts'], // 2. Output Configuration format: ['esm'], @@ -27,13 +27,19 @@ export default defineConfig({ paths: { '@modelcontextprotocol/core': ['../core/src/index.ts'], '@modelcontextprotocol/core/public': ['../core/src/exports/public/index.ts'], + '@modelcontextprotocol/core/validators/ajv': ['../core/src/validators/ajvProvider.ts'], '@modelcontextprotocol/core/validators/cfWorker': ['../core/src/validators/cfWorkerProvider.ts'] } } }, - // 5. Vendoring Strategy - Bundle the code for this specific package into the output, - // but treat all other dependencies as external (require/import). - noExternal: ['@modelcontextprotocol/core'], + // 5. Vendoring Strategy - Bundle this package's core implementation into the output, + // but treat most dependencies as external (require/import). + // + // The runtime `_shims` entries choose default JSON Schema validators: AJV on Node and + // @cfworker/json-schema on workerd/browser. Server users should not have to install a + // validator backend just to use the runtime default, so bundle the default backends into + // the shim chunks that select them. + noExternal: ['@modelcontextprotocol/core', 'ajv', 'ajv-formats', '@cfworker/json-schema'], // 6. External packages - keep self-reference imports external for runtime resolution external: ['@modelcontextprotocol/server/_shims'] diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4baf23d803..5d52cda35f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -544,6 +544,12 @@ importers: '@typescript/native-preview': specifier: catalog:devTools version: 7.0.0-dev.20260327.2 + ajv: + specifier: catalog:runtimeShared + version: 8.18.0 + ajv-formats: + specifier: catalog:runtimeShared + version: 3.0.1(ajv@8.18.0) eslint: specifier: catalog:devTools version: 9.39.4 @@ -574,12 +580,6 @@ importers: packages/core: dependencies: - ajv: - specifier: catalog:runtimeShared - version: 8.18.0 - ajv-formats: - specifier: catalog:runtimeShared - version: 3.0.1(ajv@8.18.0) json-schema-typed: specifier: catalog:runtimeShared version: 8.0.2 @@ -623,6 +623,12 @@ importers: '@typescript/native-preview': specifier: catalog:devTools version: 7.0.0-dev.20260327.2 + ajv: + specifier: catalog:runtimeShared + version: 8.18.0 + ajv-formats: + specifier: catalog:runtimeShared + version: 3.0.1(ajv@8.18.0) eslint: specifier: catalog:devTools version: 9.39.4 @@ -907,6 +913,12 @@ importers: '@typescript/native-preview': specifier: catalog:devTools version: 7.0.0-dev.20260327.2 + ajv: + specifier: catalog:runtimeShared + version: 8.18.0 + ajv-formats: + specifier: catalog:runtimeShared + version: 3.0.1(ajv@8.18.0) eslint: specifier: catalog:devTools version: 9.39.4 diff --git a/test/integration/test/server/elicitation.test.ts b/test/integration/test/server/elicitation.test.ts index 640e7b6378..84bb071f1b 100644 --- a/test/integration/test/server/elicitation.test.ts +++ b/test/integration/test/server/elicitation.test.ts @@ -9,7 +9,8 @@ import { Client } from '@modelcontextprotocol/client'; import type { ElicitRequestFormParams } from '@modelcontextprotocol/core'; -import { AjvJsonSchemaValidator, InMemoryTransport } from '@modelcontextprotocol/core'; +import { InMemoryTransport } from '@modelcontextprotocol/core'; +import { AjvJsonSchemaValidator } from '@modelcontextprotocol/core/validators/ajv'; import { CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/core/validators/cfWorker'; import { Server } from '@modelcontextprotocol/server'; diff --git a/test/integration/test/standardSchema.test.ts b/test/integration/test/standardSchema.test.ts index 67f16c5fa7..ffc41ce4d8 100644 --- a/test/integration/test/standardSchema.test.ts +++ b/test/integration/test/standardSchema.test.ts @@ -5,7 +5,7 @@ import { Client } from '@modelcontextprotocol/client'; import type { TextContent } from '@modelcontextprotocol/core'; -import { AjvJsonSchemaValidator, fromJsonSchema, InMemoryTransport } from '@modelcontextprotocol/core'; +import { InMemoryTransport } from '@modelcontextprotocol/core'; import { completable, fromJsonSchema as serverFromJsonSchema, McpServer } from '@modelcontextprotocol/server'; import { toStandardJsonSchema } from '@valibot/to-json-schema'; import { type } from 'arktype'; @@ -382,13 +382,12 @@ describe('Standard Schema Support', () => { }); describe('Raw JSON Schema via fromJsonSchema', () => { - const validator = new AjvJsonSchemaValidator(); - test('should register tool with raw JSON Schema input', async () => { - const inputSchema = fromJsonSchema<{ name: string }>( - { type: 'object', properties: { name: { type: 'string' } }, required: ['name'] }, - validator - ); + const inputSchema = serverFromJsonSchema<{ name: string }>({ + type: 'object', + properties: { name: { type: 'string' } }, + required: ['name'] + }); mcpServer.registerTool('greet', { inputSchema }, async ({ name }) => ({ content: [{ type: 'text', text: `Hello, ${name}!` }] @@ -407,11 +406,12 @@ describe('Standard Schema Support', () => { expect((result.content[0] as TextContent).text).toBe('Hello, World!'); }); - test('should reject invalid input via AJV validation', async () => { - const inputSchema = fromJsonSchema( - { type: 'object', properties: { count: { type: 'number' } }, required: ['count'] }, - validator - ); + test('should reject invalid input via default validation', async () => { + const inputSchema = serverFromJsonSchema({ + type: 'object', + properties: { count: { type: 'number' } }, + required: ['count'] + }); mcpServer.registerTool('double', { inputSchema }, async args => { const { count } = args as { count: number }; @@ -428,44 +428,6 @@ describe('Standard Schema Support', () => { }); }); - describe('fromJsonSchema with default validator (server wrapper)', () => { - test('should use runtime-appropriate default validator when none is provided', async () => { - const inputSchema = serverFromJsonSchema<{ name: string }>({ - type: 'object', - properties: { name: { type: 'string' } }, - required: ['name'] - }); - - mcpServer.registerTool('greet-default', { inputSchema }, async ({ name }) => ({ - content: [{ type: 'text', text: `Hello, ${name}!` }] - })); - - await connectClientAndServer(); - - const result = await client.request({ method: 'tools/call', params: { name: 'greet-default', arguments: { name: 'World' } } }); - expect((result.content[0] as TextContent).text).toBe('Hello, World!'); - }); - - test('should reject invalid input with default validator', async () => { - const inputSchema = serverFromJsonSchema({ type: 'object', properties: { count: { type: 'number' } }, required: ['count'] }); - - mcpServer.registerTool('double-default', { inputSchema }, async args => { - const { count } = args as { count: number }; - return { content: [{ type: 'text', text: `${count * 2}` }] }; - }); - - await connectClientAndServer(); - - const result = await client.request({ - method: 'tools/call', - params: { name: 'double-default', arguments: { count: 'not a number' } } - }); - expect(result.isError).toBe(true); - const errorText = (result.content[0] as TextContent).text; - expect(errorText).toContain('Input validation error'); - }); - }); - describe('Prompt completions with Zod completable', () => { // Note: completable() is currently Zod-specific // These tests verify that Zod schemas with completable still work diff --git a/test/integration/tsconfig.json b/test/integration/tsconfig.json index 4a2820da3f..26c4fb83e0 100644 --- a/test/integration/tsconfig.json +++ b/test/integration/tsconfig.json @@ -7,6 +7,7 @@ "*": ["./*"], "@modelcontextprotocol/core": ["./node_modules/@modelcontextprotocol/core/src/index.ts"], "@modelcontextprotocol/core/public": ["./node_modules/@modelcontextprotocol/core/src/exports/public/index.ts"], + "@modelcontextprotocol/core/validators/ajv": ["./node_modules/@modelcontextprotocol/core/src/validators/ajvProvider.ts"], "@modelcontextprotocol/core/validators/cfWorker": [ "./node_modules/@modelcontextprotocol/core/src/validators/cfWorkerProvider.ts" ],