Skip to content

v2: cf workers fix#1843

Open
KKonstantinov wants to merge 4 commits intomainfrom
fix/cf-workers-bundle
Open

v2: cf workers fix#1843
KKonstantinov wants to merge 4 commits intomainfrom
fix/cf-workers-bundle

Conversation

@KKonstantinov
Copy link
Copy Markdown
Contributor

fix: bundle @cfworker/json-schema inline and move to sub-path export

Motivation and Context

Consumers of @modelcontextprotocol/server and @modelcontextprotocol/client on Node.js hit a runtime error when importing from the main entry:

Error: Cannot find package '@cfworker/json-schema' imported from
  …/@modelcontextprotocol/server/dist/src-*.mjs

Root cause: tsdown bundles @modelcontextprotocol/core into a single shared chunk. When it encounters @cfworker/json-schema, it checks the package's package.json — because it's listed in peerDependencies, tsdown externalizes it (leaves a bare import { Validator } from "@cfworker/json-schema" in the output). Meanwhile, ajv is NOT in peerDependencies, so tsdown bundles it inline. This asymmetry means any import of the main entry eagerly triggers the cfworker import, which fails on Node.js where the optional peer dep isn't installed.

Why this wasn't an issue in v1.x:

  1. v1.x used tsc (per-file transpilation), not tsdown (bundling) — the cfworker import stayed in its own isolated file
  2. v1.x did NOT export CfWorkerJsonSchemaValidator from the main entry — it was only accessible via a dedicated sub-path (@modelcontextprotocol/sdk/validation/cfworker)

The v2 monorepo restructuring inadvertently collapsed both of these safeguards.

How Has This Been Tested?

  • All 1,425 tests pass across 49 test files (pnpm test:all)
  • Verified the built dist shared chunk no longer contains any bare import from "@cfworker/json-schema" — the code is now bundled inline (same as ajv)
  • Verified the new sub-path exports resolve correctly in the build output

Breaking Changes

CfWorkerJsonSchemaValidator is no longer exported from the main entry of @modelcontextprotocol/server or @modelcontextprotocol/client. It is now available via a dedicated sub-path:

