Skip to content

Bundle automatic validator defaults in client/server shims#2088

Open
mattzcarey wants to merge 3 commits into
mainfrom
fix/server-cfworker-json-schema-shim
Open

Bundle automatic validator defaults in client/server shims#2088
mattzcarey wants to merge 3 commits into
mainfrom
fix/server-cfworker-json-schema-shim

Conversation

@mattzcarey
Copy link
Copy Markdown
Contributor

@mattzcarey mattzcarey commented May 14, 2026

Summary

  • Bundle both validator backends selected by client/server runtime shims: AJV for Node defaults, @cfworker/json-schema for browser/workerd defaults.
  • Remove explicit client/server validators/cf-worker subpath exports. Validator selection is automatic for normal users; advanced users can provide their own jsonSchemaValidator implementation.
  • Mark AjvJsonSchemaValidator and CfWorkerJsonSchemaValidator @internal; drop their type-only re-exports from core/public so the runtime classes and their TypeScript type handles are both unreachable from @modelcontextprotocol/client / @modelcontextprotocol/server.
  • Rewrite .examples.ts files and JSDoc snippets in packages/core/ so they no longer demonstrate end-user code (new AjvJsonSchemaValidator(), new CfWorkerJsonSchemaValidator()) that the new surface makes impossible. fromJsonSchema's example now uses a declared jsonSchemaValidator and notes that consumers importing from server/client omit the second arg.
  • Add regression tests that built client/server shims do not leave bare imports for ajv, ajv-formats, or @cfworker/json-schema for consumers to resolve.
  • Update migration docs to say users do not need to install or import validator packages; describe runtime selection by backend name (AJV / @cfworker/json-schema) rather than the now-internal class names.

Why

Client and server choose runtime defaults via conditional _shims:

  • Node shim: AJV-backed validator
  • Browser/workerd shim: @cfworker/json-schema-backed validator

Because client/server make those default choices, consumers should not need to know about or install validator implementation dependencies. Those backends are now bundled into the shim chunks that select them. The implementation classes are also no longer part of the public surface — neither as runtime constructors nor as TypeScript types — because exposing a handle to a constructor consumers can't reach is purely confusing.

Impact

  • Client/server users: no extra validator install or validator import is needed. Node gets the bundled AJV default through the Node shim; browser/workerd gets the bundled cfworker validator through the browser/workerd shim.
  • Advanced users: custom validation is still possible by passing jsonSchemaValidator: myCustomValidator with their own implementation of the jsonSchemaValidator interface.
  • Published packages: packed client/server tarballs do not depend on @modelcontextprotocol/core, ajv, ajv-formats, or @cfworker/json-schema; those implementations are bundled where client/server need them.
  • Public surface: explicit concrete validator subpaths are removed from client/server, the runtime classes are @internal, and core/public no longer re-exports them as types. The supported customisation path is the jsonSchemaValidator interface (still exported from core/public and re-exported by client/server).

Follow-up for codemod reviewers

If this validator packaging change ships, the v1-to-v2 codemod should remove redundant manual SDK validator imports/configuration for the built-in AJV and Cloudflare Workers validators. Those validators are no longer exported from client/server (not even as types), and normal migrations should rely on automatic runtime defaults instead.

Examples the codemod should simplify/remove when they refer to the SDK built-ins:

