Repository navigation
Description
Summary
In @modelcontextprotocol/server@2.0.0-alpha.2, @cfworker/json-schema is declared as an optional peer dependency, but it is imported unconditionally at module-init time. Any consumer that doesn't install @cfworker/json-schema gets ERR_MODULE_NOT_FOUND the moment they import { McpServer } — even if they only use Standard Schema (Zod) inputs and never touch the JSON-Schema validator.
Repro
mkdir repro && cd repro
npm init -y
npm pkg set type=module
npm install @modelcontextprotocol/server@2.0.0-alpha.2
echo 'import { McpServer } from "@modelcontextprotocol/server"; new McpServer({ name: "x", version: "0" });' > index.mjs
node index.mjsResult:
Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@cfworker/json-schema' imported from .../node_modules/@modelcontextprotocol/server/dist/src-IKPjmxu7.mjs
@cfworker/json-schema is not installed (it's peerDependenciesMeta.optional: true), and yet the failing import is at line 2 of the chunk:
// node_modules/@modelcontextprotocol/server/dist/src-IKPjmxu7.mjs:2
import { Validator } from "@cfworker/json-schema";Why this is a bug
The peer is declared optional precisely because Standard Schema users (Zod / ArkType / Valibot) shouldn't need it — only fromJsonSchema() / CfWorkerJsonSchemaValidator consumers do. But because the import is at the top of a bundled chunk that's pulled in by McpServer itself, the optionality is unenforceable: every consumer must install it.
This effectively makes @cfworker/json-schema a hard dependency. The peerDependenciesMeta declaration is misleading.
Suggestions
- Lazy import — load
@cfworker/json-schemaonly insideCfWorkerJsonSchemaValidator/fromJsonSchema()via dynamicimport(). The validator is only constructed at user request, not at SDK init. - Move to a sub-export — e.g.
@modelcontextprotocol/server/cfworker, importable only by users who opt in. - Promote to a hard
dependency— if lazy loading is rejected, drop the optional flag and make it a regular dep sonpm install @modelcontextprotocol/serveralways works.
Option 1 preserves the original intent of the optional peer.
Environment
- Node v25.8.1
@modelcontextprotocol/server@2.0.0-alpha.2- npm v11.x
Related
- Optional install of HTTP/SSE transport deps (express, hono) for stdio-only servers #1924 (the v1→v2 split that introduced the optional-peer pattern in the first place)
Activity
furkankoykiran commentedon May 16, 2026
#1897 (merged 2026-04-28) ended up taking your option 2: moved the cfworker validator to the
/validators/cf-workersubpath instead of doing the lazyimport(). The published2.0.0-alpha.2was cut before that landed, which is why the repro still trips on it.Built from current
mainto confirm, and the bundle no longer reaches@cfworker/json-schemafrom the main entry:Node consumers resolve
/_shimstoshimsNode.mjs(which pullsAjvJsonSchemaValidator), so cfworker only loads if you're in workerd/browser or import the/validators/cf-workersubpath directly.Should clear up on the next alpha.
mattzcarey commentedon Sep 11, 2026
Closed as already fixed in PR #1897:
CfWorkerJsonSchemaValidatormoved out of the core barrel into the opt-in./validators/cf-workersubpath, and the default Node shim resolves to the bundled Ajv validator, so nothing loads@cfworker/json-schemaat init. Guarded by the barrel test. Verified on the published 2.0.0 with@cfworker/json-schemaabsent.