// Before (broken on Node.js, removed):
import { CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/server';

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

Most users are unaffected — the shims system (shimsWorkerd.mjs) still automatically selects CfWorkerJsonSchemaValidator as the default validator on CF Workers/browser runtimes without any explicit import. Only users who were explicitly importing the class need to update.

The CfWorkerSchemaDraft type remains available from the main entry (type-only exports have no runtime impact).

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

Options considered

  1. Remove from peerDependencies (bundle inline like ajv) — Minimal change, consistent with ajv precedent, no API break. Increases shared chunk by ~38KB (dead code on Node.js). Doesn't address the v1.x regression where cfworker code was isolated behind a sub-path.

  2. Dynamic import with top-level await + try/catch — No bundle size increase, but top-level await in the shared chunk makes it an async module, which can cause issues with downstream bundlers, test runners, and runtimes that don't fully support async module graphs.

  3. Separate sub-path export (restore v1.x pattern) — Matches v1.x design. Breaking change, but acceptable in alpha. Insufficient alone due to rolldown's shared chunk behavior (see below).

  4. Both validators as separate sub-path exports — Move both AjvJsonSchemaValidator and CfWorkerJsonSchemaValidator to sub-paths. More churn for no functional benefit since ajv is already working fine.

  5. Move to dependencies (not peerDependencies) — Forces all consumers to install @cfworker/json-schema even when they don't need it.

Chosen: Option 1 + Option 3 combined (see rationale below).

Why two changes are needed, not one

Simply moving CfWorkerJsonSchemaValidator to a sub-path export is insufficient on its own. Rolldown merges all of @modelcontextprotocol/core's bundled code into a single shared chunk (because all entry points — index.ts, shimsNode.ts, shimsWorkerd.ts — import from core via noExternal). Even after removing the re-export from the main entry, the shared chunk still contains the cfworker code (needed by shimsWorkerd.ts), and the bare external import remains at the top of that chunk — loaded by all entry points including index.mjs.

The fix therefore combines two changes:

  1. Remove @cfworker/json-schema from peerDependencies — tsdown then bundles it inline (same treatment ajv already gets). No bare external import → no runtime error. This increases the shared chunk size by ~38KB (the inlined cfworker code), which is dead code on Node.js — parsed but never executed.
  2. Move CfWorkerJsonSchemaValidator to a sub-path export (./validators/cf-worker) — removes it from the main entry's public API, restoring the v1.x pattern where CF Workers-specific code is explicitly opt-in.

Files changed

File Change
packages/core/src/exports/public/index.ts Removed CfWorkerJsonSchemaValidator value export (kept CfWorkerSchemaDraft type)
packages/server/package.json Removed peerDependencies/peerDependenciesMeta for cfworker; added ./validators/cf-worker export
packages/client/package.json Same
packages/server/tsdown.config.ts Added src/validators/cfWorker.ts entry point
packages/client/tsdown.config.ts Added src/validators/cfWorker.ts entry point
packages/server/src/validators/cfWorker.ts New sub-path entry re-exporting from core
packages/client/src/validators/cfWorker.ts New sub-path entry re-exporting from core

@KKonstantinov KKonstantinov requested a review from a team as a code owner April 1, 2026 19:12
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 1, 2026

⚠️ No Changeset found

Latest commit: 026488f

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Apr 1, 2026

Open in StackBlitz

@modelcontextprotocol/client

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

@modelcontextprotocol/server

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

@modelcontextprotocol/express

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

@modelcontextprotocol/fastify

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

@modelcontextprotocol/hono

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

@modelcontextprotocol/node

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

commit: 026488f

Comment on lines 860 to +870
```

You can still explicitly override the validator if needed. The validators are available via the `_shims` export:
You can still explicitly override the validator if needed:

```typescript
// Runtime-aware default (auto-selects AjvJsonSchemaValidator or CfWorkerJsonSchemaValidator)
import { DefaultJsonSchemaValidator } from '@modelcontextprotocol/server/_shims';
// or
import { AjvJsonSchemaValidator, CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/server';

// Specific validators
import { AjvJsonSchemaValidator } from '@modelcontextprotocol/server';
import { CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/server/validators/cf-worker';
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 (both docs/migration.md and docs/migration-SKILL.md) document explicit validator import paths only for @modelcontextprotocol/server, omitting the equivalent paths for @modelcontextprotocol/client. A client-only user who wants to explicitly override CfWorkerJsonSchemaValidator needs @modelcontextprotocol/client/validators/cf-worker (not the server equivalent), which this PR adds but the docs do not mention. Adding client package examples to Section 14 of both docs would make the guide complete.

Extended reasoning...

What the bug is and how it manifests

Section 14 of both docs/migration.md (lines 860–870) and docs/migration-SKILL.md (lines 484–486) was updated by this PR to document explicit validator import paths. However, every example references only the @modelcontextprotocol/server family:

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

The client package has identical sub-path exports added by this same PR: packages/client/package.json now exports ./validators/cf-worker and ./_shims, and packages/client/src/validators/cfWorker.ts exists as the corresponding entry file. Neither doc mentions @modelcontextprotocol/client/validators/cf-worker or @modelcontextprotocol/client/_shims.

The specific code path that triggers it

A developer using only @modelcontextprotocol/client (no server package) reads the migration guide to understand how to explicitly supply a jsonSchemaValidator to their Client instance. The ClientOptions.jsonSchemaValidator field is real and supported. They follow the import examples in Section 14, copy the server paths, and their bundler or runtime fails to resolve @modelcontextprotocol/server/validators/cf-worker because the server package is not installed.

Why existing code does not prevent it

There is no mechanism to keep migration-doc examples in sync with package.json exports. The PR author correctly added both server and client sub-path exports in code, but only updated the migration docs with server examples. The section uses McpServer in its primary example, making the server-centric framing natural, but the final explicit-override code block is generic enough to apply to both packages.

What the impact would be

This is a documentation-only gap with no runtime impact. The auto-selection behavior works correctly for both packages without any explicit import. The affected scenario is the niche explicit-override case (e.g., a CF Workers client-only user who needs to force CfWorkerJsonSchemaValidator). That user would see only server paths in the migration guide and either install an unneeded package or fail to find the correct import path. The migration-SKILL.md mechanical table is particularly affected: an LLM performing migration for a client-only project could incorrectly add @modelcontextprotocol/server as a dependency solely to import a validator class.

How to fix it

Extend the explicit-override example in both docs to mention both packages, e.g. add a note: "Replace server with client if using the client package only," or show both paths:

// For @modelcontextprotocol/server users:
import { CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/server/validators/cf-worker';
// For @modelcontextprotocol/client users:
import { CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/client/validators/cf-worker';

Step-by-step proof

  1. Developer has only @modelcontextprotocol/client installed (CF Workers client-only project).
  2. They need to explicitly pass CfWorkerJsonSchemaValidator to new Client({...}, { jsonSchemaValidator: ... }).
  3. They consult docs/migration.md Section 14 for the correct import path.
  4. The doc shows only import { CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/server/validators/cf-worker'.
  5. They either run npm install @modelcontextprotocol/server unnecessarily, or copy the import and get a module-not-found error at runtime.
  6. The correct path, @modelcontextprotocol/client/validators/cf-worker, is never surfaced by the docs.

* against their declared output schemas.
*
* @default {@linkcode DefaultJsonSchemaValidator} ({@linkcode index.AjvJsonSchemaValidator | AjvJsonSchemaValidator} on Node.js, {@linkcode index.CfWorkerJsonSchemaValidator | CfWorkerJsonSchemaValidator} on Cloudflare Workers)
* @default {@linkcode DefaultJsonSchemaValidator} ({@linkcode index.AjvJsonSchemaValidator | AjvJsonSchemaValidator} on Node.js, `CfWorkerJsonSchemaValidator` on Cloudflare Workers)
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 @default JSDoc for jsonSchemaValidator in ClientOptions (client.ts:165) and ServerOptions (server.ts:87) mentions CfWorkerJsonSchemaValidator as the Cloudflare Workers default using a plain backtick with no import path, leaving developers with no way to find where to import it. Adding (import from @modelcontextprotocol/server/validators/cf-worker or @modelcontextprotocol/client/validators/cf-worker) to those lines would match the guidance already present in ajvProvider.ts.

Extended reasoning...

What the bug is and how it manifests

This PR correctly removed the broken {@linkcode index.CfWorkerJsonSchemaValidator} reference from the @default JSDoc in both packages/client/src/client/client.ts:165 and packages/server/src/server/server.ts:87, since CfWorkerJsonSchemaValidator is no longer exported from the main entry after this PR. However, the replacement is plain backtick without any import path hint, leaving developers unable to locate the class.

The specific code path that triggers it

A developer hovering over jsonSchemaValidator in their IDE will see: @default DefaultJsonSchemaValidator (AjvJsonSchemaValidator on Node.js, CfWorkerJsonSchemaValidator on Cloudflare Workers). There is no link, no import path, and no package name. Since CfWorkerJsonSchemaValidator is no longer exported from the main entry, searching the package exports yields nothing.

Why existing code does not prevent it

The @see comment added to AjvJsonSchemaValidator in ajvProvider.ts line 36 does include both import paths. However, that hint is only visible when hovering over AjvJsonSchemaValidator itself — not when reading the jsonSchemaValidator field documentation in ClientOptions/ServerOptions. Those are completely separate hover contexts in an IDE.

What the impact would be

This is purely a documentation quality issue with no runtime impact. The auto-selection behavior works correctly without any explicit import. Developers who want to explicitly pass CfWorkerJsonSchemaValidator see the class name but have no hint about where to find it after this PR removes it from the main entry.

How to fix it

Update both @default lines to append the import hint, for example:

@default DefaultJsonSchemaValidator (AjvJsonSchemaValidator on Node.js, CfWorkerJsonSchemaValidator on Cloudflare Workers — import from @modelcontextprotocol/server/validators/cf-worker or @modelcontextprotocol/client/validators/cf-worker)

Step-by-step proof

  1. Developer writes new Client({ name: 'my-client', version: '1.0.0' }, { jsonSchemaValidator: ... }) on a CF Workers project.
  2. They hover over jsonSchemaValidator in their IDE to understand what to pass.
  3. The tooltip shows CfWorkerJsonSchemaValidator on Cloudflare Workers with no import path.
  4. They search for CfWorkerJsonSchemaValidator in @modelcontextprotocol/client main entry exports — it is not there (removed by this PR).
  5. No import path, no @see tag, no link — the developer must search GitHub or docs to find @modelcontextprotocol/client/validators/cf-worker.
  6. The equivalent @see note on AjvJsonSchemaValidator in ajvProvider.ts does provide both paths, confirming the information exists and is just missing from the most visible location.

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.

1 participant