import { AjvJsonSchemaValidator } from '@modelcontextprotocol/server';
import { CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/server/validators/cf-worker';

new McpServer(info, {
  jsonSchemaValidator: new CfWorkerJsonSchemaValidator()
});

For custom user validators, the codemod should preserve the override:

new McpServer(info, { jsonSchemaValidator: myCustomValidator });

Reviewer note: this PR intentionally makes built-in validator selection automatic for client/server users; codemod output should not teach users to import SDK concrete validators for the default case.

@mattzcarey mattzcarey requested a review from a team as a code owner May 14, 2026 10:46
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 14, 2026

🦋 Changeset detected

Latest commit: 9739e09

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 7 packages
Name Type
@modelcontextprotocol/server Patch
@modelcontextprotocol/client Patch
@modelcontextprotocol/core Minor
@modelcontextprotocol/express Patch
@modelcontextprotocol/fastify Patch
@modelcontextprotocol/hono Patch
@modelcontextprotocol/node Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Comment thread packages/codemod/src/bin/batchTest.ts Fixed
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 14, 2026

Open in StackBlitz

@modelcontextprotocol/client

npm i https://pkg.pr.new/@modelcontextprotocol/client@2088

@modelcontextprotocol/server

npm i https://pkg.pr.new/@modelcontextprotocol/server@2088

@modelcontextprotocol/express

npm i https://pkg.pr.new/@modelcontextprotocol/express@2088

@modelcontextprotocol/fastify

npm i https://pkg.pr.new/@modelcontextprotocol/fastify@2088

@modelcontextprotocol/hono

npm i https://pkg.pr.new/@modelcontextprotocol/hono@2088

@modelcontextprotocol/node

npm i https://pkg.pr.new/@modelcontextprotocol/node@2088

commit: 9739e09

@mattzcarey mattzcarey force-pushed the fix/server-cfworker-json-schema-shim branch from 54c18d7 to ba08235 Compare May 14, 2026 11:02
@mattzcarey mattzcarey changed the title Bundle workerd JSON schema validator in server shim Make validator backends optional in core and bundle client/server defaults May 14, 2026
Comment thread packages/codemod/package.json Outdated
Comment thread packages/codemod/src/bin/batchTest.ts Outdated
Comment thread packages/codemod/src/bin/batchTest.ts Outdated
@mattzcarey mattzcarey force-pushed the fix/server-cfworker-json-schema-shim branch 2 times, most recently from c7601a2 to 5db154c Compare May 14, 2026 11:14
@mattzcarey mattzcarey changed the title Make validator backends optional in core and bundle client/server defaults Bundle automatic validator defaults in client/server shims May 14, 2026
@mattzcarey mattzcarey changed the base branch from main to feature/v2-codemode-draft May 14, 2026 11:16
@mattzcarey mattzcarey force-pushed the fix/server-cfworker-json-schema-shim branch from 5db154c to 78bd6c2 Compare May 14, 2026 11:18
@mattzcarey mattzcarey changed the base branch from feature/v2-codemode-draft to main May 14, 2026 11:18
Comment thread packages/core/src/exports/public/index.ts Outdated
@mattzcarey mattzcarey force-pushed the fix/server-cfworker-json-schema-shim branch 5 times, most recently from 9c5c541 to 57c3a0b Compare May 14, 2026 11:36
Comment thread packages/server/package.json
@mattzcarey mattzcarey force-pushed the fix/server-cfworker-json-schema-shim branch from 57c3a0b to a2b954b Compare May 14, 2026 11:46
Comment thread test/integration/test/standardSchema.test.ts
Comment thread packages/core/src/index.ts Outdated
Comment thread packages/server/test/server/barrelClean.test.ts
Comment thread .changeset/workerd-shim-vendors-cfworker.md Outdated
mattzcarey added a commit that referenced this pull request May 15, 2026
After PR #2088, end users can no longer import or construct
AjvJsonSchemaValidator / CfWorkerJsonSchemaValidator — client/server
bundle them via the runtime shim and expose only the jsonSchemaValidator
interface as the public extension point.

- Strip impossible `new AjvJsonSchemaValidator()` / `new CfWorkerJsonSchemaValidator()`
  snippets from JSDoc on the validation module, ajvProvider, cfWorkerProvider,
  and fromJsonSchema; mark the provider classes @internal.
- fromJsonSchema example now declares `validator: jsonSchemaValidator` and
  notes that consumers importing from server/client omit the second arg.
- Drop the type-only re-exports of AjvJsonSchemaValidator,
  CfWorkerJsonSchemaValidator, and CfWorkerSchemaDraft from core/public —
  with the runtime classes unreachable, a type-only handle is dead surface.
- Reword the migration docs to describe backends by name (AJV /
  @cfworker/json-schema) instead of the now-internal class names.
After PR #2088, end users can no longer import or construct
AjvJsonSchemaValidator / CfWorkerJsonSchemaValidator — client/server
bundle them via the runtime shim and expose only the jsonSchemaValidator
interface as the public extension point.

- Strip impossible `new AjvJsonSchemaValidator()` / `new CfWorkerJsonSchemaValidator()`
  snippets from JSDoc on the validation module, ajvProvider, cfWorkerProvider,
  and fromJsonSchema; mark the provider classes @internal.
- fromJsonSchema example now declares `validator: jsonSchemaValidator` and
  notes that consumers importing from server/client omit the second arg.
- Drop the type-only re-exports of AjvJsonSchemaValidator,
  CfWorkerJsonSchemaValidator, and CfWorkerSchemaDraft from core/public —
  with the runtime classes unreachable, a type-only handle is dead surface.
- Reword the migration docs to describe backends by name (AJV /
  @cfworker/json-schema) instead of the now-internal class names.
Comment thread packages/server/src/server/server.ts Outdated
Comment thread .changeset/workerd-shim-vendors-cfworker.md Outdated
@mattzcarey mattzcarey force-pushed the fix/server-cfworker-json-schema-shim branch from 51f42cd to e8006c0 Compare May 15, 2026 21:39
Comment thread packages/server/test/server/barrelClean.test.ts
- Update `.changeset/cfworker-out-of-barrel.md` to say the `./validators/cf-worker` subpath was removed (was: "reachable only via" that subpath, which is no longer true).
- Replace the impossible `import { fromJsonSchema, AjvJsonSchemaValidator } from '@modelcontextprotocol/core'` sample in `.changeset/support-standard-json-schema.md` with `fromJsonSchema` from `@modelcontextprotocol/server` and no explicit validator.
- Reword `@default` JSDoc on `ServerOptions`/`ClientOptions` `jsonSchemaValidator` from internal class names to backend names (AJV-backed / `@cfworker/json-schema`-backed) — these lines surface in published `.d.mts` IDE hover.
- Rework `.changeset/workerd-shim-vendors-cfworker.md` from a consumer vantage point: drop `core/public` references, state explicitly that the `./validators/cf-worker` subpath was removed and that the validator classes are no longer exported from client/server (not even as types).
- Delete the duplicate `describe('fromJsonSchema with default validator (server wrapper)')` block in `test/integration/test/standardSchema.test.ts`; its two tests are strict subsets of the preceding `Raw JSON Schema via fromJsonSchema` block (explicit-validator coverage already lives in the new `jsonSchemaValidatorOverride.test.ts` files).
- Document the `./validators/cf-worker` subpath removal in `docs/migration.md` and `docs/migration-SKILL.md` per repo policy for breaking changes.
Comment thread docs/migration-SKILL.md
- 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.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 The migration docs added here say consumers should not install @cfworker/json-schema, but test/integration/test/server/cloudflareWorkers.test.ts:46 — the only end-to-end test that runs the packed server tarball under real workerd — still pre-installs '@cfworker/json-schema': '^4.1.1' in the generated consumer package.json. That dep is now dead weight after this PR adds @cfworker/json-schema to noExternal, and it masks a future re-externalization regression because wrangler would resolve the bare import from the test's pre-installed copy instead of failing. Removing line 46 turns the test into a true regression guard and aligns it with the docs this PR adds.

Extended reasoning...

What the issue is

docs/migration-SKILL.md:524 (and the matching paragraph in docs/migration.md and .changeset/workerd-shim-vendors-cfworker.md) now instructs consumers: "Do not install ajv, ajv-formats, or @cfworker/json-schema; client/server bundle the runtime-selected defaults." But the SDK's own consumer simulation in test/integration/test/server/cloudflareWorkers.test.ts:44-47 still does exactly the thing the docs say not to do — it writes a generated package.json with an explicit dependency:

dependencies: {
    '@modelcontextprotocol/server': `file:./${tarballName}`,
    '@cfworker/json-schema': '^4.1.1'        // <-- now contradicts the docs
},

That file is not in this PR's diff (last touched in #1652), so this is a surviving instance of the pattern the PR replaces.

Why the explicit install used to be needed, and isn't anymore

At the PR base commit, packages/server/tsdown.config.ts had noExternal: ['@modelcontextprotocol/core'] only. The built dist/shimsWorkerd.mjs (and its chunks) therefore left a bare import { Validator } from '@cfworker/json-schema' for the consumer's bundler to resolve. wrangler's esbuild step would fail without it, so the test pre-installed the package.

This PR adds 'ajv', 'ajv-formats', '@cfworker/json-schema' to noExternal in both packages/server/tsdown.config.ts and packages/client/tsdown.config.ts, inlining the cfworker code into the workerd shim chunk. The explicit dep in the test consumer's package.json is now redundant.

Why it matters: the dead dep masks a regression

cloudflareWorkers.test.ts is the only test that exercises the packed tarball under a real workerd runtime via wrangler dev. The new barrelClean.test.ts tests added in this PR only string-match the dist/*.mjs output for bare from 'ajv'|'ajv-formats'|'@cfworker/json-schema' imports — useful but static. If a future tsdown/config change re-externalizes @cfworker/json-schema (e.g. someone removes it from noExternal while refactoring), the static test would catch it — but if that test is also touched, the workerd test should be the safety net. Today it isn't, because the consumer ships its own copy of @cfworker/json-schema, so wrangler's bundler would resolve the bare import from node_modules and the test would keep passing.

Step-by-step proof

  1. Test packs @modelcontextprotocol/server to a tarball and writes a consumer package.json listing both file:./<tarball> and '@cfworker/json-schema': '^4.1.1'.
  2. npm install puts both in the consumer's node_modules.
  3. The test's server.ts constructs new McpServer(...) — the Server constructor eagerly does this._jsonSchemaValidator = options?.jsonSchemaValidator ?? new DefaultJsonSchemaValidator(), so the workerd shim's static @cfworker/json-schema import path is exercised.
  4. wrangler dev runs esbuild over server.ts. After this PR, the import resolves to the bundled copy inside dist/shimsWorkerd.mjs — no bare import to satisfy.
  5. Hypothetical regression: @cfworker/json-schema is dropped from noExternal. dist/shimsWorkerd.mjs now has from '@cfworker/json-schema'. Without line 46, esbuild fails ("Could not resolve") and the test catches it. With line 46, esbuild resolves it from the test's pre-installed copy, the test passes, and the regression ships.

How to fix

Delete the '@cfworker/json-schema': '^4.1.1' line from the generated consumer package.json at test/integration/test/server/cloudflareWorkers.test.ts:46. This is a one-line change that turns the test into a genuine end-to-end regression guard for the bundling invariant and brings it into agreement with the docs this PR adds.

Filed as a nit: the test isn't directly modified by the PR, the static barrelClean.test.ts already guards the structural invariant, and removing the line is a hygiene improvement rather than a fix for broken behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants